Rm unnecessary files, rename sellContract to sell etc, save trades to leveldb

This commit is contained in:
Jay Graber 2017-07-24 13:23:40 -07:00
parent 0df6fcd04c
commit 572de939b4
15 changed files with 127 additions and 669 deletions

View File

@ -1,138 +0,0 @@
#!/usr/bin/env python3
# Based on spend-p2sh-txout.py from python-bitcoinlib.
# Copyright (C) 2017 The Zcash developers
import sys
if sys.version_info.major < 3:
sys.stderr.write('Sorry, Python 3.x required by this example.\n')
sys.exit(1)
import bitcoin
import bitcoin.rpc
from bitcoin import SelectParams
from bitcoin.core import b2x, lx, b2lx, COIN, COutPoint, CMutableTxOut, CMutableTxIn, CMutableTransaction, Hash160
from bitcoin.core.script import CScript, OP_DUP, OP_IF, OP_ELSE, OP_ENDIF, OP_HASH160, OP_EQUALVERIFY, OP_CHECKSIG, SignatureHash, SIGHASH_ALL, OP_FALSE, OP_DROP, OP_CHECKLOCKTIMEVERIFY, OP_SHA256, OP_TRUE
from bitcoin.core.scripteval import VerifyScript, SCRIPT_VERIFY_P2SH
from bitcoin.wallet import CBitcoinAddress, CBitcoinSecret
import hashlib
# SelectParams('testnet')
SelectParams('regtest')
bitcoind = bitcoin.rpc.Proxy()
FEE = 0.001*COIN
# ========================= BITCOIN ADDRESSES =========================
alice_address = input("Enter alice bitcoin address: (type 'enter' for demo)")
bob_address = input("Enter bob bitcoin address: (type 'enter' for demo)")
# alicepubkey = CBitcoinAddress('mshp4msfzc73ebg4VzwS6nAXj9t6KqX1wd')
# bobpubkey = CBitcoinAddress('myRh2T5Kg7QJfGLeRzriT5zs9aoek5Jbha')
# bitcoind.getnewaddress() returns CBitcoinAddress
bobpubkey = bitcoind.getnewaddress()
alicepubkey = bitcoind.getnewaddress()
print("alicepubkey", alicepubkey)
print("bobpubkey", bobpubkey)
# privkey of the bob, used to sign the redeemTx
bob_seckey = bitcoind.dumpprivkey(bobpubkey)
# privkey of alice, used to refund tx in case of timeout
alice_seckey = bitcoind.dumpprivkey(alicepubkey)
# ========================= HASHLOCK SECRET PREIMAGE =========================
secret = input("Alice: Enter secret to lock funds: (type 'enter' for demo)")
# preimage = secret.encode('UTF-8')
preimage = b'preimage'
h = hashlib.sha256(preimage).digest()
# ========================= LOCKTIME SCRIPT CREATION =========================
lockduration = 10
blocknum = bitcoind.getblockcount()
redeemblocknum = blocknum + lockduration
# Create a htlc redeemScript. Similar to a scriptPubKey the redeemScript must be
# satisfied for the funds to be spent.
btc_redeemScript = CScript([OP_IF, OP_SHA256, h, OP_EQUALVERIFY,OP_DUP, OP_HASH160,
alicepubkey, OP_ELSE, redeemblocknum, OP_CHECKLOCKTIMEVERIFY, OP_DROP, OP_DUP, OP_HASH160,
bobpubkey, OP_ENDIF,OP_EQUALVERIFY, OP_CHECKSIG])
print("Redeem script:", b2x(btc_redeemScript))
# ========================= TX1: CREATE BITCOIN P2SH FROM SCRIPT =========================
txin_scriptPubKey = btc_redeemScript.to_p2sh_scriptPubKey()
# Convert the P2SH scriptPubKey to a base58 Bitcoin address
txin_p2sh_address = CBitcoinAddress.from_scriptPubKey(txin_scriptPubKey)
p2sh = str(txin_p2sh_address)
print('Bob -- Assuming Alice has created other tx on Zcash blockchain, send funds to this p2sh address:', p2sh)
## TODO: IMPORT ZCASH XCAT FUNCTIONS
# ========================= FUND BITCOIN P2SH =========================
response = input("Bob -- Type 'enter' to allow zbxcat to fund the Bitcoin p2sh on your behalf:")
send_amount = 1.0*COIN
fund_tx = bitcoind.sendtoaddress(txin_p2sh_address, send_amount)
print('Alice -- Bitcoin fund tx was sent, please wait for confirmation. Txid:', b2x(lx(b2x(fund_tx))))
# ========================= PART 2: BITCOIN P2SH FUNDED, REDEEM OR REFUND =========================
# Check that fund_tx is on the blockchain to the right address, then notify receiver
# Importing address so we can watch it
bitcoind.importaddress(p2sh)
# Get details of funding transaction
fund_txinfo = bitcoind.gettransaction(fund_tx)
fund_details = fund_txinfo['details'] # "fund_details" is an array, for now we can assume it only has one destination address
outputAddress = fund_details[0]['address']
fund_vout = fund_details[0]['vout']
if (outputAddress != p2sh):
print('Fund tx sent to wrong address! p2sh was {0}, funding tx was sent to {1}'.format(p2sh, outputAddress))
quit()
# Check amount by inspecting imported address
output_amount = bitcoind.getreceivedbyaddress(outputAddress, 0)
if (output_amount < send_amount):
print('Fund tx too small! Amount sent was {0}, amount expected was {1}'.format(output_amount, send_amount))
quit()
print("P2SH {0} successfully funded with {1}".format(p2sh, send_amount))
print('Alice -- the fund tx has been confirmed, now you can redeem your Bitcoin with the secret!')
# ========================= CHECKLOCKIME FOR BITCOIN TX1 =========================
# Mock the timeout period passing for tx1 (comment this out to proceed to redeemtx)
# bitcoind.generate(20)
# ========================= BITCOIN REFUND CONDITION =========================
# AFTER 24 HRS (by blocknum): If locktime for first tx has passed, tx1 is refunded to alice
if(bitcoind.getblockcount() >= redeemblocknum):
print("Bob -- Alice did not redeem within the timeout period, so refunding your bitcoin....... ")
txin = CMutableTxIn(COutPoint(fund_tx, fund_vout))
# The default nSequence of FFFFFFFF won't let you redeem when there's a CHECKTIMELOCKVERIFY
txin.nSequence = 0
txout = CMutableTxOut(send_amount - FEE, bobpubkey.to_scriptPubKey())
# Create the unsigned raw transaction.
tx = CMutableTransaction([txin], [txout])
# nLockTime needs to be at least as large as parameter of CHECKLOCKTIMEVERIFY for script to verify
tx.nLockTime = redeemblocknum
# Calculate the signature hash for that transaction. Note how the script we use
# is the redeemScript, not the scriptPubKey. EvalScript() will be evaluating the redeemScript
sighash = SignatureHash(btc_redeemScript, tx, 0, SIGHASH_ALL)
sig = bob_seckey.sign(sighash) + bytes([SIGHASH_ALL])
txin.scriptSig = CScript([sig, bob_seckey.pub, OP_FALSE, btc_redeemScript])
print("Time lock has passed, Bob redeeming his own tx:")
print("Refund tx hex:", b2x(tx.serialize()))
VerifyScript(txin.scriptSig, txin_scriptPubKey, tx, 0, (SCRIPT_VERIFY_P2SH,))
txid = bitcoind.sendrawtransaction(tx)
print("Txid of submitted refund tx: ", b2x(lx(b2x(txid))))
quit()
# ========================= BITCOIN REDEEM CONDITION =========================
# BEFORE 24 HRS (by blocknum): Alice redeems bitcoin tx bob funded
print("Alice -- Redeeming tx.....")
txin = CMutableTxIn(COutPoint(fund_tx, fund_vout))
txout = CMutableTxOut(send_amount - FEE, alicepubkey.to_scriptPubKey())
# Create the unsigned raw transaction.
tx = CMutableTransaction([txin], [txout])
# nLockTime needs to be at least as large as parameter of CHECKLOCKTIMEVERIFY for script to verify
tx.nLockTime = redeemblocknum
sighash = SignatureHash(btc_redeemScript, tx, 0, SIGHASH_ALL)
sig = alice_seckey.sign(sighash) + bytes([SIGHASH_ALL])
txin.scriptSig = CScript([sig, alice_seckey.pub, preimage, OP_TRUE, btc_redeemScript])
print("Redeem tx hex:", b2x(tx.serialize()))
VerifyScript(txin.scriptSig, txin_scriptPubKey, tx, 0, (SCRIPT_VERIFY_P2SH,))
txid = bitcoind.sendrawtransaction(tx)
print("Txid of submitted redeem tx: ", b2x(lx(b2x(txid))))

