Get all the way through trade

This commit is contained in:
Jay Graber 2017-05-23 12:28:48 -07:00
parent b37dbcfa9a
commit c832698870
5 changed files with 84 additions and 27 deletions

View File

@ -90,14 +90,13 @@ def search_p2sh(block, p2sh):
def get_tx_details(txid):
# must convert txid string to bytes x(txid)
fund_txinfo = bitcoind.gettransaction(x(txid))
fund_txinfo = bitcoind.gettransaction(lx(txid))
return fund_txinfo['details'][0]
def redeem(p2sh, action):
# make sure p2sh is imported to redeem:
print("Importing p2sh", p2sh)
# ensure p2sh is imported
bitcoind.importaddress(p2sh, '', False)
# action is buy or sell
contracts = get_contract()
trade = get_trade()
for key in contracts:
@ -111,28 +110,52 @@ def redeem(p2sh, action):
txid = trade[action]['fund_tx']
details = get_tx_details(txid)
txin = CMutableTxIn(COutPoint(txid, details['vout']))
print("Txid for fund tx", txid)
# must be little endian hex
txin = CMutableTxIn(COutPoint(lx(txid), details['vout']))
redeemPubKey = CBitcoinAddress(contract['redeemer'])
txout = CMutableTxOut(details['amount'] - FEE, redeemPubKey.to_scriptPubKey())
amount = trade[action]['amount'] * COIN
print("amount: {0}, fee: {1}".format(amount, FEE))
txout = CMutableTxOut(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...
# Is stored as hex, must convert to bytes
zec_redeemScript = CScript(x(zec_redeemScript))
tx.nLockTime = redeemblocknum
sighash = SignatureHash(zec_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, contract['secret'], OP_TRUE, zec_redeemScript])
# TODO: Figure out where to store secret preimage securely. Parse from scriptsig of redeemtx
secret = trade['sell']['secret']
preimage = secret.encode('utf-8')
print('preimage', preimage)
print('zec_redeemScript', zec_redeemScript)
txin.scriptSig = CScript([sig, privkey.pub, preimage, OP_TRUE, zec_redeemScript])
print("Redeem tx hex:", b2x(tx.serialize()))
# Can only call to_p2sh_scriptPubKey on CScript obj
txin_scriptPubKey = zec_redeemScript.to_p2sh_scriptPubKey()
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,))
print("script verified, sending raw tx")
print("Raw tx", b2x(tx.serialize()))
txid = bitcoind.sendrawtransaction(tx)
print("Txid of submitted redeem tx: ", b2x(lx(b2x(txid))))
txhex = b2x(lx(b2x(txid)))
print("Txid of submitted redeem tx: ", txhex)
return txhex
else:
print("No contract for this p2sh found in database", p2sh)
def new_bitcoin_addr():
addr = bitcoind.getnewaddress()
print('new btc addr', addr.to_scriptPubKey)
return addr.to_scriptPubKey()

View File

