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.
251self.restart_node(0, extra_args=["-limitclustercount=1", "-acceptnonstdtxn=1"])252assert_raises_rpc_error(-26, "too-large-cluster", node.sendrawtransaction, tx_v3_child_large2["hex"])253self.check_mempool([tx_v3_parent_large2["txid"]])255@cleanup()256def test_truc_ancestors_package(self):257self.log.info("Test that TRUC ancestor limits are checked within the package")258node = self.nodes[0]259tx_v3_parent_normal = self.wallet.create_self_transfer(260fee_rate=0,261target_vsize=1001,262version=3263)264tx_v3_parent_2_normal = self.wallet.create_self_transfer(265fee_rate=0,266target_vsize=1001,267version=3268)269tx_v3_child_multiparent = self.wallet.create_self_transfer_multi(270utxos_to_spend=[tx_v3_parent_normal["new_utxo"], tx_v3_parent_2_normal["new_utxo"]],271fee_per_output=10000,272version=3273)274tx_v3_child_heavy = self.wallet.create_self_transfer_multi(275utxos_to_spend=[tx_v3_parent_normal["new_utxo"]],276target_vsize=TRUC_CHILD_MAX_VSIZE + 1,277fee_per_output=10000,278version=3279)281self.check_mempool([])282result = node.submitpackage([tx_v3_parent_normal["hex"], tx_v3_parent_2_normal["hex"], tx_v3_child_multiparent["hex"]])283assert_equal(result['package_msg'], f"TRUC-violation, tx {tx_v3_child_multiparent['txid']} (wtxid={tx_v3_child_multiparent['wtxid']}) would have too many ancestors")284self.check_mempool([])286self.check_mempool([])287result = node.submitpackage([tx_v3_parent_normal["hex"], tx_v3_child_heavy["hex"]])288# tx_v3_child_heavy is heavy based on vsize, not sigops.289assert_equal(result['package_msg'], f"TRUC-violation, version=3 child tx {tx_v3_child_heavy['txid']} (wtxid={tx_v3_child_heavy['wtxid']}) is too big: {tx_v3_child_heavy['tx'].get_vsize()} > 1000 virtual bytes")290self.check_mempool([])292tx_v3_parent = self.wallet.create_self_transfer(version=3)293tx_v3_child = self.wallet.create_self_transfer(utxo_to_spend=tx_v3_parent["new_utxo"], version=3)294tx_v3_grandchild = self.wallet.create_self_transfer(utxo_to_spend=tx_v3_child["new_utxo"], version=3)295result = node.testmempoolaccept([tx_v3_parent["hex"], tx_v3_child["hex"], tx_v3_grandchild["hex"]])296for txresult in result:297assert_equal(txresult["package-error"], f"TRUC-violation, tx {tx_v3_grandchild['txid']} (wtxid={tx_v3_grandchild['wtxid']}) would have too many ancestors")299@cleanup(extra_args=None)300def test_truc_ancestors_package_and_mempool(self):301"""302A TRUC transaction in a package cannot have 2 TRUC parents.303Test that if we have a transaction graph A -> B -> C, where A, B, C are304all TRUC transactions, that we cannot use submitpackage to get the305transactions all into the mempool.307Verify, in particular, that if A is already in the mempool, then308submitpackage(B, C) will fail.309"""310node = self.nodes[0]311self.log.info("Test that TRUC ancestor limits include transactions within the package and all in-mempool ancestors")312# This is our transaction "A":313tx_in_mempool = self.wallet.send_self_transfer(from_node=node, version=3)315# Verify that A is in the mempool316self.check_mempool([tx_in_mempool["txid"]])318# tx_0fee_parent is our transaction "B"; just create it.319tx_0fee_parent = self.wallet.create_self_transfer(utxo_to_spend=tx_in_mempool["new_utxo"], fee=0, fee_rate=0, version=3)321# tx_child_violator is our transaction "C"; create it:322tx_child_violator = self.wallet.create_self_transfer_multi(utxos_to_spend=[tx_0fee_parent["new_utxo"]], version=3)324# submitpackage(B, C) should fail325result = node.submitpackage([tx_0fee_parent["hex"], tx_child_violator["hex"]])326assert_equal(result['package_msg'], f"TRUC-violation, tx {tx_child_violator['txid']} (wtxid={tx_child_violator['wtxid']}) would have too many ancestors")327self.check_mempool([tx_in_mempool["txid"]])329@cleanup(extra_args=None)330def test_sibling_eviction_package(self):331"""332When a transaction has a mempool sibling, it may be eligible for sibling eviction.333However, this option is only available in single transaction acceptance. It doesn't work in334a multi-testmempoolaccept (where RBF is disabled) or when doing package CPFP.335"""336self.log.info("Test TRUC sibling eviction in submitpackage and multi-testmempoolaccept")337node = self.nodes[0]338# Add a parent + child to mempool339tx_mempool_parent = self.wallet.send_self_transfer_multi(340from_node=node,341utxos_to_spend=[self.wallet.get_utxo()],342num_outputs=2,343version=3344)345tx_mempool_sibling = self.wallet.send_self_transfer(346from_node=node,347utxo_to_spend=tx_mempool_parent["new_utxos"][0],348version=3349)350self.check_mempool([tx_mempool_parent["txid"], tx_mempool_sibling["txid"]])352tx_sibling_1 = self.wallet.create_self_transfer(353utxo_to_spend=tx_mempool_parent["new_utxos"][1],354version=3,355fee_rate=DEFAULT_FEE*100,356)357tx_has_mempool_uncle = self.wallet.create_self_transfer(utxo_to_spend=tx_sibling_1["new_utxo"], version=3)359tx_sibling_2 = self.wallet.create_self_transfer(360utxo_to_spend=tx_mempool_parent["new_utxos"][0],361version=3,362fee_rate=DEFAULT_FEE*200,363)365tx_sibling_3 = self.wallet.create_self_transfer(366utxo_to_spend=tx_mempool_parent["new_utxos"][1],367version=3,368fee_rate=0,369)370tx_bumps_parent_with_sibling = self.wallet.create_self_transfer(371utxo_to_spend=tx_sibling_3["new_utxo"],372version=3,373fee_rate=DEFAULT_FEE*300,374)376# Fails with another non-related transaction via testmempoolaccept377tx_unrelated = self.wallet.create_self_transfer(version=3)378result_test_unrelated = node.testmempoolaccept([tx_sibling_1["hex"], tx_unrelated["hex"]])379assert_equal(result_test_unrelated[0]["reject-reason"], "TRUC-violation")381# Fails in a package via testmempoolaccept382result_test_1p1c = node.testmempoolaccept([tx_sibling_1["hex"], tx_has_mempool_uncle["hex"]])383assert_equal(result_test_1p1c[0]["reject-reason"], "TRUC-violation")385# Allowed when tx is submitted in a package and evaluated individually.386# Note that the child failed since it would be the 3rd generation.387result_package_indiv = node.submitpackage([tx_sibling_1["hex"], tx_has_mempool_uncle["hex"]])388self.check_mempool([tx_mempool_parent["txid"], tx_sibling_1["txid"]])389expected_error_gen3 = f"TRUC-violation, tx {tx_has_mempool_uncle['txid']} (wtxid={tx_has_mempool_uncle['wtxid']}) would have too many ancestors"391assert_equal(result_package_indiv["tx-results"][tx_has_mempool_uncle['wtxid']]['error'], expected_error_gen3)393# Allowed when tx is submitted in a package with in-mempool parent (which is deduplicated).394node.submitpackage([tx_mempool_parent["hex"], tx_sibling_2["hex"]])395self.check_mempool([tx_mempool_parent["txid"], tx_sibling_2["txid"]])397# Child cannot pay for sibling eviction for parent, as it violates TRUC topology limits398result_package_cpfp = node.submitpackage([tx_sibling_3["hex"], tx_bumps_parent_with_sibling["hex"]])399self.check_mempool([tx_mempool_parent["txid"], tx_sibling_2["txid"]])400expected_error_cpfp = f"TRUC-violation, tx {tx_mempool_parent['txid']} (wtxid={tx_mempool_parent['wtxid']}) would exceed descendant count limit"402assert_equal(result_package_cpfp["tx-results"][tx_sibling_3['wtxid']]['error'], expected_error_cpfp)405@cleanup()406def test_truc_package_inheritance(self):407self.log.info("Test that TRUC inheritance is checked within package")408node = self.nodes[0]409tx_v3_parent = self.wallet.create_self_transfer(410fee_rate=0,411target_vsize=1001,412version=3413)414tx_v2_child = self.wallet.create_self_transfer_multi(415utxos_to_spend=[tx_v3_parent["new_utxo"]],416fee_per_output=10000,417version=2418)419self.check_mempool([])420result = node.submitpackage([tx_v3_parent["hex"], tx_v2_child["hex"]])421assert_equal(result['package_msg'], f"TRUC-violation, non-version=3 tx {tx_v2_child['txid']} (wtxid={tx_v2_child['wtxid']}) cannot spend from version=3 tx {tx_v3_parent['txid']} (wtxid={tx_v3_parent['wtxid']})")422self.check_mempool([])424@cleanup(extra_args=None)425def test_truc_in_testmempoolaccept(self):426node = self.nodes[0]428self.log.info("Test that TRUC inheritance is accurately assessed in testmempoolaccept")429tx_v2 = self.wallet.create_self_transfer(version=2)430tx_v2_from_v2 = self.wallet.create_self_transfer(utxo_to_spend=tx_v2["new_utxo"], version=2)431tx_v3_from_v2 = self.wallet.create_self_transfer(utxo_to_spend=tx_v2["new_utxo"], version=3)432tx_v3 = self.wallet.create_self_transfer(version=3)433tx_v2_from_v3 = self.wallet.create_self_transfer(utxo_to_spend=tx_v3["new_utxo"], version=2)434tx_v3_from_v3 = self.wallet.create_self_transfer(utxo_to_spend=tx_v3["new_utxo"], version=3)436# testmempoolaccept paths don't require child-with-parents topology. Ensure that topology437# assumptions aren't made in inheritance checks.438test_accept_v2_and_v3 = node.testmempoolaccept([tx_v2["hex"], tx_v3["hex"]])439assert all([result["allowed"] for result in test_accept_v2_and_v3])441test_accept_v3_from_v2 = node.testmempoolaccept([tx_v2["hex"], tx_v3_from_v2["hex"]])442expected_error_v3_from_v2 = f"TRUC-violation, version=3 tx {tx_v3_from_v2['txid']} (wtxid={tx_v3_from_v2['wtxid']}) cannot spend from non-version=3 tx {tx_v2['txid']} (wtxid={tx_v2['wtxid']})"443for result in test_accept_v3_from_v2:444assert_equal(result["package-error"], expected_error_v3_from_v2)446test_accept_v2_from_v3 = node.testmempoolaccept([tx_v3["hex"], tx_v2_from_v3["hex"]])447expected_error_v2_from_v3 = f"TRUC-violation, non-version=3 tx {tx_v2_from_v3['txid']} (wtxid={tx_v2_from_v3['wtxid']}) cannot spend from version=3 tx {tx_v3['txid']} (wtxid={tx_v3['wtxid']})"448for result in test_accept_v2_from_v3:449assert_equal(result["package-error"], expected_error_v2_from_v3)451test_accept_pairs = node.testmempoolaccept([tx_v2["hex"], tx_v3["hex"], tx_v2_from_v2["hex"], tx_v3_from_v3["hex"]])452assert all([result["allowed"] for result in test_accept_pairs])454self.log.info("Test that descendant violations are caught in testmempoolaccept")455tx_v3_independent = self.wallet.create_self_transfer(version=3)456tx_v3_parent = self.wallet.create_self_transfer_multi(num_outputs=2, version=3)457tx_v3_child_1 = self.wallet.create_self_transfer(utxo_to_spend=tx_v3_parent["new_utxos"][0], version=3)458tx_v3_child_2 = self.wallet.create_self_transfer(utxo_to_spend=tx_v3_parent["new_utxos"][1], version=3)459test_accept_2children = node.testmempoolaccept([tx_v3_parent["hex"], tx_v3_child_1["hex"], tx_v3_child_2["hex"]])460expected_error_2children = f"TRUC-violation, tx {tx_v3_parent['txid']} (wtxid={tx_v3_parent['wtxid']}) would exceed descendant count limit"461for result in test_accept_2children:462assert_equal(result["package-error"], expected_error_2children)464# Extra TRUC transaction does not get incorrectly marked as extra descendant465test_accept_1child_with_exra = node.testmempoolaccept([tx_v3_parent["hex"], tx_v3_child_1["hex"], tx_v3_independent["hex"]])466assert all([result["allowed"] for result in test_accept_1child_with_exra])468# Extra TRUC transaction does not make us ignore the extra descendant469test_accept_2children_with_exra = node.testmempoolaccept([tx_v3_parent["hex"], tx_v3_child_1["hex"], tx_v3_child_2["hex"], tx_v3_independent["hex"]])470expected_error_extra = f"TRUC-violation, tx {tx_v3_parent['txid']} (wtxid={tx_v3_parent['wtxid']}) would exceed descendant count limit"471for result in test_accept_2children_with_exra:472assert_equal(result["package-error"], expected_error_extra)473# Same result if the parent is already in mempool474node.sendrawtransaction(tx_v3_parent["hex"])475test_accept_2children_with_in_mempool_parent = node.testmempoolaccept([tx_v3_child_1["hex"], tx_v3_child_2["hex"]])476for result in test_accept_2children_with_in_mempool_parent:477assert_equal(result["package-error"], expected_error_extra)479@cleanup(extra_args=None)480def test_reorg_2child_rbf(self):481node = self.nodes[0]482self.log.info("Test that children of a TRUC transaction can be replaced individually, even if there are multiple due to reorg")484# Prep for fork485fork_blocks = create_empty_fork(node)486ancestor_tx = self.wallet.send_self_transfer_multi(from_node=node, num_outputs=2, version=3)487self.check_mempool([ancestor_tx["txid"]])489self.generate(node, 1)[0]490self.check_mempool([])492child_1 = self.wallet.send_self_transfer(from_node=node, version=3, utxo_to_spend=ancestor_tx["new_utxos"][0])493child_2 = self.wallet.send_self_transfer(from_node=node, version=3, utxo_to_spend=ancestor_tx["new_utxos"][1])494self.check_mempool([child_1["txid"], child_2["txid"]])496# Create a reorg, causing ancestor_tx to exceed the 1-child limit497self.trigger_reorg(fork_blocks)498self.check_mempool([ancestor_tx["txid"], child_1["txid"], child_2["txid"]])499assert_equal(node.getmempoolentry(ancestor_tx["txid"])["descendantcount"], 3)
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.