View File

@ -1,100 +0,0 @@
#!/usr/bin/env python3
# Based on spend-p2sh-txout.py from python-bitcoinlib.
# Copyright (C) 2017 The Zcash developers
import sys
if sys.version_info.major < 3:
sys.stderr.write('Sorry, Python 3.x required by this example.\n')
sys.exit(1)
import bitcoin
import bitcoin.rpc
from bitcoin import SelectParams
from bitcoin.core import b2x, lx, b2lx, COIN, COutPoint, CMutableTxOut, CMutableTxIn, CMutableTransaction, Hash160
from bitcoin.core.script import CScript, OP_DUP, OP_IF, OP_ELSE, OP_ENDIF, OP_HASH160, OP_EQUALVERIFY, OP_CHECKSIG, SignatureHash, SIGHASH_ALL
from bitcoin.core.script import OP_DROP, OP_CHECKLOCKTIMEVERIFY, OP_SHA256, OP_TRUE
from bitcoin.core.scripteval import VerifyScript, SCRIPT_VERIFY_P2SH
from bitcoin.wallet import CBitcoinAddress, CBitcoinSecret
import hashlib
# SelectParams('testnet')
# To get transactions not in your wallet, must set -txindex=1
SelectParams('regtest')
proxy = bitcoin.rpc.Proxy()
# The parameters needed for the htlc - hash preimage, sender/seller address, recipient/buyer address, num of blocks for timeout
preimage = b'preimage'
h = hashlib.sha256(preimage).digest()
# proxy.getnewaddress() returns CBitcoinAddress
recipientpubkey = proxy.getnewaddress()
senderpubkey = proxy.getnewaddress()
# privkey of the recipient, used to sign the redeemTx
seckey = proxy.dumpprivkey(recipientpubkey)
lockduration = 10
blocknum = proxy.getblockcount()
redeemblocknum = blocknum + lockduration
# Create a htlc redeemScript. Similar to a scriptPubKey the redeemScript must be
# satisfied for the funds to be spent.
txin_redeemScript = CScript([OP_IF, OP_SHA256, h, OP_EQUALVERIFY,OP_DUP, OP_HASH160,
recipientpubkey, OP_ELSE, redeemblocknum, OP_CHECKLOCKTIMEVERIFY, OP_DROP, OP_DUP, OP_HASH160,
senderpubkey, OP_ENDIF,OP_EQUALVERIFY, OP_CHECKSIG])
print("redeem script:", b2x(txin_redeemScript))
# Create P2SH scriptPubKey from redeemScript.
txin_scriptPubKey = txin_redeemScript.to_p2sh_scriptPubKey()
print("p2sh_scriptPubKey", b2x(txin_scriptPubKey))
# Convert the P2SH scriptPubKey to a base58 Bitcoin address
txin_p2sh_address = CBitcoinAddress.from_scriptPubKey(txin_scriptPubKey)
p2sh = str(txin_p2sh_address)
print('Pay to:', p2sh)
# AUTOMATE Send funds to p2sh
amount = 1.0*COIN
# sendtoaddress return the id of the created tx
fund_tx = proxy.sendtoaddress(txin_p2sh_address, amount)
print('fund tx sent. It\'s id is:', b2x(lx(b2x(fund_tx))))
print('Now redeeming.........')
# AUTOMATE getting vout of funding tx
txinfo = proxy.gettransaction(fund_tx)
details = txinfo['details'][0]
vout = details['vout']
# Create the txin structure. scriptSig defaults to being empty.
# The input is the p2sh funding transaction txid, vout is its index
txin = CMutableTxIn(COutPoint(fund_tx, vout))
# Create the txout. Pays out to recipient, so uses recipient's pubkey
# Withdraw full amount minus fee
default_fee = 0.001*COIN
txout = CMutableTxOut(amount - default_fee, recipientpubkey.to_scriptPubKey())
# Create the unsigned raw transaction.
tx = CMutableTransaction([txin], [txout])
# Calculate the signature hash for that transaction. Note how the script we use
# is the redeemScript, not the scriptPubKey. EvalScript() will be evaluating the redeemScript
sighash = SignatureHash(txin_redeemScript, tx, 0, SIGHASH_ALL)
# Now sign it. We have to append the type of signature we want to the end, in
# this case the usual SIGHASH_ALL.
sig = seckey.sign(sighash) + bytes([SIGHASH_ALL])
# Set the scriptSig of our transaction input appropriately.
txin.scriptSig = CScript([ sig, seckey.pub, preimage, OP_TRUE, txin_redeemScript])
print("Redeem tx hex:", b2x(tx.serialize()))
# Verify the signature worked.
VerifyScript(txin.scriptSig, txin_scriptPubKey, tx, 0, (SCRIPT_VERIFY_P2SH,))
print("Now sending redeem transaction.......")
txid = proxy.sendrawtransaction(tx)
print("Txid of submitted redeem tx: ", b2x(lx(b2x(txid))))