@ -1 +1 @@
{"t2DCi5JDmTmassfxwSY23EMgP1sY6WGRzrp": {"redeemblocknum": 182, "p2sh": "t2DCi5JDmTmassfxwSY23EMgP1sY6WGRzrp", "zec_redeemScript": "63a8209f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a088876a914e712f28ffbd6fd052b9005b4edf6f81c77250e426702b600b17576a914c6caa1718668c52a8edf64c4db304d8cc686631e6888ac", "funder": "tmTqTsBFkeKXyawHfZfcAZQY47xEhpEbo1E", "redeemer": "tmWnA7ypaCtpG7KhEWfr5XA1Rpm8521yMfX"}, "2N1Up8hp196ZXknnxGAUYHXdnA6eHMLg3xM": {"redeemblocknum": 146, "p2sh": "2N1Up8hp196ZXknnxGAUYHXdnA6eHMLg3xM", "zec_redeemScript": "63a8209f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a088876a9140558ff2d7aef3bd313c6e573da17ba1b78ddb75467029200b17576a914679da51094981fee82ab4391131f0670d61401306888ac", "funder": "mpxpkAUatZR45rdrWQSjkUK7z9LyeSMoEr", "redeemer": "mg1EHcpWyErmGhMvpZ9ch2qzFE7ZTKuaEy"}}
{"t27tZqKoQhdCcDeNL2cghne6RiCA5cR4vS9": {"p2sh": "t27tZqKoQhdCcDeNL2cghne6RiCA5cR4vS9", "redeemer": "tmFRXyju7ANM7A9mg75ZjyhFW1UJEhUPwfQ", "funder": "tmTjZSg4pX2Us6V5HttiwFZwj464fD2ZgpY", "redeemblocknum": 182, "zec_redeemScript": "63a8209f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a088876a9143ea29256c9d2888ca23de42a8b8e69ca2ec235b16702b600b17576a914c5acca6ef39c843c7a9c3ad01b2da95fe2edf5ba6888ac"}, "2MwNfS9dETRaJRNM9jU14NXvSg8L2Suqygx": {"p2sh": "2MwNfS9dETRaJRNM9jU14NXvSg8L2Suqygx", "redeemer": "mrQzUGU1dwsWRx5gsKKSDPNtrsP65vCA3Z", "funder": "myfFr5twPYNwgeXyjCmGcrzXtCmfmWXKYp", "redeemblocknum": 146, "zec_redeemScript": "63a8209f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a088876a9147788b4511a25fba1092e67b307a6dcdb6da125d967029200b17576a914c7043e62a7391596116f54f6a64c8548e97d3fd96888ac"}}

View File

@ -1 +1 @@
{"id": 1, "sell": {"initiator": "mpxpkAUatZR45rdrWQSjkUK7z9LyeSMoEr", "fulfiller": "mg1EHcpWyErmGhMvpZ9ch2qzFE7ZTKuaEy", "currency": "bitcoin", "secret": "test", "amount": 1.2, "p2sh": "2N1Up8hp196ZXknnxGAUYHXdnA6eHMLg3xM", "status": "funded", "fund_tx": "d42e8537766bc50aadcfa2fced784e401a64909c65b63e6e60416eb96e7c17d8"}, "buy": {"initiator": "tmWnA7ypaCtpG7KhEWfr5XA1Rpm8521yMfX", "fulfiller": "tmTqTsBFkeKXyawHfZfcAZQY47xEhpEbo1E", "currency": "zcash", "amount": 2.45, "p2sh": "t2DCi5JDmTmassfxwSY23EMgP1sY6WGRzrp", "fund_tx": "a0c27e7cb023053d5bffd8c1bdc8a1b44a1def3e3d885d28ef37f94aef08cb1b"}}
{"id": 1, "sell": {"initiator": "myfFr5twPYNwgeXyjCmGcrzXtCmfmWXKYp", "currency": "bitcoin", "fund_tx": "3796f3ccd8ecc2b11603862648e09e7d95d3cbbe869c25f59484abcecd85f2db", "p2sh": "2MwNfS9dETRaJRNM9jU14NXvSg8L2Suqygx", "status": "redeemed", "fulfiller": "mrQzUGU1dwsWRx5gsKKSDPNtrsP65vCA3Z", "redeem_tx": "e814851af2676e4e981e6acae567071cc3531b5102239de211d4ab926e5d92ad", "amount": 1.2, "secret": "test"}, "buy": {"status": "redeemed", "redeem_tx": "d95a802145b7810c9cf1df925ab1f498d14a81c6b09b4d2e00c77e19ec6ad481", "fund_tx": "b96ab6c8112af25c2c288d983bcdb094c644f097c526d20f8493f1af39ce7d72", "amount": 2.45, "initiator": "tmFRXyju7ANM7A9mg75ZjyhFW1UJEhUPwfQ", "fulfiller": "tmTjZSg4pX2Us6V5HttiwFZwj464fD2ZgpY", "currency": "zcash", "p2sh": "t27tZqKoQhdCcDeNL2cghne6RiCA5cR4vS9"}}

