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.
252// From the topology above, tx7 must be sorted last, so it should253// definitely evicted first if we must trim. tx4 should definitely remain254// in the mempool since it has a higher feerate than its descendants and255// should be in its own chunk.256pool.TrimToSize(pool.DynamicMemoryUsage() - 1);257BOOST_CHECK(pool.exists(tx4.GetHash()));258BOOST_CHECK(!pool.exists(tx7.GetHash()));260// Tx5 and Tx6 may be removed as well because they're in the same chunk as261// tx7, but this behavior need not be guaranteed.263if (!pool.exists(tx5.GetHash()))264TryAddToMempool(pool, entry.Fee(100LL).FromTx(tx5));265if (!pool.exists(tx6.GetHash()))266TryAddToMempool(pool, entry.Fee(110LL).FromTx(tx6));267TryAddToMempool(pool, entry.Fee(900LL).FromTx(tx7));269// If we trim sufficiently, everything but tx4 should be removed.270pool.TrimToSize(usage_with_tx4_only + 1);271BOOST_CHECK(pool.exists(tx4.GetHash()));272BOOST_CHECK(!pool.exists(tx5.GetHash()));273BOOST_CHECK(!pool.exists(tx6.GetHash()));274BOOST_CHECK(!pool.exists(tx7.GetHash()));276TryAddToMempool(pool, entry.Fee(100LL).FromTx(tx5));277TryAddToMempool(pool, entry.Fee(110LL).FromTx(tx6));278TryAddToMempool(pool, entry.Fee(900LL).FromTx(tx7));280std::vector<CTransactionRef> vtx;281FakeNodeClock clock{42s};282constexpr std::chrono::seconds HALFLIFE{CTxMemPool::ROLLING_FEE_HALFLIFE};283clock += HALFLIFE;284BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), maxFeeRateRemoved.GetFeePerK() + DEFAULT_INCREMENTAL_RELAY_FEE);285// ... we should keep the same min fee until we get a block286pool.removeForBlock(vtx);287clock += HALFLIFE;288BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), llround((maxFeeRateRemoved.GetFeePerK() + DEFAULT_INCREMENTAL_RELAY_FEE)/2.0));289// ... then feerate should drop 1/2 each halflife291clock += HALFLIFE / 2;292BOOST_CHECK_EQUAL(pool.GetMinFee(pool.DynamicMemoryUsage() * 5 / 2).GetFeePerK(), llround((maxFeeRateRemoved.GetFeePerK() + DEFAULT_INCREMENTAL_RELAY_FEE)/4.0));293// ... with a 1/2 halflife when mempool is < 1/2 its target size295clock += HALFLIFE / 4;296BOOST_CHECK_EQUAL(pool.GetMinFee(pool.DynamicMemoryUsage() * 9 / 2).GetFeePerK(), llround((maxFeeRateRemoved.GetFeePerK() + DEFAULT_INCREMENTAL_RELAY_FEE)/8.0));297// ... with a 1/4 halflife when mempool is < 1/4 its target size299clock += 5 * HALFLIFE;300BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), DEFAULT_INCREMENTAL_RELAY_FEE);301// ... but feerate should never drop below DEFAULT_INCREMENTAL_RELAY_FEE303clock += HALFLIFE;304BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), 0);305// ... unless it has gone all the way to 0 (after getting past DEFAULT_INCREMENTAL_RELAY_FEE/2)306}308inline CTransactionRef make_tx(std::vector<CAmount>&& output_values, std::vector<CTransactionRef>&& inputs=std::vector<CTransactionRef>(), std::vector<uint32_t>&& input_indices=std::vector<uint32_t>())309{310CMutableTransaction tx = CMutableTransaction();311tx.vin.resize(inputs.size());312tx.vout.resize(output_values.size());313for (size_t i = 0; i < inputs.size(); ++i) {314tx.vin[i].prevout.hash = inputs[i]->GetHash();315tx.vin[i].prevout.n = input_indices.size() > i ? input_indices[i] : 0;316}317for (size_t i = 0; i < output_values.size(); ++i) {318tx.vout[i].scriptPubKey = CScript() << OP_11 << OP_EQUAL;319tx.vout[i].nValue = output_values[i];320}321return MakeTransactionRef(tx);322}325BOOST_AUTO_TEST_CASE(MempoolAncestryTests)326{327size_t ancestors, clustersize;329CTxMemPool& pool = *Assert(m_node.mempool);330LOCK2(cs_main, pool.cs);331TestMemPoolEntryHelper entry;333/* Base transaction */334//335// [tx1]336//337CTransactionRef tx1 = make_tx(/*output_values=*/{10 * COIN});338TryAddToMempool(pool, entry.Fee(10000LL).FromTx(tx1));340// Ancestors / clustersize should be 1 / 1 (itself / itself)341pool.GetTransactionAncestry(tx1->GetHash(), ancestors, clustersize);342BOOST_CHECK_EQUAL(ancestors, 1ULL);343BOOST_CHECK_EQUAL(clustersize, 1ULL);345/* Child transaction */346//347// [tx1].0 <- [tx2]348//349CTransactionRef tx2 = make_tx(/*output_values=*/{495 * CENT, 5 * COIN}, /*inputs=*/{tx1});350TryAddToMempool(pool, entry.Fee(10000LL).FromTx(tx2));352// Ancestors / clustersize should be:353// transaction ancestors clustersize354// ============ =========== ===========355// tx1 1 (tx1) 2 (tx1,2)356// tx2 2 (tx1,2) 2 (tx1,2)357pool.GetTransactionAncestry(tx1->GetHash(), ancestors, clustersize);358BOOST_CHECK_EQUAL(ancestors, 1ULL);359BOOST_CHECK_EQUAL(clustersize, 2ULL);360pool.GetTransactionAncestry(tx2->GetHash(), ancestors, clustersize);361BOOST_CHECK_EQUAL(ancestors, 2ULL);362BOOST_CHECK_EQUAL(clustersize, 2ULL);364/* Grand-child 1 */365//366// [tx1].0 <- [tx2].0 <- [tx3]367//368CTransactionRef tx3 = make_tx(/*output_values=*/{290 * CENT, 200 * CENT}, /*inputs=*/{tx2});369TryAddToMempool(pool, entry.Fee(10000LL).FromTx(tx3));371// Ancestors / clustersize should be:372// transaction ancestors clustersize373// ============ =========== ===========374// tx1 1 (tx1) 3 (tx1,2,3)375// tx2 2 (tx1,2) 3 (tx1,2,3)376// tx3 3 (tx1,2,3) 3 (tx1,2,3)377pool.GetTransactionAncestry(tx1->GetHash(), ancestors, clustersize);378BOOST_CHECK_EQUAL(ancestors, 1ULL);379BOOST_CHECK_EQUAL(clustersize, 3ULL);380pool.GetTransactionAncestry(tx2->GetHash(), ancestors, clustersize);381BOOST_CHECK_EQUAL(ancestors, 2ULL);382BOOST_CHECK_EQUAL(clustersize, 3ULL);383pool.GetTransactionAncestry(tx3->GetHash(), ancestors, clustersize);384BOOST_CHECK_EQUAL(ancestors, 3ULL);385BOOST_CHECK_EQUAL(clustersize, 3ULL);387/* Grand-child 2 */388//389// [tx1].0 <- [tx2].0 <- [tx3]390// |391// \---1 <- [tx4]392//393CTransactionRef tx4 = make_tx(/*output_values=*/{290 * CENT, 250 * CENT}, /*inputs=*/{tx2}, /*input_indices=*/{1});394TryAddToMempool(pool, entry.Fee(10000LL).FromTx(tx4));396// Ancestors / clustersize should be:397// transaction ancestors clustersize398// ============ =========== ===========399// tx1 1 (tx1) 4 (tx1,2,3,4)400// tx2 2 (tx1,2) 4 (tx1,2,3,4)401// tx3 3 (tx1,2,3) 4 (tx1,2,3,4)402// tx4 3 (tx1,2,4) 4 (tx1,2,3,4)403pool.GetTransactionAncestry(tx1->GetHash(), ancestors, clustersize);404BOOST_CHECK_EQUAL(ancestors, 1ULL);405BOOST_CHECK_EQUAL(clustersize, 4ULL);406pool.GetTransactionAncestry(tx2->GetHash(), ancestors, clustersize);407BOOST_CHECK_EQUAL(ancestors, 2ULL);408BOOST_CHECK_EQUAL(clustersize, 4ULL);409pool.GetTransactionAncestry(tx3->GetHash(), ancestors, clustersize);410BOOST_CHECK_EQUAL(ancestors, 3ULL);411BOOST_CHECK_EQUAL(clustersize, 4ULL);412pool.GetTransactionAncestry(tx4->GetHash(), ancestors, clustersize);413BOOST_CHECK_EQUAL(ancestors, 3ULL);414BOOST_CHECK_EQUAL(clustersize, 4ULL);416/* Make an alternate branch that is longer and connect it to tx3 */417//418// [ty1].0 <- [ty2].0 <- [ty3].0 <- [ty4].0 <- [ty5].0419// |420// [tx1].0 <- [tx2].0 <- [tx3].0 <- [ty6] --->--/421// |422// \---1 <- [tx4]423//424CTransactionRef ty1, ty2, ty3, ty4, ty5;425CTransactionRef* ty[5] = {&ty1, &ty2, &ty3, &ty4, &ty5};426CAmount v = 5 * COIN;427for (uint64_t i = 0; i < 5; i++) {428CTransactionRef& tyi = *ty[i];429tyi = make_tx(/*output_values=*/{v}, /*inputs=*/i > 0 ? std::vector<CTransactionRef>{*ty[i - 1]} : std::vector<CTransactionRef>{});430v -= 50 * CENT;431TryAddToMempool(pool, entry.Fee(10000LL).FromTx(tyi));432pool.GetTransactionAncestry(tyi->GetHash(), ancestors, clustersize);433BOOST_CHECK_EQUAL(ancestors, i+1);434BOOST_CHECK_EQUAL(clustersize, i+1);435}436CTransactionRef ty6 = make_tx(/*output_values=*/{5 * COIN}, /*inputs=*/{tx3, ty5});437TryAddToMempool(pool, entry.Fee(10000LL).FromTx(ty6));439// Ancestors / clustersize should be:440// transaction ancestors clustersize441// ============ =================== ===========442// tx1 1 (tx1) 10 (tx1-5, ty1-5)443// tx2 2 (tx1,2) 10444// tx3 3 (tx1,2,3) 10445// tx4 3 (tx1,2,4) 10446// ty1 1 (ty1) 10447// ty2 2 (ty1,2) 10448// ty3 3 (ty1,2,3) 10449// ty4 4 (y1234) 10450// ty5 5 (y12345) 10451// ty6 9 (tx123, ty123456) 10452pool.GetTransactionAncestry(tx1->GetHash(), ancestors, clustersize);453BOOST_CHECK_EQUAL(ancestors, 1ULL);454BOOST_CHECK_EQUAL(clustersize, 10ULL);455pool.GetTransactionAncestry(tx2->GetHash(), ancestors, clustersize);456BOOST_CHECK_EQUAL(ancestors, 2ULL);457BOOST_CHECK_EQUAL(clustersize, 10ULL);458pool.GetTransactionAncestry(tx3->GetHash(), ancestors, clustersize);459BOOST_CHECK_EQUAL(ancestors, 3ULL);460BOOST_CHECK_EQUAL(clustersize, 10ULL);461pool.GetTransactionAncestry(tx4->GetHash(), ancestors, clustersize);462BOOST_CHECK_EQUAL(ancestors, 3ULL);463BOOST_CHECK_EQUAL(clustersize, 10ULL);464pool.GetTransactionAncestry(ty1->GetHash(), ancestors, clustersize);465BOOST_CHECK_EQUAL(ancestors, 1ULL);466BOOST_CHECK_EQUAL(clustersize, 10ULL);467pool.GetTransactionAncestry(ty2->GetHash(), ancestors, clustersize);468BOOST_CHECK_EQUAL(ancestors, 2ULL);469BOOST_CHECK_EQUAL(clustersize, 10ULL);470pool.GetTransactionAncestry(ty3->GetHash(), ancestors, clustersize);471BOOST_CHECK_EQUAL(ancestors, 3ULL);472BOOST_CHECK_EQUAL(clustersize, 10ULL);473pool.GetTransactionAncestry(ty4->GetHash(), ancestors, clustersize);474BOOST_CHECK_EQUAL(ancestors, 4ULL);475BOOST_CHECK_EQUAL(clustersize, 10ULL);476pool.GetTransactionAncestry(ty5->GetHash(), ancestors, clustersize);477BOOST_CHECK_EQUAL(ancestors, 5ULL);478BOOST_CHECK_EQUAL(clustersize, 10ULL);479pool.GetTransactionAncestry(ty6->GetHash(), ancestors, clustersize);480BOOST_CHECK_EQUAL(ancestors, 9ULL);481BOOST_CHECK_EQUAL(clustersize, 10ULL);482}484BOOST_AUTO_TEST_CASE(MempoolAncestryTestsDiamond)485{486size_t ancestors, descendants;488CTxMemPool& pool = *Assert(m_node.mempool);489LOCK2(::cs_main, pool.cs);490TestMemPoolEntryHelper entry;492/* Ancestors represented more than once ("diamond") */493//494// [ta].0 <- [tb].0 -----<------- [td].0495// | |496// \---1 <- [tc].0 --<--/497//498CTransactionRef ta, tb, tc, td;499ta = make_tx(/*output_values=*/{10 * COIN});500tb = make_tx(/*output_values=*/{5 * COIN, 3 * COIN}, /*inputs=*/ {ta});
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.