Inside Bitcoin
Opening the selected file…
Inside Bitcoin
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
1#!/usr/bin/env python32# Copyright (c) 2015-present The Bitcoin Core developers3# Distributed under the MIT software license, see the accompanying4# file COPYING or http://www.opensource.org/licenses/mit-license.php.5"""Test node responses to invalid transactions.7In this test we connect to one node over p2p, and test tx requests."""8from test_framework.blocktools import create_block9from test_framework.messages import (10COIN,11COutPoint,12CTransaction,13CTxIn,14CTxOut,15)16from test_framework.p2p import P2PDataStore17from test_framework.test_framework import BitcoinTestFramework18from test_framework.util import (19assert_equal,20)21from data import invalid_txs24class InvalidTxRequestTest(BitcoinTestFramework):25def set_test_params(self):26self.num_nodes = 127self.extra_args = [[28"-acceptnonstdtxn=1",29]]30self.setup_clean_chain = True32def bootstrap_p2p(self, *, num_connections=1):33"""Add a P2P connection to the node.35Helper to connect and wait for version handshake."""36for _ in range(num_connections):37self.nodes[0].add_p2p_connection(P2PDataStore())39def reconnect_p2p(self, **kwargs):40"""Tear down and bootstrap the P2P connection to the node.42The node gets disconnected several times in this test. This helper43method reconnects the p2p and restarts the network thread."""44self.nodes[0].disconnect_p2ps()45self.bootstrap_p2p(**kwargs)47def run_test(self):48node = self.nodes[0] # convenience reference to the node50self.bootstrap_p2p() # Add one p2p connection to the node52best_block = self.nodes[0].getbestblockhash()53tip = int(best_block, 16)54best_block_time = self.nodes[0].getblock(best_block)['time']55block_time = best_block_time + 157self.log.info("Create a new block with an anyone-can-spend coinbase.")58height = 159block = create_block(tip, height=height, ntime=block_time)60block.solve()61# Save the coinbase for later62block1 = block63node.p2ps[0].send_blocks_and_test([block], node, success=True)65self.log.info("Mature the block.")66self.generatetoaddress(self.nodes[0], 100, self.nodes[0].get_deterministic_priv_key().address)68# Iterate through a list of known invalid transaction types, ensuring each is69# rejected. Some are consensus invalid and some just violate policy.70for BadTxTemplate in invalid_txs.iter_all_templates():71self.log.info("Testing invalid transaction: %s", BadTxTemplate.__name__)72template = BadTxTemplate(spend_block=block1)73tx = template.get_tx()74node.p2ps[0].send_txs_and_test(75[tx], node, success=False,76reject_reason=template.reject_reason,77)79# Make two p2p connections to provide the node with orphans80# * p2ps[0] will send valid orphan txs (one with low fee)81# * p2ps[1] will send an invalid orphan tx (and is later disconnected for that)82self.reconnect_p2p(num_connections=2)84self.log.info('Test orphan transaction handling ... ')85# Create a root transaction that we withhold until all dependent transactions86# are sent out and in the orphan cache87SCRIPT_PUB_KEY_OP_TRUE = b'\x51\x75' * 15 + b'\x51'88tx_withhold = CTransaction()89tx_withhold.vin.append(CTxIn(outpoint=COutPoint(block1.vtx[0].txid_int, 0)))90tx_withhold.vout = [CTxOut(nValue=25 * COIN - 12000, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE)] * 292# Our first orphan tx with some outputs to create further orphan txs93tx_orphan_1 = CTransaction()94tx_orphan_1.vin.append(CTxIn(outpoint=COutPoint(tx_withhold.txid_int, 0)))95tx_orphan_1.vout = [CTxOut(nValue=8 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE)] * 397# A valid transaction with low fee98tx_orphan_2_no_fee = CTransaction()99tx_orphan_2_no_fee.vin.append(CTxIn(outpoint=COutPoint(tx_orphan_1.txid_int, 0)))100tx_orphan_2_no_fee.vout.append(CTxOut(nValue=8 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE))102# A valid transaction with sufficient fee103tx_orphan_2_valid = CTransaction()104tx_orphan_2_valid.vin.append(CTxIn(outpoint=COutPoint(tx_orphan_1.txid_int, 1)))105tx_orphan_2_valid.vout.append(CTxOut(nValue=8 * COIN - 12000, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE))107# An invalid transaction with negative fee108tx_orphan_2_invalid = CTransaction()109tx_orphan_2_invalid.vin.append(CTxIn(outpoint=COutPoint(tx_orphan_1.txid_int, 2)))110tx_orphan_2_invalid.vout.append(CTxOut(nValue=11 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE))112self.log.info('Send the orphans ... ')113# Send valid orphan txs from p2ps[0]114node.p2ps[0].send_txs_and_test([tx_orphan_1, tx_orphan_2_no_fee, tx_orphan_2_valid], node, success=False)115# Send invalid tx from p2ps[1]116node.p2ps[1].send_txs_and_test([tx_orphan_2_invalid], node, success=False)118assert_equal(0, node.getmempoolinfo()['size']) # Mempool should be empty119assert_equal(2, len(node.getpeerinfo())) # p2ps[1] is still connected121self.log.info('Send the withhold tx ... ')122with node.assert_debug_log(expected_msgs=["bad-txns-in-belowout"]):123node.p2ps[0].send_txs_and_test([tx_withhold], node, success=True)125# Transactions that should end up in the mempool126expected_mempool = {127t.txid_hex128for t in [129tx_withhold, # The transaction that is the root for all orphans130tx_orphan_1, # The orphan transaction that splits the coins131tx_orphan_2_valid, # The valid transaction (with sufficient fee)132]133}134# Transactions that do not end up in the mempool:135# tx_orphan_2_no_fee, because it has too low fee (p2ps[0] is not disconnected for relaying that tx)136# tx_orphan_2_invalid, because it has negative fee (p2ps[1] is disconnected for relaying that tx)138assert_equal(expected_mempool, set(node.getrawmempool()))140self.log.info('Test orphanage can store more than 100 transactions')141orphan_tx_pool = [CTransaction() for _ in range(101)]142for i in range(len(orphan_tx_pool)):143orphan_tx_pool[i].vin.append(CTxIn(outpoint=COutPoint(i, 333)))144orphan_tx_pool[i].vout.append(CTxOut(nValue=11 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE))146node.p2ps[0].send_txs_and_test(orphan_tx_pool, node, success=False)147self.wait_until(lambda: len(node.getorphantxs()) >= 101)149self.log.info('Test orphan with rejected parents')150rejected_parent = CTransaction()151rejected_parent.vin.append(CTxIn(outpoint=COutPoint(tx_orphan_2_invalid.txid_int, 0)))152rejected_parent.vout.append(CTxOut(nValue=11 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE))153with node.assert_debug_log(['not keeping orphan with rejected parents {}'.format(rejected_parent.txid_hex)]):154node.p2ps[0].send_txs_and_test([rejected_parent], node, success=False)156self.log.info('Test that a peer disconnection causes erase its transactions from the orphan pool')157self.reconnect_p2p(num_connections=1)158self.wait_until(lambda: len(node.getorphantxs()) == 0)160self.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')161tx_withhold_until_block_A = CTransaction()162tx_withhold_until_block_A.vin.append(CTxIn(outpoint=COutPoint(tx_withhold.txid_int, 1)))163tx_withhold_until_block_A.vout = [CTxOut(nValue=12 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE)] * 2165tx_orphan_include_by_block_A = CTransaction()166tx_orphan_include_by_block_A.vin.append(CTxIn(outpoint=COutPoint(tx_withhold_until_block_A.txid_int, 0)))167tx_orphan_include_by_block_A.vout.append(CTxOut(nValue=12 * COIN - 12000, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE))169self.log.info('Send the orphan ... ')170node.p2ps[0].send_txs_and_test([tx_orphan_include_by_block_A], node, success=False)172tip = int(node.getbestblockhash(), 16)173height = node.getblockcount() + 1174block_A = create_block(tip, height=height)175block_A.vtx.extend([tx_withhold, tx_withhold_until_block_A, tx_orphan_include_by_block_A])176block_A.hashMerkleRoot = block_A.calc_merkle_root()177block_A.solve()179self.log.info('Send the block that includes the previous orphan ... ')180with node.assert_debug_log(["Erased 1 orphan transaction(s) included or conflicted by block"]):181node.p2ps[0].send_blocks_and_test([block_A], node, success=True)182node.syncwithvalidationinterfacequeue()184self.log.info('Test that a transaction in the orphan pool conflicts with a new tip block causes erase this transaction from the orphan pool')185tx_withhold_until_block_B = CTransaction()186tx_withhold_until_block_B.vin.append(CTxIn(outpoint=COutPoint(tx_withhold_until_block_A.txid_int, 1)))187tx_withhold_until_block_B.vout.append(CTxOut(nValue=11 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE))189tx_orphan_include_by_block_B = CTransaction()190tx_orphan_include_by_block_B.vin.append(CTxIn(outpoint=COutPoint(tx_withhold_until_block_B.txid_int, 0)))191tx_orphan_include_by_block_B.vout.append(CTxOut(nValue=10 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE))193tx_orphan_conflict_by_block_B = CTransaction()194tx_orphan_conflict_by_block_B.vin.append(CTxIn(outpoint=COutPoint(tx_withhold_until_block_B.txid_int, 0)))195tx_orphan_conflict_by_block_B.vout.append(CTxOut(nValue=9 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE))196self.log.info('Send the orphan ... ')197node.p2ps[0].send_txs_and_test([tx_orphan_conflict_by_block_B], node, success=False)199tip = int(node.getbestblockhash(), 16)200height = node.getblockcount() + 1201block_B = create_block(tip, height=height)202block_B.vtx.extend([tx_withhold_until_block_B, tx_orphan_include_by_block_B])203block_B.hashMerkleRoot = block_B.calc_merkle_root()204block_B.solve()206self.log.info('Send the block that includes a transaction which conflicts with the previous orphan ... ')207with node.assert_debug_log(["Erased 1 orphan transaction(s) included or conflicted by block"]):208node.p2ps[0].send_blocks_and_test([block_B], node, success=True)209node.syncwithvalidationinterfacequeue()212if __name__ == '__main__':213InvalidTxRequestTest(__file__).main()
Independent explanations by MyBitcoinExplorer. Not affiliated with or endorsed by Bitcoin Core. Guides are educational and do not replace the source or its tests.
Bitcoin Core source © its contributors, distributed under the MIT license. Original notices and bundled third-party licenses are retained in the source tree.