Add automated test, refund tx functionality

This commit is contained in:
Jay Graber 2017-05-31 12:03:16 -07:00
parent 3ee8606dcf
commit 84df93782b
6 changed files with 78 additions and 55 deletions

View File

@ -51,7 +51,7 @@ def hashtimelockcontract(funder, redeemer, secret, locktime):
blocknum = bitcoind.getblockcount()
print("Current blocknum", blocknum)
redeemblocknum = blocknum + locktime
print("REDEEMBLOCKNUM", redeemblocknum)
print("REDEEMBLOCKNUM BITCOIN", redeemblocknum)
redeemScript = CScript([OP_IF, OP_SHA256, h, OP_EQUALVERIFY,OP_DUP, OP_HASH160,
redeemerAddr, OP_ELSE, redeemblocknum, OP_CHECKLOCKTIMEVERIFY, OP_DROP, OP_DUP, OP_HASH160,
funderAddr, OP_ENDIF,OP_EQUALVERIFY, OP_CHECKSIG])
@ -123,28 +123,28 @@ def auto_redeem(contract, secret):
# Parsing redeemblocknum from the redeemscript of the p2sh
redeemblocknum = find_redeemblocknum(contract)
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)
tx.nLockTime = redeemblocknum
print('redeemPubKey', redeemPubKey)
print('refundPubKey', redeemPubKey)
# redeemPubKey = CBitcoinAddress.from_scriptPubKey(redeemPubKey)
# exit()
zec_redeemScript = CScript(x(contract.redeemScript))
# details = get_tx_details(txid)
# txin = CMutableTxIn(COutPoint(lx(txid), details['vout']))
txin = CMutableTxIn(fundtx['outpoint'])
txout = CMutableTxOut(fundtx['amount'] - FEE, redeemPubKey.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
# TODO: these things like redeemblocknum should really be properties of a tx class...
# Need: redeemblocknum, zec_redeemScript, secret (for creator...), txid, redeemer...
tx.nLockTime = redeemblocknum # Ariel: This is only needed when redeeming with the timelock
if blockcount >= redeemblocknum:
print("\nLocktime exceeded")
tx.nLockTime = redeemblocknum # Ariel: This is only needed when redeeming with the timelock
sighash = SignatureHash(zec_redeemScript, tx, 0, SIGHASH_ALL)
# TODO: figure out how to better protect privkey
privkey = bitcoind.dumpprivkey(redeemPubKey)
@ -185,7 +185,7 @@ def find_redeemAddr(contract):
def find_refundAddr(contract):
scriptarray = parse_script(contract.redeemScript)
funder = scriptarray[6]
funder = scriptarray[13]
refundAddr = P2PKHBitcoinAddress.from_bytes(x(funder))
return refundAddr
@ -209,7 +209,6 @@ def find_refundAddr(contract):
# redeemPubkey = P2PKHBitcoinAddress.from_pubkey(x(pubkey))
# print('redeemPubkey', redeemPubkey)
def find_transaction_to_address(p2sh):
bitcoind.importaddress(p2sh, "", False)
txs = bitcoind.listunspent()
@ -226,3 +225,7 @@ def new_bitcoin_addr():
addr = bitcoind.getnewaddress()
print('new btc addr', addr.to_scriptPubKey)
return addr.to_scriptPubKey()
def generate(num):
blocks = bitcoind.generate(num)
return blocks

View File

@ -1 +1 @@
1nXlLORk
Hm0Mla5n

24
test.py
View File

@ -1,3 +1,5 @@
import zXcat
import bXcat
from xcat import *
htlcTrade = Trade()
@ -33,12 +35,13 @@ def initiate(trade):
print("Generating secret to lock funds:", secret)
save_secret(secret)
# TODO: Implement locktimes and mock block passage of time
locktime = 20 # Must be more than first tx
sell_locktime = 2
buy_locktime = 4 # Must be more than first tx
create_sell_p2sh(trade, secret, locktime)
create_sell_p2sh(trade, secret, sell_locktime)
txid = fund_sell_contract(trade)
print("Sent")
create_buy_p2sh(trade, secret, locktime)
create_buy_p2sh(trade, secret, buy_locktime)
def fulfill(trade):
buy = trade.buyContract
@ -60,15 +63,17 @@ def redeem_one(trade):
else:
secret = get_secret()
print("GETTING SECRET IN TEST:", secret)
txid = redeem_p2sh(trade.buyContract, secret)
print("TX SUCCESSFULLY REDEEMED")
setattr(trade.buyContract, 'redeem_tx', txid)
tx_type, txid = redeem_p2sh(trade.buyContract, secret)
print("\nTX Type", tx_type)
setattr(trade.buyContract, 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':
raise RuntimeError("Sell contract was redeemed before buyer could retrieve funds")
elif trade.buyContract.get_status() == 'refunded':
print("buyContract was refunded to buyer")
else:
# Buy contract is where seller disclosed secret in redeeming
if trade.buyContract.currency == 'bitcoin':
@ -80,8 +85,15 @@ def redeem_two(trade):
setattr(trade.sellContract, 'redeem_tx', redeem_tx)
save(trade)
def generate_blocks(num):
bXcat.generate(num)
zXcat.generate(num)
initiate(htlcTrade)
fulfill(htlcTrade)
generate_blocks(6)
redeem_one(htlcTrade)
redeem_two(htlcTrade)

View File

@ -1 +1 @@
{"sell": {"redeemScript": "63a8208002f14ffc3869269ba5b233d3c934f37115c8821c590da9a28e4df6b44e773d8876a9147788b4511a25fba1092e67b307a6dcdb6da125d967029200b17576a914c7043e62a7391596116f54f6a64c8548e97d3fd96888ac", "redeem_tx": "ce654c459da4dae859aaf0e88ff8c427ad06874b56f0b332ae158914c67011f0", "p2sh": "2NDymuzi7wFP2SEcwicq3qBXqz2qVAyp9jJ", "currency": "bitcoin", "fund_tx": "ef4cb435c7d7e5fcf128538ae652d5f9920384f63aa39b5f29f6bc2f0fb1b0ea", "redeemblocknum": 146, "amount": "0.5", "fulfiller": "mrQzUGU1dwsWRx5gsKKSDPNtrsP65vCA3Z", "initiator": "myfFr5twPYNwgeXyjCmGcrzXtCmfmWXKYp"}, "buy": {"redeemScript": "63a8208002f14ffc3869269ba5b233d3c934f37115c8821c590da9a28e4df6b44e773d8876a9143ea29256c9d2888ca23de42a8b8e69ca2ec235b16702b600b17576a914c5acca6ef39c843c7a9c3ad01b2da95fe2edf5ba6888ac", "redeem_tx": "71fb28287ac315ad890f342b0b154f13895c836c6c7bff511c8d58bb6da0a752", "p2sh": "t26pfk2JKC4XzNLdcmXwrpPMTyb13SDWDjV", "currency": "zcash", "fund_tx": "828afa314f8ba0f75e1f3a808fbf39d57b0b2c71a445b90f6ac3b24bd8eddc3e", "redeemblocknum": 182, "amount": "1.12", "fulfiller": "tmTjZSg4pX2Us6V5HttiwFZwj464fD2ZgpY", "initiator": "tmFRXyju7ANM7A9mg75ZjyhFW1UJEhUPwfQ"}}
{"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}}

11
xcat.py
View File

@ -114,8 +114,8 @@ def seller_redeem(trade):
else:
# Seller redeems buyer's funded tx (contract in p2sh)
secret = userInput.retrieve_password()
txid = redeem_p2sh(trade.buyContract, secret)
setattr(trade.buyContract, 'redeem_tx', txid)
tx_type, txid = redeem_p2sh(trade.buyContract, secret)
setattr(trade.buyContract, tx_type, txid)
save(trade)
print("You have redeemed {0} {1}!".format(buy.amount, buy.currency))
print_trade('seller')
@ -158,16 +158,17 @@ def seller_initiate(trade):
secret = userInput.create_password()
# TODO: Implement locktimes and mock block passage of time
locktime = 20 # Must be more than first tx
sell_locktime = 5
buy_locktime = 10 # Must be more than first tx
create_sell_p2sh(trade, secret, locktime)
create_sell_p2sh(trade, secret, sell_locktime)
userInput.authorize_fund_sell(trade)
txid = fund_sell_contract(trade)
print("Sent")
create_buy_p2sh(trade, secret, locktime)
create_buy_p2sh(trade, secret, buy_locktime)
print_trade('seller')
if __name__ == '__main__':

View File

@ -41,7 +41,7 @@ def hashtimelockcontract(funder, redeemer, secret, locktime):
blocknum = zcashd.getblockcount()
print("Current blocknum", blocknum)
redeemblocknum = blocknum + locktime
print("REDEEMBLOCKNUM", redeemblocknum)
print("REDEEMBLOCKNUM ZCASH", redeemblocknum)
zec_redeemScript = CScript([OP_IF, OP_SHA256, h, OP_EQUALVERIFY,OP_DUP, OP_HASH160,
redeemerAddr, OP_ELSE, redeemblocknum, OP_CHECKLOCKTIMEVERIFY, OP_DROP, OP_DUP, OP_HASH160,
funderAddr, OP_ENDIF,OP_EQUALVERIFY, OP_CHECKSIG])
@ -145,39 +145,42 @@ def auto_redeem(contract, secret):
# Where can you find redeemblocknum in the transaction?
redeemblocknum = find_redeemblocknum(contract)
blockcount = zcashd.getblockcount()
print("\nCurrent blocknum at time of redeem on Zcash:", blockcount)
if blockcount < redeemblocknum:
redeemPubKey = find_redeemAddr(contract)
print('redeemPubKey', redeemPubKey)
zec_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(zec_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, zec_redeemScript])
print("txin.scriptSig", b2x(txin.scriptSig))
txin_scriptPubKey = zec_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))))
print("TXID SUCCESSFULLY REDEEMED")
return 'redeem_tx', b2x(lx(b2x(txid)))
else:
redeemPubKey = find_refundAddr(contract)
tx.nLockTime = redeemblocknum
print('redeemPubKey', redeemPubKey)
zec_redeemScript = CScript(x(contract.redeemScript))
# details = get_tx_details(txid)
# txin = CMutableTxIn(COutPoint(lx(txid), details['vout']))
txin = CMutableTxIn(fundtx['outpoint'])
txout = CMutableTxOut(fundtx['amount'] - FEE, redeemPubKey.to_scriptPubKey())
# Create the unsigned raw transaction.
tx = CMutableTransaction([txin], [txout])
sighash = SignatureHash(zec_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, zec_redeemScript])
print("txin.scriptSig", b2x(txin.scriptSig))
txin_scriptPubKey = zec_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)))
# if blockcount >= redeemblocknum:
# tx.nLockTime = redeemblocknum
print("nLocktime exceeded, refunding")
refundPubKey = find_refundAddr(contract)
print('refundPubKey', refundPubKey)
txid = zcashd.sendtoaddress(refundPubKey, fundtx['amount'] - FEE)
print("Txid of refund tx:", b2x(lx(b2x(txid))))
print("TXID SUCCESSFULLY REFUNDED")
return 'refund_tx', b2x(lx(b2x(txid)))
else:
print("No contract for this p2sh found in database", p2sh)
@ -199,7 +202,7 @@ def find_redeemAddr(contract):
def find_refundAddr(contract):
scriptarray = parse_script(contract.redeemScript)
funder = scriptarray[6]
funder = scriptarray[13]
refundAddr = P2PKHBitcoinAddress.from_bytes(x(funder))
return refundAddr
@ -230,3 +233,7 @@ def new_zcash_addr():
addr = zcashd.getnewaddress()
print('new ZEC addr', addr.to_p2sh_scriptPubKey)
return addr.to_scriptPubKey()
def generate(num):
blocks = zcashd.generate(num)
return blocks