39
cli.py
View File

@ -23,47 +23,46 @@ def parse_addrs(address):
return status
def checkSellActions(trade):
if trade.buyContract.get_status() == 'funded':
if trade.buy.get_status() == 'funded':
seller_redeem(trade)
elif trade.buyContract.get_status() == 'empty':
print("Buyer has not yet funded the contract where you offered to buy {0}, please wait for them to complete their part.".format(trade.buyContract.currency))
elif trade.buy.get_status() == 'empty':
print("Buyer has not yet funded the contract where you offered to buy {0}, please wait for them to complete their part.".format(trade.buy.currency))
def checkBuyActions(trade):
if trade.sellContract.get_status() == 'funded' and trade.buyContract.get_status() != 'redeemed':
if trade.sell.get_status() == 'funded' and trade.buy.get_status() != 'redeemed':
print("One active trade available, fulfilling buyer contract...")
buyer_fulfill(trade)
elif trade.buyContract.get_status() == 'redeemed':
elif trade.buy.get_status() == 'redeemed':
buyer_redeem(trade)
print("XCAT trade complete!")
def instantiateTrade(trade):
return Trade(buyContract=Contract(trade['buy']), sellContract=Contract(trade['sell']))
return Trade(buy=Contract(trade['buy']), sell=Contract(trade['sell']))
if __name__ == '__main__':
parser = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter,
description=textwrap.dedent('''\
== Contracts ==
== Trades ==
newtrade - create a new trade
checktrades - check for actions to be taken on existing trades
importcontract "hexstr" - import an existing trade from a hex string
exportcontract - export the data of an existing xcat trade as a hex string
importtrade "hexstr" - import an existing trade from a hex string
exporttrade - export the data of an existing xcat trade as a hex string
'''))
parser.add_argument("command", action="store", help="list commands")
parser.add_argument("argument", action="store", nargs="*", help="add an argument")
# parser.add_argument("-importcontract", type=str, action="store", help="import an existing trade from a hex string.")
# parser.add_argument("-newtrade", action="store", help="create a new trade.")
# parser.add_argument("-checktrades", action="store", help="check status of existing trades")
# parser.add_argument("--daemon", "-d", action="store_true", help="Run as daemon process")
args = parser.parse_args()
# how to hold state of role
command = args.command
if command == 'importcontract':
if command == 'importtrade':
hexstr = args.argument[0]
trade = x2s(hexstr)
trade = instantiateTrade(ast.literal_eval(trade))
print(trade)
elif command == 'exportcontract':
db.create(trade)
# print(trade.toJ)
elif command == 'exporttrade':
trade = get_trade()
hexstr = s2x(str(trade))
print(trade)
@ -71,7 +70,7 @@ if __name__ == '__main__':
elif command == 'checktrades':
trade = get_trade()
trade = instantiateTrade(trade)
if find_role(trade.sellContract) == 'initiator':
if find_role(trade.sell) == 'initiator':
role = 'seller'
checkSellActions(trade)
else:
@ -80,6 +79,10 @@ if __name__ == '__main__':
elif command == 'newtrade':
erase_trade()
role = 'seller'
htlcTrade = Trade()
print("Creating new XCAT trade...")
seller_initiate(htlcTrade)
trade = seller_initiate(Trade())
# Save it to leveldb
db.create(trade)
elif command == "daemon":
#TODO: implement
print("Run as daemon process")

View File

@ -2,22 +2,42 @@ import plyvel
from utils import *
import binascii
import sys
import json
db = plyvel.DB('/tmp/testdb', create_if_missing=True)
trade = get_trade()
## txid we retrieve by
if trade and 'sell' in trade:
txid = trade['sell']['fund_tx']
if 'fund_tx' in trade['sell']:
txid = trade['sell']['fund_tx']
def create(hexstr):
trade = hex2dict(hexstr)
txid = trade['sell']['fund_tx']
# Takes object, saves json as bytes
def create(trade):
trade = trade.toJSON()
jt = json.loads(trade)
txid = jt['sell']['fund_tx']
# Save trade by initiating txid
db.put(b(txid), b(hexstr))
db.put(b(txid), b(trade))
def get(txid):
return db.get(b(txid))
# db.delete(b'hello')
# hexstr = get(txid)
# print(x2s(hexstr))
#
def print_entries():
it = db.iterator()
with db.iterator() as it:
for k, v in it:
j = json.loads(x2s(b2x(v)))
print('val: ', j)
print('sell: ', j['sell'])
# print_entries()
txid = '1171aeda64eff388b3568fa4675d0ca78852911109bbe42e0ef11ad6bf1b159e'
entry = db.get(b(txid))
print(entry)
# print(it.next())

View File

