redeem with secret works
This commit is contained in:
parent
3a836cf4f5
commit
d5821f5ca7
129
bXcat.py
129
bXcat.py
|
@ -66,6 +66,7 @@ def fund_htlc(p2sh, amount):
|
||||||
send_amount = float(amount) * COIN
|
send_amount = float(amount) * COIN
|
||||||
fund_txid = bitcoind.sendtoaddress(p2sh, send_amount)
|
fund_txid = bitcoind.sendtoaddress(p2sh, send_amount)
|
||||||
txid = b2x(lx(b2x(fund_txid)))
|
txid = b2x(lx(b2x(fund_txid)))
|
||||||
|
print("funding btc sell address:", txid)
|
||||||
return txid
|
return txid
|
||||||
|
|
||||||
def check_funds(p2sh):
|
def check_funds(p2sh):
|
||||||
|
@ -73,6 +74,7 @@ def check_funds(p2sh):
|
||||||
# Get amount in address
|
# Get amount in address
|
||||||
amount = bitcoind.getreceivedbyaddress(p2sh, 0)
|
amount = bitcoind.getreceivedbyaddress(p2sh, 0)
|
||||||
amount = amount/COIN
|
amount = amount/COIN
|
||||||
|
print("Amount in bitcoin address ", p2sh, ":",amount)
|
||||||
return amount
|
return amount
|
||||||
|
|
||||||
## TODO: FIX search for p2sh in block
|
## TODO: FIX search for p2sh in block
|
||||||
|
@ -102,17 +104,19 @@ def get_tx_details(txid):
|
||||||
fund_txinfo = bitcoind.gettransaction(lx(txid))
|
fund_txinfo = bitcoind.gettransaction(lx(txid))
|
||||||
return fund_txinfo['details'][0]
|
return fund_txinfo['details'][0]
|
||||||
|
|
||||||
# redeems automatically after buyer has funded tx, by scanning for transaction to the p2sh
|
# redeems funded tx automatically, by scanning for transaction to the p2sh
|
||||||
# i.e., doesn't require buyer telling us fund txid
|
# i.e., doesn't require buyer telling us fund txid
|
||||||
def auto_redeem(contract, secret):
|
# returns false if fund tx doesn't exist or is too small
|
||||||
|
def redeem_with_secret(contract, secret):
|
||||||
# How to find redeemScript and redeemblocknum from blockchain?
|
# How to find redeemScript and redeemblocknum from blockchain?
|
||||||
print("Contract in auto redeem", contract.__dict__)
|
print("Redeeming contract using secret", contract.__dict__)
|
||||||
p2sh = contract.p2sh
|
p2sh = contract.p2sh
|
||||||
|
minamount = float(contract.amount)
|
||||||
#checking there are funds in the address
|
#checking there are funds in the address
|
||||||
amount = check_funds(p2sh)
|
amount = check_funds(p2sh)
|
||||||
if(amount == 0):
|
if(amount < minamount):
|
||||||
print("address ", p2sh, " not funded")
|
print("address ", p2sh, " not sufficiently funded")
|
||||||
quit()
|
return false
|
||||||
fundtx = find_transaction_to_address(p2sh)
|
fundtx = find_transaction_to_address(p2sh)
|
||||||
amount = fundtx['amount'] / COIN
|
amount = fundtx['amount'] / COIN
|
||||||
print("Found fundtx:", fundtx)
|
print("Found fundtx:", fundtx)
|
||||||
|
@ -120,43 +124,26 @@ def auto_redeem(contract, secret):
|
||||||
if fundtx['address'] == p2sh:
|
if fundtx['address'] == p2sh:
|
||||||
print("Found {0} in p2sh {1}, redeeming...".format(amount, p2sh))
|
print("Found {0} in p2sh {1}, redeeming...".format(amount, p2sh))
|
||||||
|
|
||||||
# Parsing redeemblocknum from the redeemscript of the p2sh
|
redeemPubKey = find_redeemAddr(contract)
|
||||||
redeemblocknum = find_redeemblocknum(contract)
|
print('redeemPubKey', redeemPubKey)
|
||||||
blockcount = bitcoind.getblockcount()
|
|
||||||
print("\nCurrent blocknum at time of redeem on Bitcoin:", blockcount)
|
|
||||||
if blockcount < redeemblocknum:
|
|
||||||
redeemPubKey = find_redeemAddr(contract)
|
|
||||||
print('redeemPubKey', redeemPubKey)
|
|
||||||
else:
|
|
||||||
print("nLocktime exceeded, refunding")
|
|
||||||
redeemPubKey = find_refundAddr(contract)
|
|
||||||
print('refundPubKey', redeemPubKey)
|
|
||||||
# redeemPubKey = CBitcoinAddress.from_scriptPubKey(redeemPubKey)
|
|
||||||
# exit()
|
|
||||||
|
|
||||||
zec_redeemScript = CScript(x(contract.redeemScript))
|
redeemScript = CScript(x(contract.redeemScript))
|
||||||
txin = CMutableTxIn(fundtx['outpoint'])
|
txin = CMutableTxIn(fundtx['outpoint'])
|
||||||
txout = CMutableTxOut(fundtx['amount'] - FEE, redeemPubKey.to_scriptPubKey())
|
txout = CMutableTxOut(fundtx['amount'] - FEE, redeemPubKey.to_scriptPubKey())
|
||||||
# Create the unsigned raw transaction.
|
# Create the unsigned raw transaction.
|
||||||
tx = CMutableTransaction([txin], [txout])
|
tx = CMutableTransaction([txin], [txout])
|
||||||
# nLockTime needs to be at least as large as parameter of CHECKLOCKTIMEVERIFY for script to verify
|
sighash = SignatureHash(redeemScript, tx, 0, SIGHASH_ALL)
|
||||||
# TODO: these things like redeemblocknum should really be properties of a tx class...
|
|
||||||
# Need: redeemblocknum, zec_redeemScript, secret (for creator...), txid, redeemer...
|
|
||||||
if blockcount >= redeemblocknum:
|
|
||||||
print("\nLocktime exceeded")
|
|
||||||
tx.nLockTime = redeemblocknum
|
|
||||||
sighash = SignatureHash(zec_redeemScript, tx, 0, SIGHASH_ALL)
|
|
||||||
# TODO: figure out how to better protect privkey
|
# TODO: figure out how to better protect privkey
|
||||||
privkey = bitcoind.dumpprivkey(redeemPubKey)
|
privkey = bitcoind.dumpprivkey(redeemPubKey)
|
||||||
sig = privkey.sign(sighash) + bytes([SIGHASH_ALL])
|
sig = privkey.sign(sighash) + bytes([SIGHASH_ALL])
|
||||||
print("SECRET", secret)
|
print("SECRET", secret)
|
||||||
preimage = secret.encode('utf-8')
|
preimage = secret.encode('utf-8')
|
||||||
txin.scriptSig = CScript([sig, privkey.pub, preimage, OP_TRUE, zec_redeemScript])
|
txin.scriptSig = CScript([sig, privkey.pub, preimage, OP_TRUE, redeemScript])
|
||||||
|
|
||||||
# exit()
|
# exit()
|
||||||
|
|
||||||
print("txin.scriptSig", b2x(txin.scriptSig))
|
print("txin.scriptSig", b2x(txin.scriptSig))
|
||||||
txin_scriptPubKey = zec_redeemScript.to_p2sh_scriptPubKey()
|
txin_scriptPubKey = redeemScript.to_p2sh_scriptPubKey()
|
||||||
print('Redeem txhex', b2x(tx.serialize()))
|
print('Redeem txhex', b2x(tx.serialize()))
|
||||||
VerifyScript(txin.scriptSig, txin_scriptPubKey, tx, 0, (SCRIPT_VERIFY_P2SH,))
|
VerifyScript(txin.scriptSig, txin_scriptPubKey, tx, 0, (SCRIPT_VERIFY_P2SH,))
|
||||||
print("script verified, sending raw tx")
|
print("script verified, sending raw tx")
|
||||||
|
@ -166,6 +153,69 @@ def auto_redeem(contract, secret):
|
||||||
else:
|
else:
|
||||||
print("No contract for this p2sh found in database", p2sh)
|
print("No contract for this p2sh found in database", p2sh)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# given a contract return true or false according to whether the relevant fund tx's timelock is still valid
|
||||||
|
def still_locked(contract):
|
||||||
|
p2sh = contract.p2sh
|
||||||
|
# Parsing redeemblocknum from the redeemscript of the p2sh
|
||||||
|
redeemblocknum = find_redeemblocknum(contract)
|
||||||
|
blockcount = bitcoind.getblockcount()
|
||||||
|
print(blockcount, redeemblocknum, blockcount<redeemblocknum)
|
||||||
|
return (int(blockcount) < int(redeemblocknum))
|
||||||
|
|
||||||
|
def redeem_after_timelock(contract):
|
||||||
|
print("Contract in auto redeem", contract.__dict__)
|
||||||
|
p2sh = contract.p2sh
|
||||||
|
fundtx = find_transaction_to_address(p2sh)
|
||||||
|
amount = fundtx['amount'] / COIN
|
||||||
|
|
||||||
|
if (fundtx['address'].__str__() != p2sh):
|
||||||
|
print("no fund transaction found to the contract p2sh address ",p2sh)
|
||||||
|
quit()
|
||||||
|
print("Found fundtx:", fundtx)
|
||||||
|
# Parsing redeemblocknum from the redeemscript of the p2sh
|
||||||
|
redeemblocknum = find_redeemblocknum(contract)
|
||||||
|
blockcount = bitcoind.getblockcount()
|
||||||
|
print ("Current block:", blockcount, "Can redeem from block:", redeemblocknum)
|
||||||
|
if(still_locked(contract)):
|
||||||
|
print("too early for redeeming with timelock")
|
||||||
|
quit()
|
||||||
|
|
||||||
|
print("Found {0} in p2sh {1}, redeeming...".format(amount, p2sh))
|
||||||
|
|
||||||
|
|
||||||
|
redeemPubKey = find_refundAddr(contract)
|
||||||
|
print('refundPubKey', redeemPubKey)
|
||||||
|
|
||||||
|
redeemScript = CScript(x(contract.redeemScript))
|
||||||
|
txin = CMutableTxIn(fundtx['outpoint'])
|
||||||
|
txout = CMutableTxOut(fundtx['amount'] - FEE, redeemPubKey.to_scriptPubKey())
|
||||||
|
# Create the unsigned raw transaction.
|
||||||
|
txin.nSequence = 0
|
||||||
|
tx = CMutableTransaction([txin], [txout])
|
||||||
|
tx.nLockTime = redeemblocknum
|
||||||
|
|
||||||
|
sighash = SignatureHash(redeemScript, tx, 0, SIGHASH_ALL)
|
||||||
|
# TODO: figure out how to better protect privkey
|
||||||
|
privkey = bitcoind.dumpprivkey(redeemPubKey)
|
||||||
|
sig = privkey.sign(sighash) + bytes([SIGHASH_ALL])
|
||||||
|
txin.scriptSig = CScript([sig, privkey.pub, OP_FALSE, redeemScript])
|
||||||
|
|
||||||
|
# exit()
|
||||||
|
|
||||||
|
print("txin.scriptSig", b2x(txin.scriptSig))
|
||||||
|
txin_scriptPubKey = redeemScript.to_p2sh_scriptPubKey()
|
||||||
|
print('Redeem txhex', b2x(tx.serialize()))
|
||||||
|
VerifyScript(txin.scriptSig, txin_scriptPubKey, tx, 0, (SCRIPT_VERIFY_P2SH,))
|
||||||
|
print("script verified, sending raw tx")
|
||||||
|
txid = bitcoind.sendrawtransaction(tx)
|
||||||
|
print("Txid of submitted redeem tx: ", b2x(lx(b2x(txid))))
|
||||||
|
return b2x(lx(b2x(txid)))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# takes hex and returns array of decoded script op codes
|
# takes hex and returns array of decoded script op codes
|
||||||
def parse_script(script_hex):
|
def parse_script(script_hex):
|
||||||
redeemScript = zcashd.decodescript(script_hex)
|
redeemScript = zcashd.decodescript(script_hex)
|
||||||
|
@ -223,9 +273,26 @@ def find_transaction_to_address(p2sh):
|
||||||
|
|
||||||
def new_bitcoin_addr():
|
def new_bitcoin_addr():
|
||||||
addr = bitcoind.getnewaddress()
|
addr = bitcoind.getnewaddress()
|
||||||
print('new btc addr', addr.to_scriptPubKey)
|
print('new btc addr', addr)
|
||||||
return addr.to_scriptPubKey()
|
return addr
|
||||||
|
|
||||||
def generate(num):
|
def generate(num):
|
||||||
blocks = bitcoind.generate(num)
|
blocks = bitcoind.generate(num)
|
||||||
return blocks
|
return blocks
|
||||||
|
|
||||||
|
def find_secret(p2sh):
|
||||||
|
bitcoind.importaddress(p2sh, "", False)
|
||||||
|
# is this working?
|
||||||
|
txs = bitcoind.listtransactions()
|
||||||
|
for tx in txs:
|
||||||
|
# print("tx addr:", tx['address'])
|
||||||
|
# print(type(tx['address']))
|
||||||
|
# print(type(p2sh))
|
||||||
|
if (tx['address'] == p2sh ) and (tx['category'] == "send"):
|
||||||
|
print(type(tx['txid']))
|
||||||
|
print(str.encode(tx['txid']))
|
||||||
|
raw = bitcoind.getrawtransaction(lx(tx['txid']),True)['hex']
|
||||||
|
decoded = bitcoind.decoderawtransaction(raw)
|
||||||
|
secret = decoded['vin'][0]['scriptSig']['asm']
|
||||||
|
print("secret:", secret)
|
||||||
|
return secret
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
9NulTQVe
|
wfP4CYjl
|
65
test.py
65
test.py
|
@ -6,10 +6,19 @@ htlcTrade = Trade()
|
||||||
print("Starting test of xcat...")
|
print("Starting test of xcat...")
|
||||||
|
|
||||||
def get_initiator_addresses():
|
def get_initiator_addresses():
|
||||||
return {'bitcoin': 'myfFr5twPYNwgeXyjCmGcrzXtCmfmWXKYp', 'zcash': 'tmFRXyju7ANM7A9mg75ZjyhFW1UJEhUPwfQ'}
|
baddr = bXcat.new_bitcoin_addr()
|
||||||
|
zaddr = zXcat.new_zcash_addr()
|
||||||
|
# print("type baddr", type(baddr))
|
||||||
|
# print("type baddr", type(baddr.to_scriptPubKey()))
|
||||||
|
return {'bitcoin': baddr.__str__(), 'zcash': zaddr.__str__()}
|
||||||
|
# return {'bitcoin': 'myfFr5twPYNwgeXyjCmGcrzXtCmfmWXKYp', 'zcash': 'tmFRXyju7ANM7A9mg75ZjyhFW1UJEhUPwfQ'}
|
||||||
|
|
||||||
def get_fulfiller_addresses():
|
def get_fulfiller_addresses():
|
||||||
return {'bitcoin': 'mrQzUGU1dwsWRx5gsKKSDPNtrsP65vCA3Z', 'zcash': 'tmTjZSg4pX2Us6V5HttiwFZwj464fD2ZgpY'}
|
baddr = bXcat.new_bitcoin_addr()
|
||||||
|
zaddr = zXcat.new_zcash_addr()
|
||||||
|
return {'bitcoin': baddr.__str__(), 'zcash': zaddr.__str__()}
|
||||||
|
# return {'bitcoin': 'myfFr5twPYNwgeXyjCmGcrzXtCmfmWXKYp', 'zcash': 'tmFRXyju7ANM7A9mg75ZjyhFW1UJEhUPwfQ'}
|
||||||
|
# return {'bitcoin': 'mrQzUGU1dwsWRx5gsKKSDPNtrsP65vCA3Z', 'zcash': 'tmTjZSg4pX2Us6V5HttiwFZwj464fD2ZgpY'}
|
||||||
|
|
||||||
def initiate(trade):
|
def initiate(trade):
|
||||||
# Get amounts
|
# Get amounts
|
||||||
|
@ -43,33 +52,38 @@ def initiate(trade):
|
||||||
print("Sent")
|
print("Sent")
|
||||||
create_buy_p2sh(trade, secret, buy_locktime)
|
create_buy_p2sh(trade, secret, buy_locktime)
|
||||||
|
|
||||||
def fulfill(trade):
|
# buyer checks that seller funded the sell contract, and if so funds the buy contract
|
||||||
|
def buyer_fulfill(trade):
|
||||||
|
print("BUYER FULFILL")
|
||||||
|
print("=============")
|
||||||
buy = trade.buyContract
|
buy = trade.buyContract
|
||||||
sell = trade.sellContract
|
sell = trade.sellContract
|
||||||
buy_p2sh_balance = check_p2sh(buy.currency, buy.p2sh)
|
# buy_p2sh_balance = check_p2sh(buy.currency, buy.p2sh)
|
||||||
sell_p2sh_balance = check_p2sh(sell.currency, sell.p2sh)
|
sell_p2sh_balance = check_p2sh(sell.currency, sell.p2sh)
|
||||||
|
if (sell_p2sh_balance < float(sell.amount)):
|
||||||
|
raise ValueError("Sell p2sh not funded, buyer cannot redeem")
|
||||||
|
print("Seller has deposited funds, so funding the buy contract:")
|
||||||
|
txid = fund_buy_contract(trade)
|
||||||
|
print("Buyer Fund tx txid:", txid)
|
||||||
|
|
||||||
if buy_p2sh_balance == 0:
|
def redeem_seller(trade):
|
||||||
print("Buy amt:", buy.amount)
|
print("SELLER REDEEMING BUY CONTRACT")
|
||||||
txid = fund_buy_contract(trade)
|
print("=============================")
|
||||||
print("Fund tx txid:", txid)
|
|
||||||
else:
|
|
||||||
raise ValueError("Sell p2sh not funded, buyer cannot redeem")
|
|
||||||
|
|
||||||
def redeem_one(trade):
|
|
||||||
buy = trade.buyContract
|
buy = trade.buyContract
|
||||||
if trade.sellContract.get_status() == 'redeemed':
|
if trade.sellContract.get_status() == 'redeemed':
|
||||||
raise RuntimeError("Sell contract status was already redeemed before seller could redeem buyer's tx")
|
raise RuntimeError("Sell contract status was already redeemed before seller could redeem buyer's tx")
|
||||||
else:
|
else:
|
||||||
secret = get_secret()
|
secret = get_secret() # Just the seller getting his local copy of the secret
|
||||||
print("GETTING SECRET IN TEST:", secret)
|
print("SELLER SECRET IN TEST:", secret)
|
||||||
tx_type, txid = redeem_p2sh(trade.buyContract, secret)
|
txid = redeem_p2sh(trade.buyContract, secret, trade.sellContract)
|
||||||
print("\nTX Type", tx_type)
|
setattr(trade.buyContract, 'redeem_tx', txid)
|
||||||
setattr(trade.buyContract, tx_type, txid)
|
|
||||||
save(trade)
|
save(trade)
|
||||||
print("You have redeemed {0} {1}!".format(buy.amount, buy.currency))
|
print("You have redeemed {0} {1}!".format(buy.amount, buy.currency))
|
||||||
|
|
||||||
def redeem_two(trade):
|
def redeem_buyer(trade):
|
||||||
|
print("BUYER REDEEMING SELL CONTRACT")
|
||||||
|
print("=============================")
|
||||||
|
|
||||||
if trade.sellContract.get_status() == 'redeemed':
|
if trade.sellContract.get_status() == 'redeemed':
|
||||||
raise RuntimeError("Sell contract was redeemed before buyer could retrieve funds")
|
raise RuntimeError("Sell contract was redeemed before buyer could retrieve funds")
|
||||||
elif trade.buyContract.get_status() == 'refunded':
|
elif trade.buyContract.get_status() == 'refunded':
|
||||||
|
@ -77,11 +91,11 @@ def redeem_two(trade):
|
||||||
else:
|
else:
|
||||||
# Buy contract is where seller disclosed secret in redeeming
|
# Buy contract is where seller disclosed secret in redeeming
|
||||||
if trade.buyContract.currency == 'bitcoin':
|
if trade.buyContract.currency == 'bitcoin':
|
||||||
secret = bXcat.parse_secret(trade.buyContract.redeem_tx)
|
secret = bXcat.find_secret(trade.buyContract.p2sh,trade.buyContract.fund_tx)
|
||||||
else:
|
else:
|
||||||
secret = zXcat.parse_secret(trade.buyContract.redeem_tx)
|
secret = zXcat.find_secret(trade.buyContract.p2sh,trade.buyContract.fund_tx)
|
||||||
print("Found secret in seller's redeem tx", secret)
|
print("Found secret in seller's redeem tx", secret)
|
||||||
redeem_tx = redeem_p2sh(trade.sellContract, secret)
|
redeem_tx = redeem_p2sh(trade.sellContract, secret, trade.buyContract)
|
||||||
setattr(trade.sellContract, 'redeem_tx', redeem_tx)
|
setattr(trade.sellContract, 'redeem_tx', redeem_tx)
|
||||||
save(trade)
|
save(trade)
|
||||||
|
|
||||||
|
@ -90,12 +104,11 @@ def generate_blocks(num):
|
||||||
zXcat.generate(num)
|
zXcat.generate(num)
|
||||||
|
|
||||||
initiate(htlcTrade)
|
initiate(htlcTrade)
|
||||||
fulfill(htlcTrade)
|
buyer_fulfill(htlcTrade)
|
||||||
|
|
||||||
generate_blocks(6)
|
# generate_blocks(12)
|
||||||
|
redeem_seller(htlcTrade)
|
||||||
redeem_one(htlcTrade)
|
redeem_buyer(htlcTrade)
|
||||||
redeem_two(htlcTrade)
|
|
||||||
|
|
||||||
# addr = CBitcoinAddress('tmFRXyju7ANM7A9mg75ZjyhFW1UJEhUPwfQ')
|
# addr = CBitcoinAddress('tmFRXyju7ANM7A9mg75ZjyhFW1UJEhUPwfQ')
|
||||||
# print(addr)
|
# print(addr)
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
{"sell": {"currency": "bitcoin", "p2sh": "2NGSX1vKsbUoNYusifWXStb1QBRmsh4KYte", "redeemblocknum": 6482, "amount": "0.5", "redeemScript": "63a820dc1abac5a0994304dffd84e49e4a0b9d65decf323025d634d0b01e48b8b444558876a9147788b4511a25fba1092e67b307a6dcdb6da125d967025219b17576a914c7043e62a7391596116f54f6a64c8548e97d3fd96888ac", "fulfiller": "mrQzUGU1dwsWRx5gsKKSDPNtrsP65vCA3Z", "initiator": "myfFr5twPYNwgeXyjCmGcrzXtCmfmWXKYp", "fund_tx": "70df04f83baeb3fc626f721945712f39eacfa72f60cc416ea07e692bb2b08c36"}, "buy": {"currency": "zcash", "p2sh": "t27J7ZQYmbu8AVxTEMkDE92JTPoA1kag3hV", "redeemblocknum": 646, "amount": "1.12", "redeemScript": "63a820dc1abac5a0994304dffd84e49e4a0b9d65decf323025d634d0b01e48b8b444558876a9143ea29256c9d2888ca23de42a8b8e69ca2ec235b167028602b17576a914c5acca6ef39c843c7a9c3ad01b2da95fe2edf5ba6888ac", "refund_tx": "8ace9f4ea8fbc570147b134fc7eda8448396bf19453e704315ed97bbff947d59", "fulfiller": "tmTjZSg4pX2Us6V5HttiwFZwj464fD2ZgpY", "initiator": "tmFRXyju7ANM7A9mg75ZjyhFW1UJEhUPwfQ", "fund_tx": "1af049328d793c616862b0946e14e998966d16e8102726a0252d41db992095cd"}}
|
{"sell": {"redeemblocknum": 7118, "redeem_tx": "707015331518425a82672ae90ab1a04dd482be485961ba150895479f48aafb41", "redeemScript": "63a8201f6c4e355816e9660f156acd281f3e2dc3d71d4da1fe63560ef7b68b2384a9f08876a91447c15e08aa91bd2cb9ab48bd22c14a0bcdcc635d6702ce1bb17576a914ce26fe4177ea51f9ac1606f6558d6b1d3c6753a56888ac", "fulfiller": "mn4Mv54k2Pem7KfCdpuv6rRX3SUUqUMHdE", "initiator": "mzJzCs8ikH9zf9o7WPBfyi7tjuyPwuFfsF", "amount": "0.5", "currency": "bitcoin", "fund_tx": "3751cd28a7db614417d88462931d355552006074922f48c2b723d05a49f5ea9f", "p2sh": "2N6xFjJvP693JyaY9tVendhiQHgFEwarzbV"}, "buy": {"redeemblocknum": 1284, "redeem_tx": "837445320d9ad9e329f35a293d9f105af62a749b052936d77951491ac4b314dd", "redeemScript": "63a8201f6c4e355816e9660f156acd281f3e2dc3d71d4da1fe63560ef7b68b2384a9f08876a914f1fcc37118dc3cba7988dcad373139bdee70f33b67020405b17576a914a4424b672dd9656f439a113cd29479ed8f5cf1c76888ac", "fulfiller": "tmQgsZrgZuGb1CRxd2qTRW93Ppec1JNf9fX", "initiator": "tmXmryiQfjvbgZ5C3cjbDtzisAmyMuTpqkY", "amount": "1.12", "currency": "zcash", "fund_tx": "26a10d06edc78a5d57510e990971f1a3b44a1217b42b74881230c0f374781efc", "p2sh": "t2DBJ2JdWhcakL4LHz4uyeb9jpTGuBHQDBR"}}
|
21
xcat.py
21
xcat.py
|
@ -70,14 +70,25 @@ def create_buy_p2sh(trade, secret, locktime):
|
||||||
|
|
||||||
save(trade)
|
save(trade)
|
||||||
|
|
||||||
def redeem_p2sh(contract, secret):
|
# we try to redeem contract with secret
|
||||||
|
# we try to redeem revertcontract with time lock
|
||||||
|
def redeem_p2sh(contract, secret, revertcontract):
|
||||||
currency = contract.currency
|
currency = contract.currency
|
||||||
print("in redeem function zcash")
|
|
||||||
if currency == 'bitcoin':
|
if currency == 'bitcoin':
|
||||||
res = bXcat.auto_redeem(contract, secret)
|
if(bXcat.still_locked(contract)):
|
||||||
|
print("redeeming btc with secret:")
|
||||||
|
res = bXcat.redeem_with_secret(contract, secret)
|
||||||
|
else:
|
||||||
|
print("redeeming zec with timelock:")
|
||||||
|
res = zXcat.redeem_after_timelock(revertcontract)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
res = zXcat.auto_redeem(contract, secret)
|
if(zXcat.still_locked(contract)):
|
||||||
print("in redeem function zcash")
|
print("redeeming zec with secret:")
|
||||||
|
res = zXcat.redeem_with_secret(contract, secret)
|
||||||
|
else:
|
||||||
|
print("redeeming btc with timelock:")
|
||||||
|
res = bXcat.redeem_after_timelock(revertcontract)
|
||||||
|
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
|
151
zXcat.py
151
zXcat.py
|
@ -60,9 +60,10 @@ def fund_htlc(p2sh, amount):
|
||||||
return txid
|
return txid
|
||||||
|
|
||||||
def check_funds(p2sh):
|
def check_funds(p2sh):
|
||||||
zcashd.importaddress(p2sh, "", True) #Ariel: changed this to true
|
zcashd.importaddress(p2sh, "", False) #Ariel: changed this to true
|
||||||
print("Imported address", p2sh)
|
print("Imported address", p2sh)
|
||||||
# Get amount in address
|
# Get amount in address
|
||||||
|
print("findtxtoaddr:::::::", find_transaction_to_address(p2sh))
|
||||||
amount = zcashd.getreceivedbyaddress(p2sh, 0)
|
amount = zcashd.getreceivedbyaddress(p2sh, 0)
|
||||||
print("Amount in address", amount)
|
print("Amount in address", amount)
|
||||||
amount = amount/COIN
|
amount = amount/COIN
|
||||||
|
@ -93,24 +94,28 @@ def find_transaction_to_address(p2sh):
|
||||||
#
|
#
|
||||||
# return fund_txinfo['details'][0]
|
# return fund_txinfo['details'][0]
|
||||||
|
|
||||||
def find_secret(p2sh):
|
def find_secret(p2sh,vinid):
|
||||||
return parse_secret('4c25b5db9f3df48e48306891d8437c69308afa122f92416df1a3ba0d3604882f')
|
zcashd.importaddress(p2sh, "", True)
|
||||||
zcashd.importaddress(p2sh, "", False)
|
|
||||||
# is this working?
|
# is this working?
|
||||||
txs = zcashd.listtransactions()
|
txs = zcashd.listtransactions()
|
||||||
for tx in txs:
|
for tx in txs:
|
||||||
# print("tx addr:", tx['address'])
|
print("tx addr:", tx['txid'])
|
||||||
# print(type(tx['address']))
|
# print(type(tx['address']))
|
||||||
# print(type(p2sh))
|
# print(type(p2sh))
|
||||||
if (tx['address'] == p2sh ) and (tx['category'] == "send"):
|
raw = zcashd.getrawtransaction(lx(tx['txid']),True)['hex']
|
||||||
print(type(tx['txid']))
|
decoded = zcashd.decoderawtransaction(raw)
|
||||||
print(str.encode(tx['txid']))
|
print("fdsfdfds", decoded['vin'][0])
|
||||||
raw = zcashd.getrawtransaction(lx(tx['txid']),True)['hex']
|
if('txid' in decoded['vin'][0]):
|
||||||
decoded = zcashd.decoderawtransaction(raw)
|
sendid = decoded['vin'][0]['txid']
|
||||||
print("deo:", decoded['vin'][0]['scriptSig']['asm'])
|
print("sendid:", sendid)
|
||||||
|
|
||||||
|
if (sendid == vinid ):
|
||||||
|
print(type(tx['txid']))
|
||||||
|
print(str.encode(tx['txid']))
|
||||||
|
return parse_secret(lx(tx['txid']))
|
||||||
|
|
||||||
def parse_secret(txid):
|
def parse_secret(txid):
|
||||||
raw = zcashd.gettransaction(lx(txid), True)['hex']
|
raw = zcashd.gettransaction(txid, True)['hex']
|
||||||
# print("Raw", raw)
|
# print("Raw", raw)
|
||||||
decoded = zcashd.decoderawtransaction(raw)
|
decoded = zcashd.decoderawtransaction(raw)
|
||||||
scriptSig = decoded['vin'][0]['scriptSig']
|
scriptSig = decoded['vin'][0]['scriptSig']
|
||||||
|
@ -144,8 +149,8 @@ def auto_redeem(contract, secret):
|
||||||
|
|
||||||
# Parsing redeemblocknum from the redeemscript of the p2sh
|
# Parsing redeemblocknum from the redeemscript of the p2sh
|
||||||
redeemblocknum = find_redeemblocknum(contract)
|
redeemblocknum = find_redeemblocknum(contract)
|
||||||
blockcount = bitcoind.getblockcount()
|
blockcount = zcashd.getblockcount()
|
||||||
print("\nCurrent blocknum at time of redeem on Bitcoin:", blockcount)
|
print("\nCurrent blocknum at time of redeem on Zcash chain:", blockcount)
|
||||||
if blockcount < redeemblocknum:
|
if blockcount < redeemblocknum:
|
||||||
redeemPubKey = find_redeemAddr(contract)
|
redeemPubKey = find_redeemAddr(contract)
|
||||||
print('redeemPubKey', redeemPubKey)
|
print('redeemPubKey', redeemPubKey)
|
||||||
|
@ -169,7 +174,7 @@ def auto_redeem(contract, secret):
|
||||||
tx.nLockTime = redeemblocknum
|
tx.nLockTime = redeemblocknum
|
||||||
sighash = SignatureHash(zec_redeemScript, tx, 0, SIGHASH_ALL)
|
sighash = SignatureHash(zec_redeemScript, tx, 0, SIGHASH_ALL)
|
||||||
# TODO: figure out how to better protect privkey
|
# TODO: figure out how to better protect privkey
|
||||||
privkey = bitcoind.dumpprivkey(redeemPubKey)
|
privkey = zcashd.dumpprivkey(redeemPubKey)
|
||||||
sig = privkey.sign(sighash) + bytes([SIGHASH_ALL])
|
sig = privkey.sign(sighash) + bytes([SIGHASH_ALL])
|
||||||
print("SECRET", secret)
|
print("SECRET", secret)
|
||||||
preimage = secret.encode('utf-8')
|
preimage = secret.encode('utf-8')
|
||||||
|
@ -235,9 +240,121 @@ def find_recipient(contract):
|
||||||
|
|
||||||
def new_zcash_addr():
|
def new_zcash_addr():
|
||||||
addr = zcashd.getnewaddress()
|
addr = zcashd.getnewaddress()
|
||||||
print('new ZEC addr', addr.to_p2sh_scriptPubKey)
|
print('new ZEC addr', addr)
|
||||||
return addr.to_scriptPubKey()
|
return addr
|
||||||
|
|
||||||
def generate(num):
|
def generate(num):
|
||||||
blocks = zcashd.generate(num)
|
blocks = zcashd.generate(num)
|
||||||
return blocks
|
return blocks
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# redeems funded tx automatically, by scanning for transaction to the p2sh
|
||||||
|
# i.e., doesn't require buyer telling us fund txid
|
||||||
|
# returns false if fund tx doesn't exist or is too small
|
||||||
|
def redeem_with_secret(contract, secret):
|
||||||
|
# How to find redeemScript and redeemblocknum from blockchain?
|
||||||
|
print("Redeeming contract using secret", contract.__dict__)
|
||||||
|
p2sh = contract.p2sh
|
||||||
|
minamount = float(contract.amount)
|
||||||
|
#checking there are funds in the address
|
||||||
|
amount = check_funds(p2sh)
|
||||||
|
if(amount < minamount):
|
||||||
|
print("address ", p2sh, " not sufficiently funded")
|
||||||
|
return false
|
||||||
|
fundtx = find_transaction_to_address(p2sh)
|
||||||
|
amount = fundtx['amount'] / COIN
|
||||||
|
print("Found fundtx:", fundtx)
|
||||||
|
p2sh = P2SHBitcoinAddress(p2sh)
|
||||||
|
if fundtx['address'] == p2sh:
|
||||||
|
print("Found {0} in p2sh {1}, redeeming...".format(amount, p2sh))
|
||||||
|
|
||||||
|
redeemPubKey = find_redeemAddr(contract)
|
||||||
|
print('redeemPubKey', redeemPubKey)
|
||||||
|
|
||||||
|
redeemScript = CScript(x(contract.redeemScript))
|
||||||
|
txin = CMutableTxIn(fundtx['outpoint'])
|
||||||
|
txout = CMutableTxOut(fundtx['amount'] - FEE, redeemPubKey.to_scriptPubKey())
|
||||||
|
# Create the unsigned raw transaction.
|
||||||
|
tx = CMutableTransaction([txin], [txout])
|
||||||
|
sighash = SignatureHash(redeemScript, tx, 0, SIGHASH_ALL)
|
||||||
|
# TODO: figure out how to better protect privkey
|
||||||
|
privkey = zcashd.dumpprivkey(redeemPubKey)
|
||||||
|
sig = privkey.sign(sighash) + bytes([SIGHASH_ALL])
|
||||||
|
print("SECRET", secret)
|
||||||
|
preimage = secret.encode('utf-8')
|
||||||
|
txin.scriptSig = CScript([sig, privkey.pub, preimage, OP_TRUE, redeemScript])
|
||||||
|
|
||||||
|
# exit()
|
||||||
|
|
||||||
|
print("txin.scriptSig", b2x(txin.scriptSig))
|
||||||
|
txin_scriptPubKey = redeemScript.to_p2sh_scriptPubKey()
|
||||||
|
print('Redeem txhex', b2x(tx.serialize()))
|
||||||
|
VerifyScript(txin.scriptSig, txin_scriptPubKey, tx, 0, (SCRIPT_VERIFY_P2SH,))
|
||||||
|
print("script verified, sending raw tx")
|
||||||
|
txid = zcashd.sendrawtransaction(tx)
|
||||||
|
print("Txid of submitted redeem tx: ", b2x(lx(b2x(txid))))
|
||||||
|
return b2x(lx(b2x(txid)))
|
||||||
|
else:
|
||||||
|
print("No contract for this p2sh found in database", p2sh)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# given a contract return true or false according to whether the relevant fund tx's timelock is still valid
|
||||||
|
def still_locked(contract):
|
||||||
|
p2sh = contract.p2sh
|
||||||
|
# Parsing redeemblocknum from the redeemscript of the p2sh
|
||||||
|
redeemblocknum = find_redeemblocknum(contract)
|
||||||
|
blockcount = zcashd.getblockcount()
|
||||||
|
print(blockcount, redeemblocknum, blockcount<redeemblocknum)
|
||||||
|
return (int(blockcount) < int(redeemblocknum))
|
||||||
|
|
||||||
|
def redeem_after_timelock(contract):
|
||||||
|
print("Contract in auto redeem", contract.__dict__)
|
||||||
|
p2sh = contract.p2sh
|
||||||
|
fundtx = find_transaction_to_address(p2sh)
|
||||||
|
amount = fundtx['amount'] / COIN
|
||||||
|
|
||||||
|
if (fundtx['address'].__str__() != p2sh):
|
||||||
|
print("no fund transaction found to the contract p2sh address ",p2sh)
|
||||||
|
quit()
|
||||||
|
print("Found fundtx:", fundtx)
|
||||||
|
# Parsing redeemblocknum from the redeemscript of the p2sh
|
||||||
|
redeemblocknum = find_redeemblocknum(contract)
|
||||||
|
blockcount = zcashd.getblockcount()
|
||||||
|
print ("Current block:", blockcount, "Can redeem from block:", redeemblocknum)
|
||||||
|
if(still_locked(contract)):
|
||||||
|
print("too early for redeeming with timelock")
|
||||||
|
quit()
|
||||||
|
|
||||||
|
print("Found {0} in p2sh {1}, redeeming...".format(amount, p2sh))
|
||||||
|
|
||||||
|
|
||||||
|
redeemPubKey = find_refundAddr(contract)
|
||||||
|
print('refundPubKey', redeemPubKey)
|
||||||
|
|
||||||
|
redeemScript = CScript(x(contract.redeemScript))
|
||||||
|
txin = CMutableTxIn(fundtx['outpoint'])
|
||||||
|
txout = CMutableTxOut(fundtx['amount'] - FEE, redeemPubKey.to_scriptPubKey())
|
||||||
|
# Create the unsigned raw transaction.
|
||||||
|
txin.nSequence = 0
|
||||||
|
tx = CMutableTransaction([txin], [txout])
|
||||||
|
tx.nLockTime = redeemblocknum
|
||||||
|
|
||||||
|
sighash = SignatureHash(redeemScript, tx, 0, SIGHASH_ALL)
|
||||||
|
# TODO: figure out how to better protect privkey
|
||||||
|
privkey = zcashd.dumpprivkey(redeemPubKey)
|
||||||
|
sig = privkey.sign(sighash) + bytes([SIGHASH_ALL])
|
||||||
|
txin.scriptSig = CScript([sig, privkey.pub, OP_FALSE, redeemScript])
|
||||||
|
|
||||||
|
# exit()
|
||||||
|
|
||||||
|
print("txin.scriptSig", b2x(txin.scriptSig))
|
||||||
|
txin_scriptPubKey = redeemScript.to_p2sh_scriptPubKey()
|
||||||
|
print('Redeem txhex', b2x(tx.serialize()))
|
||||||
|
VerifyScript(txin.scriptSig, txin_scriptPubKey, tx, 0, (SCRIPT_VERIFY_P2SH,))
|
||||||
|
print("script verified, sending raw tx")
|
||||||
|
txid = zcashd.sendrawtransaction(tx)
|
||||||
|
print("Txid of submitted redeem tx: ", b2x(lx(b2x(txid))))
|
||||||
|
return b2x(lx(b2x(txid)))
|
||||||
|
|
Loading…
Reference in New Issue