46
xcat.py
View File

@ -93,16 +93,24 @@ def get_addresses():
buy = trade['buy']['currency']
init_offer_addr = input("Enter your {0} address: ".format(sell))
init_offer_addr = 'mpxpkAUatZR45rdrWQSjkUK7z9LyeSMoEr'
# init_offer_addr = bXcat.new_bitcoin_addr()
init_offer_addr = 'myfFr5twPYNwgeXyjCmGcrzXtCmfmWXKYp'
print(init_offer_addr)
init_bid_addr = input("Enter your {0} address: ".format(buy))
init_bid_addr = 'tmWnA7ypaCtpG7KhEWfr5XA1Rpm8521yMfX'
# init_bid_addr = zXcat.new_zcash_addr()
init_bid_addr = 'tmFRXyju7ANM7A9mg75ZjyhFW1UJEhUPwfQ'
print(init_bid_addr)
trade['sell']['initiator'] = init_offer_addr
trade['buy']['initiator'] = init_bid_addr
fulfill_offer_addr = input("Enter the {0} address of the party you want to trade with: ".format(sell))
fulfill_offer_addr = 'mg1EHcpWyErmGhMvpZ9ch2qzFE7ZTKuaEy'
# fulfill_offer_addr = bXcat.new_bitcoin_addr()
fulfill_offer_addr = 'mrQzUGU1dwsWRx5gsKKSDPNtrsP65vCA3Z'
print(fulfill_offer_addr)
fulfill_bid_addr = input("Enter the {0} address of the party you want to trade with: ".format(buy))
fulfill_bid_addr = 'tmTqTsBFkeKXyawHfZfcAZQY47xEhpEbo1E'
# fulfill_bid_addr = zXcat.new_zcash_addr()
print(fulfill_bid_addr)
fulfill_bid_addr = 'tmTjZSg4pX2Us6V5HttiwFZwj464fD2ZgpY'
trade['sell']['fulfiller'] = fulfill_offer_addr
trade['buy']['fulfiller'] = fulfill_bid_addr
@ -131,11 +139,12 @@ def buyer_fulfill():
input("Type 'enter' to allow this program to send the agreed upon funds on your behalf")
txid = fund_htlc(currency, p2sh, amount)
trade['buy']['fund_tx'] = txid
save_trade(trade)
else:
print("It looks like you've already funded the contract to buy {1}, the amount in escrow in the p2sh is {0}.".format(amount, currency))
print("Please wait for the seller to remove your funds from escrow to complete the trade.")
save_trade(trade)
def check_blocks(p2sh):
# blocks = []
@ -161,16 +170,23 @@ def seller_redeem():
# Seller redeems buyer's funded tx (contract in p2sh)
p2sh = trade['buy']['p2sh']
currency = trade['buy']['currency']
redeem_p2sh(currency, p2sh, 'buy')
redeem_tx = redeem_p2sh(currency, p2sh, 'buy')
trade['buy']['redeem_tx'] = redeem_tx
trade['buy']['status'] = 'redeemed'
save_trade(trade)
def buyer_redeem():
trade = get_trade()
# Buyer redeems seller's funded tx
p2sh = trade['sell']['p2sh']
currency = trade['sell']['currency']
redeem_p2sh(currency, p2sh, 'sell')
redeem_tx = redeem_p2sh(currency, p2sh, 'sell')
trade['sell']['redeem_tx'] = redeem_tx
trade['sell']['status'] = 'redeemed'
save_trade(trade)
if __name__ == '__main__':
print("ZEC <-> BTC XCAT (Cross-Chain Atomic Transactions)")
role = input("Would you like to initiate or accept a trade?")
# Have initiator propose amounts to trade
@ -182,6 +198,10 @@ if __name__ == '__main__':
# If there is no status on a sell order (for this json file db...) we assume you must initiate_trade
if 'status' not in trade['sell']:
role = 'i'
elif trade['sell']['status'] == 'redeemed' and trade['buy']['status'] == 'redeemed':
print("This trade is already complete! Trade details:")
pprint(trade)
exit()
if role == "i":
if 'status' not in trade['sell']:
@ -195,16 +215,18 @@ if __name__ == '__main__':
print("Buyer funded the contract where you offered to buy {0}, redeeming funds from {1}...".format(trade['buy']['currency'], trade['buy']['p2sh']))
seller_redeem()
else:
# if 'status' not in trade['buy']:
if trade['sell']['status'] == 'funded':
if trade['buy']['status'] == 'redeemed':
# Seller has redeemed buyer's tx, buyer can now redeem.
print("The seller has redeemed the contract where you paid them in {0}, now redeeming your funds from {1}".format(trade['buy']['currency'], trade['sell']['p2sh']))
buyer_redeem()
elif trade['sell']['status'] == 'funded':
trade = get_trade()
buyer_fulfill()
# How to monitor if txs are included in blocks -- should use blocknotify and a monitor daemon?
# For regtest, can mock in a function
# p2sh = trade['buy']['p2sh']
# check_blocks(p2sh)
elif trade['sell']['status'] == 'redeemed':
# Seller has redeemed buyer's tx, buyer can now redeem.
buyer_redeem()
pprint(get_trade())
# Note: there is some little endian weirdness in the bXcat and zXcat files, need to handle the endianness of txids better & more consistently

