def cltv_validate(node, tx, height): '''Modify the signature in vin 0 of the tx to pass CLTV Prepends <height> CLTV DROP in the scriptSig, and sets the locktime to height''' tx.vin[0].nSequence = 0 tx.nLockTime = height # Need to re-sign, since nSequence and nLockTime changed signed_result = node.signrawtransaction(ToHex(tx)) new_tx = CTransaction() new_tx.deserialize(BytesIO(hex_str_to_bytes(signed_result['hex']))) new_tx.vin[0].scriptSig = CScript( [CScriptNum(height), OP_CHECKLOCKTIMEVERIFY, OP_DROP] + list(CScript(new_tx.vin[0].scriptSig))) return new_tx
def cltv_lock_to_height(node, tx, to_address, amount, height=-1): '''Modify the scriptPubKey to add an OP_CHECKLOCKTIMEVERIFY, and make a transaction that spends it. This transforms the output script to anyone can spend (OP_TRUE) if the lock time condition is valid. Default height is -1 which leads CLTV to fail TODO: test more ways that transactions using CLTV could be invalid (eg locktime requirements fail, sequence time requirements fail, etc). ''' height_op = OP_1NEGATE if(height > 0): tx.vin[0].nSequence = 0 tx.nLockTime = height height_op = CScriptNum(height) tx.vout[0].scriptPubKey = CScript( [height_op, OP_CHECKLOCKTIMEVERIFY, OP_DROP, OP_TRUE]) pad_tx(tx) fundtx_raw = node.signrawtransactionwithwallet(ToHex(tx))['hex'] fundtx = FromHex(CTransaction(), fundtx_raw) fundtx.rehash() # make spending tx from_txid = fundtx.hash inputs = [{ "txid": fundtx.hash, "vout": 0 }] output = {to_address: amount} spendtx_raw = node.createrawtransaction(inputs, output) spendtx = FromHex(CTransaction(), spendtx_raw) pad_tx(spendtx) return fundtx, spendtx
def run_test(self): self.mine_chain() node = self.nodes[0] def assert_submitblock(block, result_str_1, result_str_2=None): block.solve() result_str_2 = result_str_2 or 'duplicate-invalid' assert_equal(result_str_1, node.submitblock(hexdata=b2x(block.serialize()))) assert_equal(result_str_2, node.submitblock(hexdata=b2x(block.serialize()))) self.log.info('getmininginfo') mining_info = node.getmininginfo() assert_equal(mining_info['blocks'], 200) assert_equal(mining_info['chain'], 'regtest') assert 'currentblocktx' not in mining_info assert 'currentblockweight' not in mining_info assert_equal(mining_info['difficulty'], Decimal('4.656542373906925E-10')) assert_equal(mining_info['networkhashps'], Decimal('0.003333333333333334')) assert_equal(mining_info['pooledtx'], 0) # Mine a block to leave initial block download node.generatetoaddress(1, node.get_deterministic_priv_key().address) tmpl = node.getblocktemplate({'rules': ['segwit']}) self.log.info("getblocktemplate: Test capability advertised") assert 'proposal' in tmpl['capabilities'] assert 'coinbasetxn' not in tmpl next_height = int(tmpl["height"]) coinbase_tx = create_coinbase(height=next_height) # sequence numbers must not be max for nLockTime to have effect coinbase_tx.vin[0].nSequence = 2**32 - 2 coinbase_tx.rehash() # round-trip the encoded bip34 block height commitment assert_equal(CScriptNum.decode(coinbase_tx.vin[0].scriptSig), next_height) # round-trip negative and multi-byte CScriptNums to catch python regression assert_equal(CScriptNum.decode(CScriptNum.encode(CScriptNum(1500))), 1500) assert_equal(CScriptNum.decode(CScriptNum.encode(CScriptNum(-1500))), -1500) assert_equal(CScriptNum.decode(CScriptNum.encode(CScriptNum(-1))), -1) block = CBlock() block.nVersion = tmpl["version"] block.hashPrevBlock = int(tmpl["previousblockhash"], 16) block.nTime = tmpl["curtime"] block.nBits = int(tmpl["bits"], 16) block.nNonce = 0 block.vtx = [coinbase_tx] self.log.info("getblocktemplate: segwit rule must be set") assert_raises_rpc_error( -8, "getblocktemplate must be called with the segwit rule set", node.getblocktemplate) self.log.info("getblocktemplate: Test valid block") assert_template(node, block, None) self.log.info("submitblock: Test block decode failure") assert_raises_rpc_error(-22, "Block decode failed", node.submitblock, b2x(block.serialize()[:-15])) self.log.info( "getblocktemplate: Test bad input hash for coinbase transaction") bad_block = copy.deepcopy(block) bad_block.vtx[0].vin[0].prevout.hash += 1 bad_block.vtx[0].rehash() assert_template(node, bad_block, 'bad-cb-missing') self.log.info("submitblock: Test invalid coinbase transaction") assert_raises_rpc_error(-22, "Block does not start with a coinbase", node.submitblock, b2x(bad_block.serialize())) self.log.info("getblocktemplate: Test truncated final transaction") assert_raises_rpc_error( -22, "Block decode failed", node.getblocktemplate, { 'data': b2x(block.serialize()[:-1]), 'mode': 'proposal', 'rules': ['segwit'] }) self.log.info("getblocktemplate: Test duplicate transaction") bad_block = copy.deepcopy(block) bad_block.vtx.append(bad_block.vtx[0]) assert_template(node, bad_block, 'bad-txns-duplicate') assert_submitblock(bad_block, 'bad-txns-duplicate', 'bad-txns-duplicate') self.log.info("getblocktemplate: Test invalid transaction") bad_block = copy.deepcopy(block) bad_tx = copy.deepcopy(bad_block.vtx[0]) bad_tx.vin[0].prevout.hash = 255 bad_tx.rehash() bad_block.vtx.append(bad_tx) assert_template(node, bad_block, 'bad-txns-inputs-missingorspent') assert_submitblock(bad_block, 'bad-txns-inputs-missingorspent') self.log.info("getblocktemplate: Test nonfinal transaction") bad_block = copy.deepcopy(block) bad_block.vtx[0].nLockTime = 2**32 - 1 bad_block.vtx[0].rehash() assert_template(node, bad_block, 'bad-txns-nonfinal') assert_submitblock(bad_block, 'bad-txns-nonfinal') self.log.info("getblocktemplate: Test bad tx count") # The tx count is immediately after the block header bad_block_sn = bytearray(block.serialize()) assert_equal(bad_block_sn[BLOCK_HEADER_SIZE], 1) bad_block_sn[BLOCK_HEADER_SIZE] += 1 assert_raises_rpc_error(-22, "Block decode failed", node.getblocktemplate, { 'data': b2x(bad_block_sn), 'mode': 'proposal', 'rules': ['segwit'] }) self.log.info("getblocktemplate: Test bad bits") bad_block = copy.deepcopy(block) bad_block.nBits = 469762303 # impossible in the real world assert_template(node, bad_block, 'bad-diffbits') self.log.info("getblocktemplate: Test bad merkle root") bad_block = copy.deepcopy(block) bad_block.hashMerkleRoot += 1 assert_template(node, bad_block, 'bad-txnmrklroot', False) assert_submitblock(bad_block, 'bad-txnmrklroot', 'bad-txnmrklroot') self.log.info("getblocktemplate: Test bad timestamps") bad_block = copy.deepcopy(block) bad_block.nTime = 2**31 - 1 assert_template(node, bad_block, 'time-too-new') assert_submitblock(bad_block, 'time-too-new', 'time-too-new') bad_block.nTime = 0 assert_template(node, bad_block, 'time-too-old') assert_submitblock(bad_block, 'time-too-old', 'time-too-old') self.log.info("getblocktemplate: Test not best block") bad_block = copy.deepcopy(block) bad_block.hashPrevBlock = 123 assert_template(node, bad_block, 'inconclusive-not-best-prevblk') assert_submitblock(bad_block, 'prev-blk-not-found', 'prev-blk-not-found') self.log.info('submitheader tests') assert_raises_rpc_error( -22, 'Block header decode failed', lambda: node.submitheader(hexdata='xx' * BLOCK_HEADER_SIZE)) assert_raises_rpc_error( -22, 'Block header decode failed', lambda: node.submitheader(hexdata='ff' * (BLOCK_HEADER_SIZE - 2))) assert_raises_rpc_error( -25, 'Must submit previous header', lambda: node.submitheader( hexdata=super(CBlock, bad_block).serialize().hex())) block.nTime += 1 block.solve() def chain_tip(b_hash, *, status='headers-only', branchlen=1): return { 'hash': b_hash, 'height': 202, 'branchlen': branchlen, 'status': status } assert chain_tip(block.hash) not in node.getchaintips() node.submitheader(hexdata=b2x(block.serialize())) assert chain_tip(block.hash) in node.getchaintips() node.submitheader(hexdata=b2x(CBlockHeader(block).serialize())) # Noop assert chain_tip(block.hash) in node.getchaintips() bad_block_root = copy.deepcopy(block) bad_block_root.hashMerkleRoot += 2 bad_block_root.solve() assert chain_tip(bad_block_root.hash) not in node.getchaintips() node.submitheader( hexdata=b2x(CBlockHeader(bad_block_root).serialize())) assert chain_tip(bad_block_root.hash) in node.getchaintips() # Should still reject invalid blocks, even if we have the header: assert_equal(node.submitblock(hexdata=b2x(bad_block_root.serialize())), 'bad-txnmrklroot') assert_equal(node.submitblock(hexdata=b2x(bad_block_root.serialize())), 'bad-txnmrklroot') assert chain_tip(bad_block_root.hash) in node.getchaintips() # We know the header for this invalid block, so should just return early without error: node.submitheader( hexdata=b2x(CBlockHeader(bad_block_root).serialize())) assert chain_tip(bad_block_root.hash) in node.getchaintips() bad_block_lock = copy.deepcopy(block) bad_block_lock.vtx[0].nLockTime = 2**32 - 1 bad_block_lock.vtx[0].rehash() bad_block_lock.hashMerkleRoot = bad_block_lock.calc_merkle_root() bad_block_lock.solve() assert_equal(node.submitblock(hexdata=b2x(bad_block_lock.serialize())), 'bad-txns-nonfinal') assert_equal(node.submitblock(hexdata=b2x(bad_block_lock.serialize())), 'duplicate-invalid') # Build a "good" block on top of the submitted bad block bad_block2 = copy.deepcopy(block) bad_block2.hashPrevBlock = bad_block_lock.sha256 bad_block2.solve() assert_raises_rpc_error( -25, 'bad-prevblk', lambda: node.submitheader(hexdata=b2x( CBlockHeader(bad_block2).serialize()))) # Should reject invalid header right away bad_block_time = copy.deepcopy(block) bad_block_time.nTime = 1 bad_block_time.solve() assert_raises_rpc_error( -25, 'time-too-old', lambda: node.submitheader(hexdata=b2x( CBlockHeader(bad_block_time).serialize()))) # Should ask for the block from a p2p node, if they announce the header as well: node.add_p2p_connection(P2PDataStore()) node.p2p.wait_for_getheaders(timeout=5) # Drop the first getheaders node.p2p.send_blocks_and_test(blocks=[block], node=node) # Must be active now: assert chain_tip(block.hash, status='active', branchlen=0) in node.getchaintips() # Building a few blocks should give the same results node.generatetoaddress(10, node.get_deterministic_priv_key().address) assert_raises_rpc_error( -25, 'time-too-old', lambda: node.submitheader(hexdata=b2x( CBlockHeader(bad_block_time).serialize()))) assert_raises_rpc_error( -25, 'bad-prevblk', lambda: node.submitheader(hexdata=b2x( CBlockHeader(bad_block2).serialize()))) node.submitheader(hexdata=b2x(CBlockHeader(block).serialize())) node.submitheader( hexdata=b2x(CBlockHeader(bad_block_root).serialize())) assert_equal(node.submitblock(hexdata=b2x(block.serialize())), 'duplicate') # valid
def cltv_validate(node, tx, height): # Modify the signature in vin 0 and nSequence/nLockTime of the tx to pass CLTV scheme = [[CScriptNum(height), OP_CHECKLOCKTIMEVERIFY, OP_DROP], 0, height] return cltv_modify_tx(node, tx, prepend_scriptsig=scheme[0], nsequence=scheme[1], nlocktime=scheme[2])
def run_test(self): self.mine_chain() node = self.nodes[0] def assert_submitblock(block, result_str_1, result_str_2=None): block.solve() result_str_2 = result_str_2 or 'duplicate-invalid' assert_equal(result_str_1, node.submitblock(hexdata=block.serialize().hex())) assert_equal(result_str_2, node.submitblock(hexdata=block.serialize().hex())) self.log.info('getmininginfo') mining_info = node.getmininginfo() assert_equal(mining_info['blocks'], 200) assert_equal(mining_info['chain'], 'regtest') assert 'currentblocktx' not in mining_info assert 'currentblockweight' not in mining_info assert_equal(mining_info['difficulty'], Decimal('4.656542373906925E-10')) assert_equal(mining_info['networkhashps'], Decimal('0.003333333333333334')) assert_equal(mining_info['pooledtx'], 0) # Mine a block to leave initial block download node.generatetoaddress(1, node.get_deterministic_priv_key().address) tmpl = node.getblocktemplate({'rules': ['segwit']}) self.log.info("getblocktemplate: Test capability advertised") assert 'proposal' in tmpl['capabilities'] assert 'coinbasetxn' not in tmpl next_height = int(tmpl["height"]) coinbase_tx = create_coinbase(height=next_height) # sequence numbers must not be max for nLockTime to have effect coinbase_tx.vin[0].nSequence = 2 ** 32 - 2 coinbase_tx.rehash() # round-trip the encoded bip34 block height commitment assert_equal(CScriptNum.decode(coinbase_tx.vin[0].scriptSig), next_height) # round-trip negative and multi-byte CScriptNums to catch python regression assert_equal(CScriptNum.decode(CScriptNum.encode(CScriptNum(1500))), 1500) assert_equal(CScriptNum.decode(CScriptNum.encode(CScriptNum(-1500))), -1500) assert_equal(CScriptNum.decode(CScriptNum.encode(CScriptNum(-1))), -1) block = CBlock() block.nVersion = tmpl["version"] block.hashPrevBlock = int(tmpl["previousblockhash"], 16) block.nTime = tmpl["curtime"] block.nBits = int(tmpl["bits"], 16) block.nNonce = 0 block.vtx = [coinbase_tx] self.log.info("getblocktemplate: segwit rule must be set") assert_raises_rpc_error(-8, "getblocktemplate must be called with the segwit rule set", node.getblocktemplate) self.log.info("getblocktemplate: Test valid block") assert_template(node, block, None) self.log.info("submitblock: Test block decode failure") assert_raises_rpc_error(-22, "Block decode failed", node.submitblock, block.serialize()[:-15].hex()) self.log.info("getblocktemplate: Test bad input hash for coinbase transaction") bad_block = copy.deepcopy(block) bad_block.vtx[0].vin[0].prevout.hash += 1 bad_block.vtx[0].rehash() assert_template(node, bad_block, 'bad-cb-missing') self.log.info("submitblock: Test invalid coinbase transaction") assert_raises_rpc_error(-22, "Block does not start with a coinbase", node.submitblock, bad_block.serialize().hex()) self.log.info("getblocktemplate: Test truncated final transaction") assert_raises_rpc_error(-22, "Block decode failed", node.getblocktemplate, {'data': block.serialize()[:-1].hex(), 'mode': 'proposal', 'rules': ['segwit']}) self.log.info("getblocktemplate: Test duplicate transaction") bad_block = copy.deepcopy(block) bad_block.vtx.append(bad_block.vtx[0]) assert_template(node, bad_block, 'bad-txns-duplicate') assert_submitblock(bad_block, 'bad-txns-duplicate', 'bad-txns-duplicate') self.log.info("getblocktemplate: Test invalid transaction") bad_block = copy.deepcopy(block) bad_tx = copy.deepcopy(bad_block.vtx[0]) bad_tx.vin[0].prevout.hash = 255 bad_tx.rehash() bad_block.vtx.append(bad_tx) assert_template(node, bad_block, 'bad-txns-inputs-missingorspent') assert_submitblock(bad_block, 'bad-txns-inputs-missingorspent') self.log.info("getblocktemplate: Test nonfinal transaction") bad_block = copy.deepcopy(block) bad_block.vtx[0].nLockTime = 2 ** 32 - 1 bad_block.vtx[0].rehash() assert_template(node, bad_block, 'bad-txns-nonfinal') assert_submitblock(bad_block, 'bad-txns-nonfinal') self.log.info("getblocktemplate: Test bad tx count") # The tx count is immediately after the block header bad_block_sn = bytearray(block.serialize()) assert_equal(bad_block_sn[BLOCK_HEADER_SIZE], 1) bad_block_sn[BLOCK_HEADER_SIZE] += 1 assert_raises_rpc_error(-22, "Block decode failed", node.getblocktemplate, {'data': bad_block_sn.hex(), 'mode': 'proposal', 'rules': ['segwit']}) self.log.info("getblocktemplate: Test bad bits") bad_block = copy.deepcopy(block) bad_block.nBits = 469762303 # impossible in the real world assert_template(node, bad_block, 'bad-diffbits') self.log.info("getblocktemplate: Test bad merkle root") bad_block = copy.deepcopy(block) bad_block.hashMerkleRoot += 1 assert_template(node, bad_block, 'bad-txnmrklroot', False) assert_submitblock(bad_block, 'bad-txnmrklroot', 'bad-txnmrklroot') self.log.info("getblocktemplate: Test bad timestamps") bad_block = copy.deepcopy(block) bad_block.nTime = 2 ** 31 - 1 assert_template(node, bad_block, 'time-too-new') assert_submitblock(bad_block, 'time-too-new', 'time-too-new') bad_block.nTime = 0 assert_template(node, bad_block, 'time-too-old') assert_submitblock(bad_block, 'time-too-old', 'time-too-old') self.log.info("getblocktemplate: Test not best block") bad_block = copy.deepcopy(block) bad_block.hashPrevBlock = 123 assert_template(node, bad_block, 'inconclusive-not-best-prevblk') assert_submitblock(bad_block, 'prev-blk-not-found', 'prev-blk-not-found') self.log.info('submitheader tests') assert_raises_rpc_error(-22, 'Block header decode failed', lambda: node.submitheader(hexdata='xx' * BLOCK_HEADER_SIZE)) assert_raises_rpc_error(-22, 'Block header decode failed', lambda: node.submitheader(hexdata='ff' * (BLOCK_HEADER_SIZE-2))) assert_raises_rpc_error(-25, 'Must submit previous header', lambda: node.submitheader(hexdata=super(CBlock, bad_block).serialize().hex())) block.nTime += 1 block.solve() def chain_tip(b_hash, *, status='headers-only', branchlen=1): return {'hash': b_hash, 'height': 202, 'branchlen': branchlen, 'status': status} assert chain_tip(block.hash) not in node.getchaintips() node.submitheader(hexdata=block.serialize().hex()) assert chain_tip(block.hash) in node.getchaintips() node.submitheader(hexdata=CBlockHeader(block).serialize().hex()) # Noop assert chain_tip(block.hash) in node.getchaintips() bad_block_root = copy.deepcopy(block) bad_block_root.hashMerkleRoot += 2 bad_block_root.solve() assert chain_tip(bad_block_root.hash) not in node.getchaintips() node.submitheader(hexdata=CBlockHeader(bad_block_root).serialize().hex()) assert chain_tip(bad_block_root.hash) in node.getchaintips() # Should still reject invalid blocks, even if we have the header: assert_equal(node.submitblock(hexdata=bad_block_root.serialize().hex()), 'bad-txnmrklroot') assert_equal(node.submitblock(hexdata=bad_block_root.serialize().hex()), 'bad-txnmrklroot') assert chain_tip(bad_block_root.hash) in node.getchaintips() # We know the header for this invalid block, so should just return early without error: node.submitheader(hexdata=CBlockHeader(bad_block_root).serialize().hex()) assert chain_tip(bad_block_root.hash) in node.getchaintips() bad_block_lock = copy.deepcopy(block) bad_block_lock.vtx[0].nLockTime = 2**32 - 1 bad_block_lock.vtx[0].rehash() bad_block_lock.hashMerkleRoot = bad_block_lock.calc_merkle_root() bad_block_lock.solve() assert_equal(node.submitblock(hexdata=bad_block_lock.serialize().hex()), 'bad-txns-nonfinal') assert_equal(node.submitblock(hexdata=bad_block_lock.serialize().hex()), 'duplicate-invalid') # Build a "good" block on top of the submitted bad block bad_block2 = copy.deepcopy(block) bad_block2.hashPrevBlock = bad_block_lock.sha256 bad_block2.solve() assert_raises_rpc_error(-25, 'bad-prevblk', lambda: node.submitheader(hexdata=CBlockHeader(bad_block2).serialize().hex())) # Should reject invalid header right away bad_block_time = copy.deepcopy(block) bad_block_time.nTime = 1 bad_block_time.solve() assert_raises_rpc_error(-25, 'time-too-old', lambda: node.submitheader(hexdata=CBlockHeader(bad_block_time).serialize().hex())) # Should ask for the block from a p2p node, if they announce the header as well: node.add_p2p_connection(P2PDataStore()) node.p2p.wait_for_getheaders(timeout=5) # Drop the first getheaders node.p2p.send_blocks_and_test(blocks=[block], node=node) # Must be active now: assert chain_tip(block.hash, status='active', branchlen=0) in node.getchaintips() # Building a few blocks should give the same results node.generatetoaddress(10, node.get_deterministic_priv_key().address) assert_raises_rpc_error(-25, 'time-too-old', lambda: node.submitheader(hexdata=CBlockHeader(bad_block_time).serialize().hex())) assert_raises_rpc_error(-25, 'bad-prevblk', lambda: node.submitheader(hexdata=CBlockHeader(bad_block2).serialize().hex())) node.submitheader(hexdata=CBlockHeader(block).serialize().hex()) node.submitheader(hexdata=CBlockHeader(bad_block_root).serialize().hex()) assert_equal(node.submitblock(hexdata=block.serialize().hex()), 'duplicate') # valid
def run_test(self): node = self.nodes[0] self.bootstrap_p2p() self.log.info("Create some blocks with OP_1 coinbase for spending.") tip = self.getbestblock(node) blocks = [] for _ in range(10): tip = self.build_block(tip) blocks.append(tip) node.p2p.send_blocks_and_test(blocks, node, success=True) spendable_txns = [block.vtx[0] for block in blocks] self.log.info("Mature the blocks and get out of IBD.") self.generatetoaddress(node, 100, node.get_deterministic_priv_key().address) self.log.info("Setting up spends to test and mining the fundings") # Generate a key pair privkeybytes = b"INT64!!!" * 4 private_key = ECKey() private_key.set(privkeybytes, True) # get uncompressed public key serialization public_key = private_key.get_pubkey().get_bytes() def create_fund_and_spend_tx(scriptsigextra, redeemextra) -> Tuple[CTransaction, CTransaction]: spendfrom = spendable_txns.pop() redeem_script = CScript(redeemextra + [OP_1, public_key, OP_1, OP_CHECKMULTISIG]) script_pubkey = CScript([OP_HASH160, hash160(redeem_script), OP_EQUAL]) value = spendfrom.vout[0].nValue value1 = value - 500 # Fund transaction txfund = create_tx_with_script(spendfrom, 0, b'', value1, script_pubkey) txfund.rehash() p2sh = script_to_p2sh(redeem_script) self.log.info(f"scriptPubKey {script_pubkey!r}") self.log.info(f"redeemScript {redeem_script!r} -> p2sh address {p2sh}") # Spend transaction value2 = value1 - 500 txspend = CTransaction() txspend.vout.append( CTxOut(value2, CScript([OP_TRUE]))) txspend.vin.append( CTxIn(COutPoint(txfund.sha256, 0), b'')) # Sign the transaction sighashtype = SIGHASH_ALL | SIGHASH_FORKID hashbyte = bytes([sighashtype & 0xff]) sighash = SignatureHashForkId( redeem_script, txspend, 0, sighashtype, value1) txsig = schnorr.sign(privkeybytes, sighash) + hashbyte dummy = OP_1 # Required for 1-of-1 schnorr sig txspend.vin[0].scriptSig = ss = CScript([dummy, txsig] + scriptsigextra + [redeem_script]) self.log.info(f"scriptSig: {ss!r}") txspend.rehash() return txfund, txspend mempool = [] # Basic test of OP_MUL 2 * 3 = 6 tx0, tx = create_fund_and_spend_tx([OP_2, OP_3], [OP_MUL, OP_6, OP_EQUALVERIFY]) node.p2p.send_txs_and_test([tx0, tx], node) mempool += [tx0.hash, tx.hash] spendable_txns.insert(0, tx) # Recycle the output from this tx assert_equal(node.getrawmempool(), mempool) # Basic test of OP_DIV 6 / 3 = 2 tx0, tx = create_fund_and_spend_tx([OP_6, OP_3], [OP_DIV, OP_2, OP_EQUALVERIFY]) node.p2p.send_txs_and_test([tx0, tx], node) mempool += [tx0.hash, tx.hash] spendable_txns.insert(0, tx) # Recycle te output from this tx assert_equal(node.getrawmempool(), mempool) # Divide 2^63-1 by 1 -- This should be 100% ok ssextra = [CScriptNum(int(2**63 - 1)), OP_1] rsextra = [OP_DIV, CScriptNum(int(2**63 - 1) // 1), OP_EQUALVERIFY] tx0, tx = create_fund_and_spend_tx(ssextra, rsextra) node.p2p.send_txs_and_test([tx0, tx], node) mempool += [tx0.hash, tx.hash] spendable_txns.insert(0, tx) # Recycle te output from this tx assert_equal(node.getrawmempool(), mempool) # Divide -2^63-1 / -2^63-1 -- This should be 100% ok ssextra = [CScriptNum(-int(2**63 - 1)), CScriptNum(-int(2**63 - 1))] rsextra = [OP_DIV, OP_1, OP_EQUALVERIFY] tx0, tx = create_fund_and_spend_tx(ssextra, rsextra) node.p2p.send_txs_and_test([tx0, tx], node) mempool += [tx0.hash, tx.hash] spendable_txns.insert(0, tx) # Recycle te output from this tx assert_equal(node.getrawmempool(), mempool) # Divide -2^63-1 / 2^63-1 -- This should be 100% ok ssextra = [CScriptNum(-int(2**63 - 1)), CScriptNum(int(2**63 - 1))] rsextra = [OP_DIV, OP_1NEGATE, OP_EQUALVERIFY] tx0, tx = create_fund_and_spend_tx(ssextra, rsextra) node.p2p.send_txs_and_test([tx0, tx], node) mempool += [tx0.hash, tx.hash] spendable_txns.insert(0, tx) # Recycle te output from this tx assert_equal(node.getrawmempool(), mempool) # Divide 2^63-1 / -2^63-1 -- This should be 100% ok ssextra = [CScriptNum(int(2**63 - 1)), CScriptNum(-int(2**63 - 1))] rsextra = [OP_DIV, OP_1NEGATE, OP_EQUALVERIFY] tx0, tx = create_fund_and_spend_tx(ssextra, rsextra) node.p2p.send_txs_and_test([tx0, tx], node) mempool += [tx0.hash, tx.hash] spendable_txns.insert(0, tx) # Recycle te output from this tx assert_equal(node.getrawmempool(), mempool) # Divide 2^63-1 / 2^63-1 -- This should be 100% ok ssextra = [CScriptNum(int(2**63 - 1)), CScriptNum(int(2**63 - 1))] rsextra = [OP_DIV, OP_1, OP_EQUALVERIFY] tx0, tx = create_fund_and_spend_tx(ssextra, rsextra) node.p2p.send_txs_and_test([tx0, tx], node) mempool += [tx0.hash, tx.hash] spendable_txns.insert(0, tx) # Recycle te output from this tx assert_equal(node.getrawmempool(), mempool) # Multiply past 2^32 -- should work ssextra = [CScriptNum(int(2**31)), CScriptNum(int(2**31))] rsextra = [OP_MUL, CScriptNum(int(2**62)), OP_EQUALVERIFY] tx0, tx = create_fund_and_spend_tx(ssextra, rsextra) node.p2p.send_txs_and_test([tx0, tx], node) mempool += [tx0.hash, tx.hash] spendable_txns.insert(0, tx) # Recycle te output from this tx assert_equal(node.getrawmempool(), mempool) # Add past (2^31 - 1) -- should work ssextra = [CScriptNum(int(2**31)), CScriptNum(int(2**31))] rsextra = [OP_ADD, CScriptNum(int(2**32)), OP_EQUALVERIFY] tx0, tx = create_fund_and_spend_tx(ssextra, rsextra) node.p2p.send_txs_and_test([tx0, tx], node) mempool += [tx0.hash, tx.hash] spendable_txns.insert(0, tx) # Recycle te output from this tx assert_equal(node.getrawmempool(), mempool) # Sub below -(2^31 - 1) -- should work ssextra = [CScriptNum(-int(2**31 - 1)), CScriptNum(int(2**31 - 1))] rsextra = [OP_SUB, CScriptNum(-int(2**32 - 2)), OP_EQUALVERIFY] tx0, tx = create_fund_and_spend_tx(ssextra, rsextra) node.p2p.send_txs_and_test([tx0, tx], node) mempool += [tx0.hash, tx.hash] spendable_txns.insert(0, tx) # Recycle te output from this tx assert_equal(node.getrawmempool(), mempool) # Divide/multiply mixed: -2^60 * 3 / 6 == -2^59 ssextra = [CScriptNum(-int(2**60)), OP_3] rsextra = [OP_MUL, OP_6, OP_DIV, CScriptNum(-int(2**59)), OP_EQUALVERIFY] tx0, tx = create_fund_and_spend_tx(ssextra, rsextra) node.p2p.send_txs_and_test([tx0, tx], node) mempool += [tx0.hash, tx.hash] spendable_txns.insert(0, tx) # Recycle te output from this tx assert_equal(node.getrawmempool(), mempool) # Divide: -2^31 * 3 / -6 == 2^30 (intermediate value outside of 32-bit range) ssextra = [CScriptNum(-int(2**31)), OP_3] rsextra = [OP_MUL, CScriptNum(-6), OP_DIV, CScriptNum(int(2**30)), OP_EQUALVERIFY] tx0, tx = create_fund_and_spend_tx(ssextra, rsextra) node.p2p.send_txs_and_test([tx0, tx], node) mempool += [tx0.hash, tx.hash] spendable_txns.insert(0, tx) # Recycle te output from this tx assert_equal(node.getrawmempool(), mempool) # Divide: -2^32 * 3 / -6 == 2^31 (1 operand & intermediate value > 2^31 - 1) ssextra = [CScriptNum(-int(2**32)), OP_3] rsextra = [OP_MUL, CScriptNum(-6), OP_DIV, CScriptNum(int(2**31)), OP_EQUALVERIFY] tx0, tx = create_fund_and_spend_tx(ssextra, rsextra) node.p2p.send_txs_and_test([tx0, tx], node) mempool += [tx0.hash, tx.hash] spendable_txns.insert(0, tx) # Recycle te output from this tx assert_equal(node.getrawmempool(), mempool) # Multiply past 2^63 - 1 -- funding tx is ok, spending should not be accepted due to out-of-range operand ssextra = [CScriptNum(int((2**63) - 1)), OP_3] rsextra = [OP_MUL, OP_DROP, OP_1, OP_1, OP_EQUALVERIFY] tx0, tx = create_fund_and_spend_tx(ssextra, rsextra) node.p2p.send_txs_and_test([tx0], node) node.p2p.send_txs_and_test([tx], node, success=False, expect_disconnect=True, reject_reason=OVERFLOW_ERROR_BAD_OPERAND) mempool += [tx0.hash] assert_equal(node.getrawmempool(), mempool) self.reconnect_p2p() # we lost the connection from above bad tx, reconnect # Add past 2^63 - 1 -- funding tx is ok, spending should not be accepted due to bad operand ssextra = [CScriptNum(int((2**63) - 1)), OP_1] rsextra = [OP_ADD, OP_DROP, OP_1, OP_1, OP_EQUALVERIFY] tx0, tx = create_fund_and_spend_tx(ssextra, rsextra) node.p2p.send_txs_and_test([tx0], node) node.p2p.send_txs_and_test([tx], node, success=False, expect_disconnect=True, reject_reason=OVERFLOW_ERROR_BAD_OPERAND) mempool += [tx0.hash] assert_equal(node.getrawmempool(), mempool) self.reconnect_p2p() # we lost the connection from above bad tx, reconnect # Sub below -2^63 - 1 -- funding tx is ok, spending should not be accepted due to bad operand ssextra = [CScriptNum(-int((2**63) - 1)), OP_10] rsextra = [OP_SUB, OP_DROP, OP_1, OP_1, OP_EQUALVERIFY] tx0, tx = create_fund_and_spend_tx(ssextra, rsextra) node.p2p.send_txs_and_test([tx0], node) node.p2p.send_txs_and_test([tx], node, success=False, expect_disconnect=True, reject_reason=OVERFLOW_ERROR_BAD_OPERAND) mempool += [tx0.hash] assert_equal(node.getrawmempool(), mempool) self.reconnect_p2p() # we lost the connection from above bad tx, reconnect # Modulo: -(2^63 - 1) % -1. Should not overflow, but yield 0 ssextra = [CScriptNum(-int((2**63) - 1)), OP_1NEGATE] rsextra = [OP_MOD, OP_0, OP_EQUALVERIFY] tx0, tx = create_fund_and_spend_tx(ssextra, rsextra) node.p2p.send_txs_and_test([tx0, tx], node) mempool += [tx0.hash, tx.hash] spendable_txns.insert(0, tx) # Recycle te output from this tx assert_equal(node.getrawmempool(), mempool) # Modulo: -(2^63 - 1) % -(2^63 - 1). Should not overflow, but yield 0 ssextra = [CScriptNum(-int((2**63) - 1)), CScriptNum(-int((2**63) - 1))] rsextra = [OP_MOD, OP_0, OP_EQUALVERIFY] tx0, tx = create_fund_and_spend_tx(ssextra, rsextra) node.p2p.send_txs_and_test([tx0, tx], node) mempool += [tx0.hash, tx.hash] spendable_txns.insert(0, tx) # Recycle te output from this tx assert_equal(node.getrawmempool(), mempool) # Attempt to create the forbidden: -(2^63), but don't use it as a number ssextra = [bytes((0x80,)) + bytes((0x7f,)) + bytes((0xff,) * 7)] rsextra = [bytes((0x80,)) + bytes((0x7f,)) + bytes((0xff,) * 7), OP_EQUALVERIFY] tx0, tx = create_fund_and_spend_tx(ssextra, rsextra) node.p2p.send_txs_and_test([tx0, tx], node) mempool += [tx0.hash, tx.hash] spendable_txns.insert(0, tx) # Recycle te output from this tx assert_equal(node.getrawmempool(), mempool) # Attempt to create the forbidden: -(2^63), use it as a number # Note: This generates an overflow exception when deserializing, hence the "unknown error" -- known issue # with the interpreter. ssextra = [bytes((0x80,)) + bytes((0x7f,)) + bytes((0xff,) * 7)] rsextra = [bytes((0x80,)) + bytes((0x7f,)) + bytes((0xff,) * 7), OP_SUB, OP_0, OP_EQUALVERIFY] tx0, tx = create_fund_and_spend_tx(ssextra, rsextra) node.p2p.send_txs_and_test([tx0], node) node.p2p.send_txs_and_test([tx], node, success=False, expect_disconnect=True, reject_reason=OVERFLOW_ERROR_UNK) mempool += [tx0.hash] assert_equal(node.getrawmempool(), mempool) self.reconnect_p2p() # we lost the connection from above bad tx, reconnect # OP_NUM2BIN - {2^63 - 1} -> 8-byte BIN should succeed ssextra = [CScriptNum(int(2**63 - 1)), OP_8] rsextra = [OP_NUM2BIN, bytes.fromhex('ffffffffffffff7f'), OP_EQUALVERIFY] tx0, tx = create_fund_and_spend_tx(ssextra, rsextra) node.p2p.send_txs_and_test([tx0, tx], node) mempool += [tx0.hash, tx.hash] spendable_txns.insert(0, tx) # Recycle te output from this tx assert_equal(node.getrawmempool(), mempool) # OP_NUM2BIN - {2^63 - 1} -> 16-byte BIN should succeed ssextra = [CScriptNum(int(2**63 - 1)), OP_16] rsextra = [OP_NUM2BIN, bytes.fromhex('ffffffffffffff7f0000000000000000'), OP_EQUALVERIFY] tx0, tx = create_fund_and_spend_tx(ssextra, rsextra) node.p2p.send_txs_and_test([tx0, tx], node) mempool += [tx0.hash, tx.hash] spendable_txns.insert(0, tx) # Recycle te output from this tx assert_equal(node.getrawmempool(), mempool) # OP_NUM2BIN - {-2^63 + 1} -> 8-byte BIN should succeed ssextra = [CScriptNum(int(-2**63 + 1)), OP_8] rsextra = [OP_NUM2BIN, bytes.fromhex('ffffffffffffffff'), OP_EQUALVERIFY] tx0, tx = create_fund_and_spend_tx(ssextra, rsextra) node.p2p.send_txs_and_test([tx0, tx], node) mempool += [tx0.hash, tx.hash] spendable_txns.insert(0, tx) # Recycle te output from this tx assert_equal(node.getrawmempool(), mempool) # OP_NUM2BIN - {2^63 - 1} -> 7-byte BIN should fail because it won't fit within requested size ssextra = [CScriptNum(int(2**63 - 1)), OP_7] rsextra = [OP_NUM2BIN, OP_DROP] tx0, tx = create_fund_and_spend_tx(ssextra, rsextra) node.p2p.send_txs_and_test([tx0], node) node.p2p.send_txs_and_test([tx], node, success=False, expect_disconnect=True, reject_reason=IMPOSSIBLE_ENCODING_ERROR) mempool += [tx0.hash] assert_equal(node.getrawmempool(), mempool) self.reconnect_p2p() # we lost the connection from above bad tx, reconnect # OP_NUM2BIN - {2^31 - 1} -> 8-byte BIN should succeed (check that old functionality still works) ssextra = [CScriptNum(int(2**31 - 1)), OP_8] rsextra = [OP_NUM2BIN, bytes.fromhex('ffffff7f00000000'), OP_EQUALVERIFY] tx0, tx = create_fund_and_spend_tx(ssextra, rsextra) node.p2p.send_txs_and_test([tx0, tx], node) mempool += [tx0.hash, tx.hash] spendable_txns.insert(0, tx) # Recycle te output from this tx assert_equal(node.getrawmempool(), mempool) # OP_NUM2BIN - {2^31 - 1} -> 4-byte BIN should succeed (check that old functionality still works) ssextra = [CScriptNum(int(2**31 - 1)), OP_4] rsextra = [OP_NUM2BIN, bytes.fromhex('ffffff7f'), OP_EQUALVERIFY] tx0, tx = create_fund_and_spend_tx(ssextra, rsextra) node.p2p.send_txs_and_test([tx0, tx], node) mempool += [tx0.hash, tx.hash] spendable_txns.insert(0, tx) # Recycle te output from this tx assert_equal(node.getrawmempool(), mempool) # OP_BIN2NUM - {BIN 2^63 - 1} should succeed ssextra = [CScriptNum(int(2**63 - 1))] rsextra = [OP_BIN2NUM, bytes.fromhex('ffffffffffffff7f'), OP_EQUALVERIFY] tx0, tx = create_fund_and_spend_tx(ssextra, rsextra) node.p2p.send_txs_and_test([tx0, tx], node) mempool += [tx0.hash, tx.hash] spendable_txns.insert(0, tx) # Recycle te output from this tx assert_equal(node.getrawmempool(), mempool) # OP_BIN2NUM - {BIN 2^63-1 padded with 8 extra bytes of zeroes} should succeed ssextra = [CScriptNum.encode(CScriptNum(int(2**63 - 1)))[1:] + bytes.fromhex('00') * 8] rsextra = [OP_BIN2NUM, bytes.fromhex('ffffffffffffff7f'), OP_EQUALVERIFY] tx0, tx = create_fund_and_spend_tx(ssextra, rsextra) node.p2p.send_txs_and_test([tx0, tx], node) mempool += [tx0.hash, tx.hash] spendable_txns.insert(0, tx) # Recycle te output from this tx assert_equal(node.getrawmempool(), mempool) # OP_BIN2NUM - {BIN -2^63+1} should succeed ssextra = [CScriptNum(int(-2**63 + 1))] rsextra = [OP_BIN2NUM, bytes.fromhex('ffffffffffffffff'), OP_EQUALVERIFY] tx0, tx = create_fund_and_spend_tx(ssextra, rsextra) node.p2p.send_txs_and_test([tx0, tx], node) mempool += [tx0.hash, tx.hash] spendable_txns.insert(0, tx) # Recycle te output from this tx assert_equal(node.getrawmempool(), mempool) # OP_BIN2NUM - {BIN 2^63} when crammed into 8 bytes will be treated as '-0'.. which, oddly, ends up as # 0 when using BIN2NUM. This is the same quirky behavior as before this feature was added, # e.g.: 0x80 BIN2NUM -> 0 ssextra = [bytes.fromhex('0000000000000080')] rsextra = [OP_BIN2NUM, OP_0, OP_EQUALVERIFY] tx0, tx = create_fund_and_spend_tx(ssextra, rsextra) node.p2p.send_txs_and_test([tx0, tx], node) mempool += [tx0.hash, tx.hash] assert_equal(node.getrawmempool(), mempool) # OP_BIN2NUM - {2^63} -> When encoding as not '-0', but what 2^63 would encode as (9-byte value), it should fail # because it's out of range. ssextra = [bytes.fromhex('000000000000008000')] rsextra = [OP_BIN2NUM, OP_DROP] tx0, tx = create_fund_and_spend_tx(ssextra, rsextra) node.p2p.send_txs_and_test([tx0], node) node.p2p.send_txs_and_test([tx], node, success=False, expect_disconnect=True, reject_reason=OVERFLOW_ERROR_BAD_OPERAND) mempool += [tx0.hash] assert_equal(node.getrawmempool(), mempool) self.reconnect_p2p() # we lost the connection from above bad tx, reconnect # OP_BIN2NUM - {BIN 2^31-1} should succeed (check that old functionality still works) ssextra = [CScriptNum(int(2**31 - 1))] rsextra = [OP_BIN2NUM, bytes.fromhex('ffffff7f'), OP_EQUALVERIFY] tx0, tx = create_fund_and_spend_tx(ssextra, rsextra) node.p2p.send_txs_and_test([tx0, tx], node) mempool += [tx0.hash, tx.hash] spendable_txns.insert(0, tx) # Recycle te output from this tx assert_equal(node.getrawmempool(), mempool) # OP_PICK - {4294967297 OP_PICK} should FAIL on both 64-bit and 32-bit systems ssextra = [OP_16, OP_15, CScriptNum(4294967297)] rsextra = [OP_PICK, OP_2, OP_PICK, OP_EQUALVERIFY, OP_DROP, OP_DROP] tx0, tx = create_fund_and_spend_tx(ssextra, rsextra) node.p2p.send_txs_and_test([tx0], node) node.p2p.send_txs_and_test([tx], node, success=False, expect_disconnect=True, reject_reason=INVALID_STACK_OPERATION) mempool += [tx0.hash] assert_equal(node.getrawmempool(), mempool) self.reconnect_p2p() # we lost the connection from above bad tx, reconnect # Finally, mine the mempool and ensure that all txns made it into a block prevtiphash = node.getbestblockhash() tiphash = self.generatetoaddress(node, 1, node.get_deterministic_priv_key().address)[0] assert prevtiphash != tiphash assert_equal(node.getrawmempool(), []) blockinfo = node.getblock(tiphash, 1) assert all(txid in blockinfo['tx'] for txid in mempool) # -------------------------------------------------------------------- # Test that scripts fail evaluation if bigint64 feature is disabled # -------------------------------------------------------------------- # 1. Restart the node with -reindex-chainstate (to be paranoid) self.restart_node(0, self.extra_args[0] + ["-reindex-chainstate=1"]) assert_equal(node.getbestblockhash(), tiphash) # The below tests have been disabled since BCHN no longer has the -upgrade8activationtime argument # (it has activated already and the height has been hard-coded for all chains). """
def script_BIP34_coinbase_height(height): if height <= 16: res = CScriptOp.encode_op_n(height) # Append dummy to increase scriptSig size above 2 (see bad-cb-length consensus rule) return CScript([res, OP_1]) return CScript([CScriptNum(height)])
def tapscript_satisfy_test(self, script, inputs=[], add_issuance=False, add_pegin=False, fail=None, add_prevout=False, add_asset=False, add_value=False, add_spk=False, seq=0, add_out_spk=None, add_out_asset=None, add_out_value=None, add_out_nonce=None, ver=2, locktime=0, add_num_outputs=False, add_weight=False, blind=False): # Create a taproot utxo scripts = [("s0", script)] prev_tx, prev_vout, spk, sec, pub, tap = self.create_taproot_utxo( scripts) if add_pegin: fund_info = self.nodes[0].getpeginaddress() peg_id = self.nodes[0].sendtoaddress( fund_info["mainchain_address"], 1) raw_peg_tx = self.nodes[0].gettransaction(peg_id)["hex"] peg_txid = self.nodes[0].sendrawtransaction(raw_peg_tx) self.nodes[0].generate(101) peg_prf = self.nodes[0].gettxoutproof([peg_txid]) claim_script = fund_info["claim_script"] raw_claim = self.nodes[0].createrawpegin(raw_peg_tx, peg_prf, claim_script) tx = FromHex(CTransaction(), raw_claim['hex']) else: tx = CTransaction() tx.nVersion = ver tx.nLockTime = locktime # Spend the pegin and taproot tx together in_total = prev_tx.vout[prev_vout].nValue.getAmount() fees = 1000 tap_in_pos = 0 if blind: # Add an unrelated output key = ECKey() key.generate() tx.vout.append( CTxOut(nValue=CTxOutValue(10000), scriptPubKey=spk, nNonce=CTxOutNonce(key.get_pubkey().get_bytes()))) tx_hex = self.nodes[0].fundrawtransaction(tx.serialize().hex()) tx = FromHex(CTransaction(), tx_hex['hex']) tx.vin.append( CTxIn(COutPoint(prev_tx.sha256, prev_vout), nSequence=seq)) tx.vout.append( CTxOut(nValue=CTxOutValue(in_total - fees), scriptPubKey=spk)) # send back to self tx.vout.append(CTxOut(CTxOutValue(fees))) if add_issuance: blind_addr = self.nodes[0].getnewaddress() issue_addr = self.nodes[0].validateaddress( blind_addr)['unconfidential'] # Issuances only require one fee output and that output must the last # one. However the way, the current code is structured, it is not possible # to this in a super clean without special casing. if add_pegin: tx.vout.pop() tx.vout.pop() tx.vout.insert(0, CTxOut(nValue=CTxOutValue(in_total), scriptPubKey=spk)) # send back to self) issued_tx = self.nodes[0].rawissueasset( tx.serialize().hex(), [{ "asset_amount": 2, "asset_address": issue_addr, "blind": False }])[0]["hex"] tx = FromHex(CTransaction(), issued_tx) # Sign inputs if add_pegin: signed = self.nodes[0].signrawtransactionwithwallet( tx.serialize().hex()) tx = FromHex(CTransaction(), signed['hex']) tap_in_pos += 1 else: # Need to create empty witness when not deserializing from rpc tx.wit.vtxinwit.append(CTxInWitness()) if blind: tx.vin[0], tx.vin[1] = tx.vin[1], tx.vin[0] utxo = self.get_utxo(tx, 1) zero_str = "0" * 64 blinded_raw = self.nodes[0].rawblindrawtransaction( tx.serialize().hex(), [zero_str, utxo["amountblinder"]], [1.2, utxo['amount']], [utxo['asset'], utxo['asset']], [zero_str, utxo['assetblinder']]) tx = FromHex(CTransaction(), blinded_raw) signed_raw_tx = self.nodes[0].signrawtransactionwithwallet( tx.serialize().hex()) tx = FromHex(CTransaction(), signed_raw_tx['hex']) suffix_annex = [] control_block = bytes([ tap.leaves["s0"].version + tap.negflag ]) + tap.inner_pubkey + tap.leaves["s0"].merklebranch # Add the prevout to the top of inputs. The witness script will check for equality. if add_prevout: inputs = [ prev_vout.to_bytes(4, 'little'), ser_uint256(prev_tx.sha256) ] if add_asset: assert blind # only used with blinding in testing utxo = self.nodes[0].gettxout( ser_uint256(tx.vin[1].prevout.hash)[::-1].hex(), tx.vin[1].prevout.n) if "assetcommitment" in utxo: asset = bytes.fromhex(utxo["assetcommitment"]) else: asset = b"\x01" + bytes.fromhex(utxo["asset"])[::-1] inputs = [asset[0:1], asset[1:33]] if add_value: utxo = self.nodes[0].gettxout( ser_uint256(tx.vin[1].prevout.hash)[::-1].hex(), tx.vin[1].prevout.n) if "valuecommitment" in utxo: value = bytes.fromhex(utxo["valuecommitment"]) inputs = [value[0:1], value[1:33]] else: value = b"\x01" + int( satoshi_round(utxo["value"]) * COIN).to_bytes(8, 'little') inputs = [value[0:1], value[1:9]] if add_spk: ver = CScriptOp.decode_op_n(int.from_bytes(spk[0:1], 'little')) inputs = [CScriptNum.encode(CScriptNum(ver))[1:], spk[2:len(spk)]] # always segwit # Add witness for outputs if add_out_asset is not None: asset = tx.vout[add_out_asset].nAsset.vchCommitment inputs = [asset[0:1], asset[1:33]] if add_out_value is not None: value = tx.vout[add_out_value].nValue.vchCommitment if len(value) == 9: inputs = [value[0:1], value[1:9][::-1]] else: inputs = [value[0:1], value[1:33]] if add_out_nonce is not None: nonce = tx.vout[add_out_nonce].nNonce.vchCommitment if len(nonce) == 1: inputs = [b''] else: inputs = [nonce] if add_out_spk is not None: out_spk = tx.vout[add_out_spk].scriptPubKey if len(out_spk) == 0: # Python upstream encoding CScriptNum interesting behaviour where it also encodes the length # This assumes the implicit wallet behaviour of using segwit outputs. # This is useful while sending scripts, but not while using CScriptNums in constructing scripts inputs = [ CScriptNum.encode(CScriptNum(-1))[1:], sha256(out_spk) ] else: ver = CScriptOp.decode_op_n( int.from_bytes(out_spk[0:1], 'little')) inputs = [ CScriptNum.encode(CScriptNum(ver))[1:], out_spk[2:len(out_spk)] ] # always segwit if add_num_outputs: num_outs = len(tx.vout) inputs = [CScriptNum.encode(CScriptNum(num_outs))[1:]] if add_weight: # Add a dummy input and check the overall weight inputs = [int(5).to_bytes(8, 'little')] wit = inputs + [bytes(tap.leaves["s0"].script), control_block ] + suffix_annex tx.wit.vtxinwit[tap_in_pos].scriptWitness.stack = wit exp_weight = self.nodes[0].decoderawtransaction( tx.serialize().hex())["weight"] inputs = [exp_weight.to_bytes(8, 'little')] wit = inputs + [bytes(tap.leaves["s0"].script), control_block ] + suffix_annex tx.wit.vtxinwit[tap_in_pos].scriptWitness.stack = wit if fail: assert_raises_rpc_error(-26, fail, self.nodes[0].sendrawtransaction, tx.serialize().hex()) return self.nodes[0].sendrawtransaction(hexstring=tx.serialize().hex()) self.nodes[0].generate(1) last_blk = self.nodes[0].getblock(self.nodes[0].getbestblockhash()) tx.rehash() assert (tx.hash in last_blk['tx'])
def run_test(self): node = self.nodes[0] self.log.info('getmininginfo') mining_info = node.getmininginfo() assert_equal(mining_info['blocks'], 200) assert_equal(mining_info['chain'], 'regtest') assert_equal(mining_info['currentblocktx'], 0) assert_equal(mining_info['currentblockweight'], 0) # assert_equal(mining_info['difficulty'], Decimal('4.159690237829E-9')) # assert_equal(mining_info['networkhashps'], Decimal('0.1841805555555555')) assert_equal(mining_info['pooledtx'], 0) # Mine a block to leave initial block download node.generate(1) tmpl = node.getblocktemplate() self.log.info("getblocktemplate: Test capability advertised") assert 'proposal' in tmpl['capabilities'] assert 'coinbasetxn' not in tmpl newaddress = node.getnewaddress() pubkey = node.validateaddress(newaddress)["pubkey"] next_height = int(tmpl["height"]) coinbase_key = CECKey() coinbase_key.set_secretbytes(b"horsebattery") coinbase_pubkey = coinbase_key.get_pubkey() coinbase_tx = create_coinbase( height=next_height, pubkey=coinbase_pubkey) #bytes(pubkey,encoding='utf-8')) # sequence numbers must not be max for nLockTime to have effect coinbase_tx.vin[0].nSequence = 2**32 - 2 coinbase_tx.rehash() # round-trip the encoded bip34 block height commitment assert_equal(CScriptNum.decode(coinbase_tx.vin[0].scriptSig), next_height) # round-trip negative and multi-byte CScriptNums to catch python regression assert_equal(CScriptNum.decode(CScriptNum.encode(CScriptNum(1500))), 1500) assert_equal(CScriptNum.decode(CScriptNum.encode(CScriptNum(-1500))), -1500) assert_equal(CScriptNum.decode(CScriptNum.encode(CScriptNum(-1))), -1) block = CBlock() block.nVersion = tmpl["version"] block.hashPrevBlock = int(tmpl["previousblockhash"], 16) block.nTime = tmpl["curtime"] block.nBits = int(tmpl["bits"], 16) block.nNonce = 0 block.vtx = [coinbase_tx] self.log.info("getblocktemplate: Test valid block") assert_template(node, block, None) self.log.info("submitblock: Test block decode failure") assert_raises_rpc_error(-22, "Block decode failed", node.submitblock, b2x(block.serialize()[:-15])) self.log.info( "getblocktemplate: Test bad input hash for coinbase transaction") bad_block = copy.deepcopy(block) bad_block.vtx[0].vin[0].prevout.hash += 1 bad_block.vtx[0].rehash() assert_template(node, bad_block, 'bad-cb-missing') self.log.info("submitblock: Test invalid coinbase transaction") assert_raises_rpc_error(-22, "Block does not start with a coinbase", node.submitblock, b2x(bad_block.serialize())) self.log.info("getblocktemplate: Test truncated final transaction") assert_raises_rpc_error(-22, "Block decode failed", node.getblocktemplate, { 'data': b2x(block.serialize()[:-1]), 'mode': 'proposal' }) self.log.info("getblocktemplate: Test duplicate transaction") bad_block = copy.deepcopy(block) bad_block.vtx.append(bad_block.vtx[0]) assert_template(node, bad_block, 'bad-txns-duplicate') self.log.info("getblocktemplate: Test invalid transaction") bad_block = copy.deepcopy(block) bad_tx = copy.deepcopy(bad_block.vtx[0]) bad_tx.vin[0].prevout.hash = 255 bad_tx.rehash() bad_block.vtx.append(bad_tx) assert_template(node, bad_block, 'bad-txns-inputs-missingorspent') self.log.info("getblocktemplate: Test nonfinal transaction") bad_block = copy.deepcopy(block) bad_block.vtx[0].nLockTime = 2**32 - 1 bad_block.vtx[0].rehash() assert_template(node, bad_block, 'bad-txns-nonfinal') self.log.info("getblocktemplate: Test bad tx count") # The tx count is immediately after the block header TX_COUNT_OFFSET = 80 bad_block_sn = bytearray(block.serialize()) assert_equal(bad_block_sn[TX_COUNT_OFFSET], 1) bad_block_sn[TX_COUNT_OFFSET] += 1 assert_raises_rpc_error(-22, "Block decode failed", node.getblocktemplate, { 'data': b2x(bad_block_sn), 'mode': 'proposal' }) self.log.info("getblocktemplate: Test bad bits") bad_block = copy.deepcopy(block) bad_block.nBits = 469762303 # impossible in the real world assert_template(node, bad_block, 'bad-diffbits') self.log.info("getblocktemplate: Test bad merkle root") bad_block = copy.deepcopy(block) bad_block.hashMerkleRoot += 1 assert_template(node, bad_block, 'bad-txnmrklroot', False) self.log.info("getblocktemplate: Test bad timestamps") bad_block = copy.deepcopy(block) bad_block.nTime = 2**31 - 1 assert_template(node, bad_block, 'time-too-new') bad_block.nTime = 0 assert_template(node, bad_block, 'time-too-old') self.log.info("getblocktemplate: Test not best block") bad_block = copy.deepcopy(block) bad_block.hashPrevBlock = 123 assert_template(node, bad_block, 'inconclusive-not-best-prevblk')
def run_test(self): node = self.nodes[0] # Generate 6 keys. rawkeys = [] pubkeys = [] for i in range(6): raw_key = CECKey() raw_key.set_secretbytes(('privkey%d' % i).encode('ascii')) rawkeys.append(raw_key) pubkeys = [CPubKey(key.get_pubkey()) for key in rawkeys] # Create a 4-of-6 multi-sig wallet with CLTV. height = 210 redeem_script = CScript( [CScriptNum(height), OP_CHECKLOCKTIMEVERIFY, OP_DROP ] # CLTV (lock_time >= 210) + [OP_4] + pubkeys + [OP_6, OP_CHECKMULTISIG]) # multi-sig hex_redeem_script = bytes_to_hex_str(redeem_script) p2sh_address = script_to_p2sh(redeem_script, main=False) # Send 1 coin to the mult-sig wallet. txid = node.sendtoaddress(p2sh_address, 1.0) raw_tx = node.getrawtransaction(txid, True) try: node.importaddress(hex_redeem_script, 'cltv', True, True) except Exception: pass assert_equal( sig(node.getreceivedbyaddress(p2sh_address, 0) - Decimal(1.0)), 0) # Mine one block to confirm the transaction. node.generate(1) # block 201 assert_equal( sig(node.getreceivedbyaddress(p2sh_address, 1) - Decimal(1.0)), 0) # Try to spend the coin. addr_to = node.getnewaddress('') # (1) Find the UTXO for vout in raw_tx['vout']: if vout['scriptPubKey']['addresses'] == [p2sh_address]: vout_n = vout['n'] hex_script_pubkey = raw_tx['vout'][vout_n]['scriptPubKey']['hex'] value = raw_tx['vout'][vout_n]['value'] # (2) Create a tx inputs = [{ "txid": txid, "vout": vout_n, "scriptPubKey": hex_script_pubkey, "redeemScript": hex_redeem_script, "amount": value, }] outputs = {addr_to: 0.999} lock_time = height hex_spend_raw_tx = node.createrawtransaction(inputs, outputs, lock_time) hex_funding_raw_tx = node.getrawtransaction(txid, False) # (3) Try to sign the spending tx. tx0 = CTransaction() tx0.deserialize(io.BytesIO(hex_str_to_bytes(hex_funding_raw_tx))) tx1 = CTransaction() tx1.deserialize(io.BytesIO(hex_str_to_bytes(hex_spend_raw_tx))) self.sign_tx(tx1, tx0, vout_n, redeem_script, 0, rawkeys[:4]) # Sign with key[0:4] # Mine some blocks to pass the lock time. node.generate(10) # Spend the CLTV multi-sig coins. raw_tx1 = tx1.serialize() hex_raw_tx1 = bytes_to_hex_str(raw_tx1) node.sendrawtransaction(hex_raw_tx1) # Check the tx is accepted by mempool but not confirmed. assert_equal( sig(node.getreceivedbyaddress(addr_to, 0) - Decimal(0.999)), 0) assert_equal(sig(node.getreceivedbyaddress(addr_to, 1)), 0) # Mine a block to confirm the tx. node.generate(1) assert_equal( sig(node.getreceivedbyaddress(addr_to, 1) - Decimal(0.999)), 0)