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) 2021-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"""RPCs that handle raw transaction packages."""7from decimal import Decimal8import random10from test_framework.blocktools import COINBASE_MATURITY11from test_framework.mempool_util import (12fill_mempool,13)14from test_framework.messages import (15MAX_BIP125_RBF_SEQUENCE,16tx_from_hex,17)18from test_framework.p2p import P2PTxInvStore19from test_framework.test_framework import BitcoinTestFramework20from test_framework.util import (21assert_equal,22assert_fee_amount,23assert_raises_rpc_error,24)25from test_framework.wallet import (26COIN,27DEFAULT_FEE,28MiniWallet,29)32MAX_PACKAGE_COUNT = 2535class RPCPackagesTest(BitcoinTestFramework):36def set_test_params(self):37self.num_nodes = 138self.setup_clean_chain = True39# whitelist peers to speed up tx relay / mempool sync40self.noban_tx_relay = True42def assert_testres_equal(self, package_hex, testres_expected):43"""Shuffle package_hex and assert that the testmempoolaccept result matches testres_expected. This should only44be used to test packages where the order does not matter. The ordering of transactions in package_hex and45testres_expected must match.46"""47shuffled_indices = list(range(len(package_hex)))48random.shuffle(shuffled_indices)49shuffled_package = [package_hex[i] for i in shuffled_indices]50shuffled_testres = [testres_expected[i] for i in shuffled_indices]51assert_equal(shuffled_testres, self.nodes[0].testmempoolaccept(shuffled_package))53def run_test(self):54node = self.nodes[0]56# get an UTXO that requires signature to be spent57deterministic_address = node.get_deterministic_priv_key().address58blockhash = self.generatetoaddress(node, 1, deterministic_address)[0]59coinbase = node.getblock(blockhash=blockhash, verbosity=2)["tx"][0]60coin = {61"txid": coinbase["txid"],62"amount": coinbase["vout"][0]["value"],63"scriptPubKey": coinbase["vout"][0]["scriptPubKey"],64"vout": 0,65"height": 066}68self.wallet = MiniWallet(self.nodes[0])69self.generate(self.wallet, COINBASE_MATURITY + 100) # blocks generated for inputs71self.log.info("Create some transactions")72# Create some transactions that can be reused throughout the test. Never submit these to mempool.73self.independent_txns_hex = []74self.independent_txns_testres = []75for _ in range(3):76tx_hex = self.wallet.create_self_transfer(fee_rate=Decimal("0.0001"))["hex"]77testres = self.nodes[0].testmempoolaccept([tx_hex])78assert testres[0]["allowed"]79self.independent_txns_hex.append(tx_hex)80# testmempoolaccept returns a list of length one, avoid creating a 2D list81self.independent_txns_testres.append(testres[0])82self.independent_txns_testres_blank = [{83"txid": res["txid"], "wtxid": res["wtxid"]} for res in self.independent_txns_testres]85self.test_submitpackage_with_ancestors()86self.test_independent(coin)87self.test_chain()88self.test_multiple_children()89self.test_multiple_parents()90self.test_conflicting()91self.test_rbf()92self.test_submitpackage()93self.test_maxfeerate_submitpackage()94self.test_maxburn_submitpackage()96def test_independent(self, coin):97self.log.info("Test multiple independent transactions in a package")98node = self.nodes[0]99# For independent transactions, order doesn't matter.100self.assert_testres_equal(self.independent_txns_hex, self.independent_txns_testres)102self.log.info("Test an otherwise valid package with an extra garbage tx appended")103address = node.get_deterministic_priv_key().address104garbage_tx = node.createrawtransaction([{"txid": "00" * 32, "vout": 5}], {address: 1})105tx = tx_from_hex(garbage_tx)106# Only the txid and wtxids are returned because validation is incomplete for the independent txns.107# Package validation is atomic: if the node cannot find a UTXO for any single tx in the package,108# it terminates immediately to avoid unnecessary, expensive signature verification.109package_bad = self.independent_txns_hex + [garbage_tx]110testres_bad = self.independent_txns_testres_blank + [{"txid": tx.txid_hex, "wtxid": tx.wtxid_hex, "allowed": False, "reject-reason": "missing-inputs"}]111self.assert_testres_equal(package_bad, testres_bad)113self.log.info("Check testmempoolaccept tells us when some transactions completed validation successfully")114tx_bad_sig_hex = node.createrawtransaction([{"txid": coin["txid"], "vout": coin["vout"]}],115{address : coin["amount"] - Decimal("0.0001")})116tx_bad_sig = tx_from_hex(tx_bad_sig_hex)117testres_bad_sig = node.testmempoolaccept(self.independent_txns_hex + [tx_bad_sig_hex])118# By the time the signature for the last transaction is checked, all the other transactions119# have been fully validated, which is why the node returns full validation results for all120# transactions here but empty results in other cases.121tx_bad_sig_txid = tx_bad_sig.txid_hex122tx_bad_sig_wtxid = tx_bad_sig.wtxid_hex123assert_equal(testres_bad_sig, self.independent_txns_testres + [{124"txid": tx_bad_sig_txid,125"wtxid": tx_bad_sig_wtxid, "allowed": False,126"reject-reason": "mempool-script-verify-flag-failed (Operation not valid with the current stack size)",127"reject-details": "mempool-script-verify-flag-failed (Operation not valid with the current stack size), " +128f"input 0 of {tx_bad_sig_txid} (wtxid {tx_bad_sig_wtxid}), spending {coin['txid']}:{coin['vout']}"129}])131self.log.info("Check testmempoolaccept reports txns in packages that exceed max feerate")132tx_high_fee = self.wallet.create_self_transfer(fee=Decimal("0.999"))133testres_high_fee = node.testmempoolaccept([tx_high_fee["hex"]])134assert_equal(testres_high_fee, [135{"txid": tx_high_fee["txid"], "wtxid": tx_high_fee["wtxid"], "allowed": False, "reject-reason": "max-fee-exceeded"}136])137package_high_fee = [tx_high_fee["hex"]] + self.independent_txns_hex138testres_package_high_fee = node.testmempoolaccept(package_high_fee)139assert_equal(testres_package_high_fee, testres_high_fee + self.independent_txns_testres_blank)141def test_chain(self):142node = self.nodes[0]144chain = self.wallet.create_self_transfer_chain(chain_length=25)145chain_hex = [t["hex"] for t in chain]146chain_txns = [t["tx"] for t in chain]148self.log.info("Check that testmempoolaccept requires packages to be sorted by dependency")149assert_equal(node.testmempoolaccept(rawtxs=chain_hex[::-1]),150[{"txid": tx.txid_hex, "wtxid": tx.wtxid_hex, "package-error": "package-not-sorted"} for tx in chain_txns[::-1]])152self.log.info("Testmempoolaccept a chain of 25 transactions")153testres_multiple = node.testmempoolaccept(rawtxs=chain_hex)155testres_single = []156# Test accept and then submit each one individually, which should be identical to package test accept157for rawtx in chain_hex:158testres = node.testmempoolaccept([rawtx])159testres_single.append(testres[0])160# Submit the transaction now so its child should have no problem validating161node.sendrawtransaction(rawtx)162assert_equal(testres_single, testres_multiple)164# Clean up by clearing the mempool165self.generate(node, 1)167def test_multiple_children(self):168node = self.nodes[0]169self.log.info("Testmempoolaccept a package in which a transaction has two children within the package")171parent_tx = self.wallet.create_self_transfer_multi(num_outputs=2)172assert node.testmempoolaccept([parent_tx["hex"]])[0]["allowed"]174# Child A175child_a_tx = self.wallet.create_self_transfer(utxo_to_spend=parent_tx["new_utxos"][0])176assert not node.testmempoolaccept([child_a_tx["hex"]])[0]["allowed"]178# Child B179child_b_tx = self.wallet.create_self_transfer(utxo_to_spend=parent_tx["new_utxos"][1])180assert not node.testmempoolaccept([child_b_tx["hex"]])[0]["allowed"]182self.log.info("Testmempoolaccept with entire package, should work with children in either order")183testres_multiple_ab = node.testmempoolaccept(rawtxs=[parent_tx["hex"], child_a_tx["hex"], child_b_tx["hex"]])184testres_multiple_ba = node.testmempoolaccept(rawtxs=[parent_tx["hex"], child_b_tx["hex"], child_a_tx["hex"]])185assert all([testres["allowed"] for testres in testres_multiple_ab + testres_multiple_ba])187testres_single = []188# Test accept and then submit each one individually, which should be identical to package testaccept189for rawtx in [parent_tx["hex"], child_a_tx["hex"], child_b_tx["hex"]]:190testres = node.testmempoolaccept([rawtx])191testres_single.append(testres[0])192# Submit the transaction now so its child should have no problem validating193node.sendrawtransaction(rawtx)194assert_equal(testres_single, testres_multiple_ab)196def test_multiple_parents(self):197node = self.nodes[0]198self.log.info("Testmempoolaccept a package in which a transaction has multiple parents within the package")200for num_parents in [2, 10, 24]:201# Test a package with num_parents parents and 1 child transaction.202parent_coins = []203package_hex = []205for _ in range(num_parents):206# Package accept should work with the parents in any order (as long as parents come before child)207parent_tx = self.wallet.create_self_transfer()208parent_coins.append(parent_tx["new_utxo"])209package_hex.append(parent_tx["hex"])211child_tx = self.wallet.create_self_transfer_multi(utxos_to_spend=parent_coins, fee_per_output=2000)212for _ in range(10):213random.shuffle(package_hex)214testres_multiple = node.testmempoolaccept(rawtxs=package_hex + [child_tx['hex']])215assert all([testres["allowed"] for testres in testres_multiple])217testres_single = []218# Test accept and then submit each one individually, which should be identical to package testaccept219for rawtx in package_hex + [child_tx["hex"]]:220testres_single.append(node.testmempoolaccept([rawtx])[0])221# Submit the transaction now so its child should have no problem validating222node.sendrawtransaction(rawtx)223assert_equal(testres_single, testres_multiple)225def test_conflicting(self):226node = self.nodes[0]227coin = self.wallet.get_utxo()229# tx1 and tx2 share the same inputs230tx1 = self.wallet.create_self_transfer(utxo_to_spend=coin, fee_rate=DEFAULT_FEE)231tx2 = self.wallet.create_self_transfer(utxo_to_spend=coin, fee_rate=2*DEFAULT_FEE)233# Ensure tx1 and tx2 are valid by themselves234assert node.testmempoolaccept([tx1["hex"]])[0]["allowed"]235assert node.testmempoolaccept([tx2["hex"]])[0]["allowed"]237self.log.info("Test duplicate transactions in the same package")238testres = node.testmempoolaccept([tx1["hex"], tx1["hex"]])239assert_equal(testres, [240{"txid": tx1["txid"], "wtxid": tx1["wtxid"], "package-error": "package-contains-duplicates"},241{"txid": tx1["txid"], "wtxid": tx1["wtxid"], "package-error": "package-contains-duplicates"}242])244self.log.info("Test conflicting transactions in the same package")245testres = node.testmempoolaccept([tx1["hex"], tx2["hex"]])246assert_equal(testres, [247{"txid": tx1["txid"], "wtxid": tx1["wtxid"], "package-error": "conflict-in-package"},248{"txid": tx2["txid"], "wtxid": tx2["wtxid"], "package-error": "conflict-in-package"}249])
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.