diff --git a/xcat/bitcoinRPC.py b/xcat/bitcoinRPC.py index 487e3a1..338d8e3 100644 --- a/xcat/bitcoinRPC.py +++ b/xcat/bitcoinRPC.py @@ -10,16 +10,20 @@ import bitcoin.rpc from bitcoin import SelectParams from bitcoin.core import b2x, lx, b2lx, x, COIN, COutPoint, CMutableTxOut, CMutableTxIn, CMutableTransaction, Hash160, CTransaction from bitcoin.base58 import decode -from bitcoin.core.script import CScript, OP_DUP, OP_IF, OP_ELSE, OP_ENDIF, OP_HASH160, OP_EQUALVERIFY, OP_CHECKSIG, SignatureHash, SIGHASH_ALL, OP_FALSE, OP_DROP, OP_CHECKLOCKTIMEVERIFY, OP_SHA256, OP_TRUE +from bitcoin.core.script import CScript, OP_DUP, OP_IF, OP_ELSE, OP_ENDIF, OP_HASH160, OP_EQUALVERIFY, OP_CHECKSIG, SignatureHash, SIGHASH_ALL, OP_FALSE, OP_DROP, OP_CHECKLOCKTIMEVERIFY, OP_SHA256, OP_TRUE, OP_FALSE from bitcoin.core.scripteval import VerifyScript, SCRIPT_VERIFY_P2SH from bitcoin.wallet import CBitcoinAddress, CBitcoinSecret, P2SHBitcoinAddress, P2PKHBitcoinAddress from xcat.utils import * +import logging FEE = 0.001*COIN class bitcoinProxy(): def __init__(self, network='regtest', timeout=900): + if network is not 'testnet' and network is not 'mainnet': + network='regtest' + logging.debug("NETWORK in proxy: {0}".format(network)) self.network = network self.timeout = timeout @@ -143,7 +147,7 @@ class bitcoinProxy(): print("Parsing script for redeem_contract...") scriptarray = self.parse_script(contract.redeemScript) redeemblocknum = scriptarray[8] - redeemPubKey = P2PKHBitcoinAddress.from_bytes(x(scriptarray[6])) + self.redeemPubKey = P2PKHBitcoinAddress.from_bytes(x(scriptarray[6])) refundPubKey = P2PKHBitcoinAddress.from_bytes(x(scriptarray[13])) p2sh = contract.p2sh #checking there are funds in the address @@ -161,38 +165,64 @@ class bitcoinProxy(): blockcount = self.bitcoind.getblockcount() print("\nCurrent blocknum at time of redeem on Zcash:", blockcount) if blockcount < int(redeemblocknum): - 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: protect privkey better, separate signing from rawtx creation - privkey = self.bitcoind.dumpprivkey(redeemPubKey) - sig = privkey.sign(sighash) + bytes([SIGHASH_ALL]) - 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('Raw redeem transaction hex: ', b2x(tx.serialize())) - VerifyScript(txin.scriptSig, txin_scriptPubKey, tx, 0, (SCRIPT_VERIFY_P2SH,)) - print("Script verified, sending raw transaction...") - txid = self.bitcoind.sendrawtransaction(tx) - fund_tx = str(fundtx['outpoint']) - redeem_tx = b2x(lx(b2x(txid))) - return {"redeem_tx": redeem_tx, "fund_tx": fund_tx} + return self.redeem(contract, fundtx, secret) else: print("nLocktime exceeded, refunding") - print('refundPubKey', refundPubKey) - txid = self.bitcoind.sendtoaddress(refundPubKey, fundtx['amount'] - FEE) - fund_tx = str(fundtx['outpoint']) - refund_tx = b2x(lx(b2x(txid))) - return {"refund_tx": refund_tx, "fund_tx": fund_tx} + return self.refund(contract) else: print("No contract for this p2sh found in database", p2sh) + def redeem(self, contract, fundtx, secret): + print('redeemPubKey', self.redeemPubKey) + # TODO: Compare with script on blockchain? + redeemScript = CScript(x(contract.redeemScript)) + txin = CMutableTxIn(fundtx['outpoint']) + txout = CMutableTxOut(fundtx['amount'] - FEE, self.redeemPubKey.to_scriptPubKey()) + # Create the unsigned raw transaction. + tx = CMutableTransaction([txin], [txout]) + sighash = SignatureHash(redeemScript, tx, 0, SIGHASH_ALL) + # TODO: protect privkey better, separate signing from rawtx creation + privkey = self.bitcoind.dumpprivkey(self.redeemPubKey) + sig = privkey.sign(sighash) + bytes([SIGHASH_ALL]) + preimage = secret.encode('utf-8') + txin.scriptSig = CScript([sig, privkey.pub, preimage, OP_TRUE, redeemScript]) + + # print("txin.scriptSig", b2x(txin.scriptSig)) + txin_scriptPubKey = redeemScript.to_p2sh_scriptPubKey() + print('Raw redeem transaction hex: ', b2x(tx.serialize())) + VerifyScript(txin.scriptSig, txin_scriptPubKey, tx, 0, (SCRIPT_VERIFY_P2SH,)) + print("Script verified, sending raw transaction...") + txid = self.bitcoind.sendrawtransaction(tx) + fund_tx = str(fundtx['outpoint']) + redeem_tx = b2x(lx(b2x(txid))) + return {"redeem_tx": redeem_tx, "fund_tx": fund_tx} + + def refund(self, contract): + fundtx = self.find_transaction_to_address(contract.p2sh) + print("Fund tx found in refund: ", fundtx) + refundPubKey = self.find_refundAddr(contract) + print('refundPubKey: {0}'.format(refundPubKey)) + + redeemScript = CScript(x(contract.redeemScript)) + txin = CMutableTxIn(fundtx['outpoint']) + txout = CMutableTxOut(fundtx['amount'] - FEE, refundPubKey.to_scriptPubKey()) + # Create the unsigned raw transaction. + tx = CMutableTransaction([txin], [txout]) + sighash = SignatureHash(redeemScript, tx, 0, SIGHASH_ALL) + privkey = self.bitcoind.dumpprivkey(refundPubKey) + sig = privkey.sign(sighash) + bytes([SIGHASH_ALL]) + # Sign without secret + txin.scriptSig = CScript([sig, privkey.pub, OP_FALSE, redeemScript]) + # txin.nSequence = 2185 + txin_scriptPubKey = redeemScript.to_p2sh_scriptPubKey() + print('Raw redeem transaction hex: {0}'.format(b2x(tx.serialize()))) + res = VerifyScript(txin.scriptSig, txin_scriptPubKey, tx, 0, (SCRIPT_VERIFY_P2SH,)) + print("Script verified, sending raw transaction... (NOT)", res) + txid = self.bitcoind.sendrawtransaction(tx) + refund_tx = b2x(lx(b2x(txid))) + fund_tx = str(fundtx['outpoint']) + return {"refund_tx": refund_tx, "fund_tx": fund_tx} + def parse_script(self, script_hex): redeemScript = self.bitcoind.call('decodescript', script_hex) scriptarray = redeemScript['asm'].split(' ') @@ -210,7 +240,7 @@ class bitcoinProxy(): return redeemAddr def find_refundAddr(self, contract): - scriptarray = parse_script(contract.redeemScript) + scriptarray = self.parse_script(contract.redeemScript) funder = scriptarray[13] refundAddr = P2PKHBitcoinAddress.from_bytes(x(funder)) return refundAddr @@ -220,7 +250,7 @@ class bitcoinProxy(): txs = self.bitcoind.listunspent() for tx in txs: if tx['address'] == CBitcoinAddress(p2sh): - print("Found tx to p2sh", p2sh) + logging.debug("Found tx to p2sh: {0}".format(p2sh)) return tx def new_bitcoin_addr(self): diff --git a/xcat/cli.py b/xcat/cli.py index 5b41fc1..ea2b440 100644 --- a/xcat/cli.py +++ b/xcat/cli.py @@ -27,11 +27,12 @@ def checkSellStatus(tradeid): if 'redeem_tx' in txs: trade.buy.redeem_tx = txs['redeem_tx'] print("Redeem tx: ", txs['redeem_tx']) - elif 'refund_tx' in txs: + if 'refund_tx' in txs: trade.buy.redeem_tx = txs['refund_tx'] - print("Refund tx: ", txs['refund_tx']) + print("Buyer refund tx: ", txs['refund_tx']) + txs = refund_contract(trade.sell) # Refund to seller + print("Your refund txid: ", txs['refund_tx']) save_state(trade, tradeid) - # Remove from db? Or just from temporary file storage cleanup(tradeid) elif status == 'sellerFunded': 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)) @@ -101,6 +102,7 @@ def checkBuyStatus(tradeid): save_state(trade, tradeid) print("XCAT trade complete!") else: + # Search if tx has been refunded from p2sh print("Secret not found in redeemtx") # Import a trade in hex, and save to db @@ -173,9 +175,9 @@ def checktrade(tradeid): def newtrade(tradeid, **kwargs): print("Creating new XCAT trade...") erase_trade() - tradeid, trade= initialize_trade(tradeid, conf=kwargs['conf']) - print("Trade", trade) - trade = seller_init(tradeid, trade) + tradeid, trade= initialize_trade(tradeid, conf=kwargs['conf'], network=kwargs['network']) + print("New trade created: {0}".format(trade)) + trade = seller_init(tradeid, trade, network=kwargs['network']) print("\nUse 'xcat exporttrade [tradeid]' to export the trade and sent to the buyer.\n") save_state(trade, tradeid) return trade @@ -199,12 +201,24 @@ def main(): ''')) parser.add_argument("command", action="store", help="list commands") parser.add_argument("arguments", action="store", nargs="*", help="add arguments") + parser.add_argument("-d", "--debug", action="store_true", help="Enable debug mode. Defaults to false") parser.add_argument("-w", "--wormhole", action="store_true", help="Transfer trade data through magic-wormhole") parser.add_argument("-c", "--conf", action="store", help="Use default trade data in conf file.") parser.add_argument("-n", "--network", action="store", help="Set network to regtest or mainnet. Defaults to testnet while in alpha.") # parser.add_argument("--daemon", "-d", action="store_true", help="Run as daemon process") args = parser.parse_args() + if args.debug: + numeric_level = getattr(logging, 'DEBUG', None) + logging.basicConfig(format='%(levelname)s: %(message)s', level=numeric_level) + else: + logging.basicConfig(format='%(levelname)s: %(message)s', level='INFO') + + if args.network: + NETWORK = args.network + else: + NETWORK = 'testnet' + command = args.command if command == 'importtrade': if args.wormhole: @@ -234,9 +248,9 @@ def main(): if len(args.arguments) < 1: throw("Usage: newtrade [tradeid]") tradeid = args.arguments[0] if args.conf == None: - newtrade(tradeid, network=args.network, conf='cli') + newtrade(tradeid, network=NETWORK, conf='cli') else: - newtrade(tradeid, network=args.network, conf=args.conf) + newtrade(tradeid, network=NETWORK, conf=args.conf) elif command == "daemon": #TODO: not implemented print("Run as daemon process") @@ -248,8 +262,10 @@ def main(): tradeid = args.arguments[0] checkBuyStatus(tradeid) elif command == "step3": + generate(31) tradeid = args.arguments[0] checkSellStatus(tradeid) elif command == "step4": + # generate(1) tradeid = args.arguments[0] checkBuyStatus(tradeid) diff --git a/xcat/protocol.py b/xcat/protocol.py index 27f8721..7f0281c 100644 --- a/xcat/protocol.py +++ b/xcat/protocol.py @@ -8,24 +8,35 @@ import xcat.db as db from xcat.xcatconf import * from xcat.bitcoinRPC import bitcoinProxy from xcat.zcashRPC import zcashProxy +import logging bitcoinRPC = bitcoinProxy() zcashRPC = zcashProxy() +def generate(num): + bitcoinRPC.generate(num) + zcashRPC.generate(num) + def is_myaddr(address): + # Handle different network prefixes if address[:1] == 'm': status = bitcoinRPC.validateaddress(address) else: status = zcashRPC.validateaddress(address) - status = status['ismine'] - # print("Address {0} is mine: {1}".format(address, status)) + logging.debug("Address status: ", status) + if status['isvalid'] is False: + raise ValueError("Invalid address: %s" % address) + elif 'ismine' in status: + status = status['ismine'] return status def find_secret_from_fundtx(currency, p2sh, fundtx): if currency == 'bitcoin': secret = bitcoinRPC.find_secret(p2sh, fundtx) - else: + elif currency == 'zcash': secret = zcashRPC.find_secret(p2sh, fundtx) + else: + raise ValueError("Currency not recognized: ", currency) return secret def import_addrs(trade): @@ -36,17 +47,21 @@ def check_p2sh(currency, address): if currency == 'bitcoin': print("Checking funds in Bitcoin p2sh") return bitcoinRPC.check_funds(address) - else: + elif currency == 'zcash': print("Checking funds in Zcash p2sh") return zcashRPC.check_funds(address) + else: + raise ValueError("Currency not recognized: ", currency) def check_fund_status(currency, address): if currency == 'bitcoin': print("Checking funds in Bitcoin p2sh") return bitcoinRPC.get_fund_status(address) - else: + elif currency == 'zcash': print("Checking funds in Zcash p2sh") return zcashRPC.get_fund_status(address) + else: + raise ValueError("Currency not recognized: ", currency) # TODO: function to calculate appropriate locktimes between chains # def verify_p2sh(trade): @@ -61,17 +76,50 @@ def check_fund_status(currency, address): def create_htlc(currency, funder, redeemer, commitment, locktime): if currency == 'bitcoin': sell_p2sh = bitcoinRPC.hashtimelockcontract(funder, redeemer, commitment, locktime) - else: + elif currency == 'zcash': sell_p2sh = zcashRPC.hashtimelockcontract(funder, redeemer, commitment, locktime) + else: + raise ValueError("Currency not recognized: ", currency) return sell_p2sh def fund_htlc(currency, p2sh, amount): if currency == 'bitcoin': txid = bitcoinRPC.fund_htlc(p2sh, amount) - else: + elif currency == 'zcash': txid = zcashRPC.fund_htlc(p2sh, amount) + else: + raise ValueError("Currency not recognized: ", currency) return txid +def redeem_p2sh(contract, secret): + currency = contract.currency + if currency == 'bitcoin': + res = bitcoinRPC.redeem_contract(contract, secret) + elif currency == 'zcash': + res = zcashRPC.redeem_contract(contract, secret) + else: + raise ValueError("Currency not recognized: ", currency) + return res + +def refund_contract(contract): + currency = contract.currency + if currency == 'bitcoin': + res = bitcoinRPC.refund(contract) + elif currency == 'zcash': + res = zcashRPC.refund(contract) + else: + raise ValueError("Currency not recognized: ", currency) + return res + +def parse_secret(currency, txid): + if currency == 'bitcoin': + secret = bitcoinRPC.parse_secret(txid) + elif currency == 'zcash': + secret = zcashRPC.parse_secret(txid) + else: + raise ValueError("Currency not recognized: ", currency) + return secret + def fund_contract(contract): txid = fund_htlc(contract.currency, contract.p2sh, contract.amount) return txid @@ -109,21 +157,6 @@ def create_buy_p2sh(trade, commitment, locktime): save(trade) -def redeem_p2sh(contract, secret): - currency = contract.currency - if currency == 'bitcoin': - res = bitcoinRPC.redeem_contract(contract, secret) - else: - res = zcashRPC.redeem_contract(contract, secret) - return res - -def parse_secret(chain, txid): - if chain == 'bitcoin': - secret = bitcoinRPC.parse_secret(txid) - else: - secret = zcashRPC.parse_secret(txid) - return secret - #### Main functions determining user flow from command line def buyer_redeem(trade): userInput.authorize_buyer_redeem(trade) @@ -148,7 +181,6 @@ def buyer_redeem(trade): def seller_redeem_p2sh(trade, secret): buy = trade.buy userInput.authorize_seller_redeem(buy) - if trade.sell.get_status() == 'redeemed': print("You already redeemed the funds and acquired {0} {1}".format(buy.amount, buy.currency)) exit() @@ -203,11 +235,10 @@ def initialize_trade(tradeid, **kwargs): print(trade.buy.__dict__) return tradeid, trade - -def seller_init(tradeid, trade): +def seller_init(tradeid, trade, network): secret = generate_password() db.save_secret(tradeid, secret) - print("\nGenerated a secret preimage to lock funds. This will only be stored locally: ", secret) + print("Generated a secret preimage to lock funds. This will only be stored locally: {0}".format(secret)) hash_of_secret = sha256(secret) # TODO: Implement locktimes and mock block passage of time @@ -220,5 +251,5 @@ def seller_init(tradeid, trade): create_buy_p2sh(trade, hash_of_secret, buy_locktime) trade.commitment = b2x(hash_of_secret) - print("TRADE after seller init", trade.toJSON()) + print("TRADE after seller init: {0}".format(trade.toJSON())) return trade diff --git a/xcat/userInput.py b/xcat/userInput.py index f01076f..bf67a88 100644 --- a/xcat/userInput.py +++ b/xcat/userInput.py @@ -2,6 +2,7 @@ from xcat.utils import * from xcat.db import * from xcat.bitcoinRPC import bitcoinProxy from xcat.zcashRPC import zcashProxy +from xcat.xcatconf import * def enter_trade_id(): tradeid = input("Enter a unique identifier for this trade: ") diff --git a/xcat/xcatconf.py b/xcat/xcatconf.py index 6efb053..6c77cab 100644 --- a/xcat/xcatconf.py +++ b/xcat/xcatconf.py @@ -17,9 +17,11 @@ ADDRS = { "zcash": "tmTF7LMLjvEsGdcepWPUsh4vgJNrKMWwEyc" }, "fulfiller": { - "bitcoin": "mm2smEJjRN4xoijEfpb5XvYd8e3EYWezom", - "zcash": "tmPwPdceaJAHQn7UiRCVnJ5tXBXHVqWMkis" + "bitcoin": "mn2boR7rYq9DaAWWrVN5MazHKFyf7UhdyU", + "zcash": "tmErB22A1G74aq32aAh5AoqgQSJsAAAdT2p" }, "amounts": {'buy': {'currency': 'zcash', 'amount': 0.02}, 'sell': {'currency': 'bitcoin', 'amount': 0.01}} } } + +NETWORK = 'testnet' diff --git a/xcat/zcashRPC.py b/xcat/zcashRPC.py index 2baefd9..b2cafd0 100644 --- a/xcat/zcashRPC.py +++ b/xcat/zcashRPC.py @@ -12,6 +12,7 @@ from zcash.core import b2x, lx, x, b2lx, COIN, COutPoint, CMutableTxOut, CMutabl from zcash.core.script import CScript, OP_DUP, OP_IF, OP_ELSE, OP_ENDIF, OP_HASH160, OP_EQUALVERIFY, OP_CHECKSIG, SignatureHash, SIGHASH_ALL, OP_FALSE, OP_DROP, OP_CHECKLOCKTIMEVERIFY, OP_SHA256, OP_TRUE from zcash.core.scripteval import VerifyScript, SCRIPT_VERIFY_P2SH from zcash.wallet import CBitcoinAddress, CBitcoinSecret, P2SHBitcoinAddress, P2PKHBitcoinAddress +import logging from xcat.utils import x2s @@ -104,6 +105,7 @@ class zcashProxy(): for tx in txs: raw = self.zcashd.gettransaction(lx(tx['txid']))['hex'] decoded = self.zcashd.decoderawtransaction(raw) + # print("TXINFO", decoded['vin'][0]) if('txid' in decoded['vin'][0]): sendid = decoded['vin'][0]['txid'] if (sendid == fundtx_input ): @@ -112,14 +114,42 @@ class zcashProxy(): print("Redeem transaction with secret not found") return + # def find_secret(self, p2sh, fundtx_input): + # print("In find secret zcashrpc") + # txs = self.zcashd.call('listtransactions', "*", 200, 0, True) + # for tx in txs: + # if tx['address'] == p2sh: # Only check txs involving imported p2sh + # raw = self.zcashd.gettransaction(lx(tx['txid']))['hex'] + # decoded = self.zcashd.decoderawtransaction(raw) + # print('decoded', decoded) + # if('txid' in decoded['vin'][0]): + # sendid = decoded['vin'][0]['txid'] + # print("sendid", sendid) + # if (sendid == fundtx_input ): + # print("Found funding zcash tx: ", sendid) + # res = self.parse_secret(lx(tx['txid'])) + # secret = res[0] + # redeemPubkey = res[1] + # if secret is None: + # print("Secret not found") + # res = self.validateaddress(redeemPubkey) + # if res['ismine']: + # print("Funding tx already refunded. Sent to your address {0}".format(redeemPubkey)) + # logging.debug("Redeem transaction with secret not found") + # return + def parse_secret(self, txid): raw = self.zcashd.gettransaction(txid, True)['hex'] decoded = self.zcashd.decoderawtransaction(raw) scriptSig = decoded['vin'][0]['scriptSig'] asm = scriptSig['asm'].split(" ") pubkey = asm[1] - secret = x2s(asm[2]) - redeemPubkey = P2PKHBitcoinAddress.from_pubkey(x(pubkey)) + try: + secret = x2s(asm[2]) + except: + secret = None + self.redeemPubkey = P2PKHBitcoinAddress.from_pubkey(x(pubkey)) + print("redeemPubkey: ", self.redeemPubkey) return secret def redeem_contract(self, contract, secret): @@ -136,48 +166,69 @@ class zcashProxy(): 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 = self.zcashd.getblockcount() print("\nCurrent blocknum at time of redeem on Zcash:", blockcount) if blockcount < contract.redeemblocknum: - # TODO: parse the script once, up front. - redeemPubKey = self.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 = self.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]) - txin_scriptPubKey = zec_redeemScript.to_p2sh_scriptPubKey() - print('Raw redeem transaction hex: ', b2x(tx.serialize())) - VerifyScript(txin.scriptSig, txin_scriptPubKey, tx, 0, (SCRIPT_VERIFY_P2SH,)) - print("Script verified, sending raw redeem transaction...") - txid = self.zcashd.sendrawtransaction(tx) - redeem_tx = b2x(lx(b2x(txid))) - fund_tx = str(fundtx['outpoint']) - return {"redeem_tx": redeem_tx, "fund_tx": fund_tx} + return self.redeem(contract, fundtx, secret) else: print("nLocktime exceeded, refunding") - refundPubKey = self.find_refundAddr(contract) - print('refundPubKey', refundPubKey) - txid = self.zcashd.sendtoaddress(refundPubKey, fundtx['amount'] - FEE) - refund_tx = b2x(lx(b2x(txid))) - fund_tx = str(fundtx['outpoint']) - return {"refund_tx": refund_tx, "fund_tx": fund_tx} + return self.refund(contract) else: print("No contract for this p2sh found in database", p2sh) + def redeem(self, contract, fundtx, secret): + # TODO: parse the script once, up front. + redeemPubKey = self.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 = self.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]) + txin_scriptPubKey = zec_redeemScript.to_p2sh_scriptPubKey() + print('Raw redeem transaction hex: ', b2x(tx.serialize())) + VerifyScript(txin.scriptSig, txin_scriptPubKey, tx, 0, (SCRIPT_VERIFY_P2SH,)) + print("Script verified, sending raw redeem transaction...") + txid = self.zcashd.sendrawtransaction(tx) + redeem_tx = b2x(lx(b2x(txid))) + fund_tx = str(fundtx['outpoint']) + return {"redeem_tx": redeem_tx, "fund_tx": fund_tx} + + def refund(self, contract): + fundtx = self.find_transaction_to_address(contract.p2sh) + print("Fund tx found in refund: ", fundtx) + refundPubKey = self.find_refundAddr(contract) + print('refundPubKey: {0}'.format(refundPubKey)) + + redeemScript = CScript(x(contract.redeemScript)) + txin = CMutableTxIn(fundtx['outpoint']) + txout = CMutableTxOut(fundtx['amount'] - FEE, refundPubKey.to_scriptPubKey()) + # Create the unsigned raw transaction. + tx = CMutableTransaction([txin], [txout]) + sighash = SignatureHash(redeemScript, tx, 0, SIGHASH_ALL) + privkey = self.zcashd.dumpprivkey(refundPubKey) + sig = privkey.sign(sighash) + bytes([SIGHASH_ALL]) + # Sign without secret + txin.scriptSig = CScript([sig, privkey.pub, OP_FALSE, redeemScript]) + # txin.nSequence = 2185 + txin_scriptPubKey = redeemScript.to_p2sh_scriptPubKey() + print('Raw redeem transaction hex: {0}'.format(b2x(tx.serialize()))) + res = VerifyScript(txin.scriptSig, txin_scriptPubKey, tx, 0, (SCRIPT_VERIFY_P2SH,)) + print("Script verified, sending raw transaction... (NOT)", res) + txid = self.zcashd.sendrawtransaction(tx) + refund_tx = b2x(lx(b2x(txid))) + fund_tx = str(fundtx['outpoint']) + return {"refund_tx": refund_tx, "fund_tx": fund_tx} + def parse_script(self, script_hex): redeemScript = self.zcashd.decodescript(script_hex) scriptarray = redeemScript['asm'].split(' ')