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.
751{752AssertLockHeld(cs);753auto it{mapTx.project<0>(mapTx.get<index_by_wtxid>().find(wtxid))};754return it != mapTx.end() ? std::make_optional(it) : std::nullopt;755}757CTxMemPool::setEntries CTxMemPool::GetIterSet(const std::set<Txid>& hashes) const758{759CTxMemPool::setEntries ret;760for (const auto& h : hashes) {761const auto mi = GetIter(h);762if (mi) ret.insert(*mi);763}764return ret;765}767std::vector<CTxMemPool::txiter> CTxMemPool::GetIterVec(const std::vector<Txid>& txids) const768{769AssertLockHeld(cs);770std::vector<txiter> ret;771ret.reserve(txids.size());772for (const auto& txid : txids) {773const auto it{GetIter(txid)};774if (!it) return {};775ret.push_back(*it);776}777return ret;778}780bool CTxMemPool::HasNoInputsOf(const CTransaction &tx) const781{782for (unsigned int i = 0; i < tx.vin.size(); i++)783if (exists(tx.vin[i].prevout.hash))784return false;785return true;786}788CCoinsViewMemPool::CCoinsViewMemPool(CCoinsView* baseIn, const CTxMemPool& mempoolIn) : CCoinsViewBacked(baseIn), mempool(mempoolIn) { }790std::optional<Coin> CCoinsViewMemPool::GetCoin(const COutPoint& outpoint) const791{792// Check to see if the inputs are made available by another tx in the package.793// These Coins would not be available in the underlying CoinsView.794if (auto it = m_temp_added.find(outpoint); it != m_temp_added.end()) {795return it->second;796}798// If an entry in the mempool exists, always return that one, as it's guaranteed to never799// conflict with the underlying cache, and it cannot have pruned entries (as it contains full)800// transactions. First checking the underlying cache risks returning a pruned entry instead.801CTransactionRef ptx = mempool.get(outpoint.hash);802if (ptx) {803if (outpoint.n < ptx->vout.size()) {804Coin coin(ptx->vout[outpoint.n], MEMPOOL_HEIGHT, false);805m_non_base_coins.emplace(outpoint);806return coin;807}808return std::nullopt;809}810return base->GetCoin(outpoint);811}813void CCoinsViewMemPool::PackageAddTransaction(const CTransactionRef& tx)814{815for (unsigned int n = 0; n < tx->vout.size(); ++n) {816m_temp_added.emplace(COutPoint(tx->GetHash(), n), Coin(tx->vout[n], MEMPOOL_HEIGHT, false));817m_non_base_coins.emplace(tx->GetHash(), n);818}819}820void CCoinsViewMemPool::Reset()821{822m_temp_added.clear();823m_non_base_coins.clear();824}826size_t CTxMemPool::DynamicMemoryUsage() const {827LOCK(cs);828// Estimate the overhead of mapTx to be 9 pointers (3 pointers per index) + an allocation, as no exact formula for boost::multi_index_contained is implemented.829return memusage::MallocUsage(sizeof(CTxMemPoolEntry) + 9 * sizeof(void*)) * mapTx.size() + memusage::DynamicUsage(mapNextTx) + memusage::DynamicUsage(mapDeltas) + memusage::DynamicUsage(txns_randomized) + m_txgraph->GetMainMemoryUsage() + cachedInnerUsage;830}832void CTxMemPool::RemoveUnbroadcastTx(const Txid& txid, const bool unchecked) {833LOCK(cs);835if (m_unbroadcast_txids.erase(txid))836{837LogDebug(BCLog::MEMPOOL, "Removed %s from set of unbroadcast txns%s", txid.GetHex(), (unchecked ? " before confirmation that txn was sent out" : ""));838}839}841void CTxMemPool::RemoveStaged(setEntries &stage, MemPoolRemovalReason reason) {842AssertLockHeld(cs);843for (txiter it : stage) {844removeUnchecked(it, reason);845}846}848bool CTxMemPool::CheckPolicyLimits(const CTransactionRef& tx)849{850LOCK(cs);851// Use ChangeSet interface to check whether the cluster count852// limits would be violated. Note that the changeset will be destroyed853// when it goes out of scope.854auto changeset = GetChangeSet();855(void) changeset->StageAddition(tx, /*fee=*/0, /*time=*/0, /*entry_height=*/0, /*entry_sequence=*/0, /*spends_coinbase=*/false, /*sigops_cost=*/0, LockPoints{});856return changeset->CheckMemPoolPolicyLimits();857}859int CTxMemPool::Expire(std::chrono::seconds time)860{861AssertLockHeld(cs);862Assume(!m_have_changeset);863indexed_transaction_set::index<entry_time>::type::iterator it = mapTx.get<entry_time>().begin();864setEntries toremove;865while (it != mapTx.get<entry_time>().end() && it->GetTime() < time) {866toremove.insert(mapTx.project<0>(it));867it++;868}869setEntries stage;870for (txiter removeit : toremove) {871CalculateDescendants(removeit, stage);872}873RemoveStaged(stage, MemPoolRemovalReason::EXPIRY);874return stage.size();875}877CFeeRate CTxMemPool::GetMinFee(size_t sizelimit) const {878LOCK(cs);879if (!blockSinceLastRollingFeeBump || rollingMinimumFeeRate == 0)880return CFeeRate(llround(rollingMinimumFeeRate));882int64_t time = GetTime();883if (time > lastRollingFeeUpdate + 10) {884double halflife = ROLLING_FEE_HALFLIFE;885if (DynamicMemoryUsage() < sizelimit / 4)886halflife /= 4;887else if (DynamicMemoryUsage() < sizelimit / 2)888halflife /= 2;890rollingMinimumFeeRate = rollingMinimumFeeRate / pow(2.0, (time - lastRollingFeeUpdate) / halflife);891lastRollingFeeUpdate = time;893if (rollingMinimumFeeRate < (double)m_opts.incremental_relay_feerate.GetFeePerK() / 2) {894rollingMinimumFeeRate = 0;895return CFeeRate(0);896}897}898return std::max(CFeeRate(llround(rollingMinimumFeeRate)), m_opts.incremental_relay_feerate);899}901void CTxMemPool::trackPackageRemoved(const CFeeRate& rate) {902AssertLockHeld(cs);903if (rate.GetFeePerK() > rollingMinimumFeeRate) {904rollingMinimumFeeRate = rate.GetFeePerK();905blockSinceLastRollingFeeBump = false;906}907}909void CTxMemPool::TrimToSize(size_t sizelimit, std::vector<COutPoint>* pvNoSpendsRemaining) {910AssertLockHeld(cs);911Assume(!m_have_changeset);913unsigned nTxnRemoved = 0;914CFeeRate maxFeeRateRemoved(0);916while (!mapTx.empty() && DynamicMemoryUsage() > sizelimit) {917const auto &[worst_chunk, feeperweight] = m_txgraph->GetWorstMainChunk();918FeePerVSize feerate = ToFeePerVSize(feeperweight);919CFeeRate removed{feerate.fee, feerate.size};921// We set the new mempool min fee to the feerate of the removed set, plus the922// "minimum reasonable fee rate" (ie some value under which we consider txn923// to have 0 fee). This way, we don't allow txn to enter mempool with feerate924// equal to txn which were removed with no block in between.925removed += m_opts.incremental_relay_feerate;926trackPackageRemoved(removed);927maxFeeRateRemoved = std::max(maxFeeRateRemoved, removed);929nTxnRemoved += worst_chunk.size();931std::vector<CTransaction> txn;932if (pvNoSpendsRemaining) {933txn.reserve(worst_chunk.size());934for (auto ref : worst_chunk) {935txn.emplace_back(static_cast<const CTxMemPoolEntry&>(*ref).GetTx());936}937}939setEntries stage;940for (auto ref : worst_chunk) {941stage.insert(mapTx.iterator_to(static_cast<const CTxMemPoolEntry&>(*ref)));942}943for (auto e : stage) {944removeUnchecked(e, MemPoolRemovalReason::SIZELIMIT);945}946if (pvNoSpendsRemaining) {947for (const CTransaction& tx : txn) {948for (const CTxIn& txin : tx.vin) {949if (exists(txin.prevout.hash)) continue;950pvNoSpendsRemaining->push_back(txin.prevout);951}952}953}954}956if (maxFeeRateRemoved > CFeeRate(0)) {957LogDebug(BCLog::MEMPOOL, "Removed %u txn, rolling minimum fee bumped to %s\n", nTxnRemoved, maxFeeRateRemoved.ToString());958}959}961std::tuple<size_t, size_t, CAmount> CTxMemPool::CalculateAncestorData(const CTxMemPoolEntry& entry) const962{963auto ancestors = m_txgraph->GetAncestors(entry, TxGraph::Level::MAIN);965size_t ancestor_count = ancestors.size();966size_t ancestor_size = 0;967CAmount ancestor_fees = 0;968for (auto tx: ancestors) {969const CTxMemPoolEntry& anc = static_cast<const CTxMemPoolEntry&>(*tx);970ancestor_size += anc.GetTxSize();971ancestor_fees += anc.GetModifiedFee();972}973return {ancestor_count, ancestor_size, ancestor_fees};974}976std::tuple<size_t, size_t, CAmount> CTxMemPool::CalculateDescendantData(const CTxMemPoolEntry& entry) const977{978auto descendants = m_txgraph->GetDescendants(entry, TxGraph::Level::MAIN);979size_t descendant_count = descendants.size();980size_t descendant_size = 0;981CAmount descendant_fees = 0;983for (auto tx: descendants) {984const CTxMemPoolEntry &desc = static_cast<const CTxMemPoolEntry&>(*tx);985descendant_size += desc.GetTxSize();986descendant_fees += desc.GetModifiedFee();987}988return {descendant_count, descendant_size, descendant_fees};989}991void CTxMemPool::GetTransactionAncestry(const Txid& txid, size_t& ancestors, size_t& cluster_count, size_t* const ancestorsize, CAmount* const ancestorfees) const {992LOCK(cs);993auto it = mapTx.find(txid);994ancestors = cluster_count = 0;995if (it != mapTx.end()) {996auto [ancestor_count, ancestor_size, ancestor_fees] = CalculateAncestorData(*it);997ancestors = ancestor_count;998if (ancestorsize) *ancestorsize = ancestor_size;999if (ancestorfees) *ancestorfees = ancestor_fees;1000cluster_count = m_txgraph->GetCluster(*it, TxGraph::Level::MAIN).size();