def run_test(self): node = self.nodes[0] # convenience reference to the node self.bootstrap_p2p() # Add one p2p connection to the node best_block = self.nodes[0].getbestblockhash() tip = int(best_block, 16) best_block_time = self.nodes[0].getblock(best_block)['time'] block_time = best_block_time + 1 self.log.info("Create a new block with an anyone-can-spend coinbase.") height = 1 block = create_block(tip, create_coinbase(height), block_time) block.solve() # Save the coinbase for later block1 = block node.p2ps[0].send_blocks_and_test([block], node, success=True) self.log.info("Mature the block.") self.generatetoaddress( self.nodes[0], 100, self.nodes[0].get_deterministic_priv_key().address) # Iterate through a list of known invalid transaction types, ensuring each is # rejected. Some are consensus invalid and some just violate policy. for BadTxTemplate in invalid_txs.iter_all_templates(): self.log.info("Testing invalid transaction: %s", BadTxTemplate.__name__) template = BadTxTemplate(spend_block=block1) tx = template.get_tx() node.p2ps[0].send_txs_and_test( [tx], node, success=False, expect_disconnect=template.expect_disconnect, reject_reason=template.reject_reason, ) if template.expect_disconnect: self.log.info("Reconnecting to peer") self.reconnect_p2p() # Make two p2p connections to provide the node with orphans # * p2ps[0] will send valid orphan txs (one with low fee) # * p2ps[1] will send an invalid orphan tx (and is later disconnected for that) self.reconnect_p2p(num_connections=2) self.log.info('Test orphan transaction handling ... ') # Create a root transaction that we withhold until all dependent transactions # are sent out and in the orphan cache SCRIPT_PUB_KEY_OP_TRUE = b'\x51\x75' * 15 + b'\x51' tx_withhold = CTransaction() tx_withhold.vin.append( CTxIn(outpoint=COutPoint(block1.vtx[0].sha256, 0))) tx_withhold.vout = [ CTxOut(nValue=25 * COIN - 12000, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE) ] * 2 tx_withhold.calc_sha256() # Our first orphan tx with some outputs to create further orphan txs tx_orphan_1 = CTransaction() tx_orphan_1.vin.append( CTxIn(outpoint=COutPoint(tx_withhold.sha256, 0))) tx_orphan_1.vout = [ CTxOut(nValue=8 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE) ] * 3 tx_orphan_1.calc_sha256() # A valid transaction with low fee tx_orphan_2_no_fee = CTransaction() tx_orphan_2_no_fee.vin.append( CTxIn(outpoint=COutPoint(tx_orphan_1.sha256, 0))) tx_orphan_2_no_fee.vout.append( CTxOut(nValue=8 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE)) # A valid transaction with sufficient fee tx_orphan_2_valid = CTransaction() tx_orphan_2_valid.vin.append( CTxIn(outpoint=COutPoint(tx_orphan_1.sha256, 1))) tx_orphan_2_valid.vout.append( CTxOut(nValue=8 * COIN - 12000, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE)) tx_orphan_2_valid.calc_sha256() # An invalid transaction with negative fee tx_orphan_2_invalid = CTransaction() tx_orphan_2_invalid.vin.append( CTxIn(outpoint=COutPoint(tx_orphan_1.sha256, 2))) tx_orphan_2_invalid.vout.append( CTxOut(nValue=11 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE)) tx_orphan_2_invalid.calc_sha256() self.log.info('Send the orphans ... ') # Send valid orphan txs from p2ps[0] node.p2ps[0].send_txs_and_test( [tx_orphan_1, tx_orphan_2_no_fee, tx_orphan_2_valid], node, success=False) # Send invalid tx from p2ps[1] node.p2ps[1].send_txs_and_test([tx_orphan_2_invalid], node, success=False) assert_equal(0, node.getmempoolinfo()['size']) # Mempool should be empty assert_equal(2, len(node.getpeerinfo())) # p2ps[1] is still connected self.log.info('Send the withhold tx ... ') with node.assert_debug_log(expected_msgs=["bad-txns-in-belowout"]): node.p2ps[0].send_txs_and_test([tx_withhold], node, success=True) # Transactions that should end up in the mempool expected_mempool = { t.hash for t in [ tx_withhold, # The transaction that is the root for all orphans tx_orphan_1, # The orphan transaction that splits the coins tx_orphan_2_valid, # The valid transaction (with sufficient fee) ] } # Transactions that do not end up in the mempool: # tx_orphan_2_no_fee, because it has too low fee (p2ps[0] is not disconnected for relaying that tx) # tx_orphan_2_invalid, because it has negative fee (p2ps[1] is disconnected for relaying that tx) self.wait_until(lambda: 1 == len(node.getpeerinfo()), timeout=12) # p2ps[1] is no longer connected assert_equal(expected_mempool, set(node.getrawmempool())) self.log.info('Test orphan pool overflow') orphan_tx_pool = [CTransaction() for _ in range(101)] for i in range(len(orphan_tx_pool)): orphan_tx_pool[i].vin.append(CTxIn(outpoint=COutPoint(i, 333))) orphan_tx_pool[i].vout.append( CTxOut(nValue=11 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE)) with node.assert_debug_log(['orphanage overflow, removed 1 tx']): node.p2ps[0].send_txs_and_test(orphan_tx_pool, node, success=False) self.log.info('Test orphan with rejected parents') rejected_parent = CTransaction() rejected_parent.vin.append( CTxIn(outpoint=COutPoint(tx_orphan_2_invalid.sha256, 0))) rejected_parent.vout.append( CTxOut(nValue=11 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE)) rejected_parent.rehash() with node.assert_debug_log([ 'not keeping orphan with rejected parents {}'.format( rejected_parent.hash) ]): node.p2ps[0].send_txs_and_test([rejected_parent], node, success=False) self.log.info( 'Test that a peer disconnection causes erase its transactions from the orphan pool' ) with node.assert_debug_log(['Erased 100 orphan tx from peer=25']): self.reconnect_p2p(num_connections=1) self.log.info( 'Test that a transaction in the orphan pool is included in a new tip block causes erase this transaction from the orphan pool' ) tx_withhold_until_block_A = CTransaction() tx_withhold_until_block_A.vin.append( CTxIn(outpoint=COutPoint(tx_withhold.sha256, 1))) tx_withhold_until_block_A.vout = [ CTxOut(nValue=12 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE) ] * 2 tx_withhold_until_block_A.calc_sha256() tx_orphan_include_by_block_A = CTransaction() tx_orphan_include_by_block_A.vin.append( CTxIn(outpoint=COutPoint(tx_withhold_until_block_A.sha256, 0))) tx_orphan_include_by_block_A.vout.append( CTxOut(nValue=12 * COIN - 12000, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE)) tx_orphan_include_by_block_A.calc_sha256() self.log.info('Send the orphan ... ') node.p2ps[0].send_txs_and_test([tx_orphan_include_by_block_A], node, success=False) tip = int(node.getbestblockhash(), 16) height = node.getblockcount() + 1 block_A = create_block(tip, create_coinbase(height)) block_A.vtx.extend([ tx_withhold, tx_withhold_until_block_A, tx_orphan_include_by_block_A ]) block_A.hashMerkleRoot = block_A.calc_merkle_root() block_A.solve() self.log.info('Send the block that includes the previous orphan ... ') with node.assert_debug_log( ["Erased 1 orphan tx included or conflicted by block"]): node.p2ps[0].send_blocks_and_test([block_A], node, success=True) self.log.info( 'Test that a transaction in the orphan pool conflicts with a new tip block causes erase this transaction from the orphan pool' ) tx_withhold_until_block_B = CTransaction() tx_withhold_until_block_B.vin.append( CTxIn(outpoint=COutPoint(tx_withhold_until_block_A.sha256, 1))) tx_withhold_until_block_B.vout.append( CTxOut(nValue=11 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE)) tx_withhold_until_block_B.calc_sha256() tx_orphan_include_by_block_B = CTransaction() tx_orphan_include_by_block_B.vin.append( CTxIn(outpoint=COutPoint(tx_withhold_until_block_B.sha256, 0))) tx_orphan_include_by_block_B.vout.append( CTxOut(nValue=10 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE)) tx_orphan_include_by_block_B.calc_sha256() tx_orphan_conflict_by_block_B = CTransaction() tx_orphan_conflict_by_block_B.vin.append( CTxIn(outpoint=COutPoint(tx_withhold_until_block_B.sha256, 0))) tx_orphan_conflict_by_block_B.vout.append( CTxOut(nValue=9 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE)) tx_orphan_conflict_by_block_B.calc_sha256() self.log.info('Send the orphan ... ') node.p2ps[0].send_txs_and_test([tx_orphan_conflict_by_block_B], node, success=False) tip = int(node.getbestblockhash(), 16) height = node.getblockcount() + 1 block_B = create_block(tip, create_coinbase(height)) block_B.vtx.extend( [tx_withhold_until_block_B, tx_orphan_include_by_block_B]) block_B.hashMerkleRoot = block_B.calc_merkle_root() block_B.solve() self.log.info( 'Send the block that includes a transaction which conflicts with the previous orphan ... ' ) with node.assert_debug_log( ["Erased 1 orphan tx included or conflicted by block"]): node.p2ps[0].send_blocks_and_test([block_B], node, success=True)
def run_test(self): node = self.nodes[0] # convenience reference to the node self.bootstrap_p2p() # Add one p2p connection to the node best_block = self.nodes[0].getbestblockhash() tip = int(best_block, 16) best_block_time = self.nodes[0].getblock(best_block)['time'] block_time = best_block_time + 1 self.log.info("Create a new block with an anyone-can-spend coinbase.") height = 1 block = create_block(tip, create_coinbase(height), block_time) block.solve() # Save the coinbase for later block1 = block tip = block.sha256 node.p2p.send_blocks_and_test([block], node, success=True) self.log.info("Mature the block.") self.nodes[0].generate(100) # Iterate through a list of known invalid transaction types, ensuring each is # rejected. Some are consensus invalid and some just violate policy. for BadTxTemplate in invalid_txs.iter_all_templates(): self.log.info("Testing invalid transaction: %s", BadTxTemplate.__name__) template = BadTxTemplate(spend_block=block1) tx = template.get_tx() node.p2p.send_txs_and_test( [tx], node, success=False, expect_disconnect=template.expect_disconnect, reject_reason=template.reject_reason, ) if template.expect_disconnect: self.log.info("Reconnecting to peer") self.reconnect_p2p() # Make two p2p connections to provide the node with orphans # * p2ps[0] will send valid orphan txs (one with low fee) # * p2ps[1] will send an invalid orphan tx (and is later disconnected for that) self.reconnect_p2p(num_connections=2) self.log.info('Test orphan transaction handling ... ') # Create a root transaction that we withhold until all dependent transactions # are sent out and in the orphan cache SCRIPT_PUB_KEY_OP_TRUE = b'\x51\x75' * 15 + b'\x51' tx_withhold = CTransaction() tx_withhold.vin.append(CTxIn(outpoint=COutPoint(block1.vtx[0].sha256, 0))) tx_withhold.vout.append(CTxOut(nValue=50 * COIN - 12000, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE)) tx_withhold.calc_sha256() # Our first orphan tx with some outputs to create further orphan txs tx_orphan_1 = CTransaction() tx_orphan_1.vin.append(CTxIn(outpoint=COutPoint(tx_withhold.sha256, 0))) tx_orphan_1.vout = [CTxOut(nValue=10 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE)] * 3 tx_orphan_1.calc_sha256() # A valid transaction with low fee tx_orphan_2_no_fee = CTransaction() tx_orphan_2_no_fee.vin.append(CTxIn(outpoint=COutPoint(tx_orphan_1.sha256, 0))) tx_orphan_2_no_fee.vout.append(CTxOut(nValue=10 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE)) # A valid transaction with sufficient fee tx_orphan_2_valid = CTransaction() tx_orphan_2_valid.vin.append(CTxIn(outpoint=COutPoint(tx_orphan_1.sha256, 1))) tx_orphan_2_valid.vout.append(CTxOut(nValue=10 * COIN - 12000, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE)) tx_orphan_2_valid.calc_sha256() # An invalid transaction with negative fee tx_orphan_2_invalid = CTransaction() tx_orphan_2_invalid.vin.append(CTxIn(outpoint=COutPoint(tx_orphan_1.sha256, 2))) tx_orphan_2_invalid.vout.append(CTxOut(nValue=11 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE)) self.log.info('Send the orphans ... ') # Send valid orphan txs from p2ps[0] node.p2p.send_txs_and_test([tx_orphan_1, tx_orphan_2_no_fee, tx_orphan_2_valid], node, success=False) # Send invalid tx from p2ps[1] node.p2ps[1].send_txs_and_test([tx_orphan_2_invalid], node, success=False) assert_equal(0, node.getmempoolinfo()['size']) # Mempool should be empty assert_equal(2, len(node.getpeerinfo())) # p2ps[1] is still connected self.log.info('Send the withhold tx ... ') with node.assert_debug_log(expected_msgs=["bad-txns-in-belowout"]): node.p2p.send_txs_and_test([tx_withhold], node, success=True) # Transactions that should end up in the mempool expected_mempool = { t.hash for t in [ tx_withhold, # The transaction that is the root for all orphans tx_orphan_1, # The orphan transaction that splits the coins tx_orphan_2_valid, # The valid transaction (with sufficient fee) ] } # Transactions that do not end up in the mempool # tx_orphan_no_fee, because it has too low fee (p2ps[0] is not disconnected for relaying that tx) # tx_orphan_invaid, because it has negative fee (p2ps[1] is disconnected for relaying that tx) wait_until(lambda: 1 == len(node.getpeerinfo()), timeout=12) # p2ps[1] is no longer connected assert_equal(expected_mempool, set(node.getrawmempool()))
def run_test(self): node = self.nodes[0] # convenience reference to the node self.bootstrap_p2p() # Add one p2p connection to the node best_block = self.nodes[0].getbestblockhash() tip = int(best_block, 16) best_block_time = self.nodes[0].getblock(best_block)['time'] block_time = best_block_time + 1 self.log.info("Create a new block with an anyone-can-spend coinbase.") height = 1 blocks = [] for _ in invalid_txs.iter_all_templates(): block = create_block(tip, create_coinbase(height), block_time) block.nHeight = height prepare_block(block) block_time = block.nTime + 1 height += 1 # Save the coinbase for later blocks.append(block) tip = block.sha256 node.p2p.send_blocks_and_test([block], node, success=True) self.log.info("Mature the blocks.") self.nodes[0].generatetoaddress( 100, self.nodes[0].get_deterministic_priv_key().address) # Iterate through a list of known invalid transaction types, ensuring each is # rejected. Some are consensus invalid and some just violate policy. setup_txs = [] for block, BadTxTemplate in zip(blocks, invalid_txs.iter_all_templates()): self.log.info("Testing invalid transaction: %s", BadTxTemplate.__name__) template = BadTxTemplate(spend_block=block) setup_tx = template.get_setup_tx() if setup_tx is not None: node.p2p.send_txs_and_test([setup_tx], node) setup_txs.append(setup_tx) tx = template.get_tx(setup_tx) else: tx = template.get_tx() node.p2p.send_txs_and_test( [tx], node, success=False, expect_disconnect=template.expect_disconnect, reject_reason=template.reject_reason, ) if template.expect_disconnect: self.log.info("Reconnecting to peer") self.reconnect_p2p() # Make two p2p connections to provide the node with orphans # * p2ps[0] will send valid orphan txs (one with low fee) # * p2ps[1] will send an invalid orphan tx (and is later disconnected for that) self.reconnect_p2p(num_connections=2) self.log.info('Test orphan transaction handling ... ') # Create a root transaction that we withold until all dependend transactions # are sent out and in the orphan cache SCRIPT_PUB_KEY_OP_TRUE = CScript([OP_TRUE]) tx_withhold = CTransaction() tx_withhold.vin.append( CTxIn(outpoint=COutPoint(blocks[0].vtx[0].txid, 1))) tx_withhold.vout.append( CTxOut(nValue=int(SUBSIDY * COIN) - 12000, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE)) pad_tx(tx_withhold) tx_withhold.calc_txid() # Our first orphan tx with some outputs to create further orphan txs tx_orphan_1 = CTransaction() tx_orphan_1.vin.append(CTxIn(outpoint=COutPoint(tx_withhold.txid, 0))) tx_orphan_1.vout = [ CTxOut(nValue=int(0.1 * COIN), scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE) ] * 3 pad_tx(tx_orphan_1) tx_orphan_1.calc_txid() # A valid transaction with low fee tx_orphan_2_no_fee = CTransaction() tx_orphan_2_no_fee.vin.append( CTxIn(outpoint=COutPoint(tx_orphan_1.txid, 0))) tx_orphan_2_no_fee.vout.append( CTxOut(nValue=int(0.1 * COIN), scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE)) pad_tx(tx_orphan_2_no_fee) # A valid transaction with sufficient fee tx_orphan_2_valid = CTransaction() tx_orphan_2_valid.vin.append( CTxIn(outpoint=COutPoint(tx_orphan_1.txid, 1))) tx_orphan_2_valid.vout.append( CTxOut(nValue=int(0.1 * COIN) - 12000, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE)) tx_orphan_2_valid.calc_txid() pad_tx(tx_orphan_2_valid) # An invalid transaction with negative fee tx_orphan_2_invalid = CTransaction() tx_orphan_2_invalid.vin.append( CTxIn(outpoint=COutPoint(tx_orphan_1.txid, 2))) tx_orphan_2_invalid.vout.append( CTxOut(nValue=int(1.1 * COIN), scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE)) pad_tx(tx_orphan_2_invalid) tx_orphan_2_invalid.calc_txid() self.log.info('Send the orphans ... ') # Send valid orphan txs from p2ps[0] node.p2p.send_txs_and_test( [tx_orphan_1, tx_orphan_2_no_fee, tx_orphan_2_valid], node, success=False) # Send invalid tx from p2ps[1] node.p2ps[1].send_txs_and_test([tx_orphan_2_invalid], node, success=False) # Mempool should only have setup txs assert_equal(len(setup_txs), node.getmempoolinfo()['size']) # p2ps[1] is still connected assert_equal(2, len(node.getpeerinfo())) self.log.info('Send the withhold tx ... ') with node.assert_debug_log(expected_msgs=["bad-txns-in-belowout"]): node.p2p.send_txs_and_test([tx_withhold], node, success=True) # Transactions that should end up in the mempool expected_mempool = { t.txid_hex for t in [ tx_withhold, # The transaction that is the root for all orphans tx_orphan_1, # The orphan transaction that splits the coins # The valid transaction (with sufficient fee) tx_orphan_2_valid, ] + setup_txs # The setup transactions we added in the beginning } # Transactions that do not end up in the mempool # tx_orphan_no_fee, because it has too low fee (p2ps[0] is not disconnected for relaying that tx) # tx_orphan_invaid, because it has negative fee (p2ps[1] is # disconnected for relaying that tx) # p2ps[1] is no longer connected wait_until(lambda: 1 == len(node.getpeerinfo()), timeout=12) assert_equal(expected_mempool, set(node.getrawmempool())) self.log.info('Test orphan pool overflow') orphan_tx_pool = [CTransaction() for _ in range(101)] for i in range(len(orphan_tx_pool)): orphan_tx_pool[i].vin.append(CTxIn(outpoint=COutPoint(i, 333))) orphan_tx_pool[i].vout.append( CTxOut(nValue=int(1.1 * COIN), scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE)) pad_tx(orphan_tx_pool[i]) with node.assert_debug_log(['mapOrphan overflow, removed 1 tx']): node.p2p.send_txs_and_test(orphan_tx_pool, node, success=False) rejected_parent = CTransaction() rejected_parent.vin.append( CTxIn(outpoint=COutPoint(tx_orphan_2_invalid.txid, 0))) rejected_parent.vout.append( CTxOut(nValue=int(1.1 * COIN), scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE)) pad_tx(rejected_parent) rejected_parent.rehash() with node.assert_debug_log([ 'not keeping orphan with rejected parents {}'.format( rejected_parent.txid_hex) ]): node.p2p.send_txs_and_test([rejected_parent], node, success=False)