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.
251const CFeeRate tx4_feerate = CFeeRate(tx4_dimensions.mod_fee + tx5_dimensions.mod_fee, tx4_dimensions.vsize + tx5_dimensions.vsize);252CAmount bumpfee4 = Find(bump_fees, COutPoint{tx4->GetHash(), 1});253if (target_feerate <= tx4_feerate) {254// As long as target feerate is below tx5's ancestor feerate, there is no bump fee.255BOOST_CHECK_EQUAL(bumpfee4, 0);256} else {257// Difference is fee to bump tx4 from current to target feerate, without tx5.258BOOST_CHECK_EQUAL(bumpfee4, target_feerate.GetFee(tx4_dimensions.vsize) - tx4_dimensions.mod_fee);259}260}261// Spent outpoints should usually not be requested as they would not be262// considered available. However, when they are explicitly requested, we263// can calculate their bumpfee to facilitate RBF-replacements264for (const auto& target_feerate : various_normal_feerates) {265node::MiniMiner mini_miner_all_spent(pool, all_spent_outpoints);266BOOST_CHECK(mini_miner_all_spent.IsReadyToCalculate());267auto bump_fees_all_spent = mini_miner_all_spent.CalculateBumpFees(target_feerate);268BOOST_CHECK(!mini_miner_all_spent.IsReadyToCalculate());269BOOST_CHECK_EQUAL(bump_fees_all_spent.size(), all_spent_outpoints.size());270node::MiniMiner mini_miner_all_parents(pool, all_parent_outputs);271BOOST_CHECK(mini_miner_all_parents.IsReadyToCalculate());272auto bump_fees_all_parents = mini_miner_all_parents.CalculateBumpFees(target_feerate);273BOOST_CHECK(!mini_miner_all_parents.IsReadyToCalculate());274BOOST_CHECK_EQUAL(bump_fees_all_parents.size(), all_parent_outputs.size());275for (auto& bump_fees : {bump_fees_all_parents, bump_fees_all_spent}) {276// For all_parents case, both outputs from the parent should have the same bump fee,277// even though only one of them is in a to-be-replaced transaction.278BOOST_CHECK(sanity_check(all_transactions, bump_fees));280// Check tx0 bumpfee: no other bumper.281const TxDimensions& tx0_dimensions = tx_dims.find(tx0->GetHash())->second;282CAmount it0_spent = Find(bump_fees, COutPoint{tx0->GetHash(), 0});283if (target_feerate <= tx0_dimensions.feerate) {284BOOST_CHECK_EQUAL(it0_spent, 0);285} else {286// Difference is fee to bump tx0 from current to target feerate.287BOOST_CHECK_EQUAL(it0_spent, target_feerate.GetFee(tx0_dimensions.vsize) - tx0_dimensions.mod_fee);288}290// Check tx2 bumpfee: no other bumper, because tx3 is to-be-replaced.291const TxDimensions& tx2_dimensions = tx_dims.find(tx2->GetHash())->second;292const CFeeRate tx2_feerate_unbumped = tx2_dimensions.feerate;293auto it2_spent = Find(bump_fees, COutPoint{tx2->GetHash(), 0});294if (target_feerate <= tx2_feerate_unbumped) {295BOOST_CHECK_EQUAL(it2_spent, 0);296} else {297// Difference is fee to bump tx2 from current to target feerate, without tx3.298BOOST_CHECK_EQUAL(it2_spent, target_feerate.GetFee(tx2_dimensions.vsize) - tx2_dimensions.mod_fee);299}301// Check tx4 bumpfee: no other bumper, because tx5 is to-be-replaced.302const TxDimensions& tx4_dimensions = tx_dims.find(tx4->GetHash())->second;303const CFeeRate tx4_feerate_unbumped = tx4_dimensions.feerate;304auto it4_spent = Find(bump_fees, COutPoint{tx4->GetHash(), 0});305if (target_feerate <= tx4_feerate_unbumped) {306BOOST_CHECK_EQUAL(it4_spent, 0);307} else {308// Difference is fee to bump tx4 from current to target feerate, without tx5.309BOOST_CHECK_EQUAL(it4_spent, target_feerate.GetFee(tx4_dimensions.vsize) - tx4_dimensions.mod_fee);310}311}312}314// Check m_inclusion_order for equivalent mempool- and manually-constructed MiniMiners.315// (We cannot check bump fees in manually-constructed MiniMiners because it doesn't know what316// outpoints are requested).317std::vector<node::MiniMinerMempoolEntry> miniminer_info;318{319const int32_t tx0_vsize{tx_dims.at(tx0->GetHash()).vsize};320const int32_t tx1_vsize{tx_dims.at(tx1->GetHash()).vsize};321const int32_t tx2_vsize{tx_dims.at(tx2->GetHash()).vsize};322const int32_t tx3_vsize{tx_dims.at(tx3->GetHash()).vsize};323const int32_t tx4_vsize{tx_dims.at(tx4->GetHash()).vsize};324const int32_t tx5_vsize{tx_dims.at(tx5->GetHash()).vsize};325const int32_t tx6_vsize{tx_dims.at(tx6->GetHash()).vsize};326const int32_t tx7_vsize{tx_dims.at(tx7->GetHash()).vsize};328miniminer_info.emplace_back(tx0,/*vsize_self=*/tx0_vsize,/*vsize_ancestor=*/tx0_vsize,/*fee_self=*/med_fee,/*fee_ancestor=*/med_fee);329miniminer_info.emplace_back(tx1, tx1_vsize, tx0_vsize + tx1_vsize, med_fee, 2*med_fee);330miniminer_info.emplace_back(tx2, tx2_vsize, tx2_vsize, low_fee, low_fee);331miniminer_info.emplace_back(tx3, tx3_vsize, tx2_vsize + tx3_vsize, high_fee, low_fee + high_fee);332miniminer_info.emplace_back(tx4, tx4_vsize, tx4_vsize, low_fee, low_fee);333miniminer_info.emplace_back(tx5, tx5_vsize, tx4_vsize + tx5_vsize, tx5_mod_fee, low_fee + tx5_mod_fee);334miniminer_info.emplace_back(tx6, tx6_vsize, tx6_vsize, high_fee, high_fee);335miniminer_info.emplace_back(tx7, tx7_vsize, tx6_vsize + tx7_vsize, low_fee, high_fee + low_fee);336}337std::map<Txid, std::set<Txid>> descendant_caches;338descendant_caches.emplace(tx0->GetHash(), std::set<Txid>{tx0->GetHash(), tx1->GetHash()});339descendant_caches.emplace(tx1->GetHash(), std::set<Txid>{tx1->GetHash()});340descendant_caches.emplace(tx2->GetHash(), std::set<Txid>{tx2->GetHash(), tx3->GetHash()});341descendant_caches.emplace(tx3->GetHash(), std::set<Txid>{tx3->GetHash()});342descendant_caches.emplace(tx4->GetHash(), std::set<Txid>{tx4->GetHash(), tx5->GetHash()});343descendant_caches.emplace(tx5->GetHash(), std::set<Txid>{tx5->GetHash()});344descendant_caches.emplace(tx6->GetHash(), std::set<Txid>{tx6->GetHash(), tx7->GetHash()});345descendant_caches.emplace(tx7->GetHash(), std::set<Txid>{tx7->GetHash()});347node::MiniMiner miniminer_manual(miniminer_info, descendant_caches);348// Use unspent outpoints to avoid entries being omitted.349node::MiniMiner miniminer_pool(pool, all_unspent_outpoints);350BOOST_CHECK(miniminer_manual.IsReadyToCalculate());351BOOST_CHECK(miniminer_pool.IsReadyToCalculate());352for (const auto& sequences : {miniminer_manual.Linearize(), miniminer_pool.Linearize()}) {353// tx6 is selected first: high feerate with no parents to bump354BOOST_CHECK_EQUAL(Find(sequences, tx6->GetHash()), 0);356// tx2 + tx3 CPFP are selected next357BOOST_CHECK_EQUAL(Find(sequences, tx2->GetHash()), 1);358BOOST_CHECK_EQUAL(Find(sequences, tx3->GetHash()), 1);360// tx4 + prioritised tx5 CPFP361BOOST_CHECK_EQUAL(Find(sequences, tx4->GetHash()), 2);362BOOST_CHECK_EQUAL(Find(sequences, tx5->GetHash()), 2);364BOOST_CHECK_EQUAL(Find(sequences, tx0->GetHash()), 3);365BOOST_CHECK_EQUAL(Find(sequences, tx1->GetHash()), 3);368// tx7 is selected last: low feerate with no children369BOOST_CHECK_EQUAL(Find(sequences, tx7->GetHash()), 4);370}371}373BOOST_FIXTURE_TEST_CASE(miniminer_overlap, TestChain100Setup)374{375/* Tx graph for `miniminer_overlap` unit test:376*377* coinbase_tx [mined] ... block-chain378* -------------------------------------------------379* / | \ \ ... mempool380* / | \ |381* tx0 tx1 tx2 tx4382* [low] [med] [high] [high]383* \ | / |384* \ | / tx5385* \ | / [low]386* tx3 / \387* [high] tx6 tx7388* [med] [high]389*390* NOTE:391* -> "low"/"med"/"high" denote the _absolute_ fee of each tx392* -> tx3 has 3 inputs and 3 outputs, all other txs have 1 input and 2 outputs393* -> tx3's feerate is lower than tx2's, as tx3 has more weight (due to having more inputs and outputs)394*395* -> tx2_FR = high / tx2_vsize396* -> tx3_FR = high / tx3_vsize397* -> tx3_ASFR = (low+med+high+high) / (tx0_vsize + tx1_vsize + tx2_vsize + tx3_vsize)398* -> tx4_FR = high / tx4_vsize399* -> tx6_ASFR = (high+low+med) / (tx4_vsize + tx5_vsize + tx6_vsize)400* -> tx7_ASFR = (high+low+high) / (tx4_vsize + tx5_vsize + tx7_vsize) */402CTxMemPool& pool = *Assert(m_node.mempool);403LOCK2(::cs_main, pool.cs);404TestMemPoolEntryHelper entry;406// Create 3 parents of different feerates, and 1 child spending outputs from all 3 parents.407const auto tx0 = make_tx({COutPoint{m_coinbase_txns[0]->GetHash(), 0}}, /*num_outputs=*/2);408TryAddToMempool(pool, entry.Fee(low_fee).FromTx(tx0));409const auto tx1 = make_tx({COutPoint{m_coinbase_txns[1]->GetHash(), 0}}, /*num_outputs=*/2);410TryAddToMempool(pool, entry.Fee(med_fee).FromTx(tx1));411const auto tx2 = make_tx({COutPoint{m_coinbase_txns[2]->GetHash(), 0}}, /*num_outputs=*/2);412TryAddToMempool(pool, entry.Fee(high_fee).FromTx(tx2));413const auto tx3 = make_tx({COutPoint{tx0->GetHash(), 0}, COutPoint{tx1->GetHash(), 0}, COutPoint{tx2->GetHash(), 0}}, /*num_outputs=*/3);414TryAddToMempool(pool, entry.Fee(high_fee).FromTx(tx3));416// Create 1 grandparent and 1 parent, then 2 children.417const auto tx4 = make_tx({COutPoint{m_coinbase_txns[3]->GetHash(), 0}}, /*num_outputs=*/2);418TryAddToMempool(pool, entry.Fee(high_fee).FromTx(tx4));419const auto tx5 = make_tx({COutPoint{tx4->GetHash(), 0}}, /*num_outputs=*/3);420TryAddToMempool(pool, entry.Fee(low_fee).FromTx(tx5));421const auto tx6 = make_tx({COutPoint{tx5->GetHash(), 0}}, /*num_outputs=*/2);422TryAddToMempool(pool, entry.Fee(med_fee).FromTx(tx6));423const auto tx7 = make_tx({COutPoint{tx5->GetHash(), 1}}, /*num_outputs=*/2);424TryAddToMempool(pool, entry.Fee(high_fee).FromTx(tx7));426std::vector<CTransactionRef> all_transactions{tx0, tx1, tx2, tx3, tx4, tx5, tx6, tx7};427std::vector<int64_t> tx_vsizes;428tx_vsizes.reserve(all_transactions.size());429for (const auto& tx : all_transactions) tx_vsizes.push_back(GetVirtualTransactionSize(*tx));431std::vector<COutPoint> all_unspent_outpoints({432COutPoint{tx0->GetHash(), 1},433COutPoint{tx1->GetHash(), 1},434COutPoint{tx2->GetHash(), 1},435COutPoint{tx3->GetHash(), 0},436COutPoint{tx3->GetHash(), 1},437COutPoint{tx3->GetHash(), 2},438COutPoint{tx4->GetHash(), 1},439COutPoint{tx5->GetHash(), 2},440COutPoint{tx6->GetHash(), 0},441COutPoint{tx7->GetHash(), 0}442});443for (const auto& outpoint : all_unspent_outpoints) BOOST_CHECK(!pool.isSpent(outpoint));445const auto tx2_feerate = CFeeRate(high_fee, tx_vsizes[2]);446const auto tx3_feerate = CFeeRate(high_fee, tx_vsizes[3]);447// tx3's feerate is lower than tx2's. same fee, different weight.448BOOST_CHECK(tx2_feerate > tx3_feerate);449const auto tx3_anc_feerate = CFeeRate(low_fee + med_fee + high_fee + high_fee, tx_vsizes[0] + tx_vsizes[1] + tx_vsizes[2] + tx_vsizes[3]);450const auto& tx3_entry{*Assert(pool.GetEntry(tx3->GetHash()))};452// Check that ancestor feerate is calculated correctly.453auto [dummy_count, ancestor_vsize, mod_fees] = pool.CalculateAncestorData(tx3_entry);454BOOST_CHECK(tx3_anc_feerate == CFeeRate(mod_fees, ancestor_vsize));456const auto tx4_feerate = CFeeRate(high_fee, tx_vsizes[4]);457const auto tx6_anc_feerate = CFeeRate(high_fee + low_fee + med_fee, tx_vsizes[4] + tx_vsizes[5] + tx_vsizes[6]);458const auto& tx6_entry{*Assert(pool.GetEntry(tx6->GetHash()))};460std::tie(std::ignore, ancestor_vsize, mod_fees) = pool.CalculateAncestorData(tx6_entry);461BOOST_CHECK(tx6_anc_feerate == CFeeRate(mod_fees, ancestor_vsize));462const auto tx7_anc_feerate = CFeeRate(high_fee + low_fee + high_fee, tx_vsizes[4] + tx_vsizes[5] + tx_vsizes[7]);463const auto& tx7_entry{*Assert(pool.GetEntry(tx7->GetHash()))};465std::tie(std::ignore, ancestor_vsize, mod_fees) = pool.CalculateAncestorData(tx7_entry);466BOOST_CHECK(tx7_anc_feerate == CFeeRate(mod_fees, ancestor_vsize));467BOOST_CHECK(tx4_feerate > tx6_anc_feerate);468BOOST_CHECK(tx4_feerate > tx7_anc_feerate);470// Extremely high feerate: everybody's bumpfee is from their full ancestor set.471{472node::MiniMiner mini_miner(pool, all_unspent_outpoints);473const CFeeRate very_high_feerate(COIN);474BOOST_CHECK(tx3_anc_feerate < very_high_feerate);475BOOST_CHECK(mini_miner.IsReadyToCalculate());476auto bump_fees = mini_miner.CalculateBumpFees(very_high_feerate);477BOOST_CHECK_EQUAL(bump_fees.size(), all_unspent_outpoints.size());478BOOST_CHECK(!mini_miner.IsReadyToCalculate());479BOOST_CHECK(sanity_check(all_transactions, bump_fees));480const auto tx0_bumpfee = bump_fees.find(COutPoint{tx0->GetHash(), 1});481BOOST_CHECK(tx0_bumpfee != bump_fees.end());482BOOST_CHECK_EQUAL(tx0_bumpfee->second, very_high_feerate.GetFee(tx_vsizes[0]) - low_fee);483const auto tx3_bumpfee = bump_fees.find(COutPoint{tx3->GetHash(), 0});484BOOST_CHECK(tx3_bumpfee != bump_fees.end());485BOOST_CHECK_EQUAL(tx3_bumpfee->second,486very_high_feerate.GetFee(tx_vsizes[0] + tx_vsizes[1] + tx_vsizes[2] + tx_vsizes[3]) - (low_fee + med_fee + high_fee + high_fee));487const auto tx6_bumpfee = bump_fees.find(COutPoint{tx6->GetHash(), 0});488BOOST_CHECK(tx6_bumpfee != bump_fees.end());489BOOST_CHECK_EQUAL(tx6_bumpfee->second,490very_high_feerate.GetFee(tx_vsizes[4] + tx_vsizes[5] + tx_vsizes[6]) - (high_fee + low_fee + med_fee));491const auto tx7_bumpfee = bump_fees.find(COutPoint{tx7->GetHash(), 0});492BOOST_CHECK(tx7_bumpfee != bump_fees.end());493BOOST_CHECK_EQUAL(tx7_bumpfee->second,494very_high_feerate.GetFee(tx_vsizes[4] + tx_vsizes[5] + tx_vsizes[7]) - (high_fee + low_fee + high_fee));495// Total fees: if spending multiple outputs from tx3 don't double-count fees.496node::MiniMiner mini_miner_total_tx3(pool, {COutPoint{tx3->GetHash(), 0}, COutPoint{tx3->GetHash(), 1}});497BOOST_CHECK(mini_miner_total_tx3.IsReadyToCalculate());498const auto tx3_bump_fee = mini_miner_total_tx3.CalculateTotalBumpFees(very_high_feerate);499BOOST_CHECK(!mini_miner_total_tx3.IsReadyToCalculate());500BOOST_CHECK(tx3_bump_fee.has_value());
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.