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 e04c20e..553b5bb 100644 --- a/xcat/cli.py +++ b/xcat/cli.py @@ -32,9 +32,11 @@ 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 utils.cleanup(tradeid) @@ -123,6 +125,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") @@ -211,9 +214,12 @@ def newtrade(tradeid, **kwargs): protocol = Protocol() print("Creating new XCAT trade...") utils.erase_trade() - tradeid, trade = protocol.initialize_trade(tradeid, conf=kwargs['conf']) - print("Trade", trade) - trade = protocol.seller_init(tradeid, trade) + tradeid, trade = protocol.initialize_trade( + tradeid, + conf=kwargs['conf'], + network=kwargs['network']) + print("New trade created: {0}".format(trade)) + trade = protocol.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) @@ -260,6 +266,17 @@ def main(): 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: @@ -294,9 +311,9 @@ def main(): utils.throw("Usage: newtrade [tradeid]") tradeid = args.arguments[0] if args.conf is 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") @@ -308,8 +325,10 @@ def main(): tradeid = args.arguments[0] checkBuyStatus(tradeid) elif command == "step3": + protocol.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 f8f10c8..c24336f 100644 --- a/xcat/protocol.py +++ b/xcat/protocol.py @@ -5,6 +5,7 @@ from xcat.xcatconf import ADDRS from xcat.trades import Contract, Trade from xcat.bitcoinRPC import bitcoinProxy from xcat.zcashRPC import zcashProxy +import logging class Protocol(): @@ -13,20 +14,33 @@ class Protocol(): self.bitcoinRPC = bitcoinProxy() self.zcashRPC = zcashProxy() + def generate(self, num): + self.bitcoinRPC.generate(num) + self.zcashRPC.generate(num) + def is_myaddr(self, address): + # Handle differnt network prefixes if address[:1] == 'm': status = self.bitcoinRPC.validateaddress(address) else: status = self.zcashRPC.validateaddress(address) - status = status['ismine'] + + logging.debug("Address status: ", status) + + if not status['isvalid']: + raise ValueError("Invalid address: %s" % address) + elif 'ismine' in status: + status = status['ismine'] # print("Address {0} is mine: {1}".format(address, status)) return status def find_secret_from_fundtx(self, currency, p2sh, fundtx): if currency == 'bitcoin': secret = self.bitcoinRPC.find_secret(p2sh, fundtx) - else: + elif currency == 'zcash': secret = self.zcashRPC.find_secret(p2sh, fundtx) + else: + raise ValueError('Currency not recognized: %s' % currency) return secret def import_addrs(self, trade): @@ -37,17 +51,21 @@ class Protocol(): if currency == 'bitcoin': print("Checking funds in Bitcoin p2sh") return self.bitcoinRPC.check_funds(address) - else: + elif currency == 'zcash': print("Checking funds in Zcash p2sh") return self.zcashRPC.check_funds(address) + else: + raise ValueError('Currency not recognized: %s' % currency) def check_fund_status(self, currency, address): if currency == 'bitcoin': print("Checking funds in Bitcoin p2sh") return self.bitcoinRPC.get_fund_status(address) - else: + elif currency == 'zcash': print("Checking funds in Zcash p2sh") return self.zcashRPC.get_fund_status(address) + else: + raise ValueError('Currency not recognized: %s' % currency) # TODO: function to calculate appropriate locktimes between chains # def verify_p2sh(trade): @@ -66,16 +84,20 @@ class Protocol(): if currency == 'bitcoin': sell_p2sh = self.bitcoinRPC.hashtimelockcontract( funder, redeemer, commitment, locktime) - else: + elif currency == 'zcash': sell_p2sh = self.zcashRPC.hashtimelockcontract( funder, redeemer, commitment, locktime) + else: + raise ValueError('Currency not recognized: %s' % currency) return sell_p2sh def fund_htlc(self, currency, p2sh, amount): if currency == 'bitcoin': txid = self.bitcoinRPC.fund_htlc(p2sh, amount) - else: + elif currency == 'zcash': txid = self.zcashRPC.fund_htlc(p2sh, amount) + else: + raise ValueError('Currency not recognized: %s' % currency) return txid def fund_contract(self, contract): @@ -124,15 +146,29 @@ class Protocol(): currency = contract.currency if currency == 'bitcoin': res = self.bitcoinRPC.redeem_contract(contract, secret) - else: + elif currency == 'zcash': res = self.zcashRPC.redeem_contract(contract, secret) + else: + raise ValueError('Currency not recognized: %s' % currency) return res - def parse_secret(self, chain, txid): - if chain == 'bitcoin': - secret = self.bitcoinRPC.parse_secret(txid) + def refund_contract(self, contract): + currency = contract.currency + if currency == 'bitcoin': + res = self.bitcoinRPC.refund(contract) + elif currency == 'zcash': + res = self.zcashRPC.refund(contract) else: + raise ValueError('Currency not recognized: %s', currency) + return res + + def parse_secret(self, currency, txid): + if currency == 'bitcoin': + secret = self.bitcoinRPC.parse_secret(txid) + elif currency == 'zcash': secret = self.zcashRPC.parse_secret(txid) + else: + raise ValueError('Currency not recognized: %s', currency) return secret # Main functions determining user flow from command line @@ -185,7 +221,7 @@ class Protocol(): buy_p2sh_balance, buy.currency) print("Buy amt:", buy.amount) - txid = fund_buy_contract(trade) + txid = self.fund_buy_contract(trade) print("Fund tx txid:", txid) else: print("It looks like you've already funded the contract to buy " @@ -193,7 +229,7 @@ class Protocol(): "{0}.".format(buy_p2sh_balance, buy.currency)) print("Please wait for the seller to remove your funds from " "escrow to complete the trade.") - print_trade('buyer') + self.print_trade('buyer') def initialize_trade(self, tradeid, **kwargs): trade = Trade() @@ -228,7 +264,7 @@ class Protocol(): secret = utils.generate_password() db.save_secret(tradeid, secret) print("\nGenerated a secret preimage to lock funds. This will only " - "be stored locally: ", secret) + "be stored locally: {0}".format(secret)) hash_of_secret = utils.sha256(secret) # TODO: Implement locktimes and mock block passage of time @@ -241,5 +277,5 @@ class Protocol(): self.create_buy_p2sh(trade, hash_of_secret, buy_locktime) trade.commitment = utils.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(' ')