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.
501BOOST_CHECK_EQUAL(tx3_bump_fee.value(),502very_high_feerate.GetFee(tx_vsizes[0] + tx_vsizes[1] + tx_vsizes[2] + tx_vsizes[3]) - (low_fee + med_fee + high_fee + high_fee));503// Total fees: if spending both tx6 and tx7, don't double-count fees.504node::MiniMiner mini_miner_tx6_tx7(pool, {COutPoint{tx6->GetHash(), 0}, COutPoint{tx7->GetHash(), 0}});505BOOST_CHECK(mini_miner_tx6_tx7.IsReadyToCalculate());506const auto tx6_tx7_bumpfee = mini_miner_tx6_tx7.CalculateTotalBumpFees(very_high_feerate);507BOOST_CHECK(!mini_miner_tx6_tx7.IsReadyToCalculate());508BOOST_CHECK(tx6_tx7_bumpfee.has_value());509BOOST_CHECK_EQUAL(tx6_tx7_bumpfee.value(),510very_high_feerate.GetFee(tx_vsizes[4] + tx_vsizes[5] + tx_vsizes[6] + tx_vsizes[7]) - (high_fee + low_fee + med_fee + high_fee));511}512// Feerate just below tx4: tx6 and tx7 have different bump fees.513{514const auto just_below_tx4 = CFeeRate(tx4_feerate.GetFeePerK() - 5);515node::MiniMiner mini_miner(pool, all_unspent_outpoints);516BOOST_CHECK(mini_miner.IsReadyToCalculate());517auto bump_fees = mini_miner.CalculateBumpFees(just_below_tx4);518BOOST_CHECK(!mini_miner.IsReadyToCalculate());519BOOST_CHECK_EQUAL(bump_fees.size(), all_unspent_outpoints.size());520BOOST_CHECK(sanity_check(all_transactions, bump_fees));521const auto tx6_bumpfee = bump_fees.find(COutPoint{tx6->GetHash(), 0});522BOOST_CHECK(tx6_bumpfee != bump_fees.end());523BOOST_CHECK_EQUAL(tx6_bumpfee->second, just_below_tx4.GetFee(tx_vsizes[5] + tx_vsizes[6]) - (low_fee + med_fee));524const auto tx7_bumpfee = bump_fees.find(COutPoint{tx7->GetHash(), 0});525BOOST_CHECK(tx7_bumpfee != bump_fees.end());526BOOST_CHECK_EQUAL(tx7_bumpfee->second, just_below_tx4.GetFee(tx_vsizes[5] + tx_vsizes[7]) - (low_fee + high_fee));527// Total fees: if spending both tx6 and tx7, don't double-count fees.528node::MiniMiner mini_miner_tx6_tx7(pool, {COutPoint{tx6->GetHash(), 0}, COutPoint{tx7->GetHash(), 0}});529BOOST_CHECK(mini_miner_tx6_tx7.IsReadyToCalculate());530const auto tx6_tx7_bumpfee = mini_miner_tx6_tx7.CalculateTotalBumpFees(just_below_tx4);531BOOST_CHECK(!mini_miner_tx6_tx7.IsReadyToCalculate());532BOOST_CHECK(tx6_tx7_bumpfee.has_value());533BOOST_CHECK_EQUAL(tx6_tx7_bumpfee.value(), just_below_tx4.GetFee(tx_vsizes[5] + tx_vsizes[6]) - (low_fee + med_fee));534}535// Feerate between tx6 and tx7's ancestor feerates: don't need to bump tx5 because tx7 already does.536{537const auto just_above_tx6 = CFeeRate(med_fee + 10, tx_vsizes[6]);538BOOST_CHECK(just_above_tx6 <= CFeeRate(low_fee + high_fee, tx_vsizes[5] + tx_vsizes[7]));539node::MiniMiner mini_miner(pool, all_unspent_outpoints);540BOOST_CHECK(mini_miner.IsReadyToCalculate());541auto bump_fees = mini_miner.CalculateBumpFees(just_above_tx6);542BOOST_CHECK(!mini_miner.IsReadyToCalculate());543BOOST_CHECK_EQUAL(bump_fees.size(), all_unspent_outpoints.size());544BOOST_CHECK(sanity_check(all_transactions, bump_fees));545const auto tx6_bumpfee = bump_fees.find(COutPoint{tx6->GetHash(), 0});546BOOST_CHECK(tx6_bumpfee != bump_fees.end());547BOOST_CHECK_EQUAL(tx6_bumpfee->second, just_above_tx6.GetFee(tx_vsizes[6]) - (med_fee));548const auto tx7_bumpfee = bump_fees.find(COutPoint{tx7->GetHash(), 0});549BOOST_CHECK(tx7_bumpfee != bump_fees.end());550BOOST_CHECK_EQUAL(tx7_bumpfee->second, 0);551}552// Check linearization order553std::vector<node::MiniMinerMempoolEntry> miniminer_info;554miniminer_info.emplace_back(tx0,/*vsize_self=*/tx_vsizes[0], /*vsize_ancestor=*/tx_vsizes[0], /*fee_self=*/low_fee, /*fee_ancestor=*/low_fee);555miniminer_info.emplace_back(tx1, tx_vsizes[1], tx_vsizes[1], med_fee, med_fee);556miniminer_info.emplace_back(tx2, tx_vsizes[2], tx_vsizes[2], high_fee, high_fee);557miniminer_info.emplace_back(tx3, tx_vsizes[3], tx_vsizes[0]+tx_vsizes[1]+tx_vsizes[2]+tx_vsizes[3], high_fee, low_fee+med_fee+2*high_fee);558miniminer_info.emplace_back(tx4, tx_vsizes[4], tx_vsizes[4], high_fee, high_fee);559miniminer_info.emplace_back(tx5, tx_vsizes[5], tx_vsizes[4]+tx_vsizes[5], low_fee, low_fee + high_fee);560miniminer_info.emplace_back(tx6, tx_vsizes[6], tx_vsizes[4]+tx_vsizes[5]+tx_vsizes[6], med_fee, high_fee+low_fee+med_fee);561miniminer_info.emplace_back(tx7, tx_vsizes[7], tx_vsizes[4]+tx_vsizes[5]+tx_vsizes[7], high_fee, high_fee+low_fee+high_fee);563std::map<Txid, std::set<Txid>> descendant_caches;564descendant_caches.emplace(tx0->GetHash(), std::set<Txid>{tx0->GetHash(), tx3->GetHash()});565descendant_caches.emplace(tx1->GetHash(), std::set<Txid>{tx1->GetHash(), tx3->GetHash()});566descendant_caches.emplace(tx2->GetHash(), std::set<Txid>{tx2->GetHash(), tx3->GetHash()});567descendant_caches.emplace(tx3->GetHash(), std::set<Txid>{tx3->GetHash()});568descendant_caches.emplace(tx4->GetHash(), std::set<Txid>{tx4->GetHash(), tx5->GetHash(), tx6->GetHash(), tx7->GetHash()});569descendant_caches.emplace(tx5->GetHash(), std::set<Txid>{tx5->GetHash(), tx6->GetHash(), tx7->GetHash()});570descendant_caches.emplace(tx6->GetHash(), std::set<Txid>{tx6->GetHash()});571descendant_caches.emplace(tx7->GetHash(), std::set<Txid>{tx7->GetHash()});573node::MiniMiner miniminer_manual(miniminer_info, descendant_caches);574// Use unspent outpoints to avoid entries being omitted.575node::MiniMiner miniminer_pool(pool, all_unspent_outpoints);576BOOST_CHECK(miniminer_manual.IsReadyToCalculate());577BOOST_CHECK(miniminer_pool.IsReadyToCalculate());578for (const auto& sequences : {miniminer_manual.Linearize(), miniminer_pool.Linearize()}) {579// tx2 and tx4 selected first: high feerate with nothing to bump580BOOST_CHECK_EQUAL(Find(sequences, tx4->GetHash()), 0);581BOOST_CHECK_EQUAL(Find(sequences, tx2->GetHash()), 1);583// tx5 + tx7 CPFP584BOOST_CHECK_EQUAL(Find(sequences, tx5->GetHash()), 2);585BOOST_CHECK_EQUAL(Find(sequences, tx7->GetHash()), 2);587// tx0 and tx1 CPFP'd by tx3588BOOST_CHECK_EQUAL(Find(sequences, tx0->GetHash()), 3);589BOOST_CHECK_EQUAL(Find(sequences, tx1->GetHash()), 3);590BOOST_CHECK_EQUAL(Find(sequences, tx3->GetHash()), 3);592// tx6 at medium feerate593BOOST_CHECK_EQUAL(Find(sequences, tx6->GetHash()), 4);594}595}596BOOST_FIXTURE_TEST_CASE(calculate_cluster, TestChain100Setup)597{598CTxMemPool& pool = *Assert(m_node.mempool);599LOCK2(cs_main, pool.cs);601// Add 500 transactions in 10 clusters602std::vector<Txid> last_txs;603std::vector<Txid> chain_txids;604auto& lasttx = m_coinbase_txns[0];605TestMemPoolEntryHelper entry;606for (int cluster_count=0; cluster_count < 10; ++cluster_count) {607// Add chain of size 50608lasttx = m_coinbase_txns[cluster_count];609for (auto i{0}; i < 50; ++i) {610const auto tx = make_tx({COutPoint{lasttx->GetHash(), 0}}, /*num_outputs=*/1);611TryAddToMempool(pool, entry.Fee(CENT).FromTx(tx));612chain_txids.push_back(tx->GetHash());613lasttx = tx;614}615last_txs.emplace_back(lasttx->GetHash());616}617const auto cluster_500tx = pool.GatherClusters(last_txs);618CTxMemPool::setEntries cluster_500tx_set{cluster_500tx.begin(), cluster_500tx.end()};619BOOST_CHECK_EQUAL(cluster_500tx.size(), cluster_500tx_set.size());620const auto vec_iters_500 = pool.GetIterVec(chain_txids);621for (const auto& iter : vec_iters_500) BOOST_CHECK(cluster_500tx_set.count(iter));623// GatherClusters stops at 500 transactions.624const auto tx_501 = make_tx({COutPoint{lasttx->GetHash(), 0}}, /*num_outputs=*/1);625TryAddToMempool(pool, entry.Fee(CENT).FromTx(tx_501));626const auto cluster_501 = pool.GatherClusters(last_txs);627BOOST_CHECK_EQUAL(cluster_501.size(), 0);629/* Zig Zag cluster:630* txp0 txp1 txp2 ... txp30 txp31631* \ / \ / \ \ /632* txc0 txc1 txc2 ... txc30633* Note that each transaction's ancestor size is 1 or 3, and each descendant size is 1, 2 or 3.634* However, all of these transactions are in the same cluster. */635std::vector<Txid> zigzag_txids;636for (auto p{0}; p < 32; ++p) {637const auto txp = make_tx({COutPoint{Txid::FromUint256(GetRandHash()), 0}}, /*num_outputs=*/2);638TryAddToMempool(pool, entry.Fee(CENT).FromTx(txp));639zigzag_txids.push_back(txp->GetHash());640}641for (auto c{0}; c < 31; ++c) {642const auto txc = make_tx({COutPoint{zigzag_txids[c], 1}, COutPoint{zigzag_txids[c+1], 0}}, /*num_outputs=*/1);643TryAddToMempool(pool, entry.Fee(CENT).FromTx(txc));644zigzag_txids.push_back(txc->GetHash());645}646const auto vec_iters_zigzag = pool.GetIterVec(zigzag_txids);647// It doesn't matter which tx we calculate cluster for, everybody is in it.648const std::vector<size_t> indices{0, 22, 52, zigzag_txids.size() - 1};649for (const auto index : indices) {650const auto cluster = pool.GatherClusters({zigzag_txids[index]});651BOOST_CHECK_EQUAL(cluster.size(), zigzag_txids.size());652CTxMemPool::setEntries clusterset{cluster.begin(), cluster.end()};653BOOST_CHECK_EQUAL(cluster.size(), clusterset.size());654for (const auto& iter : vec_iters_zigzag) BOOST_CHECK(clusterset.count(iter));655}656}658BOOST_FIXTURE_TEST_CASE(manual_ctor, TestChain100Setup)659{660CTxMemPool& pool = *Assert(m_node.mempool);661LOCK2(cs_main, pool.cs);662{663// 3 pairs of grandparent + fee-bumping parent, plus 1 low-feerate child.664// 0 fee + high fee665auto grandparent_zero_fee = make_tx({{m_coinbase_txns.at(0)->GetHash(), 0}}, 1);666auto parent_high_feerate = make_tx({{grandparent_zero_fee->GetHash(), 0}}, 1);667// double low fee + med fee668auto grandparent_double_low_feerate = make_tx({{m_coinbase_txns.at(2)->GetHash(), 0}}, 1);669auto parent_med_feerate = make_tx({{grandparent_double_low_feerate->GetHash(), 0}}, 1);670// low fee + double low fee671auto grandparent_low_feerate = make_tx({{m_coinbase_txns.at(1)->GetHash(), 0}}, 1);672auto parent_double_low_feerate = make_tx({{grandparent_low_feerate->GetHash(), 0}}, 1);673// child is below the cpfp package feerates because it is larger than everything else674auto child = make_tx({{parent_high_feerate->GetHash(), 0}, {parent_double_low_feerate->GetHash(), 0}, {parent_med_feerate->GetHash(), 0}}, 1);676// We artificially record each transaction (except the child) with a uniform vsize of 100vB.677const int64_t tx_vsize{100};678const int64_t child_vsize{1000};680std::vector<node::MiniMinerMempoolEntry> miniminer_info;681miniminer_info.emplace_back(grandparent_zero_fee, /*vsize_self=*/tx_vsize,/*vsize_ancestor=*/tx_vsize, /*fee_self=*/0,/*fee_ancestor=*/0);682miniminer_info.emplace_back(parent_high_feerate, tx_vsize, 2*tx_vsize, high_fee, high_fee);683miniminer_info.emplace_back(grandparent_double_low_feerate, tx_vsize, tx_vsize, 2*low_fee, 2*low_fee);684miniminer_info.emplace_back(parent_med_feerate, tx_vsize, 2*tx_vsize, med_fee, 2*low_fee+med_fee);685miniminer_info.emplace_back(grandparent_low_feerate, tx_vsize, tx_vsize, low_fee, low_fee);686miniminer_info.emplace_back(parent_double_low_feerate, tx_vsize, 2*tx_vsize, 2*low_fee, 3*low_fee);687miniminer_info.emplace_back(child, child_vsize, 6*tx_vsize+child_vsize, low_fee, high_fee+med_fee+6*low_fee);688std::map<Txid, std::set<Txid>> descendant_caches;689descendant_caches.emplace(grandparent_zero_fee->GetHash(), std::set<Txid>{grandparent_zero_fee->GetHash(), parent_high_feerate->GetHash(), child->GetHash()});690descendant_caches.emplace(grandparent_low_feerate->GetHash(), std::set<Txid>{grandparent_low_feerate->GetHash(), parent_double_low_feerate->GetHash(), child->GetHash()});691descendant_caches.emplace(grandparent_double_low_feerate->GetHash(), std::set<Txid>{grandparent_double_low_feerate->GetHash(), parent_med_feerate->GetHash(), child->GetHash()});692descendant_caches.emplace(parent_high_feerate->GetHash(), std::set<Txid>{parent_high_feerate->GetHash(), child->GetHash()});693descendant_caches.emplace(parent_med_feerate->GetHash(), std::set<Txid>{parent_med_feerate->GetHash(), child->GetHash()});694descendant_caches.emplace(parent_double_low_feerate->GetHash(), std::set<Txid>{parent_double_low_feerate->GetHash(), child->GetHash()});695descendant_caches.emplace(child->GetHash(), std::set<Txid>{child->GetHash()});697node::MiniMiner miniminer_manual(miniminer_info, descendant_caches);698BOOST_CHECK(miniminer_manual.IsReadyToCalculate());699const auto sequences{miniminer_manual.Linearize()};701// CPFP zero + high702BOOST_CHECK_EQUAL(sequences.at(grandparent_zero_fee->GetHash()), 0);703BOOST_CHECK_EQUAL(sequences.at(parent_high_feerate->GetHash()), 0);705// CPFP double low + med706BOOST_CHECK_EQUAL(sequences.at(grandparent_double_low_feerate->GetHash()), 1);707BOOST_CHECK_EQUAL(sequences.at(parent_med_feerate->GetHash()), 1);709// CPFP low + double low710BOOST_CHECK_EQUAL(sequences.at(grandparent_low_feerate->GetHash()), 2);711BOOST_CHECK_EQUAL(sequences.at(parent_double_low_feerate->GetHash()), 2);713// Child at the end714BOOST_CHECK_EQUAL(sequences.at(child->GetHash()), 3);715}716}718BOOST_AUTO_TEST_SUITE_END()
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.