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.
751BOOST_CHECK(it_grandchild->second.m_result_type == MempoolAcceptResult::ResultType::VALID);752}753}755// A package Package{parent1, parent2, parent3, child} where the parents are a mixture of756// identical-tx-in-mempool, same-txid-different-witness-in-mempool, and new transactions.757Package package_mixed;759// Give all the parents anyone-can-spend scripts so we don't have to deal with signing the child.760CScript acs_script = CScript() << OP_TRUE;761CScript acs_spk = GetScriptForDestination(WitnessV0ScriptHash(acs_script));762CScriptWitness acs_witness;763acs_witness.stack.emplace_back(acs_script.begin(), acs_script.end());765// parent1 will already be in the mempool766auto mtx_parent1 = CreateValidMempoolTransaction(/*input_transaction=*/m_coinbase_txns[1], /*input_vout=*/0,767/*input_height=*/0, /*input_signing_key=*/coinbaseKey,768/*output_destination=*/acs_spk,769/*output_amount=*/CAmount(49 * COIN), /*submit=*/true);770CTransactionRef ptx_parent1 = MakeTransactionRef(mtx_parent1);771package_mixed.push_back(ptx_parent1);773// parent2 will have a same-txid-different-witness tx already in the mempool774CScript grandparent2_script = CScript() << OP_DROP << OP_TRUE;775CScript grandparent2_spk = GetScriptForDestination(WitnessV0ScriptHash(grandparent2_script));776CScriptWitness parent2_witness1;777parent2_witness1.stack.emplace_back(1);778parent2_witness1.stack.emplace_back(grandparent2_script.begin(), grandparent2_script.end());779CScriptWitness parent2_witness2;780parent2_witness2.stack.emplace_back(2);781parent2_witness2.stack.emplace_back(grandparent2_script.begin(), grandparent2_script.end());783// Create grandparent2 creating an output with multiple spending paths. Submit to mempool.784auto mtx_grandparent2 = CreateValidMempoolTransaction(/*input_transaction=*/m_coinbase_txns[2], /*input_vout=*/0,785/*input_height=*/0, /*input_signing_key=*/coinbaseKey,786/*output_destination=*/grandparent2_spk,787/*output_amount=*/CAmount(49 * COIN), /*submit=*/true);788CTransactionRef ptx_grandparent2 = MakeTransactionRef(mtx_grandparent2);790CMutableTransaction mtx_parent2_v1;791mtx_parent2_v1.version = 1;792mtx_parent2_v1.vin.resize(1);793mtx_parent2_v1.vin[0].prevout.hash = ptx_grandparent2->GetHash();794mtx_parent2_v1.vin[0].prevout.n = 0;795mtx_parent2_v1.vin[0].scriptSig = CScript();796mtx_parent2_v1.vin[0].scriptWitness = parent2_witness1;797mtx_parent2_v1.vout.resize(1);798mtx_parent2_v1.vout[0].nValue = CAmount(48 * COIN);799mtx_parent2_v1.vout[0].scriptPubKey = acs_spk;801CMutableTransaction mtx_parent2_v2{mtx_parent2_v1};802mtx_parent2_v2.vin[0].scriptWitness = parent2_witness2;804CTransactionRef ptx_parent2_v1 = MakeTransactionRef(mtx_parent2_v1);805CTransactionRef ptx_parent2_v2 = MakeTransactionRef(mtx_parent2_v2);806// Put parent2_v1 in the package, submit parent2_v2 to the mempool.807const MempoolAcceptResult parent2_v2_result = m_node.chainman->ProcessTransaction(ptx_parent2_v2);808BOOST_CHECK(parent2_v2_result.m_result_type == MempoolAcceptResult::ResultType::VALID);809package_mixed.push_back(ptx_parent2_v1);811// parent3 will be a new transaction. Put a low feerate to make it invalid on its own.812auto mtx_parent3 = CreateValidMempoolTransaction(/*input_transaction=*/m_coinbase_txns[3], /*input_vout=*/0,813/*input_height=*/0, /*input_signing_key=*/coinbaseKey,814/*output_destination=*/acs_spk,815/*output_amount=*/CAmount(50 * COIN - low_fee_amt), /*submit=*/false);816CTransactionRef ptx_parent3 = MakeTransactionRef(mtx_parent3);817package_mixed.push_back(ptx_parent3);818BOOST_CHECK(m_node.mempool->GetMinFee().GetFee(GetVirtualTransactionSize(*ptx_parent3)) > low_fee_amt);819BOOST_CHECK(m_node.mempool->m_opts.min_relay_feerate.GetFee(GetVirtualTransactionSize(*ptx_parent3)) <= low_fee_amt);821// child spends parent1, parent2, and parent3822CKey mixed_grandchild_key = GenerateRandomKey();823CScript mixed_child_spk = GetScriptForDestination(WitnessV0KeyHash(mixed_grandchild_key.GetPubKey()));825CMutableTransaction mtx_mixed_child;826mtx_mixed_child.vin.emplace_back(COutPoint(ptx_parent1->GetHash(), 0));827mtx_mixed_child.vin.emplace_back(COutPoint(ptx_parent2_v1->GetHash(), 0));828mtx_mixed_child.vin.emplace_back(COutPoint(ptx_parent3->GetHash(), 0));829mtx_mixed_child.vin[0].scriptWitness = acs_witness;830mtx_mixed_child.vin[1].scriptWitness = acs_witness;831mtx_mixed_child.vin[2].scriptWitness = acs_witness;832mtx_mixed_child.vout.emplace_back((48 + 49 + 50 - 1) * COIN, mixed_child_spk);833CTransactionRef ptx_mixed_child = MakeTransactionRef(mtx_mixed_child);834package_mixed.push_back(ptx_mixed_child);836// Submit package:837// parent1 should be ignored838// parent2_v1 should be ignored (and v2 wtxid returned)839// parent3 should be accepted840// child should be accepted841{842const auto mixed_result = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool, package_mixed, false, /*client_maxfeerate=*/{});843if (auto err_mixed{CheckPackageMempoolAcceptResult(package_mixed, mixed_result, /*expect_valid=*/true, m_node.mempool.get())}) {844BOOST_ERROR(err_mixed.value());845} else {846auto it_parent1 = mixed_result.m_tx_results.find(ptx_parent1->GetWitnessHash());847auto it_parent2 = mixed_result.m_tx_results.find(ptx_parent2_v1->GetWitnessHash());848auto it_parent3 = mixed_result.m_tx_results.find(ptx_parent3->GetWitnessHash());849auto it_child = mixed_result.m_tx_results.find(ptx_mixed_child->GetWitnessHash());851BOOST_CHECK(it_parent1->second.m_result_type == MempoolAcceptResult::ResultType::MEMPOOL_ENTRY);852BOOST_CHECK(it_parent2->second.m_result_type == MempoolAcceptResult::ResultType::DIFFERENT_WITNESS);853BOOST_CHECK(it_parent3->second.m_result_type == MempoolAcceptResult::ResultType::VALID);854BOOST_CHECK(it_child->second.m_result_type == MempoolAcceptResult::ResultType::VALID);855BOOST_CHECK_EQUAL(ptx_parent2_v2->GetWitnessHash(), it_parent2->second.m_other_wtxid.value());857// package feerate should include parent3 and child. It should not include parent1 or parent2_v1.858const CFeeRate expected_feerate(1 * COIN, GetVirtualTransactionSize(*ptx_parent3) + GetVirtualTransactionSize(*ptx_mixed_child));859BOOST_CHECK(it_parent3->second.m_effective_feerate.value() == expected_feerate);860BOOST_CHECK(it_child->second.m_effective_feerate.value() == expected_feerate);861std::vector<Wtxid> expected_wtxids({ptx_parent3->GetWitnessHash(), ptx_mixed_child->GetWitnessHash()});862BOOST_CHECK(it_parent3->second.m_wtxids_fee_calculations.value() == expected_wtxids);863BOOST_CHECK(it_child->second.m_wtxids_fee_calculations.value() == expected_wtxids);864}865}866}868BOOST_AUTO_TEST_CASE(package_cpfp_tests)869{870mineBlocks(5);871MockMempoolMinFee(CFeeRate(5000), *m_node.mempool);872LOCK(::cs_main);873size_t expected_pool_size = m_node.mempool->size();874CKey child_key = GenerateRandomKey();875CScript parent_spk = GetScriptForDestination(WitnessV0KeyHash(child_key.GetPubKey()));876CKey grandchild_key = GenerateRandomKey();877CScript child_spk = GetScriptForDestination(WitnessV0KeyHash(grandchild_key.GetPubKey()));879// low-fee parent and high-fee child package880const CAmount coinbase_value{50 * COIN};881const CAmount parent_value{coinbase_value - low_fee_amt};882const CAmount child_value{parent_value - COIN};884Package package_cpfp;885auto mtx_parent = CreateValidMempoolTransaction(/*input_transaction=*/m_coinbase_txns[0], /*input_vout=*/0,886/*input_height=*/0, /*input_signing_key=*/coinbaseKey,887/*output_destination=*/parent_spk,888/*output_amount=*/parent_value, /*submit=*/false);889CTransactionRef tx_parent = MakeTransactionRef(mtx_parent);890package_cpfp.push_back(tx_parent);892auto mtx_child = CreateValidMempoolTransaction(/*input_transaction=*/tx_parent, /*input_vout=*/0,893/*input_height=*/101, /*input_signing_key=*/child_key,894/*output_destination=*/child_spk,895/*output_amount=*/child_value, /*submit=*/false);896CTransactionRef tx_child = MakeTransactionRef(mtx_child);897package_cpfp.push_back(tx_child);899// Package feerate is calculated using modified fees, and prioritisetransaction accepts negative900// fee deltas. This should be taken into account. De-prioritise the parent transaction901// to bring the package feerate to 0.902m_node.mempool->PrioritiseTransaction(tx_parent->GetHash(), child_value - coinbase_value);903{904BOOST_CHECK_EQUAL(m_node.mempool->size(), expected_pool_size);905const auto submit_cpfp_deprio = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool,906package_cpfp, /*test_accept=*/ false, /*client_maxfeerate=*/{});907if (auto err_cpfp_deprio{CheckPackageMempoolAcceptResult(package_cpfp, submit_cpfp_deprio, /*expect_valid=*/false, m_node.mempool.get())}) {908BOOST_ERROR(err_cpfp_deprio.value());909} else {910BOOST_CHECK_EQUAL(submit_cpfp_deprio.m_state.GetResult(), PackageValidationResult::PCKG_TX);911BOOST_CHECK_EQUAL(submit_cpfp_deprio.m_tx_results.find(tx_parent->GetWitnessHash())->second.m_state.GetResult(),912TxValidationResult::TX_RECONSIDERABLE);913BOOST_CHECK_EQUAL(submit_cpfp_deprio.m_tx_results.find(tx_child->GetWitnessHash())->second.m_state.GetResult(),914TxValidationResult::TX_RECONSIDERABLE);915BOOST_CHECK_EQUAL(submit_cpfp_deprio.m_tx_results.find(tx_parent->GetWitnessHash())->second.m_state.GetRejectReason(), "mempool min fee not met");916BOOST_CHECK_EQUAL(m_node.mempool->size(), expected_pool_size);917}918}920// Clear the prioritisation of the parent transaction.921WITH_LOCK(m_node.mempool->cs, m_node.mempool->ClearPrioritisation(tx_parent->GetHash()));923// Package CPFP: Even though the parent's feerate is below the mempool minimum feerate, the924// child pays enough for the package feerate to meet the threshold.925{926BOOST_CHECK_EQUAL(m_node.mempool->size(), expected_pool_size);927const auto submit_cpfp = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool,928package_cpfp, /*test_accept=*/ false, /*client_maxfeerate=*/{});929if (auto err_cpfp{CheckPackageMempoolAcceptResult(package_cpfp, submit_cpfp, /*expect_valid=*/true, m_node.mempool.get())}) {930BOOST_ERROR(err_cpfp.value());931} else {932auto it_parent = submit_cpfp.m_tx_results.find(tx_parent->GetWitnessHash());933auto it_child = submit_cpfp.m_tx_results.find(tx_child->GetWitnessHash());934BOOST_CHECK(it_parent->second.m_result_type == MempoolAcceptResult::ResultType::VALID);935BOOST_CHECK(it_parent->second.m_base_fees.value() == coinbase_value - parent_value);936BOOST_CHECK(it_child->second.m_result_type == MempoolAcceptResult::ResultType::VALID);937BOOST_CHECK(it_child->second.m_base_fees.value() == COIN);939const CFeeRate expected_feerate(coinbase_value - child_value,940GetVirtualTransactionSize(*tx_parent) + GetVirtualTransactionSize(*tx_child));941BOOST_CHECK(it_parent->second.m_effective_feerate.value() == expected_feerate);942BOOST_CHECK(it_child->second.m_effective_feerate.value() == expected_feerate);943std::vector<Wtxid> expected_wtxids({tx_parent->GetWitnessHash(), tx_child->GetWitnessHash()});944BOOST_CHECK(it_parent->second.m_wtxids_fee_calculations.value() == expected_wtxids);945BOOST_CHECK(it_child->second.m_wtxids_fee_calculations.value() == expected_wtxids);946BOOST_CHECK(expected_feerate.GetFeePerK() > 1000);947}948expected_pool_size += 2;949BOOST_CHECK_EQUAL(m_node.mempool->size(), expected_pool_size);950}952// Just because we allow low-fee parents doesn't mean we allow low-feerate packages.953// The mempool minimum feerate is 5sat/vB, but this package just pays 800 satoshis total.954// The child fees would be able to pay for itself, but isn't enough for the entire package.955Package package_still_too_low;956const CAmount parent_fee{200};957const CAmount child_fee{600};958auto mtx_parent_cheap = CreateValidMempoolTransaction(/*input_transaction=*/m_coinbase_txns[1], /*input_vout=*/0,959/*input_height=*/0, /*input_signing_key=*/coinbaseKey,960/*output_destination=*/parent_spk,961/*output_amount=*/coinbase_value - parent_fee, /*submit=*/false);962CTransactionRef tx_parent_cheap = MakeTransactionRef(mtx_parent_cheap);963package_still_too_low.push_back(tx_parent_cheap);964BOOST_CHECK(m_node.mempool->GetMinFee().GetFee(GetVirtualTransactionSize(*tx_parent_cheap)) > parent_fee);965BOOST_CHECK(m_node.mempool->m_opts.min_relay_feerate.GetFee(GetVirtualTransactionSize(*tx_parent_cheap)) <= parent_fee);967auto mtx_child_cheap = CreateValidMempoolTransaction(/*input_transaction=*/tx_parent_cheap, /*input_vout=*/0,968/*input_height=*/101, /*input_signing_key=*/child_key,969/*output_destination=*/child_spk,970/*output_amount=*/coinbase_value - parent_fee - child_fee, /*submit=*/false);971CTransactionRef tx_child_cheap = MakeTransactionRef(mtx_child_cheap);972package_still_too_low.push_back(tx_child_cheap);973BOOST_CHECK(m_node.mempool->GetMinFee().GetFee(GetVirtualTransactionSize(*tx_child_cheap)) <= child_fee);974BOOST_CHECK(m_node.mempool->GetMinFee().GetFee(GetVirtualTransactionSize(*tx_parent_cheap) + GetVirtualTransactionSize(*tx_child_cheap)) > parent_fee + child_fee);975BOOST_CHECK_EQUAL(m_node.mempool->size(), expected_pool_size);977// Cheap package should fail for being too low fee.978{979const auto submit_package_too_low = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool,980package_still_too_low, /*test_accept=*/false, /*client_maxfeerate=*/{});981if (auto err_package_too_low{CheckPackageMempoolAcceptResult(package_still_too_low, submit_package_too_low, /*expect_valid=*/false, m_node.mempool.get())}) {982BOOST_ERROR(err_package_too_low.value());983} else {984// Individual feerate of parent is too low.985BOOST_CHECK_EQUAL(submit_package_too_low.m_tx_results.at(tx_parent_cheap->GetWitnessHash()).m_state.GetResult(),986TxValidationResult::TX_RECONSIDERABLE);987BOOST_CHECK(submit_package_too_low.m_tx_results.at(tx_parent_cheap->GetWitnessHash()).m_effective_feerate.value() ==988CFeeRate(parent_fee, GetVirtualTransactionSize(*tx_parent_cheap)));989// Package feerate of parent + child is too low.990BOOST_CHECK_EQUAL(submit_package_too_low.m_tx_results.at(tx_child_cheap->GetWitnessHash()).m_state.GetResult(),991TxValidationResult::TX_RECONSIDERABLE);992BOOST_CHECK(submit_package_too_low.m_tx_results.at(tx_child_cheap->GetWitnessHash()).m_effective_feerate.value() ==993CFeeRate(parent_fee + child_fee, GetVirtualTransactionSize(*tx_parent_cheap) + GetVirtualTransactionSize(*tx_child_cheap)));994}995BOOST_CHECK_EQUAL(submit_package_too_low.m_state.GetResult(), PackageValidationResult::PCKG_TX);996BOOST_CHECK_EQUAL(submit_package_too_low.m_state.GetRejectReason(), "transaction failed");997BOOST_CHECK_EQUAL(m_node.mempool->size(), expected_pool_size);998}1000// Package feerate includes the modified fees of the transactions.
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.