Rename to commitment
This commit is contained in:
parent
2b7d00b73e
commit
941ef40390
79
bXcat.py
79
bXcat.py
|
@ -47,15 +47,15 @@ def get_keys(funder_address, redeemer_address):
|
|||
def privkey(address):
|
||||
bitcoind.dumpprivkey(address)
|
||||
|
||||
def hashtimelockcontract(funder, redeemer, secret, locktime):
|
||||
def hashtimelockcontract(funder, redeemer, commitment, locktime):
|
||||
funderAddr = CBitcoinAddress(funder)
|
||||
redeemerAddr = CBitcoinAddress(redeemer)
|
||||
h = sha256(secret)
|
||||
# h = sha256(secret)
|
||||
blocknum = bitcoind.getblockcount()
|
||||
print("Current blocknum", blocknum)
|
||||
redeemblocknum = blocknum + locktime
|
||||
print("REDEEMBLOCKNUM BITCOIN", redeemblocknum)
|
||||
redeemScript = CScript([OP_IF, OP_SHA256, h, OP_EQUALVERIFY,OP_DUP, OP_HASH160,
|
||||
redeemScript = CScript([OP_IF, OP_SHA256, x(commitment), 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])
|
||||
print("Redeem script for p2sh contract on Bitcoin blockchain:", b2x(redeemScript))
|
||||
|
@ -108,6 +108,11 @@ def get_tx_details(txid):
|
|||
# redeems automatically after buyer has funded tx, by scanning for transaction to the p2sh
|
||||
# i.e., doesn't require buyer telling us fund txid
|
||||
def auto_redeem(contract, secret):
|
||||
print("Parsing script for auto_redeem...")
|
||||
scriptarray = parse_script(contract.redeemScript)
|
||||
redeemblocknum = scriptarray[8]
|
||||
redeemPubkey = scriptarray[6]
|
||||
refundPubkey = scriptarray[13]
|
||||
# How to find redeemScript and redeemblocknum from blockchain?
|
||||
print("Contract in auto redeem", contract.__dict__)
|
||||
p2sh = contract.p2sh
|
||||
|
@ -124,11 +129,11 @@ def auto_redeem(contract, secret):
|
|||
print("Found {0} in p2sh {1}, redeeming...".format(amount, p2sh))
|
||||
|
||||
# Parsing redeemblocknum from the redeemscript of the p2sh
|
||||
redeemblocknum = find_redeemblocknum(contract)
|
||||
# redeemblocknum = find_redeemblocknum(contract)
|
||||
blockcount = bitcoind.getblockcount()
|
||||
print("\nCurrent blocknum at time of redeem on Bitcoin:", blockcount)
|
||||
if blockcount < redeemblocknum:
|
||||
redeemPubKey = find_redeemAddr(contract)
|
||||
# redeemPubKey = find_redeemAddr(contract)
|
||||
print('redeemPubKey', redeemPubKey)
|
||||
else:
|
||||
print("nLocktime exceeded, refunding")
|
||||
|
@ -169,7 +174,68 @@ def auto_redeem(contract, secret):
|
|||
else:
|
||||
print("No contract for this p2sh found in database", p2sh)
|
||||
|
||||
def redeem_contract(contract, secret):
|
||||
# How to find redeemScript and redeemblocknum from blockchain?
|
||||
print("Contract in redeem_contract", contract.__dict__)
|
||||
p2sh = contract.p2sh
|
||||
#checking there are funds in the address
|
||||
amount = check_funds(p2sh)
|
||||
if(amount == 0):
|
||||
print("address ", p2sh, " not funded")
|
||||
quit()
|
||||
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))
|
||||
|
||||
# TODO: Decodescript is not working, add back in.
|
||||
# redeemblocknum = find_redeemblocknum(contract)
|
||||
|
||||
blockcount = bitcoind.getblockcount()
|
||||
print("\nCurrent blocknum at time of redeem on Zcash:", blockcount)
|
||||
if blockcount < contract.redeemblocknum:
|
||||
|
||||
# redeemPubKey = find_redeemAddr(contract)
|
||||
redeemPubKey = P2PKHBitcoinAddress.from_bytes(x('7788b4511a25fba1092e67b307a6dcdb6da125d9'))
|
||||
|
||||
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 = bitcoind.dumpprivkey(redeemPubKey)
|
||||
sig = privkey.sign(sighash) + bytes([SIGHASH_ALL])
|
||||
print("SECRET", secret)
|
||||
preimage = b(secret)
|
||||
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 = bitcoind.sendrawtransaction(tx)
|
||||
print("Txid of submitted redeem tx: ", b2x(lx(b2x(txid))))
|
||||
print("TXID SUCCESSFULLY REDEEMED")
|
||||
return 'redeem_tx', b2x(lx(b2x(txid)))
|
||||
else:
|
||||
print("nLocktime exceeded, refunding")
|
||||
refundPubKey = find_refundAddr(contract)
|
||||
print('refundPubKey', refundPubKey)
|
||||
txid = bitcoind.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)
|
||||
|
||||
# takes hex and returns array of decoded script op codes
|
||||
# This seems to be a costly operation, minimize frequency of calls.
|
||||
def parse_script(script_hex):
|
||||
redeemScript = zcashd.decodescript(script_hex)
|
||||
scriptarray = redeemScript['asm'].split(' ')
|
||||
|
@ -216,9 +282,6 @@ def find_transaction_to_address(p2sh):
|
|||
bitcoind.importaddress(p2sh, "", False)
|
||||
txs = bitcoind.listunspent()
|
||||
for tx in txs:
|
||||
# print("tx addr:", tx['address'])
|
||||
# print(type(tx['address']))
|
||||
# print(type(p2sh))
|
||||
if tx['address'] == CBitcoinAddress(p2sh):
|
||||
print("Found tx to p2sh", p2sh)
|
||||
print(tx)
|
||||
|
|
72
cli.py
72
cli.py
|
@ -6,34 +6,44 @@ from trades import *
|
|||
from xcat import *
|
||||
import ast
|
||||
|
||||
def find_role(contract):
|
||||
# Obviously when regtest created both addrs on same machine, role is both.
|
||||
if parse_addrs(contract.initiator):
|
||||
return 'initiator'
|
||||
else:
|
||||
return 'fulfiller'
|
||||
def save_state(trade):
|
||||
save(trade)
|
||||
db.create
|
||||
|
||||
def parse_addrs(address):
|
||||
if address[:1] == 'm':
|
||||
status = bXcat.validateaddress(address)
|
||||
else:
|
||||
status = zXcat.validateaddress(address)
|
||||
status = status['ismine']
|
||||
print("Address {0} is mine: {1}".format(address, status))
|
||||
return status
|
||||
|
||||
def checkSellActions(trade):
|
||||
def checkSellStatus(trade):
|
||||
if trade.buy.get_status() == 'funded':
|
||||
seller_redeem(trade)
|
||||
secret = get_secret()
|
||||
print("SECRET found in checksellactions", secret)
|
||||
trade = seller_redeem_p2sh(trade, secret)
|
||||
print("TRADE SUCCESSFULLY REDEEMED", trade)
|
||||
save_state(trade)
|
||||
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))
|
||||
elif trade.buy.get_status() == 'redeemed':
|
||||
print("You have already redeemed the p2sh on the second chain of this trade.")
|
||||
|
||||
def checkBuyActions(trade):
|
||||
|
||||
def checkBuyStatus(trade):
|
||||
if trade.sell.get_status() == 'funded' and trade.buy.get_status() != 'redeemed':
|
||||
print("One active trade available, fulfilling buyer contract...")
|
||||
buyer_fulfill(trade)
|
||||
# they should calculate redeemScript for themselves
|
||||
htlc = create_htlc(trade.buy.currency, trade.buy.fulfiller, trade.buy.initiator, trade.commitment, trade.buy.locktime)
|
||||
print("Buyer p2sh:", htlc['p2sh'])
|
||||
# If the two p2sh match...
|
||||
if buyer_p2sh == contract.buy.p2sh:
|
||||
fund_tx = fund_contract(trade.buy)
|
||||
trade.buy.fund_tx = fund_tx
|
||||
print("trade buy with redeemscript?", trade.buy.__dict__)
|
||||
save_state(trade)
|
||||
else:
|
||||
print("Compiled p2sh for htlc does not match what seller sent.")
|
||||
elif trade.buy.get_status() == 'redeemed':
|
||||
buyer_redeem(trade)
|
||||
# TODO: secret parsing
|
||||
# secret = parse_secret(trade.buy.currency, trade.buy.redeem_tx)
|
||||
secret = get_secret()
|
||||
print("Found secret", secret)
|
||||
txid = auto_redeem_p2sh(trade.sell, secret)
|
||||
print("TXID after buyer redeem", txid)
|
||||
print("XCAT trade complete!")
|
||||
|
||||
def instantiateTrade(trade):
|
||||
|
@ -61,7 +71,7 @@ if __name__ == '__main__':
|
|||
hexstr = args.argument[0]
|
||||
trade = x2s(hexstr)
|
||||
trade = instantiateTrade(ast.literal_eval(trade))
|
||||
db.create(trade)
|
||||
save_state(trade)
|
||||
# print(trade.toJ)
|
||||
elif command == 'exporttrade':
|
||||
trade = get_trade()
|
||||
|
@ -73,10 +83,10 @@ if __name__ == '__main__':
|
|||
trade = instantiateTrade(trade)
|
||||
if find_role(trade.sell) == 'initiator':
|
||||
role = 'seller'
|
||||
checkSellActions(trade)
|
||||
checkSellStatus(trade)
|
||||
else:
|
||||
role = 'buyer'
|
||||
checkBuyActions(trade)
|
||||
checkBuyStatus(trade)
|
||||
elif command == 'newtrade':
|
||||
erase_trade()
|
||||
role = 'seller'
|
||||
|
@ -92,3 +102,19 @@ if __name__ == '__main__':
|
|||
txid = args.argument[0]
|
||||
trade = db.get(txid)
|
||||
print(x2s(b2x(trade)))
|
||||
# Ad hoc testing starts here
|
||||
elif command == "step1":
|
||||
erase_trade()
|
||||
print("Creating new XCAT trade...")
|
||||
trade = seller_initiate(Trade())
|
||||
# Save it to leveldb
|
||||
save_state(trade)
|
||||
elif command == "step2":
|
||||
trade = get_trade()
|
||||
checkBuyStatus(trade)
|
||||
elif command == "step3":
|
||||
trade = get_trade()
|
||||
checkSellStatus(trade)
|
||||
elif command == "step4":
|
||||
trade = get_trade()
|
||||
checkBuyStatus(trade)
|
||||
|
|
|
@ -8,9 +8,9 @@ db = plyvel.DB('/tmp/testdb', create_if_missing=True)
|
|||
|
||||
trade = get_trade()
|
||||
## txid we retrieve by
|
||||
if trade and 'sell' in trade:
|
||||
if 'fund_tx' in trade['sell']:
|
||||
txid = trade['sell']['fund_tx']
|
||||
if trade and trade.sell:
|
||||
if hasattr(trade.sell, 'fund_tx'):
|
||||
txid = trade.sell.fund_tx
|
||||
|
||||
# Takes object, saves json as bytes
|
||||
def create(trade):
|
||||
|
|
|
@ -1 +1 @@
|
|||
UYH0XxCs
|
||||
2E8ASX0w
|
|
@ -1,10 +1,11 @@
|
|||
import json
|
||||
|
||||
class Trade(object):
|
||||
def __init__(self, sell=None, buy=None):
|
||||
def __init__(self, sell=None, buy=None, commitment=None):
|
||||
'''Create a new trade with a sell contract and buy contract across two chains'''
|
||||
self.sell = sell
|
||||
self.buy = buy
|
||||
self.commitment = commitment
|
||||
|
||||
def toJSON(self):
|
||||
return json.dumps(self, default=lambda o: o.__dict__,
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
{"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"}}
|
126
xcat.py
126
xcat.py
|
@ -17,11 +17,11 @@ def check_p2sh(currency, address):
|
|||
print("Checking funds in Zcash p2sh")
|
||||
return zXcat.check_funds(address)
|
||||
|
||||
def create_htlc(currency, funder, redeemer, secret, locktime):
|
||||
def create_htlc(currency, funder, redeemer, commitment, locktime):
|
||||
if currency == 'bitcoin':
|
||||
sell_p2sh = bXcat.hashtimelockcontract(funder, redeemer, secret, locktime)
|
||||
sell_p2sh = bXcat.hashtimelockcontract(funder, redeemer, commitment, locktime)
|
||||
else:
|
||||
sell_p2sh = zXcat.hashtimelockcontract(funder, redeemer, secret, locktime)
|
||||
sell_p2sh = zXcat.hashtimelockcontract(funder, redeemer, commitment, locktime)
|
||||
return sell_p2sh
|
||||
|
||||
def fund_htlc(currency, p2sh, amount):
|
||||
|
@ -30,12 +30,16 @@ def fund_htlc(currency, p2sh, amount):
|
|||
else:
|
||||
txid = zXcat.fund_htlc(p2sh, amount)
|
||||
return txid
|
||||
#
|
||||
# def fund_buy_contract(trade):
|
||||
# buy = trade.buy
|
||||
# txid = fund_htlc(buy.currency, buy.p2sh, buy.amount)
|
||||
# setattr(trade.buy, 'fund_tx', txid)
|
||||
# save(trade)
|
||||
# return txid
|
||||
|
||||
def fund_buy_contract(trade):
|
||||
buy = trade.buy
|
||||
txid = fund_htlc(buy.currency, buy.p2sh, buy.amount)
|
||||
setattr(trade.buy, 'fund_tx', txid)
|
||||
save(trade)
|
||||
def fund_contract(contract):
|
||||
txid = fund_htlc(contract.currency, contract.p2sh, contract.amount)
|
||||
return txid
|
||||
|
||||
def fund_sell_contract(trade):
|
||||
|
@ -45,22 +49,22 @@ def fund_sell_contract(trade):
|
|||
save(trade)
|
||||
return txid
|
||||
|
||||
def create_sell_p2sh(trade, secret, locktime):
|
||||
def create_sell_p2sh(trade, commitment, locktime):
|
||||
# CREATE SELL CONTRACT
|
||||
sell = trade.sell
|
||||
contract = create_htlc(sell.currency, sell.initiator, sell.fulfiller, secret, locktime)
|
||||
contract = create_htlc(sell.currency, sell.initiator, sell.fulfiller, commitment, locktime)
|
||||
print("sell contract", contract)
|
||||
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):
|
||||
def create_buy_p2sh(trade, commitment, locktime):
|
||||
## CREATE BUY CONTRACT
|
||||
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("HTLC DETAILS", buy.currency, buy.fulfiller, buy.initiator, commitment, locktime)
|
||||
buy_contract = create_htlc(buy.currency, buy.fulfiller, buy.initiator, commitment, locktime)
|
||||
print("Buy contract", buy_contract)
|
||||
|
||||
setattr(trade.buy, 'p2sh', buy_contract['p2sh'])
|
||||
|
@ -70,7 +74,7 @@ def create_buy_p2sh(trade, secret, locktime):
|
|||
|
||||
save(trade)
|
||||
|
||||
def redeem_p2sh(contract, secret):
|
||||
def auto_redeem_p2sh(contract, secret):
|
||||
currency = contract.currency
|
||||
if currency == 'bitcoin':
|
||||
res = bXcat.auto_redeem(contract, secret)
|
||||
|
@ -78,10 +82,20 @@ def redeem_p2sh(contract, secret):
|
|||
res = zXcat.auto_redeem(contract, secret)
|
||||
return res
|
||||
|
||||
def print_trade(role):
|
||||
print("\nTrade status for {0}:".format(role))
|
||||
trade = get_trade()
|
||||
pprint(trade)
|
||||
|
||||
def redeem_p2sh(contract, secret):
|
||||
currency = contract.currency
|
||||
if currency == 'bitcoin':
|
||||
res = bXcat.redeem_contract(contract, secret)
|
||||
else:
|
||||
res = zXcat.redeem_contract(contract, secret)
|
||||
return res
|
||||
|
||||
def parse_secret(chain, txid):
|
||||
if chain == 'bitcoin':
|
||||
secret = bXcat.parse_secret(txid)
|
||||
else:
|
||||
secret = zXcat.parse_secret(txid)
|
||||
|
||||
#### Main functions determining user flow from command line
|
||||
def buyer_redeem(trade):
|
||||
|
@ -104,7 +118,7 @@ def buyer_redeem(trade):
|
|||
save(trade)
|
||||
exit()
|
||||
|
||||
def seller_redeem(trade):
|
||||
def seller_redeem_p2sh(trade, secret):
|
||||
buy = trade.buy
|
||||
userInput.authorize_seller_redeem(buy)
|
||||
|
||||
|
@ -113,12 +127,11 @@ def seller_redeem(trade):
|
|||
exit()
|
||||
else:
|
||||
# Seller redeems buyer's funded tx (contract in p2sh)
|
||||
secret = userInput.retrieve_password()
|
||||
tx_type, txid = redeem_p2sh(trade.buy, secret)
|
||||
print("Setting tx_type: txid", tx_type, txid)
|
||||
setattr(trade.buy, tx_type, txid)
|
||||
save(trade)
|
||||
print("You have redeemed {0} {1}!".format(buy.amount, buy.currency))
|
||||
print_trade('seller')
|
||||
return trade
|
||||
|
||||
def buyer_fulfill(trade):
|
||||
buy = trade.buy
|
||||
|
@ -157,11 +170,13 @@ def seller_initiate(trade):
|
|||
print(trade.buy.__dict__)
|
||||
|
||||
secret = userInput.create_password()
|
||||
save_secret(secret)
|
||||
hash_of_secret = sha256(secret)
|
||||
# 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)
|
||||
create_sell_p2sh(trade, hash_of_secret, sell_locktime)
|
||||
|
||||
userInput.authorize_fund_sell(trade)
|
||||
|
||||
|
@ -170,68 +185,5 @@ def seller_initiate(trade):
|
|||
|
||||
create_buy_p2sh(trade, secret, buy_locktime)
|
||||
|
||||
trade.commitment = b2x(hash_of_secret)
|
||||
return trade
|
||||
|
||||
if __name__ == '__main__':
|
||||
print("ZEC <-> BTC XCAT (Cross-Chain Atomic Transactions)")
|
||||
print("=" * 50)
|
||||
|
||||
trade = get_trade()
|
||||
|
||||
if trade == None:
|
||||
htlcTrade = Trade()
|
||||
print("New empty trade")
|
||||
else:
|
||||
buy = Contract(trade['buy'])
|
||||
sell = Contract(trade['sell'])
|
||||
htlcTrade = Trade(buy=buy, sell=sell)
|
||||
|
||||
try:
|
||||
if sys.argv[1] == 'new':
|
||||
erase_trade()
|
||||
role = 'seller'
|
||||
htlcTrade = Trade()
|
||||
print("Creating new XCAT transaction...")
|
||||
else:
|
||||
role = sys.argv[1]
|
||||
print("Your role in demo:", role)
|
||||
except:
|
||||
if trade == None:
|
||||
print("No active trades available.")
|
||||
res = input("Would you like to initiate a trade? (y/n) ")
|
||||
if res == 'y':
|
||||
role = 'seller'
|
||||
else:
|
||||
exit()
|
||||
else:
|
||||
print("Trade exists, run script as buyer or seller to complete trade.")
|
||||
exit()
|
||||
|
||||
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.sell == None:
|
||||
seller_initiate(htlcTrade)
|
||||
elif htlcTrade.buy.get_status() == 'funded':
|
||||
seller_redeem(htlcTrade)
|
||||
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.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.buy.get_status() == 'redeemed':
|
||||
# Seller has redeemed buyer's tx, buyer can now redeem.
|
||||
buyer_redeem(htlcTrade)
|
||||
print("XCAT trade complete!")
|
||||
|
||||
# Note: there is some little endian weirdness in the bXcat and zXcat files, need to handle the endianness of txids better & more consistently
|
||||
|
|
70
zXcat.py
70
zXcat.py
|
@ -36,16 +36,16 @@ def get_keys(funder_address, redeemer_address):
|
|||
def privkey(address):
|
||||
zcashd.dumpprivkey(address)
|
||||
|
||||
def hashtimelockcontract(funder, redeemer, secret, locktime):
|
||||
def hashtimelockcontract(funder, redeemer, commitment, locktime):
|
||||
funderAddr = CBitcoinAddress(funder)
|
||||
redeemerAddr = CBitcoinAddress(redeemer)
|
||||
h = sha256(secret)
|
||||
# h = sha256(secret)
|
||||
blocknum = zcashd.getblockcount()
|
||||
print("Current blocknum", blocknum)
|
||||
redeemblocknum = blocknum + locktime
|
||||
print("REDEEMBLOCKNUM ZCASH", redeemblocknum)
|
||||
# can rm op_dup and op_hash160 if you replace addrs with pubkeys (as raw hex/bin data?), and can rm last op_equalverify (for direct pubkey comparison)
|
||||
zec_redeemScript = CScript([OP_IF, OP_SHA256, h, OP_EQUALVERIFY,OP_DUP, OP_HASH160,
|
||||
zec_redeemScript = CScript([OP_IF, OP_SHA256, x(commitment), 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])
|
||||
print("Redeem script for p2sh contract on Zcash blockchain:", b2x(zec_redeemScript))
|
||||
|
@ -77,7 +77,7 @@ def get_tx_details(txid):
|
|||
|
||||
def find_transaction_to_address(p2sh):
|
||||
zcashd.importaddress(p2sh, "", False)
|
||||
txs = zcashd.listunspent()
|
||||
txs = zcashd.listunspent(0, 100)
|
||||
for tx in txs:
|
||||
# print("tx addr:", tx['address'])
|
||||
# print(type(tx['address']))
|
||||
|
@ -128,7 +128,6 @@ def parse_secret(txid):
|
|||
|
||||
# redeems automatically after buyer has funded tx, by scanning for transaction to the p2sh
|
||||
# i.e., doesn't require buyer telling us fund txid
|
||||
|
||||
def auto_redeem(contract, secret):
|
||||
# How to find redeemScript and redeemblocknum from blockchain?
|
||||
print("Contract in auto redeem", contract.__dict__)
|
||||
|
@ -187,13 +186,74 @@ def auto_redeem(contract, secret):
|
|||
else:
|
||||
print("No contract for this p2sh found in database", p2sh)
|
||||
|
||||
def redeem_contract(contract, secret):
|
||||
# How to find redeemScript and redeemblocknum from blockchain?
|
||||
print("Contract in redeem contract", contract.__dict__)
|
||||
p2sh = contract.p2sh
|
||||
#checking there are funds in the address
|
||||
amount = check_funds(p2sh)
|
||||
if(amount == 0):
|
||||
print("address ", p2sh, " not funded")
|
||||
quit()
|
||||
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))
|
||||
|
||||
# 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 < contract.d:
|
||||
# TODO: parse the script once, up front.
|
||||
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:
|
||||
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)
|
||||
|
||||
def parse_script(script_hex):
|
||||
redeemScript = zcashd.decodescript(script_hex)
|
||||
scriptarray = redeemScript['asm'].split(' ')
|
||||
return scriptarray
|
||||
|
||||
def find_redeemblocknum(contract):
|
||||
print("In find_redeemblocknum")
|
||||
scriptarray = parse_script(contract.redeemScript)
|
||||
print("Returning scriptarray", scriptarray)
|
||||
redeemblocknum = scriptarray[8]
|
||||
return int(redeemblocknum)
|
||||
|
||||
|
|
Loading…
Reference in New Issue