diff --git a/xcat/bitcoinRPC.py b/xcat/bitcoinRPC.py index d614cf5..3b880a1 100644 --- a/xcat/bitcoinRPC.py +++ b/xcat/bitcoinRPC.py @@ -10,7 +10,7 @@ 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 @@ -147,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 @@ -172,22 +172,23 @@ class bitcoinProxy(): else: print("No contract for this p2sh found in database", p2sh) - def redeem(self, contract): - print('redeemPubKey', redeemPubKey) - zec_redeemScript = CScript(x(contract.redeemScript)) + 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, redeemPubKey.to_scriptPubKey()) + txout = CMutableTxOut(fundtx['amount'] - FEE, self.redeemPubKey.to_scriptPubKey()) # Create the unsigned raw transaction. tx = CMutableTransaction([txin], [txout]) - sighash = SignatureHash(zec_redeemScript, tx, 0, SIGHASH_ALL) + sighash = SignatureHash(redeemScript, tx, 0, SIGHASH_ALL) # TODO: protect privkey better, separate signing from rawtx creation - privkey = self.bitcoind.dumpprivkey(redeemPubKey) + 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, zec_redeemScript]) + txin.scriptSig = CScript([sig, privkey.pub, preimage, OP_TRUE, redeemScript]) # print("txin.scriptSig", b2x(txin.scriptSig)) - txin_scriptPubKey = zec_redeemScript.to_p2sh_scriptPubKey() + 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...") @@ -198,11 +199,32 @@ class bitcoinProxy(): def refund(self, contract): fundtx = self.find_transaction_to_address(contract.p2sh) - # Refund self on other chain + print("Fund tx found in refund: ", fundtx) refundPubKey = self.find_refundAddr(contract) - logging.debug('refundPubKey: {0}'.format(refundPubKey)) - txid = self.bitcoind.sendtoaddress(refundPubKey, fundtx['amount'] - FEE) - refund_tx = b2x(lx(b2x(txid))) + 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]) + tx.nLockTime = 2430 + sighash = SignatureHash(redeemScript, tx, 0, SIGHASH_ALL) + privkey = self.bitcoind.dumpprivkey(refundPubKey) + sig = privkey.sign(sighash) + bytes([SIGHASH_ALL]) + # Sign without secret + # OP_IF, OP_SHA256, 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 + 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} diff --git a/xcat/checklocktimeverify.py b/xcat/checklocktimeverify.py new file mode 100644 index 0000000..72a8bfd --- /dev/null +++ b/xcat/checklocktimeverify.py @@ -0,0 +1,83 @@ +#!/usr/bin/env python3 + +import sys +if sys.version_info.major < 3: + sys.stderr.write('Sorry, Python 3.x required by this example.\n') + sys.exit(1) + +import bitcoin +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, 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 + +SelectParams('regtest') +bitcoind = bitcoin.rpc.Proxy() + +# Simple CLTV test p2sh +blocknum = bitcoind.getblockcount() +print("Current blocknum on Bitcoin: ", blocknum) +redeemblocknum = blocknum + 1 +print("Redeemblocknum on Bitcoin: ", redeemblocknum) +redeemScript = CScript([redeemblocknum, OP_CHECKLOCKTIMEVERIFY]) +print("Redeem script for p2sh contract on Bitcoin blockchain: {0}".format(b2x(redeemScript))) +txin_scriptPubKey = redeemScript.to_p2sh_scriptPubKey() +# Convert the P2SH scriptPubKey to a base58 Bitcoin address +txin_p2sh_address = CBitcoinAddress.from_scriptPubKey(txin_scriptPubKey) +p2sh = str(txin_p2sh_address) +# Import address at same time you create +bitcoind.importaddress(p2sh, "", False) + +bitcoind.generate(3) +blocknum = bitcoind.getblockcount() +print("Current blocknum on Bitcoin 2: ", blocknum) +print("Redeemblocknum on Bitcoin 2: ", redeemblocknum) + +send_amount = float(0.01) * COIN +# Import address at same time that you fund it +bitcoind.importaddress(p2sh, "", False) +fund_txid = bitcoind.sendtoaddress(p2sh, send_amount) +txid = b2x(lx(b2x(fund_txid))) +print("fund txid", txid) + +# Find the fund tx... +txs = bitcoind.listunspent() +for tx in txs: + if tx['address'] == CBitcoinAddress(p2sh): + print("Found tx to p2sh: {0}".format(p2sh)) + fundtx = tx + +# redeemScript = CScript(x(redeemScript)) +txin = CMutableTxIn(fundtx['outpoint']) + +refundAddr = CBitcoinAddress('mvc56qCEVj6p57xZ5URNC3v7qbatudHQ9b') +txout = CMutableTxOut(fundtx['amount'] - FEE, refundAddr.to_scriptPubKey()) +# Create the unsigned raw transaction. +tx = CMutableTransaction([txin], [txout]) +# tx.nLockTime = 2430 +sighash = SignatureHash(redeemScript, tx, 0, SIGHASH_ALL) + +# privkey = bitcoind.dumpprivkey(refundPubKey) +# sig = privkey.sign(sighash) + bytes([SIGHASH_ALL]) +# Sign without secret +# OP_IF, OP_SHA256, 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 +txin.scriptSig = CScript([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... ", res) +txid = bitcoind.sendrawtransaction(tx) +refund_tx = b2x(lx(b2x(txid))) diff --git a/xcat/cli.py b/xcat/cli.py index 3b4970a..ea2b440 100644 --- a/xcat/cli.py +++ b/xcat/cli.py @@ -262,10 +262,10 @@ def main(): tradeid = args.arguments[0] checkBuyStatus(tradeid) elif command == "step3": - generate(11) + generate(31) tradeid = args.arguments[0] checkSellStatus(tradeid) elif command == "step4": - generate(1) + # generate(1) tradeid = args.arguments[0] checkBuyStatus(tradeid) diff --git a/xcat/zcashRPC.py b/xcat/zcashRPC.py index 91f7245..6d91fc7 100644 --- a/xcat/zcashRPC.py +++ b/xcat/zcashRPC.py @@ -105,21 +105,39 @@ 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 ): - 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") + print("Found funding tx: ", sendid) + return self.parse_secret(lx(tx['txid'])) + 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) @@ -130,9 +148,9 @@ class zcashProxy(): secret = x2s(asm[2]) except: secret = None - redeemPubkey = P2PKHBitcoinAddress.from_pubkey(x(pubkey)) - print("redeemPubkey: ", redeemPubkey) - return secret, redeemPubkey + self.redeemPubkey = P2PKHBitcoinAddress.from_pubkey(x(pubkey)) + print("redeemPubkey: ", self.redeemPubkey) + return secret def redeem_contract(self, contract, secret): # How to find redeemScript and redeemblocknum from blockchain?