Inside Bitcoin
Opening the selected file…
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) 2009-2010 Satoshi Nakamoto2// Copyright (c) 2009-present The Bitcoin Core developers3// Distributed under the MIT software license, see the accompanying4// file COPYING or http://www.opensource.org/licenses/mit-license.php.6#include <txmempool.h>8#include <chain.h>9#include <coins.h>10#include <common/system.h>11#include <consensus/consensus.h>12#include <consensus/tx_verify.h>13#include <consensus/validation.h>14#include <policy/policy.h>15#include <policy/settings.h>16#include <random.h>17#include <tinyformat.h>18#include <util/check.h>19#include <util/feefrac.h>20#include <util/log.h>21#include <util/moneystr.h>22#include <util/overflow.h>23#include <util/result.h>24#include <util/time.h>25#include <util/trace.h>26#include <util/translation.h>27#include <validationinterface.h>29#include <algorithm>30#include <cmath>31#include <numeric>32#include <optional>33#include <ranges>34#include <string_view>35#include <utility>37TRACEPOINT_SEMAPHORE(mempool, added);38TRACEPOINT_SEMAPHORE(mempool, removed);40bool TestLockPointValidity(CChain& active_chain, const LockPoints& lp)41{42AssertLockHeld(cs_main);43// If there are relative lock times then the maxInputBlock will be set44// If there are no relative lock times, the LockPoints don't depend on the chain45if (lp.maxInputBlock) {46// Check whether active_chain is an extension of the block at which the LockPoints47// calculation was valid. If not LockPoints are no longer valid48if (!active_chain.Contains(*lp.maxInputBlock)) {49return false;50}51}53// LockPoints still valid54return true;55}57std::vector<CTxMemPoolEntry::CTxMemPoolEntryRef> CTxMemPool::GetChildren(const CTxMemPoolEntry& entry) const58{59std::vector<CTxMemPoolEntry::CTxMemPoolEntryRef> ret;60const auto& hash = entry.GetTx().GetHash();61{62LOCK(cs);63auto iter = mapNextTx.lower_bound(COutPoint(hash, 0));64for (; iter != mapNextTx.end() && iter->first->hash == hash; ++iter) {65ret.emplace_back(*(iter->second));66}67}68std::ranges::sort(ret, CompareIteratorByHash{});69auto removed = std::ranges::unique(ret, [](auto& a, auto& b) noexcept { return &a.get() == &b.get(); });70ret.erase(removed.begin(), removed.end());71return ret;72}74std::vector<CTxMemPoolEntry::CTxMemPoolEntryRef> CTxMemPool::GetParents(const CTxMemPoolEntry& entry) const75{76LOCK(cs);77std::vector<CTxMemPoolEntry::CTxMemPoolEntryRef> ret;78std::set<Txid> inputs;79for (const auto& txin : entry.GetTx().vin) {80inputs.insert(txin.prevout.hash);81}82for (const auto& hash : inputs) {83std::optional<txiter> piter = GetIter(hash);84if (piter) {85ret.emplace_back(**piter);86}87}88return ret;89}91void CTxMemPool::UpdateTransactionsFromBlock(const std::vector<Txid>& vHashesToUpdate)92{93AssertLockHeld(cs);95// Iterate in reverse, so that whenever we are looking at a transaction96// we are sure that all in-mempool descendants have already been processed.97for (const Txid& hash : vHashesToUpdate | std::views::reverse) {98// calculate children from mapNextTx99txiter it = mapTx.find(hash);100if (it == mapTx.end()) {101continue;102}103auto iter = mapNextTx.lower_bound(COutPoint(hash, 0));104{105for (; iter != mapNextTx.end() && iter->first->hash == hash; ++iter) {106txiter childIter = iter->second;107assert(childIter != mapTx.end());108// Add dependencies that are discovered between transactions in the109// block and transactions that were in the mempool to txgraph.110m_txgraph->AddDependency(/*parent=*/*it, /*child=*/*childIter);111}112}113}115auto txs_to_remove = m_txgraph->Trim(); // Enforce cluster size limits.116for (auto txptr : txs_to_remove) {117const CTxMemPoolEntry& entry = *(static_cast<const CTxMemPoolEntry*>(txptr));118removeUnchecked(mapTx.iterator_to(entry), MemPoolRemovalReason::SIZELIMIT);119}120}122bool CTxMemPool::HasDescendants(const Txid& txid) const123{124LOCK(cs);125auto entry = GetEntry(txid);126if (!entry) return false;127return m_txgraph->GetDescendants(*entry, TxGraph::Level::MAIN).size() > 1;128}130CTxMemPool::setEntries CTxMemPool::CalculateMemPoolAncestors(const CTxMemPoolEntry &entry) const131{132auto ancestors = m_txgraph->GetAncestors(entry, TxGraph::Level::MAIN);133setEntries ret;134if (ancestors.size() > 0) {135for (auto ancestor : ancestors) {136if (ancestor != &entry) {137ret.insert(mapTx.iterator_to(static_cast<const CTxMemPoolEntry&>(*ancestor)));138}139}140return ret;141}143// If we didn't get anything back, the transaction is not in the graph.144// Find each parent and call GetAncestors on each.145setEntries staged_parents;146const CTransaction &tx = entry.GetTx();148// Get parents of this transaction that are in the mempool149for (unsigned int i = 0; i < tx.vin.size(); i++) {150std::optional<txiter> piter = GetIter(tx.vin[i].prevout.hash);151if (piter) {152staged_parents.insert(*piter);153}154}156for (const auto& parent : staged_parents) {157auto parent_ancestors = m_txgraph->GetAncestors(*parent, TxGraph::Level::MAIN);158for (auto ancestor : parent_ancestors) {159ret.insert(mapTx.iterator_to(static_cast<const CTxMemPoolEntry&>(*ancestor)));160}161}163return ret;164}166static CTxMemPool::Options&& Flatten(CTxMemPool::Options&& opts, bilingual_str& error)167{168opts.check_ratio = std::clamp<int>(opts.check_ratio, 0, 1'000'000);169int64_t cluster_limit_bytes = opts.limits.cluster_size_vbytes * 40;170if (opts.max_size_bytes < 0 || (opts.max_size_bytes > 0 && opts.max_size_bytes < cluster_limit_bytes)) {171error = strprintf(_("-maxmempool must be at least %d MB"), std::ceil(cluster_limit_bytes / 1'000'000.0));172}173return std::move(opts);174}176CTxMemPool::CTxMemPool(Options opts, bilingual_str& error)177: m_opts{Flatten(std::move(opts), error)}178{179m_txgraph = MakeTxGraph(180/*max_cluster_count=*/m_opts.limits.cluster_count,181/*max_cluster_size=*/m_opts.limits.cluster_size_vbytes * WITNESS_SCALE_FACTOR,182/*acceptable_cost=*/ACCEPTABLE_COST,183/*fallback_order=*/[&](const TxGraph::Ref& a, const TxGraph::Ref& b) noexcept {184const Txid& txid_a = static_cast<const CTxMemPoolEntry&>(a).GetTx().GetHash();185const Txid& txid_b = static_cast<const CTxMemPoolEntry&>(b).GetTx().GetHash();186return txid_a <=> txid_b;187});188}190bool CTxMemPool::isSpent(const COutPoint& outpoint) const191{192LOCK(cs);193return mapNextTx.count(outpoint);194}196unsigned int CTxMemPool::GetTransactionsUpdated() const197{198return nTransactionsUpdated;199}201void CTxMemPool::AddTransactionsUpdated(unsigned int n)202{203nTransactionsUpdated += n;204}206void CTxMemPool::Apply(ChangeSet* changeset)207{208AssertLockHeld(cs);209m_txgraph->CommitStaging();211RemoveStaged(changeset->m_to_remove, MemPoolRemovalReason::REPLACED);213for (size_t i=0; i<changeset->m_entry_vec.size(); ++i) {214auto tx_entry = changeset->m_entry_vec[i];215// First splice this entry into mapTx.216auto node_handle = changeset->m_to_add.extract(tx_entry);217auto result = mapTx.insert(std::move(node_handle));219Assume(result.inserted);220txiter it = result.position;222addNewTransaction(it);223}224if (!m_txgraph->DoWork(/*max_cost=*/POST_CHANGE_COST)) {225LogDebug(BCLog::MEMPOOL, "Mempool in non-optimal ordering after addition(s).");226}227}229void CTxMemPool::addNewTransaction(CTxMemPool::txiter newit)230{231const CTxMemPoolEntry& entry = *newit;233// Update cachedInnerUsage to include contained transaction's usage.234// (When we update the entry for in-mempool parents, memory usage will be235// further updated.)236cachedInnerUsage += entry.DynamicMemoryUsage();238const CTransaction& tx = newit->GetTx();239for (unsigned int i = 0; i < tx.vin.size(); i++) {240mapNextTx.insert(std::make_pair(&tx.vin[i].prevout, newit));241}242// Don't bother worrying about child transactions of this one.243// Normal case of a new transaction arriving is that there can't be any244// children, because such children would be orphans.245// An exception to that is if a transaction enters that used to be in a block.246// In that case, our disconnect block logic will call UpdateTransactionsFromBlock247// to clean up the mess we're leaving here.249nTransactionsUpdated++;250totalTxSize += entry.GetTxSize();