Get all the way through trade
This commit is contained in:
parent
b37dbcfa9a
commit
c832698870
39
bXcat.py
39
bXcat.py
|
@ -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()
|
||||
|
|
|
@ -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"}}
|
|
@ -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
46
xcat.py
|
@ -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
|
||||
|
|
22
zXcat.py
22
zXcat.py
|
@ -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()
|
||||
|
|
Loading…
Reference in New Issue