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) 2021-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 <chain.h>6#include <coins.h>7#include <consensus/amount.h>8#include <consensus/consensus.h>9#include <consensus/validation.h>10#include <node/miner.h>11#include <node/mining_types.h>12#include <policy/feerate.h>13#include <policy/packages.h>14#include <policy/policy.h>15#include <policy/truc_policy.h>16#include <primitives/block.h>17#include <primitives/transaction.h>18#include <script/script.h>19#include <sync.h>20#include <test/fuzz/FuzzedDataProvider.h>21#include <test/fuzz/fuzz.h>22#include <test/fuzz/util.h>23#include <test/fuzz/util/mempool.h>24#include <test/util/mining.h>25#include <test/util/random.h>26#include <test/util/script.h>27#include <test/util/setup_common.h>28#include <test/util/txmempool.h>29#include <txmempool.h>30#include <util/check.h>31#include <util/string.h>32#include <util/time.h>33#include <util/translation.h>34#include <validation.h>35#include <validationinterface.h>37#include <cstddef>38#include <cstdint>39#include <functional>40#include <iterator>41#include <limits>42#include <map>43#include <memory>44#include <optional>45#include <set>46#include <span>47#include <string>48#include <utility>49#include <vector>50using node::BlockAssembler;51using node::BlockCreateOptions;52using node::NodeContext;53using util::ToString;55namespace {57const TestingSetup* g_setup;58std::vector<COutPoint> g_outpoints_coinbase_init_mature;59std::vector<COutPoint> g_outpoints_coinbase_init_immature;61struct MockedTxPool : public CTxMemPool {62void RollingFeeUpdate() EXCLUSIVE_LOCKS_REQUIRED(!cs)63{64LOCK(cs);65lastRollingFeeUpdate = GetTime();66blockSinceLastRollingFeeBump = true;67}68};70void initialize_tx_pool()71{72static const auto testing_setup = MakeNoLogFileContext<const TestingSetup>();73g_setup = testing_setup.get();74SetMockTime(WITH_LOCK(g_setup->m_node.chainman->GetMutex(), return g_setup->m_node.chainman->ActiveTip()->Time()));76for (int i = 0; i < 2 * COINBASE_MATURITY; ++i) {77COutPoint prevout{MineBlock(g_setup->m_node, {78.coinbase_output_script = P2WSH_OP_TRUE,79})};80// Remember the txids to avoid expensive disk access later on81auto& outpoints = i < COINBASE_MATURITY ?82g_outpoints_coinbase_init_mature :83g_outpoints_coinbase_init_immature;84outpoints.push_back(prevout);85}86g_setup->m_node.validation_signals->SyncWithValidationInterfaceQueue();87}89struct TransactionsDelta final : public CValidationInterface {90std::set<CTransactionRef>& m_removed;91std::set<CTransactionRef>& m_added;93explicit TransactionsDelta(std::set<CTransactionRef>& r, std::set<CTransactionRef>& a)94: m_removed{r}, m_added{a} {}96void TransactionAddedToMempool(const NewMempoolTransactionInfo& tx, uint64_t /* mempool_sequence */) override97{98Assert(m_added.insert(tx.info.m_tx).second);99}101void TransactionRemovedFromMempool(const CTransactionRef& tx, MemPoolRemovalReason reason, uint64_t /* mempool_sequence */) override102{103Assert(m_removed.insert(tx).second);104}105};107void SetMempoolConstraints(ArgsManager& args, FuzzedDataProvider& fuzzed_data_provider)108{109args.ForceSetArg("-limitclustercount",110ToString(fuzzed_data_provider.ConsumeIntegralInRange<unsigned>(1, 64)));111args.ForceSetArg("-limitclustersize",112ToString(fuzzed_data_provider.ConsumeIntegralInRange<unsigned>(1, 250)));113args.ForceSetArg("-maxmempool",114ToString(fuzzed_data_provider.ConsumeIntegralInRange<unsigned>(0, 200)));115args.ForceSetArg("-mempoolexpiry",116ToString(fuzzed_data_provider.ConsumeIntegralInRange<unsigned>(0, 999)));117}119/** Get a list of wtxids to query from the mempool for relay. Make the list heterogeneous with120* wtxids of mempool transactions, non-mempool transactions, and duplicates. */121std::vector<Wtxid> WtxidsToRelay(FuzzedDataProvider& fuzzed_data_provider, const MockedTxPool& tx_pool)122{123LOCK(tx_pool.cs);124std::vector<Wtxid> res;126uint8_t dummy{0};127const auto mempool_entries{tx_pool.entryAll()};128LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 100) {129if (!mempool_entries.empty() && fuzzed_data_provider.ConsumeBool()) {130// Wtxid of an in-mempool transaction131const auto& entry_ref{PickValue(fuzzed_data_provider, mempool_entries).get()};132res.push_back(entry_ref.GetTx().GetWitnessHash());133// Don't remove it from the mempool, so the next pick is possibly a duplicate134} else {135// Wtxid of a not-in-mempool transaction136res.push_back(Wtxid::FromUint256(uint256{dummy}));137// Possibly make the next wtxid of a not-in-mempool transaction, a duplicate138if (fuzzed_data_provider.ConsumeBool()) dummy++;139}140}142return res;143}145void Finish(FuzzedDataProvider& fuzzed_data_provider, MockedTxPool& tx_pool, Chainstate& chainstate)146{147WITH_LOCK(::cs_main, tx_pool.check(chainstate.CoinsTip(), chainstate.m_chain.Height() + 1));148{149BlockCreateOptions options{150.block_min_fee_rate = CFeeRate{ConsumeMoney(fuzzed_data_provider, /*max=*/COIN)},151.block_max_weight = fuzzed_data_provider.ConsumeIntegralInRange<uint64_t>(DEFAULT_BLOCK_RESERVED_WEIGHT, MAX_BLOCK_WEIGHT),152};153auto assembler = BlockAssembler{chainstate, &tx_pool, options};154auto block_template = assembler.CreateNewBlock();155Assert(block_template->block.vtx.size() >= 1);157// Try updating the mempool for this block, as though it were mined.158LOCK2(::cs_main, tx_pool.cs);159tx_pool.removeForBlock(block_template->block.vtx);161// Now try to add those transactions back, as though a reorg happened.162std::vector<Txid> hashes_to_update;163for (const auto& tx : block_template->block.vtx) {164const auto res = AcceptToMemoryPool(chainstate, tx, GetTime(), true, /*test_accept=*/false);165if (res.m_result_type == MempoolAcceptResult::ResultType::VALID) {166hashes_to_update.push_back(tx->GetHash());167} else {168tx_pool.removeRecursive(*tx, MemPoolRemovalReason::REORG);169}170}171tx_pool.UpdateTransactionsFromBlock(hashes_to_update);172}173const auto info_all = tx_pool.infoAll();174if (!info_all.empty()) {175const auto& tx_to_remove = *PickValue(fuzzed_data_provider, info_all).tx;176WITH_LOCK(tx_pool.cs, tx_pool.removeRecursive(tx_to_remove, MemPoolRemovalReason::BLOCK /* dummy */));177assert(tx_pool.size() < info_all.size());178}180// Query a number of mempool entries as if to relay them, and assert some invariants on the result.181auto wtxids_to_relay{WtxidsToRelay(fuzzed_data_provider, tx_pool)};182const auto wtxids_count_before{wtxids_to_relay.size()};183const auto n_to_sort{fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 100)};184const auto sorted_iter{WITH_LOCK(tx_pool.cs, return tx_pool.ExtractBestByMiningScoreWithTopology(wtxids_to_relay, n_to_sort))};185const auto expected_count{std::min(n_to_sort, wtxids_count_before)};186// We removed at least as many transactions from the list as we expected sorted entries.187Assert(wtxids_to_relay.size() <= wtxids_count_before - expected_count);188// When there is enough non-duplicate in-mempool transactions (list of remaining wtxids is189// non-empty), we must have received the expected number of entries.190Assert(sorted_iter.size() == expected_count || wtxids_to_relay.empty());191if (n_to_sort > 0) {192// If we asked for a positive number of entries, we must have removed all wtxids that do193// not correspond to a mempool entry..194const auto is_in_mempool = [&](const auto& wtxid) EXCLUSIVE_LOCKS_REQUIRED(tx_pool.cs) { return tx_pool.GetIter(wtxid).has_value(); };195Assert(WITH_LOCK(tx_pool.cs, return std::ranges::all_of(wtxids_to_relay, is_in_mempool)));196// ..As well as all duplicates.197const auto wtxids_count{wtxids_to_relay.size()};198const std::set<Wtxid> unique_wtxids{std::make_move_iterator(wtxids_to_relay.begin()), std::make_move_iterator(wtxids_to_relay.end())};199Assert(unique_wtxids.size() == wtxids_count);200}202if (fuzzed_data_provider.ConsumeBool()) {203// Try eviction204LOCK2(::cs_main, tx_pool.cs);205tx_pool.TrimToSize(fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0U, tx_pool.DynamicMemoryUsage() * 2));206}207if (fuzzed_data_provider.ConsumeBool()) {208// Try expiry209LOCK2(::cs_main, tx_pool.cs);210tx_pool.Expire(GetMockTime() - std::chrono::seconds(fuzzed_data_provider.ConsumeIntegral<uint32_t>()));211}212WITH_LOCK(::cs_main, tx_pool.check(chainstate.CoinsTip(), chainstate.m_chain.Height() + 1));213g_setup->m_node.validation_signals->SyncWithValidationInterfaceQueue();214}216void MockTime(FuzzedDataProvider& fuzzed_data_provider, const Chainstate& chainstate)217{218const auto time = ConsumeTime(fuzzed_data_provider,219chainstate.m_chain.Tip()->GetMedianTimePast() + 1,220std::numeric_limits<decltype(chainstate.m_chain.Tip()->nTime)>::max());221SetMockTime(time);222}224std::unique_ptr<CTxMemPool> MakeMempool(FuzzedDataProvider& fuzzed_data_provider, const NodeContext& node)225{226// Take the default options for tests...227CTxMemPool::Options mempool_opts{MemPoolOptionsForTest(node)};229// ...override specific options for this specific fuzz suite230mempool_opts.check_ratio = 1;231mempool_opts.require_standard = fuzzed_data_provider.ConsumeBool();233// ...and construct a CTxMemPool from it234bilingual_str error;235auto mempool{std::make_unique<CTxMemPool>(std::move(mempool_opts), error)};236// ... ignore the error since it might be beneficial to fuzz even when the237// mempool size is unreasonably small238Assert(error.empty() || error.original.starts_with("-maxmempool must be at least "));239return mempool;240}242void CheckATMPInvariants(const MempoolAcceptResult& res, bool txid_in_mempool, bool wtxid_in_mempool)243{245switch (res.m_result_type) {246case MempoolAcceptResult::ResultType::VALID:247{248Assert(txid_in_mempool);249Assert(wtxid_in_mempool);250Assert(res.m_state.IsValid());
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.