@ -1,106 +0,0 @@
#!/usr/bin/env python3
# Based on spend-p2sh-txout.py from python-bitcoinlib.
# Copyright (C) 2017 The Zcash developers
import sys
if sys.version_info.major < 3:
sys.stderr.write('Sorry, Python 3.x required by this example.\n')
sys.exit(1)
import bitcoin
import bitcoin.rpc
from bitcoin import SelectParams
from bitcoin.core import b2x, lx, COIN, COutPoint, CMutableTxOut, CMutableTxIn, CMutableTransaction, Hash160
from bitcoin.core.script import CScript, OP_DUP, OP_IF, OP_ELSE, OP_ENDIF, OP_HASH160, OP_EQUALVERIFY, OP_CHECKSIG, SignatureHash, SIGHASH_ALL
from bitcoin.core.script import OP_DROP, OP_CHECKLOCKTIMEVERIFY, OP_SHA256, OP_TRUE, OP_FALSE
from bitcoin.core.scripteval import VerifyScript, SCRIPT_VERIFY_P2SH
from bitcoin.wallet import CBitcoinAddress, CBitcoinSecret
import hashlib
# SelectParams('testnet')
# To get transactions not in your wallet, must set -txindex=1
SelectParams('regtest')
proxy = bitcoin.rpc.Proxy()
# The parameters needed for the htlc - hash preimage, sender/seller address, recipient/buyer address, num of blocks for timeout
preimage = b'preimage'
h = hashlib.sha256(preimage).digest()
# proxy.getnewaddress() returns CBitcoinAddress
recipientpubkey = proxy.getnewaddress()
senderpubkey = proxy.getnewaddress()
# privkey of the recipient, used to sign the redeemTx
seckey = proxy.dumpprivkey(senderpubkey)
#proxy.importprivkey(seckey)
lockduration = 10
blocknum = proxy.getblockcount()
print("current block num:", blocknum)
redeemblocknum = blocknum + lockduration
# Create a htlc redeemScript. Similar to a scriptPubKey the redeemScript must be
# satisfied for the funds to be spent.
txin_redeemScript = CScript([OP_IF, OP_SHA256, h, OP_EQUALVERIFY,OP_DUP, OP_HASH160,
recipientpubkey, OP_ELSE, redeemblocknum, OP_CHECKLOCKTIMEVERIFY, OP_DROP, OP_DUP, OP_HASH160,
senderpubkey, OP_ENDIF,OP_EQUALVERIFY, OP_CHECKSIG])
print("redeem script:", b2x(txin_redeemScript))
# Create P2SH scriptPubKey from redeemScript.
txin_scriptPubKey = txin_redeemScript.to_p2sh_scriptPubKey()
print("p2sh_scriptPubKey", b2x(txin_scriptPubKey))
# Convert the P2SH scriptPubKey to a base58 Bitcoin address
txin_p2sh_address = CBitcoinAddress.from_scriptPubKey(txin_scriptPubKey)
p2sh = str(txin_p2sh_address)
print('Pay to:', p2sh)
# AUTOMATE Send funds to p2sh
amount = 1.0*COIN
fund_tx = proxy.sendtoaddress(txin_p2sh_address, amount)
print('Now redeeming.........')
print('Jumping forward till after timelock')
proxy.generate(lockduration)
print('block num is now:', proxy.getblockcount())
# AUTOMATE getting vout of funding tx
txinfo = proxy.gettransaction(fund_tx)
details = txinfo['details'][0]
vout = details['vout']
# Create the txin structure. scriptSig defaults to being empty.
# The input is the p2sh funding transaction txid, vout is its index
txin = CMutableTxIn(COutPoint(fund_tx, vout))
# The default nSequence of FFFFFFFF won't let you redeem when there's a CHECKTIMELOCKVERIFY
txin.nSequence = 0
# Create the txout. Pays out to recipient, so uses recipient's pubkey
# Withdraw full amount minus fee
default_fee = 0.001*COIN
txout = CMutableTxOut(amount - default_fee, senderpubkey.to_scriptPubKey())
# Create the unsigned raw transaction.
tx = CMutableTransaction([txin], [txout])
# nLockTime needs to be at least as large as parameter of CHECKLOCKTIMEVERIFY for script to verify
tx.nLockTime=redeemblocknum
# Calculate the signature hash for that transaction. Note how the script we use
# is the redeemScript, not the scriptPubKey. EvalScript() will be evaluating the redeemScript
sighash = SignatureHash(txin_redeemScript, tx, 0, SIGHASH_ALL)
# Now sign it. We have to append the type of signature we want to the end, in
# this case the usual SIGHASH_ALL.
sig = seckey.sign(sighash) + bytes([SIGHASH_ALL])
# Set the scriptSig of our transaction input appropriately.
txin.scriptSig = CScript([sig, seckey.pub, OP_FALSE, txin_redeemScript])
print("Redeem tx hex:", b2x(tx.serialize()))
# Verify the signature worked.
VerifyScript(txin.scriptSig, txin_scriptPubKey, tx, 0, (SCRIPT_VERIFY_P2SH,))
print("Now sending redeem transaction.......")
txid = proxy.sendrawtransaction(tx)
print("Txid of submitted redeem tx: ", b2x(lx(b2x(txid))))

View File

@ -1 +1,2 @@
python-bitcoinlib
plyvel

View File

@ -1 +1 @@
Hm0Mla5n
UYH0XxCs

36
test.py
View File

@ -26,10 +26,10 @@ def initiate(trade):
sell['fulfiller'] = fulfill_addrs[sell_currency]
buy['fulfiller'] = fulfill_addrs[buy_currency]
# initializing contract classes with addresses, currencies, and amounts
trade.sellContract = Contract(sell)
trade.buyContract = Contract(buy)
print(trade.sellContract.__dict__)
print(trade.buyContract.__dict__)
trade.sell = Contract(sell)
trade.buy = Contract(buy)
print(trade.sell.__dict__)
print(trade.buy.__dict__)
secret = generate_password()
print("Generating secret to lock funds:", secret)
@ -44,8 +44,8 @@ def initiate(trade):
create_buy_p2sh(trade, secret, buy_locktime)
def fulfill(trade):
buy = trade.buyContract
sell = trade.sellContract
buy = trade.buy
sell = trade.sell
buy_p2sh_balance = check_p2sh(buy.currency, buy.p2sh)
sell_p2sh_balance = check_p2sh(sell.currency, sell.p2sh)
@ -57,32 +57,32 @@ def fulfill(trade):
raise ValueError("Sell p2sh not funded, buyer cannot redeem")
def redeem_one(trade):
buy = trade.buyContract
if trade.sellContract.get_status() == 'redeemed':
buy = trade.buy
if trade.sell.get_status() == 'redeemed':
raise RuntimeError("Sell contract status was already redeemed before seller could redeem buyer's tx")
else:
secret = get_secret()
print("GETTING SECRET IN TEST:", secret)
tx_type, txid = redeem_p2sh(trade.buyContract, secret)
tx_type, txid = redeem_p2sh(trade.buy, secret)
print("\nTX Type", tx_type)
setattr(trade.buyContract, tx_type, txid)
setattr(trade.buy, tx_type, txid)
save(trade)
print("You have redeemed {0} {1}!".format(buy.amount, buy.currency))
def redeem_two(trade):
if trade.sellContract.get_status() == 'redeemed':
if trade.sell.get_status() == 'redeemed':
raise RuntimeError("Sell contract was redeemed before buyer could retrieve funds")
elif trade.buyContract.get_status() == 'refunded':
print("buyContract was refunded to buyer")
elif trade.buy.get_status() == 'refunded':
print("buy was refunded to buyer")
else:
# Buy contract is where seller disclosed secret in redeeming
if trade.buyContract.currency == 'bitcoin':
secret = bXcat.parse_secret(trade.buyContract.redeem_tx)
if trade.buy.currency == 'bitcoin':
secret = bXcat.parse_secret(trade.buy.redeem_tx)
else:
secret = zXcat.parse_secret(trade.buyContract.redeem_tx)
secret = zXcat.parse_secret(trade.buy.redeem_tx)
print("Found secret in seller's redeem tx", secret)
redeem_tx = redeem_p2sh(trade.sellContract, secret)
setattr(trade.sellContract, 'redeem_tx', redeem_tx)
redeem_tx = redeem_p2sh(trade.sell, secret)
setattr(trade.sell, 'redeem_tx', redeem_tx)
save(trade)
def generate_blocks(num):

View File

@ -1,8 +1,14 @@
import json
class Trade(object):
def __init__(self, sellContract=None, buyContract=None):
def __init__(self, sell=None, buy=None):
'''Create a new trade with a sell contract and buy contract across two chains'''
self.sellContract = sellContract
self.buyContract = buyContract
self.sell = sell
self.buy = buy
def toJSON(self):
return json.dumps(self, default=lambda o: o.__dict__,
sort_keys=True, indent=4)
class Contract(object):
def __init__(self, data):

