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.
502Package package_missing_parent{tx_parent_1, tx_child_missing_parent};504const auto result_missing_parent = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool,505package_missing_parent, /*test_accept=*/false, /*client_maxfeerate=*/{});506if (auto err_missing_parent{CheckPackageMempoolAcceptResult(package_missing_parent, result_missing_parent, /*expect_valid=*/false, m_node.mempool.get())}) {507BOOST_ERROR(err_missing_parent.value());508} else {509auto it_parent = result_missing_parent.m_tx_results.find(tx_parent_1->GetWitnessHash());510auto it_child = result_missing_parent.m_tx_results.find(tx_child_missing_parent->GetWitnessHash());512BOOST_CHECK_EQUAL(result_missing_parent.m_state.GetResult(), PackageValidationResult::PCKG_TX);513BOOST_CHECK_EQUAL(result_missing_parent.m_state.GetRejectReason(), "transaction failed");515BOOST_CHECK_EQUAL(it_parent->second.m_state.GetResult(), TxValidationResult::TX_RECONSIDERABLE);516BOOST_CHECK_EQUAL(it_child->second.m_state.GetResult(), TxValidationResult::TX_MISSING_INPUTS);517BOOST_CHECK_EQUAL(it_child->second.m_state.GetRejectReason(), "bad-txns-inputs-missingorspent");518BOOST_CHECK_EQUAL(m_node.mempool->size(), expected_pool_size);519}521// Submit parent2 ahead of time, should become ok.522Package package_just_parent2{tx_parent_2};523expected_pool_size += 1;524const auto result_just_parent2 = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool,525package_just_parent2, /*test_accept=*/false, /*client_maxfeerate=*/{});526if (auto err_parent2{CheckPackageMempoolAcceptResult(package_just_parent2, result_just_parent2, /*expect_valid=*/true, m_node.mempool.get())}) {527BOOST_ERROR(err_parent2.value());528}529BOOST_CHECK_EQUAL(m_node.mempool->size(), expected_pool_size);531const auto result_parent_already_in = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool,532package_missing_parent, /*test_accept=*/false, /*client_maxfeerate=*/{});533expected_pool_size += 2;534if (auto err_parent_already_in{CheckPackageMempoolAcceptResult(package_missing_parent, result_parent_already_in, /*expect_valid=*/true, m_node.mempool.get())}) {535BOOST_ERROR(err_parent_already_in.value());536}537BOOST_CHECK_EQUAL(m_node.mempool->size(), expected_pool_size);538}539}541// Tests for packages containing a single transaction542BOOST_AUTO_TEST_CASE(package_single_tx)543{544// Mine blocks to mature coinbases.545mineBlocks(3);546LOCK(cs_main);547auto expected_pool_size{m_node.mempool->size()};549const CAmount high_fee{1000};551// No unconfirmed parents552CKey single_key = GenerateRandomKey();553CScript single_locking_script = GetScriptForDestination(PKHash(single_key.GetPubKey()));554auto mtx_single = CreateValidMempoolTransaction(/*input_transaction=*/m_coinbase_txns[0], /*input_vout=*/0,555/*input_height=*/0, /*input_signing_key=*/coinbaseKey,556/*output_destination=*/single_locking_script,557/*output_amount=*/CAmount(49 * COIN), /*submit=*/false);558CTransactionRef tx_single = MakeTransactionRef(mtx_single);559Package package_tx_single{tx_single};560const auto result_single_tx = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool,561package_tx_single, /*test_accept=*/false, /*client_maxfeerate=*/{});562expected_pool_size += 1;563BOOST_CHECK_MESSAGE(result_single_tx.m_state.IsValid(),564"Package validation unexpectedly failed: " << result_single_tx.m_state.ToString());565BOOST_CHECK_EQUAL(m_node.mempool->size(), expected_pool_size);567// Parent and Child. Both submitted by themselves through the ProcessNewPackage interface.568CKey parent_key = GenerateRandomKey();569CScript parent_locking_script = GetScriptForDestination(WitnessV0KeyHash(parent_key.GetPubKey()));570auto mtx_parent = CreateValidMempoolTransaction(/*input_transaction=*/m_coinbase_txns[1], /*input_vout=*/0,571/*input_height=*/0, /*input_signing_key=*/coinbaseKey,572/*output_destination=*/parent_locking_script,573/*output_amount=*/CAmount(50 * COIN) - high_fee, /*submit=*/false);574CTransactionRef tx_parent = MakeTransactionRef(mtx_parent);575Package package_just_parent{tx_parent};576const auto result_just_parent = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool, package_just_parent, /*test_accept=*/false, /*client_maxfeerate=*/{});577if (auto err_parent_child{CheckPackageMempoolAcceptResult(package_just_parent, result_just_parent, /*expect_valid=*/true, nullptr)}) {578BOOST_ERROR(err_parent_child.value());579} else {580auto it_parent = result_just_parent.m_tx_results.find(tx_parent->GetWitnessHash());581BOOST_CHECK_MESSAGE(it_parent->second.m_state.IsValid(), it_parent->second.m_state.ToString());582BOOST_CHECK(it_parent->second.m_effective_feerate.value().GetFee(GetVirtualTransactionSize(*tx_parent)) == high_fee);583BOOST_CHECK_EQUAL(it_parent->second.m_wtxids_fee_calculations.value().size(), 1);584BOOST_CHECK_EQUAL(it_parent->second.m_wtxids_fee_calculations.value().front(), tx_parent->GetWitnessHash());585}586expected_pool_size += 1;587BOOST_CHECK_EQUAL(m_node.mempool->size(), expected_pool_size);589CKey child_key = GenerateRandomKey();590CScript child_locking_script = GetScriptForDestination(WitnessV0KeyHash(child_key.GetPubKey()));591auto mtx_child = CreateValidMempoolTransaction(/*input_transaction=*/tx_parent, /*input_vout=*/0,592/*input_height=*/101, /*input_signing_key=*/parent_key,593/*output_destination=*/child_locking_script,594/*output_amount=*/CAmount(50 * COIN) - 2 * high_fee, /*submit=*/false);595CTransactionRef tx_child = MakeTransactionRef(mtx_child);596Package package_just_child{tx_child};597const auto result_just_child = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool, package_just_child, /*test_accept=*/false, /*client_maxfeerate=*/{});598if (auto err_parent_child{CheckPackageMempoolAcceptResult(package_just_child, result_just_child, /*expect_valid=*/true, nullptr)}) {599BOOST_ERROR(err_parent_child.value());600} else {601auto it_child = result_just_child.m_tx_results.find(tx_child->GetWitnessHash());602BOOST_CHECK_MESSAGE(it_child->second.m_state.IsValid(), it_child->second.m_state.ToString());603BOOST_CHECK(it_child->second.m_effective_feerate.value().GetFee(GetVirtualTransactionSize(*tx_child)) == high_fee);604BOOST_CHECK_EQUAL(it_child->second.m_wtxids_fee_calculations.value().size(), 1);605BOOST_CHECK_EQUAL(it_child->second.m_wtxids_fee_calculations.value().front(), tx_child->GetWitnessHash());606}607expected_pool_size += 1;608BOOST_CHECK_EQUAL(m_node.mempool->size(), expected_pool_size);610// Too-low fee to RBF tx_single611auto mtx_single_low_fee = CreateValidMempoolTransaction(/*input_transaction=*/m_coinbase_txns[0], /*input_vout=*/0,612/*input_height=*/0, /*input_signing_key=*/coinbaseKey,613/*output_destination=*/single_locking_script,614/*output_amount=*/CAmount(49 * COIN - 1), /*submit=*/false);615CTransactionRef tx_single_low_fee = MakeTransactionRef(mtx_single_low_fee);616Package package_tx_single_low_fee{tx_single_low_fee};617const auto result_single_tx_low_fee = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool,618package_tx_single_low_fee, /*test_accept=*/false, /*client_maxfeerate=*/{});620BOOST_CHECK_EQUAL(m_node.mempool->size(), expected_pool_size);622BOOST_CHECK(!result_single_tx_low_fee.m_state.IsValid());623BOOST_CHECK_EQUAL(result_single_tx_low_fee.m_state.GetResult(), PackageValidationResult::PCKG_TX);624auto it_low_fee = result_single_tx_low_fee.m_tx_results.find(tx_single_low_fee->GetWitnessHash());625BOOST_CHECK_EQUAL(it_low_fee->second.m_state.GetResult(), TxValidationResult::TX_RECONSIDERABLE);626if (auto err_single{CheckPackageMempoolAcceptResult(package_tx_single_low_fee, result_single_tx_low_fee, /*expect_valid=*/false, m_node.mempool.get())}) {627BOOST_ERROR(err_single.value());628}629BOOST_CHECK_EQUAL(m_node.mempool->size(), expected_pool_size);630}632// Tests for packages containing transactions that have same-txid-different-witness equivalents in633// the mempool.634BOOST_AUTO_TEST_CASE(package_witness_swap_tests)635{636// Mine blocks to mature coinbases.637mineBlocks(5);638MockMempoolMinFee(CFeeRate(5000), *m_node.mempool);639LOCK(cs_main);641// Transactions with a same-txid-different-witness transaction in the mempool should be ignored,642// and the mempool entry's wtxid returned.643CScript witnessScript = CScript() << OP_DROP << OP_TRUE;644CScript scriptPubKey = GetScriptForDestination(WitnessV0ScriptHash(witnessScript));645auto mtx_parent = CreateValidMempoolTransaction(/*input_transaction=*/m_coinbase_txns[0], /*input_vout=*/0,646/*input_height=*/0, /*input_signing_key=*/coinbaseKey,647/*output_destination=*/scriptPubKey,648/*output_amount=*/CAmount(49 * COIN), /*submit=*/false);649CTransactionRef ptx_parent = MakeTransactionRef(mtx_parent);651// Make two children with the same txid but different witnesses.652CScriptWitness witness1;653witness1.stack.emplace_back(1);654witness1.stack.emplace_back(witnessScript.begin(), witnessScript.end());656CScriptWitness witness2(witness1);657witness2.stack.emplace_back(2);658witness2.stack.emplace_back(witnessScript.begin(), witnessScript.end());660CKey child_key = GenerateRandomKey();661CScript child_locking_script = GetScriptForDestination(WitnessV0KeyHash(child_key.GetPubKey()));662CMutableTransaction mtx_child1;663mtx_child1.version = 1;664mtx_child1.vin.resize(1);665mtx_child1.vin[0].prevout.hash = ptx_parent->GetHash();666mtx_child1.vin[0].prevout.n = 0;667mtx_child1.vin[0].scriptSig = CScript();668mtx_child1.vin[0].scriptWitness = witness1;669mtx_child1.vout.resize(1);670mtx_child1.vout[0].nValue = CAmount(48 * COIN);671mtx_child1.vout[0].scriptPubKey = child_locking_script;673CMutableTransaction mtx_child2{mtx_child1};674mtx_child2.vin[0].scriptWitness = witness2;676CTransactionRef ptx_child1 = MakeTransactionRef(mtx_child1);677CTransactionRef ptx_child2 = MakeTransactionRef(mtx_child2);679// child1 and child2 have the same txid680BOOST_CHECK_EQUAL(ptx_child1->GetHash(), ptx_child2->GetHash());681// child1 and child2 have different wtxids682BOOST_CHECK(ptx_child1->GetWitnessHash() != ptx_child2->GetWitnessHash());683// Check that they have different package hashes684BOOST_CHECK(GetPackageHash({ptx_parent, ptx_child1}) != GetPackageHash({ptx_parent, ptx_child2}));686// Try submitting Package1{parent, child1} and Package2{parent, child2} where the children are687// same-txid-different-witness.688{689Package package_parent_child1{ptx_parent, ptx_child1};690const auto submit_witness1 = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool,691package_parent_child1, /*test_accept=*/false, /*client_maxfeerate=*/{});692if (auto err_witness1{CheckPackageMempoolAcceptResult(package_parent_child1, submit_witness1, /*expect_valid=*/true, m_node.mempool.get())}) {693BOOST_ERROR(err_witness1.value());694}696// Child2 would have been validated individually.697Package package_parent_child2{ptx_parent, ptx_child2};698const auto submit_witness2 = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool,699package_parent_child2, /*test_accept=*/false, /*client_maxfeerate=*/{});700if (auto err_witness2{CheckPackageMempoolAcceptResult(package_parent_child2, submit_witness2, /*expect_valid=*/true, m_node.mempool.get())}) {701BOOST_ERROR(err_witness2.value());702} else {703auto it_parent2_deduped = submit_witness2.m_tx_results.find(ptx_parent->GetWitnessHash());704auto it_child2 = submit_witness2.m_tx_results.find(ptx_child2->GetWitnessHash());705BOOST_CHECK(it_parent2_deduped->second.m_result_type == MempoolAcceptResult::ResultType::MEMPOOL_ENTRY);706BOOST_CHECK(it_child2->second.m_result_type == MempoolAcceptResult::ResultType::DIFFERENT_WITNESS);707BOOST_CHECK_EQUAL(ptx_child1->GetWitnessHash(), it_child2->second.m_other_wtxid.value());708}710// Deduplication should work when wtxid != txid. Submit package with the already-in-mempool711// transactions again, which should not fail.712const auto submit_segwit_dedup = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool,713package_parent_child1, /*test_accept=*/false, /*client_maxfeerate=*/{});714if (auto err_segwit_dedup{CheckPackageMempoolAcceptResult(package_parent_child1, submit_segwit_dedup, /*expect_valid=*/true, m_node.mempool.get())}) {715BOOST_ERROR(err_segwit_dedup.value());716} else {717auto it_parent_dup = submit_segwit_dedup.m_tx_results.find(ptx_parent->GetWitnessHash());718auto it_child_dup = submit_segwit_dedup.m_tx_results.find(ptx_child1->GetWitnessHash());719BOOST_CHECK(it_parent_dup->second.m_result_type == MempoolAcceptResult::ResultType::MEMPOOL_ENTRY);720BOOST_CHECK(it_child_dup->second.m_result_type == MempoolAcceptResult::ResultType::MEMPOOL_ENTRY);721}722}724// Try submitting Package1{child2, grandchild} where child2 is same-txid-different-witness as725// the in-mempool transaction, child1. Since child1 exists in the mempool and its outputs are726// available, child2 should be ignored and grandchild should be accepted.727//728// This tests a potential censorship vector in which an attacker broadcasts a competing package729// where a parent's witness is mutated. The honest package should be accepted despite the fact730// that we don't allow witness replacement.731CKey grandchild_key = GenerateRandomKey();732CScript grandchild_locking_script = GetScriptForDestination(WitnessV0KeyHash(grandchild_key.GetPubKey()));733auto mtx_grandchild = CreateValidMempoolTransaction(/*input_transaction=*/ptx_child2, /*input_vout=*/0,734/*input_height=*/0, /*input_signing_key=*/child_key,735/*output_destination=*/grandchild_locking_script,736/*output_amount=*/CAmount(47 * COIN), /*submit=*/false);737CTransactionRef ptx_grandchild = MakeTransactionRef(mtx_grandchild);738// Check that they have different package hashes739BOOST_CHECK(GetPackageHash({ptx_child1, ptx_grandchild}) != GetPackageHash({ptx_child2, ptx_grandchild}));740// We already submitted child1 above.741{742Package package_child2_grandchild{ptx_child2, ptx_grandchild};743const auto submit_spend_ignored = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool,744package_child2_grandchild, /*test_accept=*/false, /*client_maxfeerate=*/{});745if (auto err_spend_ignored{CheckPackageMempoolAcceptResult(package_child2_grandchild, submit_spend_ignored, /*expect_valid=*/true, m_node.mempool.get())}) {746BOOST_ERROR(err_spend_ignored.value());747} else {748auto it_child2_ignored = submit_spend_ignored.m_tx_results.find(ptx_child2->GetWitnessHash());749auto it_grandchild = submit_spend_ignored.m_tx_results.find(ptx_grandchild->GetWitnessHash());750BOOST_CHECK(it_child2_ignored->second.m_result_type == MempoolAcceptResult::ResultType::DIFFERENT_WITNESS);
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.