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.
251m_total_fee += entry.GetFee();253txns_randomized.emplace_back(tx.GetWitnessHash(), newit);254newit->idx_randomized = txns_randomized.size() - 1;256TRACEPOINT(mempool, added,257entry.GetTx().GetHash().data(),258entry.GetTxSize(),259entry.GetFee()260);261}263void CTxMemPool::removeUnchecked(txiter it, MemPoolRemovalReason reason)264{265// We increment mempool sequence value no matter removal reason266// even if not directly reported below.267uint64_t mempool_sequence = GetAndIncrementSequence();269if (reason != MemPoolRemovalReason::BLOCK && m_opts.signals) {270// Notify clients that a transaction has been removed from the mempool271// for any reason except being included in a block. Clients interested272// in transactions included in blocks can subscribe to the BlockConnected273// notification.274m_opts.signals->TransactionRemovedFromMempool(it->GetSharedTx(), reason, mempool_sequence);275}276TRACEPOINT(mempool, removed,277it->GetTx().GetHash().data(),278RemovalReasonToString(reason).c_str(),279it->GetTxSize(),280it->GetFee(),281std::chrono::duration_cast<std::chrono::duration<std::uint64_t>>(it->GetTime()).count()282);284for (const CTxIn& txin : it->GetTx().vin)285mapNextTx.erase(txin.prevout);287RemoveUnbroadcastTx(it->GetTx().GetHash(), true /* add logging because unchecked */);289if (txns_randomized.size() > 1) {290// Remove entry from txns_randomized by replacing it with the back and deleting the back.291txns_randomized[it->idx_randomized] = std::move(txns_randomized.back());292txns_randomized[it->idx_randomized].second->idx_randomized = it->idx_randomized;293txns_randomized.pop_back();294if (txns_randomized.size() * 2 < txns_randomized.capacity()) {295txns_randomized.shrink_to_fit();296}297} else {298txns_randomized.clear();299}301totalTxSize -= it->GetTxSize();302m_total_fee -= it->GetFee();303cachedInnerUsage -= it->DynamicMemoryUsage();304mapTx.erase(it);305nTransactionsUpdated++;306}308// Calculates descendants of given entry and adds to setDescendants.309void CTxMemPool::CalculateDescendants(txiter entryit, setEntries& setDescendants) const310{311(void)CalculateDescendants(*entryit, setDescendants);312return;313}315CTxMemPool::txiter CTxMemPool::CalculateDescendants(const CTxMemPoolEntry& entry, setEntries& setDescendants) const316{317for (auto tx : m_txgraph->GetDescendants(entry, TxGraph::Level::MAIN)) {318setDescendants.insert(mapTx.iterator_to(static_cast<const CTxMemPoolEntry&>(*tx)));319}320return mapTx.iterator_to(entry);321}323void CTxMemPool::removeRecursive(CTxMemPool::txiter to_remove, MemPoolRemovalReason reason)324{325AssertLockHeld(cs);326Assume(!m_have_changeset);327auto descendants = m_txgraph->GetDescendants(*to_remove, TxGraph::Level::MAIN);328for (auto tx: descendants) {329removeUnchecked(mapTx.iterator_to(static_cast<const CTxMemPoolEntry&>(*tx)), reason);330}331}333void CTxMemPool::removeRecursive(const CTransaction &origTx, MemPoolRemovalReason reason)334{335// Remove transaction from memory pool336AssertLockHeld(cs);337Assume(!m_have_changeset);338txiter origit = mapTx.find(origTx.GetHash());339if (origit != mapTx.end()) {340removeRecursive(origit, reason);341} else {342// When recursively removing but origTx isn't in the mempool343// be sure to remove any descendants that are in the pool. This can344// happen during chain re-orgs if origTx isn't re-accepted into345// the mempool for any reason.346auto iter = mapNextTx.lower_bound(COutPoint(origTx.GetHash(), 0));347std::vector<const TxGraph::Ref*> to_remove;348while (iter != mapNextTx.end() && iter->first->hash == origTx.GetHash()) {349to_remove.emplace_back(&*(iter->second));350++iter;351}352auto all_removes = m_txgraph->GetDescendantsUnion(to_remove, TxGraph::Level::MAIN);353for (auto ref : all_removes) {354auto tx = mapTx.iterator_to(static_cast<const CTxMemPoolEntry&>(*ref));355removeUnchecked(tx, reason);356}357}358}360void CTxMemPool::removeForReorg(CChain& chain, std::function<bool(txiter)> check_final_and_mature)361{362// Remove transactions spending a coinbase which are now immature and no-longer-final transactions363AssertLockHeld(cs);364AssertLockHeld(::cs_main);365Assume(!m_have_changeset);367std::vector<const TxGraph::Ref*> to_remove;368for (txiter it = mapTx.begin(); it != mapTx.end(); it++) {369if (check_final_and_mature(it)) {370to_remove.emplace_back(&*it);371}372}374auto all_to_remove = m_txgraph->GetDescendantsUnion(to_remove, TxGraph::Level::MAIN);376for (auto ref : all_to_remove) {377auto it = mapTx.iterator_to(static_cast<const CTxMemPoolEntry&>(*ref));378removeUnchecked(it, MemPoolRemovalReason::REORG);379}380for (indexed_transaction_set::const_iterator it = mapTx.begin(); it != mapTx.end(); it++) {381assert(TestLockPointValidity(chain, it->GetLockPoints()));382}383if (!m_txgraph->DoWork(/*max_cost=*/POST_CHANGE_COST)) {384LogDebug(BCLog::MEMPOOL, "Mempool in non-optimal ordering after reorg.");385}386}388void CTxMemPool::removeConflicts(const CTransaction &tx)389{390// Remove transactions which depend on inputs of tx, recursively391AssertLockHeld(cs);392for (const CTxIn &txin : tx.vin) {393auto it = mapNextTx.find(txin.prevout);394if (it != mapNextTx.end()) {395const CTransaction &txConflict = it->second->GetTx();396if (Assume(txConflict.GetHash() != tx.GetHash()))397{398ClearPrioritisation(txConflict.GetHash());399removeRecursive(it->second, MemPoolRemovalReason::CONFLICT);400}401}402}403}405std::vector<RemovedMempoolTransactionInfo> CTxMemPool::removeForBlock(const std::vector<CTransactionRef>& vtx)406{407// Remove confirmed txs and conflicts when a new block is connected, updating the fee logic408AssertLockHeld(cs);409Assume(!m_have_changeset);410std::vector<RemovedMempoolTransactionInfo> txs_removed_for_block;411if (mapTx.size() || mapNextTx.size() || mapDeltas.size()) {412txs_removed_for_block.reserve(vtx.size());413for (const auto& tx : vtx) {414txiter it = mapTx.find(tx->GetHash());415if (it != mapTx.end()) {416txs_removed_for_block.emplace_back(*it);417removeUnchecked(it, MemPoolRemovalReason::BLOCK);418}419removeConflicts(*tx);420ClearPrioritisation(tx->GetHash());421}422}423lastRollingFeeUpdate = GetTime();424blockSinceLastRollingFeeBump = true;425if (!m_txgraph->DoWork(/*max_cost=*/POST_CHANGE_COST)) {426LogDebug(BCLog::MEMPOOL, "Mempool in non-optimal ordering after block.");427}428return txs_removed_for_block;429}431void CTxMemPool::check(const CCoinsViewCache& active_coins_tip, int64_t spendheight) const432{433if (m_opts.check_ratio == 0) return;435if (FastRandomContext().randrange(m_opts.check_ratio) >= 1) return;437AssertLockHeld(::cs_main);438LOCK(cs);439LogDebug(BCLog::MEMPOOL, "Checking mempool with %u transactions and %u inputs\n", (unsigned int)mapTx.size(), (unsigned int)mapNextTx.size());441uint64_t checkTotal = 0;442CAmount check_total_fee{0};443CAmount check_total_modified_fee{0};444int64_t check_total_adjusted_weight{0};445uint64_t innerUsage = 0;447assert(!m_txgraph->IsOversized(TxGraph::Level::MAIN));448m_txgraph->SanityCheck();450CCoinsViewCache mempoolDuplicate(const_cast<CCoinsViewCache*>(&active_coins_tip));452const auto score_with_topo{GetSortedScoreWithTopology()};454// Number of chunks is bounded by number of transactions.455const auto diagram{GetFeerateDiagram()};456assert(diagram.size() <= score_with_topo.size() + 1);457assert(diagram.size() >= 1);459std::optional<txiter> last_iter = std::nullopt;460auto diagram_iter = diagram.cbegin();462for (const auto& it : score_with_topo) {463// GetSortedScoreWithTopology() contains the same chunks as the feerate464// diagram. We do not know where the chunk boundaries are, but we can465// check that there are points at which they match the cumulative fee466// and weight.467// The feerate diagram should never get behind the current transaction468// size totals.469assert(diagram_iter->size >= check_total_adjusted_weight);470if (diagram_iter->fee == check_total_modified_fee &&471diagram_iter->size == check_total_adjusted_weight) {472++diagram_iter;473}474checkTotal += it->GetTxSize();475check_total_adjusted_weight += it->GetAdjustedWeight();476check_total_fee += it->GetFee();477check_total_modified_fee += it->GetModifiedFee();478innerUsage += it->DynamicMemoryUsage();479const CTransaction& tx = it->GetTx();481if (last_iter) {482assert(m_txgraph->CompareMainOrder(**last_iter, *it) < 0);483}484last_iter = it;486std::set<CTxMemPoolEntry::CTxMemPoolEntryRef, CompareIteratorByHash> setParentCheck;487std::set<CTxMemPoolEntry::CTxMemPoolEntryRef, CompareIteratorByHash> setParentsStored;488for (const CTxIn &txin : tx.vin) {489// Check that every mempool transaction's inputs refer to available coins, or other mempool tx's.490indexed_transaction_set::const_iterator it2 = mapTx.find(txin.prevout.hash);491if (it2 != mapTx.end()) {492const CTransaction& tx2 = it2->GetTx();493assert(tx2.vout.size() > txin.prevout.n && !tx2.vout[txin.prevout.n].IsNull());494setParentCheck.insert(*it2);495}496// We are iterating through the mempool entries sorted497// topologically and by mining score. All parents must have been498// checked before their children and their coins added to the499// mempoolDuplicate coins cache.500assert(mempoolDuplicate.HaveCoin(txin.prevout));