View File

@ -40,7 +40,7 @@ def retrieve_password():
return secret
def authorize_fund_sell(htlcTrade):
print('To complete your sell, send {0} {1} to this p2sh: {2}'.format(htlcTrade.sellContract.amount, htlcTrade.sellContract.currency, htlcTrade.sellContract.p2sh))
print('To complete your sell, send {0} {1} to this p2sh: {2}'.format(htlcTrade.sell.amount, htlcTrade.sell.currency, htlcTrade.sell.p2sh))
response = input("Type 'enter' to allow this program to send funds on your behalf.")
def get_initiator_addresses():
@ -75,4 +75,4 @@ def authorize_seller_redeem(buy):
input("Buyer funded the contract where you offered to buy {0}, type 'enter' to redeem {1} {0} from {2}.".format(buy.currency, buy.amount, buy.p2sh))
def authorize_buyer_redeem(trade):
input("Seller funded the contract where you paid them in {0} to buy {1}, type 'enter' to redeem {2} {1} from {3}.".format(trade.buyContract.currency, trade.sellContract.currency, trade.sellContract.amount, trade.sellContract.p2sh))
input("Seller funded the contract where you paid them in {0} to buy {1}, type 'enter' to redeem {2} {1} from {3}.".format(trade.buy.currency, trade.sell.currency, trade.sell.amount, trade.sell.p2sh))

View File

@ -25,6 +25,7 @@ def s2x(string):
def hex2dict(hexstr):
jsonstr = x2s(hexstr)
print(hexstr['fund_tx'])
print(jsonstr)
return json.loads(jsonstr)
@ -76,7 +77,7 @@ def save_secret(secret):
def save(trade):
print("Saving trade")
trade = {
'sell': trade.sellContract.__dict__,
'buy': trade.buyContract.__dict__
'sell': trade.sell.__dict__,
'buy': trade.buy.__dict__
}
save_trade(trade)

View File

@ -1 +1 @@
{"sell": {"currency": "bitcoin", "fulfiller": "mrQzUGU1dwsWRx5gsKKSDPNtrsP65vCA3Z", "initiator": "myfFr5twPYNwgeXyjCmGcrzXtCmfmWXKYp", "amount": "0.5", "redeemScript": "63a82033fb8f68c7e079a2a35cfcd8827279f8a55fa1be04c99debd8ed3e54954e08228876a9147788b4511a25fba1092e67b307a6dcdb6da125d96702bd01b17576a914c7043e62a7391596116f54f6a64c8548e97d3fd96888ac", "fund_tx": "6cca31678d7fe6461277cea7e0614844e62ccc3ae4e247ca77d62bdd741fabef", "p2sh": "2N3rA4r6VSx65FeBTcgWWcR9HaW69DYDbrp", "redeemblocknum": 445}, "buy": {"currency": "zcash", "fulfiller": "tmTjZSg4pX2Us6V5HttiwFZwj464fD2ZgpY", "initiator": "tmFRXyju7ANM7A9mg75ZjyhFW1UJEhUPwfQ", "amount": "1.12", "refund_tx": "5e34f0c634fb6207e9c85ed52528629404d25db29b68931b4e0278377ee776d6", "redeemScript": "63a82033fb8f68c7e079a2a35cfcd8827279f8a55fa1be04c99debd8ed3e54954e08228876a9143ea29256c9d2888ca23de42a8b8e69ca2ec235b16702e301b17576a914c5acca6ef39c843c7a9c3ad01b2da95fe2edf5ba6888ac", "fund_tx": "8c9f9581a8cab00836b54237b30a6d68125067e3ffe6ae04413da2bdc9146daa", "p2sh": "t2UkYvcnigZt7FFp86UvTZ78qdGeLXw7tet", "redeemblocknum": 483}}
{"buy": {"fulfiller": "tmTjZSg4pX2Us6V5HttiwFZwj464fD2ZgpY", "initiator": "tmFRXyju7ANM7A9mg75ZjyhFW1UJEhUPwfQ", "p2sh": "t2TcYheueGfEnT2SKYMLWNXKfyzrMLKc6y2", "redeemblocknum": 113817, "currency": "zcash", "amount": 1.2, "redeemScript": "63a820b67f875a86ea6be94a1f6e44857daa739df84421ba2402dd968185b7d58371b68876a9143ea29256c9d2888ca23de42a8b8e69ca2ec235b1670399bc01b17576a914c5acca6ef39c843c7a9c3ad01b2da95fe2edf5ba6888ac"}, "sell": {"fulfiller": "mrQzUGU1dwsWRx5gsKKSDPNtrsP65vCA3Z", "initiator": "myfFr5twPYNwgeXyjCmGcrzXtCmfmWXKYp", "fund_tx": "1171aeda64eff388b3568fa4675d0ca78852911109bbe42e0ef11ad6bf1b159e", "p2sh": "2NEy1foaQU3wkMu7yygW1GZYhikeSxq6ZCq", "redeemblocknum": 911, "currency": "bitcoin", "amount": 3.5, "redeemScript": "63a820b67f875a86ea6be94a1f6e44857daa739df84421ba2402dd968185b7d58371b68876a9147788b4511a25fba1092e67b307a6dcdb6da125d967028f03b17576a914c7043e62a7391596116f54f6a64c8548e97d3fd96888ac"}}

91
xcat.py
View File

