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.
501unsigned int num_erased_this_round{0};502unsigned int starting_num_ann{it_worst_peer->second.m_count_announcements};503while (NeedsTrim()) {504if (!Assume(it_ann != m_orphans.get<ByPeer>().end())) break;505if (!Assume(it_ann->m_announcer == worst_peer)) break;507Erase<ByPeer>(it_ann++);508num_erased += 1;509num_erased_this_round += 1;511// If we erased the last orphan from this peer, it_worst_peer will be invalidated.512it_worst_peer = m_peer_orphanage_info.find(worst_peer);513if (it_worst_peer == m_peer_orphanage_info.end() ||514ByRatioNegSize{it_worst_peer->second.GetDosScore(max_lat, max_mem)} <= ByRatioNegSize{dos_threshold}) break;515}516LogDebug(BCLog::TXPACKAGES, "peer=%d orphanage overflow, removed %u of %u announcements\n", worst_peer, num_erased_this_round, starting_num_ann);518if (!NeedsTrim()) break;520// Unless this peer is empty, put it back in the heap so we continue to consider evicting its orphans.521// We may select this peer for evictions again if there are multiple DoSy peers.522if (it_worst_peer != m_peer_orphanage_info.end() && it_worst_peer->second.m_count_announcements > 0) {523heap_peer_dos.emplace_back(worst_peer, it_worst_peer->second.GetDosScore(max_lat, max_mem));524std::push_heap(heap_peer_dos.begin(), heap_peer_dos.end(), compare_score);525}526} while (true);528const auto remaining_unique_orphans{CountUniqueOrphans()};529LogDebug(BCLog::TXPACKAGES, "orphanage overflow, removed %u tx (%u announcements)\n", original_unique_txns - remaining_unique_orphans, num_erased);530}532std::vector<std::pair<Wtxid, NodeId>> TxOrphanageImpl::AddChildrenToWorkSet(const CTransaction& tx, FastRandomContext& rng)533{534if (m_orphans.empty()) return {};536std::vector<std::pair<Wtxid, NodeId>> ret;537auto& index_by_wtxid = m_orphans.get<ByWtxid>();538for (unsigned int i = 0; i < tx.vout.size(); i++) {539const auto it_by_prev = m_outpoint_to_orphan_wtxids.find(COutPoint(tx.GetHash(), i));540if (it_by_prev != m_outpoint_to_orphan_wtxids.end()) {541for (const auto& wtxid : it_by_prev->second) {542// If a reconsiderable announcement for this wtxid already exists, skip it.543if (m_reconsiderable_wtxids.contains(wtxid)) continue;545// Belt and suspenders, each entry in m_outpoint_to_orphan_wtxids should always have at least 1 announcement.546auto it = index_by_wtxid.lower_bound(ByWtxidView{wtxid, MIN_PEER});547if (!Assume(it != index_by_wtxid.end() && it->m_tx->GetWitnessHash() == wtxid)) continue;549// Select a random peer to assign orphan processing, reducing wasted work if the orphan is still missing550// inputs. However, we don't want to create an issue in which the assigned peer can purposefully stop us551// from processing the orphan by disconnecting.552auto it_end = index_by_wtxid.upper_bound(ByWtxidView{wtxid, MAX_PEER});553const auto num_announcers{std::distance(it, it_end)};554if (!Assume(num_announcers > 0)) continue;555std::advance(it, rng.randrange(num_announcers));557if (!Assume(it->m_tx->GetWitnessHash() == wtxid)) break;559// Mark this orphan as ready to be reconsidered.560static constexpr auto mark_reconsidered_modifier = [](auto& ann) { ann.m_reconsider = true; };561Assume(!it->m_reconsider);562index_by_wtxid.modify(it, mark_reconsidered_modifier);563ret.emplace_back(wtxid, it->m_announcer);564m_reconsiderable_wtxids.insert(wtxid);566LogDebug(BCLog::TXPACKAGES, "added %s (wtxid=%s) to peer %d workset\n",567it->m_tx->GetHash().ToString(), it->m_tx->GetWitnessHash().ToString(), it->m_announcer);568}569}570}571return ret;572}574bool TxOrphanageImpl::HaveTx(const Wtxid& wtxid) const575{576auto it_lower = m_orphans.get<ByWtxid>().lower_bound(ByWtxidView{wtxid, MIN_PEER});577return it_lower != m_orphans.get<ByWtxid>().end() && it_lower->m_tx->GetWitnessHash() == wtxid;578}580CTransactionRef TxOrphanageImpl::GetTx(const Wtxid& wtxid) const581{582auto it_lower = m_orphans.get<ByWtxid>().lower_bound(ByWtxidView{wtxid, MIN_PEER});583if (it_lower != m_orphans.get<ByWtxid>().end() && it_lower->m_tx->GetWitnessHash() == wtxid) return it_lower->m_tx;584return nullptr;585}587bool TxOrphanageImpl::HaveTxFromPeer(const Wtxid& wtxid, NodeId peer) const588{589return m_orphans.get<ByWtxid>().count(ByWtxidView{wtxid, peer}) > 0;590}592/** If there is a tx that can be reconsidered, return it and set it back to593* non-reconsiderable. Otherwise, return a nullptr. */594CTransactionRef TxOrphanageImpl::GetTxToReconsider(NodeId peer)595{596auto it = m_orphans.get<ByPeer>().lower_bound(ByPeerView{peer, true, 0});597if (it != m_orphans.get<ByPeer>().end() && it->m_announcer == peer && it->m_reconsider) {598// Flip m_reconsider. Even if this transaction stays in orphanage, it shouldn't be599// reconsidered again until there is a new reason to do so.600static constexpr auto mark_reconsidered_modifier = [](auto& ann) { ann.m_reconsider = false; };601m_orphans.get<ByPeer>().modify(it, mark_reconsidered_modifier);602// As there is exactly one m_reconsider announcement per reconsiderable wtxids, flipping603// the m_reconsider flag means the wtxid is no longer reconsiderable.604m_reconsiderable_wtxids.erase(it->m_tx->GetWitnessHash());605return it->m_tx;606}607return nullptr;608}610/** Return whether there is a tx that can be reconsidered. */611bool TxOrphanageImpl::HaveTxToReconsider(NodeId peer)612{613auto it = m_orphans.get<ByPeer>().lower_bound(ByPeerView{peer, true, 0});614return it != m_orphans.get<ByPeer>().end() && it->m_announcer == peer && it->m_reconsider;615}617void TxOrphanageImpl::EraseForBlock(const CBlock& block)618{619if (m_orphans.empty()) return;621std::set<Wtxid> wtxids_to_erase;622for (const CTransactionRef& ptx : block.vtx) {623const CTransaction& block_tx = *ptx;625// Which orphan pool entries must we evict?626for (const auto& input : block_tx.vin) {627auto it_prev = m_outpoint_to_orphan_wtxids.find(input.prevout);628if (it_prev != m_outpoint_to_orphan_wtxids.end()) {629// Copy all wtxids to wtxids_to_erase.630std::copy(it_prev->second.cbegin(), it_prev->second.cend(), std::inserter(wtxids_to_erase, wtxids_to_erase.end()));631}632}633}635unsigned int num_erased{0};636for (const auto& wtxid : wtxids_to_erase) {637// Don't use EraseTx here because it calls LimitOrphans and announcements deleted in that call are not reflected638// in its return result. Waiting until the end to do LimitOrphans helps save repeated computation and allows us639// to check that num_erased is what we expect.640num_erased += EraseTxInternal(wtxid) ? 1 : 0;641}643if (num_erased != 0) {644LogDebug(BCLog::TXPACKAGES, "Erased %d orphan transaction(s) included or conflicted by block\n", num_erased);645}646Assume(wtxids_to_erase.size() == num_erased);648// Deletions can cause the orphanage's MaxGlobalUsage to decrease, so we may need to trim here.649LimitOrphans();650}652std::vector<CTransactionRef> TxOrphanageImpl::GetChildrenFromSamePeer(const CTransactionRef& parent, NodeId peer) const653{654std::vector<CTransactionRef> children_found;655const auto& parent_txid{parent->GetHash()};657// Iterate through all orphans from this peer, in reverse order, so that more recent658// transactions are added first. Doing so helps avoid work when one of the orphans replaced659// an earlier one. Since we require the NodeId to match, one peer's announcement order does660// not bias how we process other peer's orphans.661auto& index_by_peer = m_orphans.get<ByPeer>();662auto it_upper = index_by_peer.upper_bound(ByPeerView{peer, true, std::numeric_limits<uint64_t>::max()});663auto it_lower = index_by_peer.lower_bound(ByPeerView{peer, false, 0});665while (it_upper != it_lower) {666--it_upper;667if (!Assume(it_upper->m_announcer == peer)) break;668// Check if this tx spends from parent.669for (const auto& input : it_upper->m_tx->vin) {670if (input.prevout.hash == parent_txid) {671children_found.emplace_back(it_upper->m_tx);672break;673}674}675}676return children_found;677}679std::vector<TxOrphanage::OrphanInfo> TxOrphanageImpl::GetOrphanTransactions() const680{681std::vector<TxOrphanage::OrphanInfo> result;682result.reserve(m_unique_orphans);684auto& index_by_wtxid = m_orphans.get<ByWtxid>();685auto it = index_by_wtxid.begin();686std::set<NodeId> this_orphan_announcers;687while (it != index_by_wtxid.end()) {688this_orphan_announcers.insert(it->m_announcer);689// If this is the last entry, or the next entry has a different wtxid, build a OrphanInfo.690if (std::next(it) == index_by_wtxid.end() || std::next(it)->m_tx->GetWitnessHash() != it->m_tx->GetWitnessHash()) {691result.emplace_back(it->m_tx, std::move(this_orphan_announcers));692this_orphan_announcers.clear();693}695++it;696}697Assume(m_unique_orphans == result.size());699return result;700}702void TxOrphanageImpl::SanityCheck() const703{704std::unordered_map<NodeId, PeerDoSInfo> reconstructed_peer_info;705std::map<Wtxid, std::pair<TxOrphanage::Usage, TxOrphanage::Count>> unique_wtxids_to_scores;706std::set<COutPoint> all_outpoints;707std::set<Wtxid> reconstructed_reconsiderable_wtxids;709for (auto it = m_orphans.begin(); it != m_orphans.end(); ++it) {710for (const auto& input : it->m_tx->vin) {711all_outpoints.insert(input.prevout);712}713unique_wtxids_to_scores.emplace(it->m_tx->GetWitnessHash(), std::make_pair(it->GetMemUsage(), it->GetLatencyScore() - 1));715auto& peer_info = reconstructed_peer_info[it->m_announcer];716peer_info.m_total_usage += it->GetMemUsage();717peer_info.m_count_announcements += 1;718peer_info.m_total_latency_score += it->GetLatencyScore();720if (it->m_reconsider) {721auto [_, added] = reconstructed_reconsiderable_wtxids.insert(it->m_tx->GetWitnessHash());722// Check that there is only ever 1 announcement per wtxid with m_reconsider set.723assert(added);724}725}726assert(reconstructed_peer_info.size() == m_peer_orphanage_info.size());728// Recalculated per-peer stats are identical to m_peer_orphanage_info729assert(reconstructed_peer_info == m_peer_orphanage_info);731// Recalculated set of reconsiderable wtxids must match.732assert(m_reconsiderable_wtxids == reconstructed_reconsiderable_wtxids);734// All outpoints exist in m_outpoint_to_orphan_wtxids, all keys in m_outpoint_to_orphan_wtxids correspond to some735// orphan, and all wtxids referenced in m_outpoint_to_orphan_wtxids are also in m_orphans.736// This ensures m_outpoint_to_orphan_wtxids is cleaned up.737assert(all_outpoints.size() == m_outpoint_to_orphan_wtxids.size());738for (const auto& [outpoint, wtxid_set] : m_outpoint_to_orphan_wtxids) {739assert(all_outpoints.contains(outpoint));740for (const auto& wtxid : wtxid_set) {741assert(unique_wtxids_to_scores.contains(wtxid));742}743}745// Cached m_unique_orphans value is correct.746assert(m_orphans.size() >= m_unique_orphans);747assert(m_orphans.size() <= m_peer_orphanage_info.size() * m_unique_orphans);748assert(unique_wtxids_to_scores.size() == m_unique_orphans);750const auto calculated_dedup_usage = std::accumulate(unique_wtxids_to_scores.begin(), unique_wtxids_to_scores.end(),
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.