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.
1// Copyright (c) 2011-present The Bitcoin Core developers2// Distributed under the MIT software license, see the accompanying3// file COPYING or http://www.opensource.org/licenses/mit-license.php.5#include <bench/bench.h>6#include <consensus/amount.h>7#include <primitives/transaction.h>8#include <random.h>9#include <script/script.h>10#include <sync.h>11#include <test/util/setup_common.h>12#include <test/util/txmempool.h>13#include <txmempool.h>14#include <validation.h>16#include <cstddef>17#include <cstdint>18#include <memory>19#include <optional>20#include <vector>22class CCoinsViewCache;24static void AddTx(const CTransactionRef& tx, CTxMemPool& pool, FastRandomContext& det_rand) EXCLUSIVE_LOCKS_REQUIRED(cs_main, pool.cs)25{26int64_t nTime = 0;27unsigned int nHeight = 1;28uint64_t sequence = 0;29bool spendsCoinbase = false;30unsigned int sigOpCost = 4;31LockPoints lp;32TryAddToMempool(pool, CTxMemPoolEntry(tx, det_rand.randrange(10000)+1000, nTime, nHeight, sequence, spendsCoinbase, sigOpCost, lp));33}35struct Available {36CTransactionRef ref;37size_t vin_left{0};38size_t tx_count;39Available(CTransactionRef& ref, size_t tx_count) : ref(ref), tx_count(tx_count){}40};42// Create a cluster of transactions, randomly.43static std::vector<CTransactionRef> CreateCoinCluster(FastRandomContext& det_rand, int childTxs, int min_ancestors)44{45std::vector<Available> available_coins;46std::vector<CTransactionRef> ordered_coins;47// Create some base transactions48size_t tx_counter = 1;49for (auto x = 0; x < 10; ++x) {50CMutableTransaction tx = CMutableTransaction();51tx.vin.resize(1);52tx.vin[0].prevout = COutPoint(Txid::FromUint256(GetRandHash()), 1);53tx.vin[0].scriptSig = CScript() << CScriptNum(tx_counter);54tx.vin[0].scriptWitness.stack.push_back(CScriptNum(x).getvch());55tx.vout.resize(det_rand.randrange(10)+2);56for (auto& out : tx.vout) {57out.scriptPubKey = CScript() << CScriptNum(tx_counter) << OP_EQUAL;58out.nValue = 10 * COIN;59}60ordered_coins.emplace_back(MakeTransactionRef(tx));61available_coins.emplace_back(ordered_coins.back(), tx_counter++);62}63for (auto x = 0; x < childTxs && !available_coins.empty(); ++x) {64CMutableTransaction tx = CMutableTransaction();65size_t n_ancestors = det_rand.randrange(10)+1;66for (size_t ancestor = 0; ancestor < n_ancestors && !available_coins.empty(); ++ancestor){67size_t idx = det_rand.randrange(available_coins.size());68Available coin = available_coins[idx];69Txid hash = coin.ref->GetHash();70// biased towards taking min_ancestors parents, but maybe more71size_t n_to_take = det_rand.randrange(2) == 0 ?72min_ancestors :73min_ancestors + det_rand.randrange(coin.ref->vout.size() - coin.vin_left);74for (size_t i = 0; i < n_to_take; ++i) {75tx.vin.emplace_back();76tx.vin.back().prevout = COutPoint(hash, coin.vin_left++);77tx.vin.back().scriptSig = CScript() << coin.tx_count;78tx.vin.back().scriptWitness.stack.push_back(CScriptNum(coin.tx_count).getvch());79}80if (coin.vin_left == coin.ref->vin.size()) {81coin = available_coins.back();82available_coins.pop_back();83}84tx.vout.resize(det_rand.randrange(10)+2);85for (auto& out : tx.vout) {86out.scriptPubKey = CScript() << CScriptNum(tx_counter) << OP_EQUAL;87out.nValue = 10 * COIN;88}89}90ordered_coins.emplace_back(MakeTransactionRef(tx));91available_coins.emplace_back(ordered_coins.back(), tx_counter++);92}93return ordered_coins;94}96static void MemPoolAddTransactions(benchmark::Bench& bench)97{98FastRandomContext det_rand{true};99int childTxs = 50;100if (bench.complexityN() > 1) {101childTxs = static_cast<int>(bench.complexityN());102}103const auto testing_setup = MakeNoLogFileContext<const TestingSetup>(ChainType::MAIN);104CTxMemPool& pool = *testing_setup.get()->m_node.mempool;106std::vector<CTransactionRef> transactions;107// Create 1000 clusters of 100 transactions each108for (int i=0; i<100; i++) {109auto new_txs = CreateCoinCluster(det_rand, childTxs, /*min_ancestors=*/ 1);110transactions.insert(transactions.end(), new_txs.begin(), new_txs.end());111}113LOCK2(cs_main, pool.cs);115bench.run([&]() NO_THREAD_SAFETY_ANALYSIS {116for (auto& tx : transactions) {117AddTx(tx, pool, det_rand);118}119pool.TrimToSize(0, nullptr);120});121}123static void ComplexMemPool(benchmark::Bench& bench)124{125FastRandomContext det_rand{true};126int childTxs = 50;127if (bench.complexityN() > 1) {128childTxs = static_cast<int>(bench.complexityN());129}130const auto testing_setup = MakeNoLogFileContext<const TestingSetup>(ChainType::MAIN);131CTxMemPool& pool = *testing_setup.get()->m_node.mempool;133std::vector<CTransactionRef> tx_remove_for_block;134std::vector<Txid> hashes_remove_for_block;136LOCK2(cs_main, pool.cs);138for (int i=0; i<1000; i++) {139std::vector<CTransactionRef> transactions = CreateCoinCluster(det_rand, childTxs, /*min_ancestors=*/1);141// Add all transactions to the mempool.142// Also store the first 10 transactions from each cluster as the143// transactions we'll "mine" in the benchmark.144int tx_count = 0;145for (auto& tx : transactions) {146if (tx_count < 10) {147tx_remove_for_block.push_back(tx);148++tx_count;149hashes_remove_for_block.emplace_back(tx->GetHash());150}151AddTx(tx, pool, det_rand);152}153}155// Since the benchmark will be run repeatedly, we have to leave the mempool156// in the same state at the end of the function, so we benchmark both157// mining a block and reorging the block's contents back into the mempool.158bench.run([&]() NO_THREAD_SAFETY_ANALYSIS {159pool.removeForBlock(tx_remove_for_block);160for (auto& tx: tx_remove_for_block) {161AddTx(tx, pool, det_rand);162}163pool.UpdateTransactionsFromBlock(hashes_remove_for_block);164});165}167static void MemPoolAncestorsDescendants(benchmark::Bench& bench)168{169FastRandomContext det_rand{true};170int childTxs = 50;171if (bench.complexityN() > 1) {172childTxs = static_cast<int>(bench.complexityN());173}174const auto testing_setup = MakeNoLogFileContext<const TestingSetup>(ChainType::MAIN);175CTxMemPool& pool = *testing_setup.get()->m_node.mempool;177LOCK2(cs_main, pool.cs);179std::vector<CTransactionRef> transactions = CreateCoinCluster(det_rand, childTxs, /*min_ancestors=*/1);180for (auto& tx : transactions) {181AddTx(tx, pool, det_rand);182}184CTxMemPool::txiter first_tx = *pool.GetIter(transactions[0]->GetHash());185CTxMemPool::txiter last_tx = *pool.GetIter(transactions.back()->GetHash());187bench.run([&]() NO_THREAD_SAFETY_ANALYSIS {188CTxMemPool::setEntries dummy;189ankerl::nanobench::doNotOptimizeAway(dummy);190pool.CalculateDescendants({first_tx}, dummy);191ankerl::nanobench::doNotOptimizeAway(pool.CalculateMemPoolAncestors(*last_tx));192});193}196static void MempoolCheck(benchmark::Bench& bench)197{198FastRandomContext det_rand{true};199auto testing_setup = MakeNoLogFileContext<TestChain100Setup>(ChainType::REGTEST, {.extra_args = {"-checkmempool=1"}});200CTxMemPool& pool = *testing_setup.get()->m_node.mempool;201LOCK2(cs_main, pool.cs);202testing_setup->PopulateMempool(det_rand, 400, true);203const CCoinsViewCache& coins_tip = testing_setup.get()->m_node.chainman->ActiveChainstate().CoinsTip();205bench.run([&]() NO_THREAD_SAFETY_ANALYSIS {206// Bump up the spendheight so we don't hit premature coinbase spend errors.207pool.check(coins_tip, /*spendheight=*/300);208});209}211BENCHMARK(MemPoolAncestorsDescendants);212BENCHMARK(MemPoolAddTransactions);213BENCHMARK(ComplexMemPool);214BENCHMARK(MempoolCheck);
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.