@ -32,41 +32,41 @@ def fund_htlc(currency, p2sh, amount):
return txid
def fund_buy_contract(trade):
buy = trade.buyContract
buy = trade.buy
txid = fund_htlc(buy.currency, buy.p2sh, buy.amount)
setattr(trade.buyContract, 'fund_tx', txid)
setattr(trade.buy, 'fund_tx', txid)
save(trade)
return txid
def fund_sell_contract(trade):
sell = trade.sellContract
sell = trade.sell
txid = fund_htlc(sell.currency, sell.p2sh, sell.amount)
setattr(trade.sellContract, 'fund_tx', txid)
setattr(trade.sell, 'fund_tx', txid)
save(trade)
return txid
def create_sell_p2sh(trade, secret, locktime):
# CREATE SELL CONTRACT
sell = trade.sellContract
sell = trade.sell
contract = create_htlc(sell.currency, sell.initiator, sell.fulfiller, secret, locktime)
print("sell contract", contract)
setattr(trade.sellContract, 'p2sh', contract['p2sh'])
setattr(trade.sellContract, 'redeemScript', contract['redeemScript'])
setattr(trade.sellContract, 'redeemblocknum', contract['redeemblocknum'])
setattr(trade.sell, 'p2sh', contract['p2sh'])
setattr(trade.sell, 'redeemScript', contract['redeemScript'])
setattr(trade.sell, 'redeemblocknum', contract['redeemblocknum'])
save(trade)
def create_buy_p2sh(trade, secret, locktime):
## CREATE BUY CONTRACT
buy = trade.buyContract
buy = trade.buy
print("Now creating buy contract on the {0} blockchain where you will wait for the buyer to send funds...".format(buy.currency))
print("HTLC DETAILS", buy.currency, buy.fulfiller, buy.initiator, secret, locktime)
buy_contract = create_htlc(buy.currency, buy.fulfiller, buy.initiator, secret, locktime)
print("Buy contract", buy_contract)
setattr(trade.buyContract, 'p2sh', buy_contract['p2sh'])
setattr(trade.buyContract, 'redeemScript', buy_contract['redeemScript'])
setattr(trade.buyContract, 'redeemblocknum', buy_contract['redeemblocknum'])
print("Now contact the buyer and tell them to send funds to this p2sh: ", trade.buyContract.p2sh)
setattr(trade.buy, 'p2sh', buy_contract['p2sh'])
setattr(trade.buy, 'redeemScript', buy_contract['redeemScript'])
setattr(trade.buy, 'redeemblocknum', buy_contract['redeemblocknum'])
print("Now contact the buyer and tell them to send funds to this p2sh: ", trade.buy.p2sh)
save(trade)
@ -86,43 +86,43 @@ def print_trade(role):
#### Main functions determining user flow from command line
def buyer_redeem(trade):
userInput.authorize_buyer_redeem(trade)
if trade.sellContract.get_status() == 'redeemed':
print("You already redeemed the funds and acquired {0} {1}".format(trade.sellContract.amount, trade.sellContract.currency))
if trade.sell.get_status() == 'redeemed':
print("You already redeemed the funds and acquired {0} {1}".format(trade.sell.amount, trade.sell.currency))
exit()
else:
# Buyer redeems seller's funded tx
p2sh = trade.sellContract.p2sh
currency = trade.sellContract.currency
p2sh = trade.sell.p2sh
currency = trade.sell.currency
# Buy contract is where seller disclosed secret in redeeming
if trade.buyContract.currency == 'bitcoin':
secret = bXcat.parse_secret(trade.buyContract.redeem_tx)
if trade.buy.currency == 'bitcoin':
secret = bXcat.parse_secret(trade.buy.redeem_tx)
else:
secret = zXcat.parse_secret(trade.buyContract.redeem_tx)
secret = zXcat.parse_secret(trade.buy.redeem_tx)
print("Found secret in seller's redeem tx", secret)
redeem_tx = redeem_p2sh(trade.sellContract, secret)
setattr(trade.sellContract, 'redeem_tx', redeem_tx)
redeem_tx = redeem_p2sh(trade.sell, secret)
setattr(trade.sell, 'redeem_tx', redeem_tx)
save(trade)
exit()
def seller_redeem(trade):
buy = trade.buyContract
buy = trade.buy
userInput.authorize_seller_redeem(buy)
if trade.sellContract.get_status() == 'redeemed':
if trade.sell.get_status() == 'redeemed':
print("You already redeemed the funds and acquired {0} {1}".format(buy.amount, buy.currency))
exit()
else:
# Seller redeems buyer's funded tx (contract in p2sh)
secret = userInput.retrieve_password()
tx_type, txid = redeem_p2sh(trade.buyContract, secret)
setattr(trade.buyContract, tx_type, txid)
tx_type, txid = redeem_p2sh(trade.buy, secret)
setattr(trade.buy, tx_type, txid)
save(trade)
print("You have redeemed {0} {1}!".format(buy.amount, buy.currency))
print_trade('seller')
def buyer_fulfill(trade):
buy = trade.buyContract
sell = trade.sellContract
buy = trade.buy
sell = trade.sell
buy_p2sh_balance = check_p2sh(buy.currency, buy.p2sh)
sell_p2sh_balance = check_p2sh(sell.currency, sell.p2sh)
@ -151,16 +151,16 @@ def seller_initiate(trade):
sell['fulfiller'] = fulfill_addrs[sell_currency]
buy['fulfiller'] = fulfill_addrs[buy_currency]
# initializing contract classes with addresses, currencies, and amounts
trade.sellContract = Contract(sell)
trade.buyContract = Contract(buy)
print(trade.sellContract.__dict__)
print(trade.buyContract.__dict__)
trade.sell = Contract(sell)
trade.buy = Contract(buy)
print(trade.sell.__dict__)
print(trade.buy.__dict__)
secret = userInput.create_password()
# TODO: Implement locktimes and mock block passage of time
sell_locktime = 5
buy_locktime = 10 # Must be more than first tx
print("Creating pay-to-script-hash for sell contract...")
create_sell_p2sh(trade, secret, sell_locktime)
userInput.authorize_fund_sell(trade)
@ -169,7 +169,8 @@ def seller_initiate(trade):
print("Sent")
create_buy_p2sh(trade, secret, buy_locktime)
print_trade('seller')
return trade
if __name__ == '__main__':
print("ZEC <-> BTC XCAT (Cross-Chain Atomic Transactions)")
@ -181,9 +182,9 @@ if __name__ == '__main__':
htlcTrade = Trade()
print("New empty trade")
else:
buyContract = Contract(trade['buy'])
sellContract = Contract(trade['sell'])
htlcTrade = Trade(buyContract=buyContract, sellContract=sellContract)
buy = Contract(trade['buy'])
sell = Contract(trade['sell'])
htlcTrade = Trade(buy=buy, sell=sell)
try:
if sys.argv[1] == 'new':
@ -206,29 +207,29 @@ if __name__ == '__main__':
print("Trade exists, run script as buyer or seller to complete trade.")
exit()
if htlcTrade.buyContract is not None and htlcTrade.sellContract is not None:
if htlcTrade.sellContract.get_status() == 'redeemed' and htlcTrade.buyContract.get_status() == 'redeemed':
if htlcTrade.buy is not None and htlcTrade.sell is not None:
if htlcTrade.sell.get_status() == 'redeemed' and htlcTrade.buy.get_status() == 'redeemed':
print("This trade is already complete! Trade details:")
pprint(trade)
exit()
if role == "seller":
if htlcTrade.sellContract == None:
if htlcTrade.sell == None:
seller_initiate(htlcTrade)
elif htlcTrade.buyContract.get_status() == 'funded':
elif htlcTrade.buy.get_status() == 'funded':
seller_redeem(htlcTrade)
elif htlcTrade.buyContract.get_status() == 'empty':
print("Buyer has not yet funded the contract where you offered to buy {0}, please wait for them to complete their part.".format(htlcTrade.buyContract.currency))
elif htlcTrade.buy.get_status() == 'empty':
print("Buyer has not yet funded the contract where you offered to buy {0}, please wait for them to complete their part.".format(htlcTrade.buy.currency))
else:
# Need better way of preventing buyer from having secret
# if 'status' not in trade['buy'] and trade['sell']['status'] == 'funded':
if htlcTrade.sellContract.get_status() == 'funded' and htlcTrade.buyContract.get_status() != 'redeemed':
if htlcTrade.sell.get_status() == 'funded' and htlcTrade.buy.get_status() != 'redeemed':
print("One active trade available, fulfilling buyer contract...")
buyer_fulfill(htlcTrade)
# How to monitor if txs are included in blocks -- should use blocknotify and a monitor daemon?
# p2sh = trade['buy']['p2sh']
# check_blocks(p2sh)
elif htlcTrade.buyContract.get_status() == 'redeemed':
elif htlcTrade.buy.get_status() == 'redeemed':
# Seller has redeemed buyer's tx, buyer can now redeem.
buyer_redeem(htlcTrade)
print("XCAT trade complete!")