View File

@ -86,7 +86,8 @@ def redeem(p2sh, action):
txid = trade[action]['fund_tx']
details = get_tx_details(txid)
print("Txid for fund tx", txid)
txin = CMutableTxIn(COutPoint(x(txid), details['vout']))
# must be little endian hex
txin = CMutableTxIn(COutPoint(lx(txid), details['vout']))
redeemPubKey = CBitcoinAddress(contract['redeemer'])
amount = trade[action]['amount'] * COIN
print("amount: {0}, fee: {1}".format(amount, FEE))
@ -96,6 +97,9 @@ def redeem(p2sh, action):
# 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...
# Is stored as hex, must convert to bytes
zec_redeemScript = CScript(x(zec_redeemScript))
tx.nLockTime = redeemblocknum
sighash = SignatureHash(zec_redeemScript, tx, 0, SIGHASH_ALL)
# TODO: figure out how to better protect privkey?
@ -105,21 +109,29 @@ def redeem(p2sh, action):
secret = trade['sell']['secret']
preimage = secret.encode('utf-8')
print('preimage', preimage)
# Is stored as hex, must convert to bytes
zec_redeemScript = x(zec_redeemScript)
print('zec_redeemScript', zec_redeemScript)
txin.scriptSig = CScript([sig, privkey.pub, preimage, OP_TRUE, zec_redeemScript])
print("Redeem tx hex:", b2x(tx.serialize()))
# Can only call to_p2sh_scriptPubKey on CScript obj
txin_scriptPubKey = CScript(zec_redeemScript).to_p2sh_scriptPubKey()
txin_scriptPubKey = zec_redeemScript.to_p2sh_scriptPubKey()
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,))
print("script verified, sending raw tx")
print("Raw tx", b2x(tx.serialize()))
txid = zcashd.sendrawtransaction(tx)
print("Txid of submitted redeem tx: ", b2x(lx(b2x(txid))))
txhex = b2x(lx(b2x(txid)))
print("Txid of submitted redeem tx: ", txhex)
return txhex
else:
print("No contract for this p2sh found in database", p2sh)
def new_zcash_addr():
addr = zcashd.getnewaddress()
print('new ZEC addr', addr.to_p2sh_scriptPubKey)
return addr.to_scriptPubKey()