View File

@ -1,140 +0,0 @@
#!/usr/bin/env python3
# Based on spend-p2sh-txout.py from python-bitcoinlib.
# Copyright (C) 2017 The Zcash developers
import sys
if sys.version_info.major < 3:
sys.stderr.write('Sorry, Python 3.x required by this example.\n')
sys.exit(1)
import zcash
import zcash.rpc
from zcash import SelectParams
from zcash.core import b2x, lx, b2lx, COIN, COutPoint, CMutableTxOut, CMutableTxIn, CMutableTransaction, Hash160
from zcash.core.script import CScript, OP_DUP, OP_IF, OP_ELSE, OP_ENDIF, OP_HASH160, OP_EQUALVERIFY, OP_CHECKSIG, SignatureHash, SIGHASH_ALL, OP_FALSE, OP_DROP, OP_CHECKLOCKTIMEVERIFY, OP_SHA256, OP_TRUE
from zcash.core.scripteval import VerifyScript, SCRIPT_VERIFY_P2SH
from zcash.wallet import CBitcoinAddress, CBitcoinSecret
import hashlib
import binascii
# SelectParams('testnet')
SelectParams('regtest')
zcashd = zcash.rpc.Proxy()
FEE = 0.001*COIN
# ========================= ZCASH ADDRESSES =========================
alice_address = input("Enter alice zcash address: (type 'enter' for demo)")
bob_address = input("Enter bob zcash address: (type 'enter' for demo)")
# These mock addresses come from regtest on the server
# alicepubkey = CBitcoinAddress('tmFUm31B9wzHWJ9jGe9L9Qb549zfC7zFsEK')
# bobpubkey = CBitcoinAddress('tmFm8R6b22485uDYm6dryC4f8R6oXUTUe5i')
# zcashd.getnewaddress() returns CBitcoinAddress
bobpubkey = zcashd.getnewaddress()
alicepubkey = zcashd.getnewaddress()
print("alicepubkey", alicepubkey)
print("bobpubkey", bobpubkey)
# privkey of the bob, used to sign the redeemTx
bob_seckey = zcashd.dumpprivkey(bobpubkey)
# privkey of alice, used to refund tx in case of timeout
alice_seckey = zcashd.dumpprivkey(alicepubkey)
print("bob_seckey", bob_seckey)
def get_keys(funder_address, redeemer_address):
# fundpubkey = CBitcoinAddress(funder_address)
# redeempubkey = CBitcoinAddress(redeemer_address)
fundpubkey = zcashd.getnewaddress()
redeempubkey = zcashd.getnewaddress()
return fundpubkey, redeempubkey
# ======= secret from Alice, other file ====
preimage = b'preimage'
h = hashlib.sha256(preimage).digest()
# ========================= LOCKTIME SCRIPT CREATION =========================
lockduration = 20 # Must be more than first tx
blocknum = zcashd.getblockcount()
redeemblocknum = blocknum + lockduration
zec_redeemScript = CScript([OP_IF, OP_SHA256, h, OP_EQUALVERIFY,OP_DUP, OP_HASH160,
bobpubkey, OP_ELSE, redeemblocknum, OP_CHECKLOCKTIMEVERIFY, OP_DROP, OP_DUP, OP_HASH160,
alicepubkey, OP_ENDIF,OP_EQUALVERIFY, OP_CHECKSIG])
print("TX2 Redeem script on Zcash blockchain:", b2x(zec_redeemScript))
# ========================= TX1: CREATE BITCOIN P2SH FROM SCRIPT =========================
txin_scriptPubKey = zec_redeemScript.to_p2sh_scriptPubKey()
# Convert the P2SH scriptPubKey to a base58 Bitcoin address
txin_p2sh_address = CBitcoinAddress.from_scriptPubKey(txin_scriptPubKey)
p2sh = str(txin_p2sh_address)
print('Alice -- send funds to this p2sh address to initiate atomic swap:', p2sh)
response = input("Alice -- Type 'enter' to allow zbxcat to fund the Zcash p2sh on your behalf:")
send_amount = 10.0*COIN
fund_tx = zcashd.sendtoaddress(txin_p2sh_address, send_amount)
print('Bob -- Alice send fund tx to the Zcash p2sh. Please wait for confirmation. Txid:', b2x(lx(b2x(fund_tx))))
# ========================= CONFIRM ZCASH FUNDING TX TO P2SH =========================
zcashd.importaddress(p2sh)
fund_txinfo = zcashd.gettransaction(fund_tx)
fund_details = fund_txinfo['details'] # "fund_details" is an array, for now we can assume it only has one destination address
outputAddress = fund_details[0]['address']
fund_vout = fund_details[0]['vout']
if (outputAddress != p2sh):
print('Fund tx sent to wrong address! p2sh was {0}, funding tx was sent to {1}'.format(p2sh, outputAddress))
quit()
# Get amount in address
output_amount = zcashd.getreceivedbyaddress(outputAddress, 0)
if (output_amount < send_amount):
print('Fund tx too small! Amount sent was {0}, amount expected was {1}'.format(output_amount, send_amount))
quit()
print('Bob -- Alice Zcash funding tx confirmed, now send funds to the Bitcoin p2sh: (other file)')
# ========================= PART 2: ZCASH P2SH FUNDED, REDEEM OR REFUND =========================
# ================= AFTER 48 HRS: ALICE REFUNDS AFTER BOB TIMES OUT =========================
# Mock passage of time -- comment out to test normal redeem condition
# zcashd.generate(25)
if(zcashd.getblockcount() >= redeemblocknum):
print("Alice -- Bob did not redeem the Zcash you put in escrow within the timeout period, so refunding you..... ")
txin = CMutableTxIn(COutPoint(fund_tx, fund_vout))
# The default nSequence of FFFFFFFF won't let you redeem when there's a CHECKTIMELOCKVERIFY
txin.nSequence = 0
txout = CMutableTxOut(send_amount - FEE, alicepubkey.to_scriptPubKey())
# Create the unsigned raw transaction.
tx = CMutableTransaction([txin], [txout])
# nLockTime needs to be at least as large as parameter of CHECKLOCKTIMEVERIFY for script to verify
tx.nLockTime = redeemblocknum
# Calculate the signature hash for that transaction. Note how the script we use
# is the redeemScript, not the scriptPubKey. EvalScript() will be evaluating the redeemScript
sighash = SignatureHash(zec_redeemScript, tx, 0, SIGHASH_ALL)
sig = alice_seckey.sign(sighash) + bytes([SIGHASH_ALL])
txin.scriptSig = CScript([sig, alice_seckey.pub, OP_FALSE, zec_redeemScript])
print("Time lock has passed, Alice redeeming her own tx:")
print("Refund tx hex:", b2x(tx.serialize()))
VerifyScript(txin.scriptSig, txin_scriptPubKey, tx, 0, (SCRIPT_VERIFY_P2SH,))
txid = zcashd.sendrawtransaction(tx)
print("Txid of submitted refund tx: ", b2x(lx(b2x(txid))))
quit()
# ================= BEFORE 48 HRS: BOB REDEEMS WITH ALICE'S REVEALED SECRET =========================
print("Bob -- Redeeming tx.....")
txin = CMutableTxIn(COutPoint(fund_tx, fund_vout))
txout = CMutableTxOut(send_amount - FEE, bobpubkey.to_scriptPubKey())
# Create the unsigned raw transaction.
tx = CMutableTransaction([txin], [txout])
# nLockTime needs to be at least as large as parameter of CHECKLOCKTIMEVERIFY for script to verify
tx.nLockTime = redeemblocknum
sighash = SignatureHash(zec_redeemScript, tx, 0, SIGHASH_ALL)
sig = bob_seckey.sign(sighash) + bytes([SIGHASH_ALL])
txin.scriptSig = CScript([sig, bob_seckey.pub, preimage, OP_TRUE, zec_redeemScript])
print("Redeem tx hex:", b2x(tx.serialize()))
print("txin.scriptSig", b2x(txin.scriptSig))
print("txin_scriptPubKey", b2x(txin_scriptPubKey))
print('tx', tx)
VerifyScript(txin.scriptSig, txin_scriptPubKey, tx, 0, (SCRIPT_VERIFY_P2SH,))
txid = zcashd.sendrawtransaction(tx)
print("Txid of submitted redeem tx: ", b2x(lx(b2x(txid))))

View File

@ -1,90 +0,0 @@
#!/usr/bin/env python3
# Copyright (C) 2017 the python-zcashlib developers
import sys
if sys.version_info.major < 3:
sys.stderr.write('Sorry, Python 3.x required by this example.\n')
sys.exit(1)
import zcash
import zcash.rpc
from zcash import SelectParams
from zcash.core import b2x, lx, COIN, COutPoint, CMutableTxOut, CMutableTxIn, CMutableTransaction, Hash160
from zcash.core.script import CScript, OP_DUP, OP_IF, OP_ELSE, OP_DROP, OP_ENDIF, OP_HASH160, OP_SHA256, OP_EQUAL, OP_EQUALVERIFY, OP_CHECKSIG, SignatureHash, SIGHASH_ALL, SIGHASH_ANYONECANPAY, OP_TRUE
from zcash.core.scripteval import VerifyScript, SCRIPT_VERIFY_P2SH
from zcash.wallet import CBitcoinAddress, CBitcoinSecret
import hashlib
SelectParams('regtest')
proxy = zcash.rpc.Proxy()
# The parameters needed for the htlc - hash preimage, sender/seller address, recipient/buyer address, num of blocks for timeout
preimage = b'preimage'
h = hashlib.sha256(preimage).digest()
# proxy.getnewaddress() returns CBitcoinAddress
recipientpubkey = proxy.getnewaddress()
senderpubkey = proxy.getnewaddress()
# privkey of the recipient, used to sign the redeemTx
seckey = proxy.dumpprivkey(recipientpubkey)
blocknum = 7
# Create a htlc redeemScript. Similar to a scriptPubKey the redeemScript must be
# satisfied for the funds to be spent.
txin_redeemScript = CScript([OP_IF, OP_SHA256, h, OP_EQUALVERIFY,OP_DUP, OP_HASH160,
recipientpubkey, OP_ELSE, blocknum, OP_DROP, OP_HASH160,
senderpubkey, OP_ENDIF,OP_EQUALVERIFY, OP_CHECKSIG])
print("redeem script:", b2x(txin_redeemScript))
# Create P2SH scriptPubKey from redeemScript.
txin_scriptPubKey = txin_redeemScript.to_p2sh_scriptPubKey()
print("p2sh_scriptPubKey", b2x(txin_scriptPubKey))
# Convert the P2SH scriptPubKey to a base58 Bitcoin address
txin_p2sh_address = CBitcoinAddress.from_scriptPubKey(txin_scriptPubKey)
p2sh = str(txin_p2sh_address)
print('Pay to:', p2sh)
# AUTOMATE Send funds to p2sh
amount = 1.0*COIN
fund_tx = proxy.sendtoaddress(txin_p2sh_address, amount)
print('Now redeeming.........')
# AUTOMATE getting vout of funding tx
txinfo = proxy.gettransaction(fund_tx)
details = txinfo['details'][0]
vout = details['vout']
# Create the txin structure. scriptSig defaults to being empty.
# The input is the p2sh funding transaction txid, vout is its index
txin = CMutableTxIn(COutPoint(fund_tx, vout))
# Create the txout. Pays out to recipient, so uses recipient's pubkey
# Withdraw full amount minus fee
default_fee = 0.001*COIN
txout = CMutableTxOut(amount - default_fee, recipientpubkey.to_scriptPubKey())
# Create the unsigned raw transaction.
tx = CMutableTransaction([txin], [txout])
# Calculate the signature hash for that transaction. Note how the script we use
# is the redeemScript, not the scriptPubKey. EvalScript() will be evaluating the redeemScript
sighash = SignatureHash(txin_redeemScript, tx, 0, SIGHASH_ALL)
# Now sign it. We have to append the type of signature we want to the end, in
# this case the usual SIGHASH_ALL.
sig = seckey.sign(sighash) + bytes([SIGHASH_ALL])
# Set the scriptSig of our transaction input appropriately.
txin.scriptSig = CScript([ sig, seckey.pub, preimage, OP_TRUE, txin_redeemScript])
print("Redeem tx hex:", b2x(tx.serialize()))
# Verify the signature worked.
VerifyScript(txin.scriptSig, txin_scriptPubKey, tx, 0, (SCRIPT_VERIFY_P2SH,))
print("Now sending redeem transaction.......")
txid = proxy.sendrawtransaction(tx)
print("Txid of submitted redeem tx: ", b2x(txid))