Coverage Report

Created: 2026-08-25 19:02

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/root/bitcoin/src/node/txorphanage.cpp
Line
Count
Source
1
// Copyright (c) 2021-present The Bitcoin Core developers
2
// Distributed under the MIT software license, see the accompanying
3
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4
5
#include <node/txorphanage.h>
6
7
#include <consensus/validation.h>
8
#include <policy/policy.h>
9
#include <primitives/transaction.h>
10
#include <util/feefrac.h>
11
#include <util/hasher.h>
12
#include <util/log.h>
13
#include <util/time.h>
14
15
#include <boost/multi_index/indexed_by.hpp>
16
#include <boost/multi_index/ordered_index.hpp>
17
#include <boost/multi_index/tag.hpp>
18
#include <boost/multi_index_container.hpp>
19
20
#include <cassert>
21
#include <cmath>
22
#include <unordered_map>
23
24
namespace node {
25
/** Minimum NodeId for lower_bound lookups (in practice, NodeIds start at 0). */
26
static constexpr NodeId MIN_PEER{std::numeric_limits<NodeId>::min()};
27
/** Maximum NodeId for upper_bound lookups. */
28
static constexpr NodeId MAX_PEER{std::numeric_limits<NodeId>::max()};
29
class TxOrphanageImpl final : public TxOrphanage {
30
    // Type alias for sequence numbers
31
    using SequenceNumber = uint64_t;
32
    /** Global sequence number, increment each time an announcement is added. */
33
    SequenceNumber m_current_sequence{0};
34
35
    /** One orphan announcement. Each announcement (i.e. combination of wtxid, nodeid) is unique. There may be multiple
36
     * announcements for the same tx, and multiple transactions with the same txid but different wtxid are possible. */
37
    struct Announcement
38
    {
39
        const CTransactionRef m_tx;
40
        /** Which peer announced this tx */
41
        const NodeId m_announcer;
42
        /** What order this transaction entered the orphanage. */
43
        const SequenceNumber m_entry_sequence;
44
        /** Whether this tx should be reconsidered. Always starts out false. A peer's workset is the collection of all
45
         * announcements with m_reconsider=true. */
46
        bool m_reconsider{false};
47
48
        Announcement(const CTransactionRef& tx, NodeId peer, SequenceNumber seq) :
49
4.79M
            m_tx{tx}, m_announcer{peer}, m_entry_sequence{seq}
50
4.79M
        { }
51
52
        /** Get an approximation for "memory usage". The total memory is a function of the memory used to store the
53
         * transaction itself, each entry in m_orphans, and each entry in m_outpoint_to_orphan_wtxids. We use weight because
54
         * it is often higher than the actual memory usage of the transaction. This metric conveniently encompasses
55
         * m_outpoint_to_orphan_wtxids usage since input data does not get the witness discount, and makes it easier to
56
         * reason about each peer's limits using well-understood transaction attributes. */
57
5.70M
        TxOrphanage::Usage GetMemUsage()  const {
58
5.70M
            return GetTransactionWeight(*m_tx);
59
5.70M
        }
60
61
        /** Get an approximation of how much this transaction contributes to latency in EraseForBlock and EraseForPeer.
62
         * The computation time is a function of the number of entries in m_orphans (thus 1 per announcement) and the
63
         * number of entries in m_outpoint_to_orphan_wtxids (thus an additional 1 for every 10 inputs). Transactions with a
64
         * small number of inputs (9 or fewer) are counted as 1 to make it easier to reason about each peer's limits in
65
         * terms of "normal" transactions. */
66
5.70M
        TxOrphanage::Count GetLatencyScore() const {
67
5.70M
            return 1 + (m_tx->vin.size() / 10);
68
5.70M
        }
69
    };
70
71
    // Index by wtxid, then peer
72
    struct ByWtxid {};
73
    using ByWtxidView = std::tuple<Wtxid, NodeId>;
74
    struct WtxidExtractor
75
    {
76
        using result_type = ByWtxidView;
77
        result_type operator()(const Announcement& ann) const
78
176M
        {
79
176M
            return ByWtxidView{ann.m_tx->GetWitnessHash(), ann.m_announcer};
80
176M
        }
81
    };
82
83
    // Sort by peer, then by whether it is ready to reconsider, then by recency.
84
    struct ByPeer {};
85
    using ByPeerView = std::tuple<NodeId, bool, SequenceNumber>;
86
    struct ByPeerViewExtractor {
87
        using result_type = ByPeerView;
88
        result_type operator()(const Announcement& ann) const
89
17.3M
        {
90
17.3M
            return ByPeerView{ann.m_announcer, ann.m_reconsider, ann.m_entry_sequence};
91
17.3M
        }
92
    };
93
94
    using AnnouncementMap = boost::multi_index::multi_index_container<
95
        Announcement,
96
        boost::multi_index::indexed_by<
97
            boost::multi_index::ordered_unique<boost::multi_index::tag<ByWtxid>, WtxidExtractor>,
98
            boost::multi_index::ordered_unique<boost::multi_index::tag<ByPeer>, ByPeerViewExtractor>
99
        >
100
    >;
101
    template<typename Tag>
102
    using Iter = typename AnnouncementMap::index<Tag>::type::iterator;
103
    AnnouncementMap m_orphans;
104
105
    const TxOrphanage::Count m_max_global_latency_score{DEFAULT_MAX_ORPHANAGE_LATENCY_SCORE};
106
    const TxOrphanage::Usage m_reserved_usage_per_peer{DEFAULT_RESERVED_ORPHAN_WEIGHT_PER_PEER};
107
108
    /** Number of unique orphans by wtxid. Less than or equal to the number of entries in m_orphans. */
109
    TxOrphanage::Count m_unique_orphans{0};
110
111
    /** Memory used by orphans (see Announcement::GetMemUsage()), deduplicated by wtxid. */
112
    TxOrphanage::Usage m_unique_orphan_usage{0};
113
114
    /** The sum of each unique transaction's latency scores including the inputs only (see Announcement::GetLatencyScore
115
     * but subtract 1 for the announcements themselves). The total orphanage's latency score is given by this value +
116
     * the number of entries in m_orphans. */
117
    TxOrphanage::Count m_unique_rounded_input_scores{0};
118
119
    /** Index from the parents' outputs to wtxids that exist in m_orphans. Used to find children of
120
     * a transaction that can be reconsidered and to remove entries that conflict with a block.*/
121
    std::unordered_map<COutPoint, std::set<Wtxid>, SaltedOutpointHasher> m_outpoint_to_orphan_wtxids;
122
123
    /** Set of Wtxids for which (exactly) one announcement with m_reconsider=true exists. */
124
    std::set<Wtxid> m_reconsiderable_wtxids;
125
126
    struct PeerDoSInfo {
127
        TxOrphanage::Usage m_total_usage{0};
128
        TxOrphanage::Count m_count_announcements{0};
129
        TxOrphanage::Count m_total_latency_score{0};
130
        bool operator==(const PeerDoSInfo& other) const
131
39.2k
        {
132
39.2k
            return m_total_usage == other.m_total_usage &&
  Branch (132:20): [True: 39.2k, False: 0]
133
39.2k
                   m_count_announcements == other.m_count_announcements &&
  Branch (133:20): [True: 39.2k, False: 0]
134
39.2k
                   m_total_latency_score == other.m_total_latency_score;
  Branch (134:20): [True: 39.2k, False: 0]
135
39.2k
        }
136
        void Add(const Announcement& ann)
137
1.51M
        {
138
1.51M
            m_total_usage += ann.GetMemUsage();
139
1.51M
            m_total_latency_score += ann.GetLatencyScore();
140
1.51M
            m_count_announcements += 1;
141
1.51M
        }
142
        bool Subtract(const Announcement& ann)
143
1.39M
        {
144
1.39M
            Assume(m_total_usage >= ann.GetMemUsage());
145
1.39M
            Assume(m_total_latency_score >= ann.GetLatencyScore());
146
1.39M
            Assume(m_count_announcements >= 1);
147
148
1.39M
            m_total_usage -= ann.GetMemUsage();
149
1.39M
            m_total_latency_score -= ann.GetLatencyScore();
150
1.39M
            m_count_announcements -= 1;
151
1.39M
            return m_count_announcements == 0;
152
1.39M
        }
153
        /** There are 2 DoS scores:
154
        * - Latency score (ratio of total latency score / max allowed latency score)
155
        * - Memory score (ratio of total memory usage / max allowed memory usage).
156
        *
157
        * If the peer is using more than the allowed for either resource, its DoS score is > 1.
158
        * A peer having a DoS score > 1 does not necessarily mean that something is wrong, since we
159
        * do not trim unless the orphanage exceeds global limits, but it means that this peer will
160
        * be selected for trimming sooner. If the global latency score or global memory usage
161
        * limits are exceeded, it must be that there is a peer whose DoS score > 1. */
162
        FeeFrac GetDosScore(TxOrphanage::Count max_peer_latency_score, TxOrphanage::Usage max_peer_memory) const
163
6.91M
        {
164
6.91M
            assert(max_peer_latency_score > 0);
  Branch (164:13): [True: 6.91M, False: 0]
165
6.91M
            assert(max_peer_memory > 0);
  Branch (165:13): [True: 6.91M, False: 0]
166
6.91M
            const FeeFrac latency_score(m_total_latency_score, max_peer_latency_score);
167
6.91M
            const FeeFrac mem_score(m_total_usage, max_peer_memory);
168
6.91M
            return std::max<ByRatioNegSize<FeeFrac>>(latency_score, mem_score);
169
6.91M
        }
170
    };
171
    /** Store per-peer statistics. Used to determine each peer's DoS score. The size of this map is used to determine the
172
     * number of peers and thus global {latency score, memory} limits. */
173
    std::unordered_map<NodeId, PeerDoSInfo> m_peer_orphanage_info;
174
175
    /** Erase from m_orphans and update m_peer_orphanage_info. */
176
    template<typename Tag>
177
    void Erase(Iter<Tag> it);
178
179
    /** Erase by wtxid. */
180
    bool EraseTxInternal(const Wtxid& wtxid);
181
182
    /** Check if there is exactly one announcement with the same wtxid as it. */
183
    bool IsUnique(Iter<ByWtxid> it) const;
184
185
    /** Check if the orphanage needs trimming. */
186
    bool NeedsTrim() const;
187
188
    /** Limit the orphanage to MaxGlobalLatencyScore and MaxGlobalUsage. */
189
    void LimitOrphans();
190
191
public:
192
24.7k
    TxOrphanageImpl() = default;
193
    TxOrphanageImpl(Count max_global_latency_score, Usage reserved_peer_usage) :
194
2.21k
        m_max_global_latency_score{max_global_latency_score},
195
2.21k
        m_reserved_usage_per_peer{reserved_peer_usage}
196
2.21k
    {}
197
26.9k
    ~TxOrphanageImpl() noexcept override = default;
198
199
    TxOrphanage::Count CountAnnouncements() const override;
200
    TxOrphanage::Count CountUniqueOrphans() const override;
201
    TxOrphanage::Count AnnouncementsFromPeer(NodeId peer) const override;
202
    TxOrphanage::Count LatencyScoreFromPeer(NodeId peer) const override;
203
    TxOrphanage::Usage UsageByPeer(NodeId peer) const override;
204
205
    TxOrphanage::Count MaxGlobalLatencyScore() const override;
206
    TxOrphanage::Count TotalLatencyScore() const override;
207
    TxOrphanage::Usage ReservedPeerUsage() const override;
208
209
    /** Maximum allowed (deduplicated) latency score for all transactions (see Announcement::GetLatencyScore()). Dynamic
210
     * based on number of peers. Each peer has an equal amount, but the global maximum latency score stays constant. The
211
     * number of peers times MaxPeerLatencyScore() (rounded) adds up to MaxGlobalLatencyScore().  As long as every peer's
212
     * m_total_latency_score / MaxPeerLatencyScore() < 1, MaxGlobalLatencyScore() is not exceeded. */
213
    TxOrphanage::Count MaxPeerLatencyScore() const override;
214
215
    /** Maximum allowed (deduplicated) memory usage for all transactions (see Announcement::GetMemUsage()). Dynamic based
216
     * on number of peers. More peers means more allowed memory usage. The number of peers times ReservedPeerUsage()
217
     * adds up to MaxGlobalUsage(). As long as every peer's m_total_usage / ReservedPeerUsage() < 1, MaxGlobalUsage() is
218
     * not exceeded. */
219
    TxOrphanage::Usage MaxGlobalUsage() const override;
220
221
    bool AddTx(const CTransactionRef& tx, NodeId peer) override;
222
    bool AddAnnouncer(const Wtxid& wtxid, NodeId peer) override;
223
    CTransactionRef GetTx(const Wtxid& wtxid) const override;
224
    bool HaveTx(const Wtxid& wtxid) const override;
225
    bool HaveTxFromPeer(const Wtxid& wtxid, NodeId peer) const override;
226
    CTransactionRef GetTxToReconsider(NodeId peer) override;
227
    bool EraseTx(const Wtxid& wtxid) override;
228
    void EraseForPeer(NodeId peer) override;
229
    void EraseForBlock(const CBlock& block) override;
230
    std::vector<std::pair<Wtxid, NodeId>> AddChildrenToWorkSet(const CTransaction& tx, FastRandomContext& rng) override;
231
    bool HaveTxToReconsider(NodeId peer) override;
232
    std::vector<CTransactionRef> GetChildrenFromSamePeer(const CTransactionRef& parent, NodeId nodeid) const override;
233
    std::vector<OrphanInfo> GetOrphanTransactions() const override;
234
    TxOrphanage::Usage TotalOrphanUsage() const override;
235
    void SanityCheck() const override;
236
};
237
238
template<typename Tag>
239
void TxOrphanageImpl::Erase(Iter<Tag> it)
240
1.39M
{
241
    // Update m_peer_orphanage_info and clean up entries if they point to an empty struct.
242
    // This means peers that are not storing any orphans do not have an entry in
243
    // m_peer_orphanage_info (they can be added back later if they announce another orphan) and
244
    // ensures disconnected peers are not tracked forever.
245
1.39M
    auto peer_it = m_peer_orphanage_info.find(it->m_announcer);
246
1.39M
    Assume(peer_it != m_peer_orphanage_info.end());
247
1.39M
    if (peer_it->second.Subtract(*it)) m_peer_orphanage_info.erase(peer_it);
  Branch (247:9): [True: 759k, False: 248k]
  Branch (247:9): [True: 161k, False: 220k]
248
249
1.39M
    if (IsUnique(m_orphans.project<ByWtxid>(it))) {
  Branch (249:9): [True: 378k, False: 629k]
  Branch (249:9): [True: 170k, False: 211k]
250
549k
        m_unique_orphans -= 1;
251
549k
        m_unique_rounded_input_scores -= it->GetLatencyScore() - 1;
252
549k
        m_unique_orphan_usage -= it->GetMemUsage();
253
254
        // Remove references in m_outpoint_to_orphan_wtxids
255
549k
        const auto& wtxid{it->m_tx->GetWitnessHash()};
256
53.1M
        for (const auto& input : it->m_tx->vin) {
  Branch (256:32): [True: 41.3M, False: 378k]
  Branch (256:32): [True: 11.8M, False: 170k]
257
53.1M
            auto it_prev = m_outpoint_to_orphan_wtxids.find(input.prevout);
258
53.1M
            if (it_prev != m_outpoint_to_orphan_wtxids.end()) {
  Branch (258:17): [True: 18.2M, False: 23.0M]
  Branch (258:17): [True: 5.78M, False: 6.03M]
259
24.0M
                it_prev->second.erase(wtxid);
260
                // Clean up keys if they point to an empty set.
261
24.0M
                if (it_prev->second.empty()) {
  Branch (261:21): [True: 3.69M, False: 14.5M]
  Branch (261:21): [True: 418k, False: 5.36M]
262
4.11M
                    m_outpoint_to_orphan_wtxids.erase(it_prev);
263
4.11M
                }
264
24.0M
            }
265
53.1M
        }
266
549k
    }
267
268
    // If this was the (unique) reconsiderable announcement for its wtxid, then the wtxid won't
269
    // have any reconsiderable announcements left after erasing.
270
1.39M
    if (it->m_reconsider) m_reconsiderable_wtxids.erase(it->m_tx->GetWitnessHash());
  Branch (270:9): [True: 22.6k, False: 985k]
  Branch (270:9): [True: 11.3k, False: 371k]
271
272
1.39M
    m_orphans.get<Tag>().erase(it);
273
1.39M
}
_ZN4node15TxOrphanageImpl5EraseINS0_7ByWtxidEEEvN5boost11multi_index21multi_index_containerINS0_12AnnouncementENS4_10indexed_byINS4_14ordered_uniqueINS4_3tagIS2_N4mpl_2naESB_SB_SB_SB_SB_SB_SB_SB_SB_SB_SB_SB_SB_SB_SB_SB_SB_SB_EENS0_14WtxidExtractorESB_EENS8_INS9_INS0_6ByPeerESB_SB_SB_SB_SB_SB_SB_SB_SB_SB_SB_SB_SB_SB_SB_SB_SB_SB_SB_EENS0_19ByPeerViewExtractorESB_EESB_SB_SB_SB_SB_SB_SB_SB_SB_SB_SB_SB_SB_SB_SB_SB_SB_SB_EESaIS6_EE5indexIT_E4type8iteratorE
Line
Count
Source
240
1.00M
{
241
    // Update m_peer_orphanage_info and clean up entries if they point to an empty struct.
242
    // This means peers that are not storing any orphans do not have an entry in
243
    // m_peer_orphanage_info (they can be added back later if they announce another orphan) and
244
    // ensures disconnected peers are not tracked forever.
245
1.00M
    auto peer_it = m_peer_orphanage_info.find(it->m_announcer);
246
1.00M
    Assume(peer_it != m_peer_orphanage_info.end());
247
1.00M
    if (peer_it->second.Subtract(*it)) m_peer_orphanage_info.erase(peer_it);
  Branch (247:9): [True: 759k, False: 248k]
248
249
1.00M
    if (IsUnique(m_orphans.project<ByWtxid>(it))) {
  Branch (249:9): [True: 378k, False: 629k]
250
378k
        m_unique_orphans -= 1;
251
378k
        m_unique_rounded_input_scores -= it->GetLatencyScore() - 1;
252
378k
        m_unique_orphan_usage -= it->GetMemUsage();
253
254
        // Remove references in m_outpoint_to_orphan_wtxids
255
378k
        const auto& wtxid{it->m_tx->GetWitnessHash()};
256
41.3M
        for (const auto& input : it->m_tx->vin) {
  Branch (256:32): [True: 41.3M, False: 378k]
257
41.3M
            auto it_prev = m_outpoint_to_orphan_wtxids.find(input.prevout);
258
41.3M
            if (it_prev != m_outpoint_to_orphan_wtxids.end()) {
  Branch (258:17): [True: 18.2M, False: 23.0M]
259
18.2M
                it_prev->second.erase(wtxid);
260
                // Clean up keys if they point to an empty set.
261
18.2M
                if (it_prev->second.empty()) {
  Branch (261:21): [True: 3.69M, False: 14.5M]
262
3.69M
                    m_outpoint_to_orphan_wtxids.erase(it_prev);
263
3.69M
                }
264
18.2M
            }
265
41.3M
        }
266
378k
    }
267
268
    // If this was the (unique) reconsiderable announcement for its wtxid, then the wtxid won't
269
    // have any reconsiderable announcements left after erasing.
270
1.00M
    if (it->m_reconsider) m_reconsiderable_wtxids.erase(it->m_tx->GetWitnessHash());
  Branch (270:9): [True: 22.6k, False: 985k]
271
272
1.00M
    m_orphans.get<Tag>().erase(it);
273
1.00M
}
_ZN4node15TxOrphanageImpl5EraseINS0_6ByPeerEEEvN5boost11multi_index21multi_index_containerINS0_12AnnouncementENS4_10indexed_byINS4_14ordered_uniqueINS4_3tagINS0_7ByWtxidEN4mpl_2naESC_SC_SC_SC_SC_SC_SC_SC_SC_SC_SC_SC_SC_SC_SC_SC_SC_SC_EENS0_14WtxidExtractorESC_EENS8_INS9_IS2_SC_SC_SC_SC_SC_SC_SC_SC_SC_SC_SC_SC_SC_SC_SC_SC_SC_SC_SC_EENS0_19ByPeerViewExtractorESC_EESC_SC_SC_SC_SC_SC_SC_SC_SC_SC_SC_SC_SC_SC_SC_SC_SC_SC_EESaIS6_EE5indexIT_E4type8iteratorE
Line
Count
Source
240
382k
{
241
    // Update m_peer_orphanage_info and clean up entries if they point to an empty struct.
242
    // This means peers that are not storing any orphans do not have an entry in
243
    // m_peer_orphanage_info (they can be added back later if they announce another orphan) and
244
    // ensures disconnected peers are not tracked forever.
245
382k
    auto peer_it = m_peer_orphanage_info.find(it->m_announcer);
246
382k
    Assume(peer_it != m_peer_orphanage_info.end());
247
382k
    if (peer_it->second.Subtract(*it)) m_peer_orphanage_info.erase(peer_it);
  Branch (247:9): [True: 161k, False: 220k]
248
249
382k
    if (IsUnique(m_orphans.project<ByWtxid>(it))) {
  Branch (249:9): [True: 170k, False: 211k]
250
170k
        m_unique_orphans -= 1;
251
170k
        m_unique_rounded_input_scores -= it->GetLatencyScore() - 1;
252
170k
        m_unique_orphan_usage -= it->GetMemUsage();
253
254
        // Remove references in m_outpoint_to_orphan_wtxids
255
170k
        const auto& wtxid{it->m_tx->GetWitnessHash()};
256
11.8M
        for (const auto& input : it->m_tx->vin) {
  Branch (256:32): [True: 11.8M, False: 170k]
257
11.8M
            auto it_prev = m_outpoint_to_orphan_wtxids.find(input.prevout);
258
11.8M
            if (it_prev != m_outpoint_to_orphan_wtxids.end()) {
  Branch (258:17): [True: 5.78M, False: 6.03M]
259
5.78M
                it_prev->second.erase(wtxid);
260
                // Clean up keys if they point to an empty set.
261
5.78M
                if (it_prev->second.empty()) {
  Branch (261:21): [True: 418k, False: 5.36M]
262
418k
                    m_outpoint_to_orphan_wtxids.erase(it_prev);
263
418k
                }
264
5.78M
            }
265
11.8M
        }
266
170k
    }
267
268
    // If this was the (unique) reconsiderable announcement for its wtxid, then the wtxid won't
269
    // have any reconsiderable announcements left after erasing.
270
382k
    if (it->m_reconsider) m_reconsiderable_wtxids.erase(it->m_tx->GetWitnessHash());
  Branch (270:9): [True: 11.3k, False: 371k]
271
272
382k
    m_orphans.get<Tag>().erase(it);
273
382k
}
274
275
bool TxOrphanageImpl::IsUnique(Iter<ByWtxid> it) const
276
2.90M
{
277
    // Iterators ByWtxid are sorted by wtxid, so check if neighboring elements have the same wtxid.
278
2.90M
    auto& index = m_orphans.get<ByWtxid>();
279
2.90M
    if (it == index.end()) return false;
  Branch (279:9): [True: 0, False: 2.90M]
280
2.90M
    if (std::next(it) != index.end() && std::next(it)->m_tx->GetWitnessHash() == it->m_tx->GetWitnessHash()) return false;
  Branch (280:9): [True: 2.26M, False: 645k]
  Branch (280:9): [True: 1.48M, False: 1.42M]
  Branch (280:41): [True: 1.48M, False: 776k]
281
1.42M
    if (it != index.begin() && std::prev(it)->m_tx->GetWitnessHash() == it->m_tx->GetWitnessHash()) return false;
  Branch (281:9): [True: 904k, False: 516k]
  Branch (281:9): [True: 294k, False: 1.12M]
  Branch (281:32): [True: 294k, False: 610k]
282
1.12M
    return true;
283
1.42M
}
284
285
TxOrphanage::Usage TxOrphanageImpl::UsageByPeer(NodeId peer) const
286
8.21M
{
287
8.21M
    auto it = m_peer_orphanage_info.find(peer);
288
8.21M
    return it == m_peer_orphanage_info.end() ? 0 : it->second.m_total_usage;
  Branch (288:12): [True: 1.95M, False: 6.25M]
289
8.21M
}
290
291
1.43k
TxOrphanage::Count TxOrphanageImpl::CountAnnouncements() const { return m_orphans.size(); }
292
293
9.50M
TxOrphanage::Usage TxOrphanageImpl::TotalOrphanUsage() const { return m_unique_orphan_usage; }
294
295
294k
TxOrphanage::Count TxOrphanageImpl::CountUniqueOrphans() const { return m_unique_orphans; }
296
297
336k
TxOrphanage::Count TxOrphanageImpl::AnnouncementsFromPeer(NodeId peer) const {
298
336k
    auto it = m_peer_orphanage_info.find(peer);
299
336k
    return it == m_peer_orphanage_info.end() ? 0 : it->second.m_count_announcements;
  Branch (299:12): [True: 330k, False: 6.19k]
300
336k
}
301
302
775k
TxOrphanage::Count TxOrphanageImpl::LatencyScoreFromPeer(NodeId peer) const {
303
775k
    auto it = m_peer_orphanage_info.find(peer);
304
775k
    return it == m_peer_orphanage_info.end() ? 0 : it->second.m_total_latency_score;
  Branch (304:12): [True: 616k, False: 158k]
305
775k
}
306
307
bool TxOrphanageImpl::AddTx(const CTransactionRef& tx, NodeId peer)
308
4.29M
{
309
4.29M
    const auto& wtxid{tx->GetWitnessHash()};
310
4.29M
    const auto& txid{tx->GetHash()};
311
312
    // Ignore transactions above max standard size to avoid a send-big-orphans memory exhaustion attack.
313
4.29M
    TxOrphanage::Usage sz = GetTransactionWeight(*tx);
314
4.29M
    if (sz > MAX_STANDARD_TX_WEIGHT) {
  Branch (314:9): [True: 121k, False: 4.17M]
315
121k
        LogDebug(BCLog::TXPACKAGES, "ignoring large orphan tx (size: %u, txid: %s, wtxid: %s)\n", sz, txid.ToString(), wtxid.ToString());
316
121k
        return false;
317
121k
    }
318
319
    // We will return false if the tx already exists under a different peer.
320
4.17M
    const bool brand_new{!HaveTx(wtxid)};
321
322
4.17M
    auto [iter, inserted] = m_orphans.get<ByWtxid>().emplace(tx, peer, m_current_sequence);
323
    // If the announcement (same wtxid, same peer) already exists, emplacement fails. Return false.
324
4.17M
    if (!inserted) return false;
  Branch (324:9): [True: 2.86M, False: 1.31M]
325
326
1.31M
    ++m_current_sequence;
327
1.31M
    auto& peer_info = m_peer_orphanage_info.try_emplace(peer).first->second;
328
1.31M
    peer_info.Add(*iter);
329
330
    // Add links in m_outpoint_to_orphan_wtxids
331
1.31M
    if (brand_new) {
  Branch (331:9): [True: 578k, False: 737k]
332
57.3M
        for (const auto& input : tx->vin) {
  Branch (332:32): [True: 57.3M, False: 578k]
333
57.3M
            auto& wtxids_for_prevout = m_outpoint_to_orphan_wtxids.try_emplace(input.prevout).first->second;
334
57.3M
            wtxids_for_prevout.emplace(wtxid);
335
57.3M
        }
336
337
578k
        m_unique_orphans += 1;
338
578k
        m_unique_orphan_usage += iter->GetMemUsage();
339
578k
        m_unique_rounded_input_scores += iter->GetLatencyScore() - 1;
340
341
578k
        LogDebug(BCLog::TXPACKAGES, "stored orphan tx %s (wtxid=%s), weight: %u (mapsz %u outsz %u)\n",
342
578k
                    txid.ToString(), wtxid.ToString(), sz, m_orphans.size(), m_outpoint_to_orphan_wtxids.size());
343
578k
        Assume(IsUnique(iter));
344
737k
    } else {
345
737k
        LogDebug(BCLog::TXPACKAGES, "added peer=%d as announcer of orphan tx %s (wtxid=%s)\n",
346
737k
                    peer, txid.ToString(), wtxid.ToString());
347
737k
        Assume(!IsUnique(iter));
348
737k
    }
349
350
    // DoS prevention: do not allow m_orphanage to grow unbounded (see CVE-2012-3789)
351
1.31M
    LimitOrphans();
352
1.31M
    return brand_new;
353
4.17M
}
354
355
bool TxOrphanageImpl::AddAnnouncer(const Wtxid& wtxid, NodeId peer)
356
1.08M
{
357
1.08M
    auto& index_by_wtxid = m_orphans.get<ByWtxid>();
358
1.08M
    auto it = index_by_wtxid.lower_bound(ByWtxidView{wtxid, MIN_PEER});
359
360
    // Do nothing if this transaction isn't already present. We can't create an entry if we don't
361
    // have the tx data.
362
1.08M
    if (it == index_by_wtxid.end()) return false;
  Branch (362:9): [True: 329k, False: 753k]
363
753k
    if (it->m_tx->GetWitnessHash() != wtxid) return false;
  Branch (363:9): [True: 135k, False: 618k]
364
365
    // Add another announcement, copying the CTransactionRef from one that already exists.
366
618k
    const auto& ptx = it->m_tx;
367
618k
    auto [iter, inserted] = index_by_wtxid.emplace(ptx, peer, m_current_sequence);
368
    // If the announcement (same wtxid, same peer) already exists, emplacement fails. Return false.
369
618k
    if (!inserted) return false;
  Branch (369:9): [True: 415k, False: 202k]
370
371
202k
    ++m_current_sequence;
372
202k
    auto& peer_info = m_peer_orphanage_info.try_emplace(peer).first->second;
373
202k
    peer_info.Add(*iter);
374
375
202k
    const auto& txid = ptx->GetHash();
376
202k
    LogDebug(BCLog::TXPACKAGES, "added peer=%d as announcer of orphan tx %s (wtxid=%s)\n",
377
202k
                peer, txid.ToString(), wtxid.ToString());
378
379
202k
    Assume(!IsUnique(iter));
380
381
    // DoS prevention: do not allow m_orphanage to grow unbounded (see CVE-2012-3789)
382
202k
    LimitOrphans();
383
202k
    return true;
384
618k
}
385
386
bool TxOrphanageImpl::EraseTxInternal(const Wtxid& wtxid)
387
1.13M
{
388
1.13M
    auto& index_by_wtxid = m_orphans.get<ByWtxid>();
389
390
1.13M
    auto it = index_by_wtxid.lower_bound(ByWtxidView{wtxid, MIN_PEER});
391
1.13M
    if (it == index_by_wtxid.end() || it->m_tx->GetWitnessHash() != wtxid) return false;
  Branch (391:9): [True: 326k, False: 804k]
  Branch (391:9): [True: 752k, False: 378k]
  Branch (391:39): [True: 425k, False: 378k]
392
393
378k
    auto it_end = index_by_wtxid.upper_bound(ByWtxidView{wtxid, MAX_PEER});
394
378k
    unsigned int num_ann{0};
395
378k
    const auto txid = it->m_tx->GetHash();
396
1.38M
    while (it != it_end) {
  Branch (396:12): [True: 1.00M, False: 378k]
397
1.00M
        Assume(it->m_tx->GetWitnessHash() == wtxid);
398
1.00M
        Erase<ByWtxid>(it++);
399
1.00M
        num_ann += 1;
400
1.00M
    }
401
378k
    LogDebug(BCLog::TXPACKAGES, "removed orphan tx %s (wtxid=%s) (%u announcements)\n", txid.ToString(), wtxid.ToString(), num_ann);
402
403
378k
    return true;
404
1.13M
}
405
406
bool TxOrphanageImpl::EraseTx(const Wtxid& wtxid)
407
1.04M
{
408
1.04M
    const auto ret = EraseTxInternal(wtxid);
409
410
    // Deletions can cause the orphanage's MaxGlobalUsage to decrease, so we may need to trim here.
411
1.04M
    LimitOrphans();
412
413
1.04M
    return ret;
414
1.04M
}
415
416
/** Erase all entries by this peer. */
417
void TxOrphanageImpl::EraseForPeer(NodeId peer)
418
495k
{
419
495k
    auto& index_by_peer = m_orphans.get<ByPeer>();
420
495k
    auto it = index_by_peer.lower_bound(ByPeerView{peer, false, 0});
421
495k
    if (it == index_by_peer.end() || it->m_announcer != peer) return;
  Branch (421:9): [True: 168k, False: 326k]
  Branch (421:9): [True: 387k, False: 107k]
  Branch (421:38): [True: 218k, False: 107k]
422
423
107k
    unsigned int num_ann{0};
424
256k
    while (it != index_by_peer.end() && it->m_announcer == peer) {
  Branch (424:12): [True: 232k, False: 24.0k]
  Branch (424:12): [True: 148k, False: 107k]
  Branch (424:41): [True: 148k, False: 83.5k]
425
        // Delete item, cleaning up m_outpoint_to_orphan_wtxids iff this entry is unique by wtxid.
426
148k
        Erase<ByPeer>(it++);
427
148k
        num_ann += 1;
428
148k
    }
429
107k
    Assume(!m_peer_orphanage_info.contains(peer));
430
431
107k
    if (num_ann > 0) LogDebug(BCLog::TXPACKAGES, "Erased %d orphan transaction(s) from peer=%d\n", num_ann, peer);
  Branch (431:9): [True: 107k, False: 0]
432
433
    // Deletions can cause the orphanage's MaxGlobalUsage to decrease, so we may need to trim here.
434
107k
    LimitOrphans();
435
107k
}
436
437
/** If the data structure needs trimming, evicts announcements by selecting the DoSiest peer and evicting its oldest
438
 * announcement (sorting non-reconsiderable orphans first, to give reconsiderable orphans a greater chance of being
439
 * processed). Does nothing if no global limits are exceeded.  This eviction strategy effectively "reserves" an
440
 * amount of announcements and space for each peer. The reserved amount is protected from eviction even if there
441
 * are peers spamming the orphanage.
442
 */
443
void TxOrphanageImpl::LimitOrphans()
444
2.73M
{
445
2.73M
    if (!NeedsTrim()) return;
  Branch (445:9): [True: 2.59M, False: 134k]
446
447
134k
    const auto original_unique_txns{CountUniqueOrphans()};
448
449
    // Even though it's possible for MaxPeerLatencyScore to increase within this call to LimitOrphans
450
    // (e.g. if a peer's orphans are removed entirely, changing the number of peers), use consistent limits throughout.
451
134k
    const auto max_lat{MaxPeerLatencyScore()};
452
134k
    const auto max_mem{ReservedPeerUsage()};
453
454
    // We have exceeded the global limit(s). Now, identify who is using too much and evict their orphans.
455
    // Create a heap of pairs (NodeId, DoS score), sorted by descending DoS score.
456
134k
    std::vector<std::pair<NodeId, FeeFrac>> heap_peer_dos;
457
134k
    heap_peer_dos.reserve(m_peer_orphanage_info.size());
458
6.65M
    for (const auto& [nodeid, entry] : m_peer_orphanage_info) {
  Branch (458:38): [True: 6.65M, False: 134k]
459
        // Performance optimization: only consider peers with a DoS score > 1.
460
6.65M
        const auto dos_score = entry.GetDosScore(max_lat, max_mem);
461
6.65M
        if (ByRatio{dos_score} > ByRatio{FeeFrac{1, 1}}) {
  Branch (461:13): [True: 5.27M, False: 1.38M]
462
5.27M
            heap_peer_dos.emplace_back(nodeid, dos_score);
463
5.27M
        }
464
6.65M
    }
465
8.86M
    static constexpr auto compare_score = [](const auto& left, const auto& right) {
466
8.86M
        if (left.second != right.second) {
  Branch (466:13): [True: 7.87M, False: 980k]
467
            // Note: if ratios are the same, this tiebreaks by denominator. In practice, since the
468
            // latency denominator (number of announcements and inputs) is always lower, this means
469
            // that a peer with only high latency scores will be targeted before a peer using a lot
470
            // of memory, even if they have the same ratios.
471
7.87M
            return ByRatioNegSize{left.second} < ByRatioNegSize{right.second};
472
7.87M
        }
473
        // Tiebreak by considering the more recent peer (higher NodeId) to be worse.
474
980k
        return left.first < right.first;
475
8.86M
    };
476
134k
    std::make_heap(heap_peer_dos.begin(), heap_peer_dos.end(), compare_score);
477
478
134k
    unsigned int num_erased{0};
479
    // This outer loop finds the peer with the highest DoS score, which is a fraction of memory and latency scores
480
    // over the respective allowances. We continue until the orphanage is within global limits. That means some peers
481
    // might still have a DoS score > 1 at the end.
482
218k
    do {
483
218k
        Assume(!heap_peer_dos.empty());
484
        // This is a max-heap, so the worst peer is at the front. pop_heap()
485
        // moves it to the back, and the next worst peer is moved to the front.
486
218k
        std::pop_heap(heap_peer_dos.begin(), heap_peer_dos.end(), compare_score);
487
218k
        const auto [worst_peer, dos_score] = std::move(heap_peer_dos.back());
488
218k
        heap_peer_dos.pop_back();
489
490
        // If needs trim, then at least one peer has a DoS score higher than 1.
491
218k
        Assume(ByRatio{dos_score} > ByRatio{FeeFrac(1, 1)});
492
493
218k
        auto it_worst_peer = m_peer_orphanage_info.find(worst_peer);
494
495
        // This inner loop trims until this peer is no longer the DoSiest one or has a score within 1. The score 1 is
496
        // just a conservative fallback: once the last peer goes below ratio 1, NeedsTrim() will return false anyway.
497
        // We evict the oldest announcement(s) from this peer, sorting non-reconsiderable before reconsiderable.
498
        // The number of inner loop iterations is bounded by the total number of announcements.
499
218k
        const auto& dos_threshold = heap_peer_dos.empty() ? FeeFrac{1, 1} : heap_peer_dos.front().second;
  Branch (499:37): [True: 59.4k, False: 158k]
500
218k
        auto it_ann = m_orphans.get<ByPeer>().lower_bound(ByPeerView{worst_peer, false, 0});
501
218k
        unsigned int num_erased_this_round{0};
502
218k
        unsigned int starting_num_ann{it_worst_peer->second.m_count_announcements};
503
252k
        while (NeedsTrim()) {
  Branch (503:16): [True: 233k, False: 18.9k]
504
233k
            if (!Assume(it_ann != m_orphans.get<ByPeer>().end())) break;
  Branch (504:17): [True: 0, False: 233k]
505
233k
            if (!Assume(it_ann->m_announcer == worst_peer)) break;
  Branch (505:17): [True: 0, False: 233k]
506
507
233k
            Erase<ByPeer>(it_ann++);
508
233k
            num_erased += 1;
509
233k
            num_erased_this_round += 1;
510
511
            // If we erased the last orphan from this peer, it_worst_peer will be invalidated.
512
233k
            it_worst_peer = m_peer_orphanage_info.find(worst_peer);
513
233k
            if (it_worst_peer == m_peer_orphanage_info.end() ||
  Branch (513:17): [True: 54.3k, False: 179k]
  Branch (513:17): [True: 199k, False: 34.2k]
514
233k
                ByRatioNegSize{it_worst_peer->second.GetDosScore(max_lat, max_mem)} <= ByRatioNegSize{dos_threshold}) break;
  Branch (514:17): [True: 145k, False: 34.2k]
515
233k
        }
516
218k
        LogDebug(BCLog::TXPACKAGES, "peer=%d orphanage overflow, removed %u of %u announcements\n", worst_peer, num_erased_this_round, starting_num_ann);
517
518
218k
        if (!NeedsTrim()) break;
  Branch (518:13): [True: 134k, False: 84.2k]
519
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.
522
84.2k
        if (it_worst_peer != m_peer_orphanage_info.end() && it_worst_peer->second.m_count_announcements > 0) {
  Branch (522:13): [True: 81.9k, False: 2.38k]
  Branch (522:13): [True: 81.9k, False: 2.38k]
  Branch (522:61): [True: 81.9k, False: 0]
523
81.9k
            heap_peer_dos.emplace_back(worst_peer, it_worst_peer->second.GetDosScore(max_lat, max_mem));
524
81.9k
            std::push_heap(heap_peer_dos.begin(), heap_peer_dos.end(), compare_score);
525
81.9k
        }
526
84.2k
    } while (true);
  Branch (526:14): [Folded - Ignored]
527
528
0
    const auto remaining_unique_orphans{CountUniqueOrphans()};
529
134k
    LogDebug(BCLog::TXPACKAGES, "orphanage overflow, removed %u tx (%u announcements)\n", original_unique_txns - remaining_unique_orphans, num_erased);
530
134k
}
531
532
std::vector<std::pair<Wtxid, NodeId>> TxOrphanageImpl::AddChildrenToWorkSet(const CTransaction& tx, FastRandomContext& rng)
533
226k
{
534
226k
    if (m_orphans.empty()) return {};
  Branch (534:9): [True: 35.5k, False: 190k]
535
536
190k
    std::vector<std::pair<Wtxid, NodeId>> ret;
537
190k
    auto& index_by_wtxid = m_orphans.get<ByWtxid>();
538
11.0M
    for (unsigned int i = 0; i < tx.vout.size(); i++) {
  Branch (538:30): [True: 10.8M, False: 190k]
539
10.8M
        const auto it_by_prev = m_outpoint_to_orphan_wtxids.find(COutPoint(tx.GetHash(), i));
540
10.8M
        if (it_by_prev != m_outpoint_to_orphan_wtxids.end()) {
  Branch (540:13): [True: 682k, False: 10.1M]
541
1.62M
            for (const auto& wtxid : it_by_prev->second) {
  Branch (541:36): [True: 1.62M, False: 682k]
542
                // If a reconsiderable announcement for this wtxid already exists, skip it.
543
1.62M
                if (m_reconsiderable_wtxids.contains(wtxid)) continue;
  Branch (543:21): [True: 1.55M, False: 74.2k]
544
545
                // Belt and suspenders, each entry in m_outpoint_to_orphan_wtxids should always have at least 1 announcement.
546
74.2k
                auto it = index_by_wtxid.lower_bound(ByWtxidView{wtxid, MIN_PEER});
547
74.2k
                if (!Assume(it != index_by_wtxid.end() && it->m_tx->GetWitnessHash() == wtxid)) continue;
  Branch (547:21): [True: 0, False: 74.2k]
548
549
                // Select a random peer to assign orphan processing, reducing wasted work if the orphan is still missing
550
                // inputs. However, we don't want to create an issue in which the assigned peer can purposefully stop us
551
                // from processing the orphan by disconnecting.
552
74.2k
                auto it_end = index_by_wtxid.upper_bound(ByWtxidView{wtxid, MAX_PEER});
553
74.2k
                const auto num_announcers{std::distance(it, it_end)};
554
74.2k
                if (!Assume(num_announcers > 0)) continue;
  Branch (554:21): [True: 0, False: 74.2k]
555
74.2k
                std::advance(it, rng.randrange(num_announcers));
556
557
74.2k
                if (!Assume(it->m_tx->GetWitnessHash() == wtxid)) break;
  Branch (557:21): [True: 0, False: 74.2k]
558
559
                // Mark this orphan as ready to be reconsidered.
560
74.2k
                static constexpr auto mark_reconsidered_modifier = [](auto& ann) { ann.m_reconsider = true; };
561
74.2k
                Assume(!it->m_reconsider);
562
74.2k
                index_by_wtxid.modify(it, mark_reconsidered_modifier);
563
74.2k
                ret.emplace_back(wtxid, it->m_announcer);
564
74.2k
                m_reconsiderable_wtxids.insert(wtxid);
565
566
74.2k
                LogDebug(BCLog::TXPACKAGES, "added %s (wtxid=%s) to peer %d workset\n",
567
74.2k
                            it->m_tx->GetHash().ToString(), it->m_tx->GetWitnessHash().ToString(), it->m_announcer);
568
74.2k
            }
569
682k
        }
570
10.8M
    }
571
190k
    return ret;
572
226k
}
573
574
bool TxOrphanageImpl::HaveTx(const Wtxid& wtxid) const
575
11.4M
{
576
11.4M
    auto it_lower = m_orphans.get<ByWtxid>().lower_bound(ByWtxidView{wtxid, MIN_PEER});
577
11.4M
    return it_lower != m_orphans.get<ByWtxid>().end() && it_lower->m_tx->GetWitnessHash() == wtxid;
  Branch (577:12): [True: 8.70M, False: 2.75M]
  Branch (577:58): [True: 6.77M, False: 1.93M]
578
11.4M
}
579
580
CTransactionRef TxOrphanageImpl::GetTx(const Wtxid& wtxid) const
581
155k
{
582
155k
    auto it_lower = m_orphans.get<ByWtxid>().lower_bound(ByWtxidView{wtxid, MIN_PEER});
583
155k
    if (it_lower != m_orphans.get<ByWtxid>().end() && it_lower->m_tx->GetWitnessHash() == wtxid) return it_lower->m_tx;
  Branch (583:9): [True: 124k, False: 31.5k]
  Branch (583:9): [True: 93.5k, False: 62.1k]
  Branch (583:55): [True: 93.5k, False: 30.6k]
584
62.1k
    return nullptr;
585
155k
}
586
587
bool TxOrphanageImpl::HaveTxFromPeer(const Wtxid& wtxid, NodeId peer) const
588
9.55M
{
589
9.55M
    return m_orphans.get<ByWtxid>().count(ByWtxidView{wtxid, peer}) > 0;
590
9.55M
}
591
592
/** If there is a tx that can be reconsidered, return it and set it back to
593
 * non-reconsiderable. Otherwise, return a nullptr. */
594
CTransactionRef TxOrphanageImpl::GetTxToReconsider(NodeId peer)
595
1.66M
{
596
1.66M
    auto it = m_orphans.get<ByPeer>().lower_bound(ByPeerView{peer, true, 0});
597
1.66M
    if (it != m_orphans.get<ByPeer>().end() && it->m_announcer == peer && it->m_reconsider) {
  Branch (597:9): [True: 119k, False: 1.54M]
  Branch (597:9): [True: 31.6k, False: 1.63M]
  Branch (597:48): [True: 31.6k, False: 87.8k]
  Branch (597:75): [True: 31.6k, False: 0]
598
        // Flip m_reconsider. Even if this transaction stays in orphanage, it shouldn't be
599
        // reconsidered again until there is a new reason to do so.
600
31.6k
        static constexpr auto mark_reconsidered_modifier = [](auto& ann) { ann.m_reconsider = false; };
601
31.6k
        m_orphans.get<ByPeer>().modify(it, mark_reconsidered_modifier);
602
        // As there is exactly one m_reconsider announcement per reconsiderable wtxids, flipping
603
        // the m_reconsider flag means the wtxid is no longer reconsiderable.
604
31.6k
        m_reconsiderable_wtxids.erase(it->m_tx->GetWitnessHash());
605
31.6k
        return it->m_tx;
606
31.6k
    }
607
1.63M
    return nullptr;
608
1.66M
}
609
610
/** Return whether there is a tx that can be reconsidered. */
611
bool TxOrphanageImpl::HaveTxToReconsider(NodeId peer)
612
531k
{
613
531k
    auto it = m_orphans.get<ByPeer>().lower_bound(ByPeerView{peer, true, 0});
614
531k
    return it != m_orphans.get<ByPeer>().end() && it->m_announcer == peer && it->m_reconsider;
  Branch (614:12): [True: 93.8k, False: 437k]
  Branch (614:51): [True: 19.6k, False: 74.1k]
  Branch (614:78): [True: 19.6k, False: 0]
615
531k
}
616
617
void TxOrphanageImpl::EraseForBlock(const CBlock& block)
618
89.1k
{
619
89.1k
    if (m_orphans.empty()) return;
  Branch (619:9): [True: 25.2k, False: 63.9k]
620
621
63.9k
    std::set<Wtxid> wtxids_to_erase;
622
1.35M
    for (const CTransactionRef& ptx : block.vtx) {
  Branch (622:37): [True: 1.35M, False: 63.9k]
623
1.35M
        const CTransaction& block_tx = *ptx;
624
625
        // Which orphan pool entries must we evict?
626
78.0M
        for (const auto& input : block_tx.vin) {
  Branch (626:32): [True: 78.0M, False: 1.35M]
627
78.0M
            auto it_prev = m_outpoint_to_orphan_wtxids.find(input.prevout);
628
78.0M
            if (it_prev != m_outpoint_to_orphan_wtxids.end()) {
  Branch (628:17): [True: 10.0M, False: 68.0M]
629
                // Copy all wtxids to wtxids_to_erase.
630
10.0M
                std::copy(it_prev->second.cbegin(), it_prev->second.cend(), std::inserter(wtxids_to_erase, wtxids_to_erase.end()));
631
10.0M
            }
632
78.0M
        }
633
1.35M
    }
634
635
63.9k
    unsigned int num_erased{0};
636
88.9k
    for (const auto& wtxid : wtxids_to_erase) {
  Branch (636:28): [True: 88.9k, False: 63.9k]
637
        // Don't use EraseTx here because it calls LimitOrphans and announcements deleted in that call are not reflected
638
        // in its return result. Waiting until the end to do LimitOrphans helps save repeated computation and allows us
639
        // to check that num_erased is what we expect.
640
88.9k
        num_erased += EraseTxInternal(wtxid) ? 1 : 0;
  Branch (640:23): [True: 88.9k, False: 0]
641
88.9k
    }
642
643
63.9k
    if (num_erased != 0) {
  Branch (643:9): [True: 29.2k, False: 34.6k]
644
29.2k
        LogDebug(BCLog::TXPACKAGES, "Erased %d orphan transaction(s) included or conflicted by block\n", num_erased);
645
29.2k
    }
646
63.9k
    Assume(wtxids_to_erase.size() == num_erased);
647
648
    // Deletions can cause the orphanage's MaxGlobalUsage to decrease, so we may need to trim here.
649
63.9k
    LimitOrphans();
650
63.9k
}
651
652
std::vector<CTransactionRef> TxOrphanageImpl::GetChildrenFromSamePeer(const CTransactionRef& parent, NodeId peer) const
653
539k
{
654
539k
    std::vector<CTransactionRef> children_found;
655
539k
    const auto& parent_txid{parent->GetHash()};
656
657
    // Iterate through all orphans from this peer, in reverse order, so that more recent
658
    // transactions are added first. Doing so helps avoid work when one of the orphans replaced
659
    // an earlier one. Since we require the NodeId to match, one peer's announcement order does
660
    // not bias how we process other peer's orphans.
661
539k
    auto& index_by_peer = m_orphans.get<ByPeer>();
662
539k
    auto it_upper = index_by_peer.upper_bound(ByPeerView{peer, true, std::numeric_limits<uint64_t>::max()});
663
539k
    auto it_lower = index_by_peer.lower_bound(ByPeerView{peer, false, 0});
664
665
1.88M
    while (it_upper != it_lower) {
  Branch (665:12): [True: 1.34M, False: 539k]
666
1.34M
        --it_upper;
667
1.34M
        if (!Assume(it_upper->m_announcer == peer)) break;
  Branch (667:13): [True: 0, False: 1.34M]
668
        // Check if this tx spends from parent.
669
156M
        for (const auto& input : it_upper->m_tx->vin) {
  Branch (669:32): [True: 156M, False: 942k]
670
156M
            if (input.prevout.hash == parent_txid) {
  Branch (670:17): [True: 404k, False: 156M]
671
404k
                children_found.emplace_back(it_upper->m_tx);
672
404k
                break;
673
404k
            }
674
156M
        }
675
1.34M
    }
676
539k
    return children_found;
677
539k
}
678
679
std::vector<TxOrphanage::OrphanInfo> TxOrphanageImpl::GetOrphanTransactions() const
680
1.45k
{
681
1.45k
    std::vector<TxOrphanage::OrphanInfo> result;
682
1.45k
    result.reserve(m_unique_orphans);
683
684
1.45k
    auto& index_by_wtxid = m_orphans.get<ByWtxid>();
685
1.45k
    auto it = index_by_wtxid.begin();
686
1.45k
    std::set<NodeId> this_orphan_announcers;
687
12.5k
    while (it != index_by_wtxid.end()) {
  Branch (687:12): [True: 11.0k, False: 1.45k]
688
11.0k
        this_orphan_announcers.insert(it->m_announcer);
689
        // If this is the last entry, or the next entry has a different wtxid, build a OrphanInfo.
690
11.0k
        if (std::next(it) == index_by_wtxid.end() || std::next(it)->m_tx->GetWitnessHash() != it->m_tx->GetWitnessHash()) {
  Branch (690:13): [True: 1.05k, False: 10.0k]
  Branch (690:13): [True: 6.05k, False: 5.00k]
  Branch (690:54): [True: 5.00k, False: 5.00k]
691
6.05k
            result.emplace_back(it->m_tx, std::move(this_orphan_announcers));
692
6.05k
            this_orphan_announcers.clear();
693
6.05k
        }
694
695
11.0k
        ++it;
696
11.0k
    }
697
1.45k
    Assume(m_unique_orphans == result.size());
698
699
1.45k
    return result;
700
1.45k
}
701
702
void TxOrphanageImpl::SanityCheck() const
703
5.14k
{
704
5.14k
    std::unordered_map<NodeId, PeerDoSInfo> reconstructed_peer_info;
705
5.14k
    std::map<Wtxid, std::pair<TxOrphanage::Usage, TxOrphanage::Count>> unique_wtxids_to_scores;
706
5.14k
    std::set<COutPoint> all_outpoints;
707
5.14k
    std::set<Wtxid> reconstructed_reconsiderable_wtxids;
708
709
144k
    for (auto it = m_orphans.begin(); it != m_orphans.end(); ++it) {
  Branch (709:39): [True: 139k, False: 5.14k]
710
31.9M
        for (const auto& input : it->m_tx->vin) {
  Branch (710:32): [True: 31.9M, False: 139k]
711
31.9M
            all_outpoints.insert(input.prevout);
712
31.9M
        }
713
139k
        unique_wtxids_to_scores.emplace(it->m_tx->GetWitnessHash(), std::make_pair(it->GetMemUsage(), it->GetLatencyScore() - 1));
714
715
139k
        auto& peer_info = reconstructed_peer_info[it->m_announcer];
716
139k
        peer_info.m_total_usage += it->GetMemUsage();
717
139k
        peer_info.m_count_announcements += 1;
718
139k
        peer_info.m_total_latency_score += it->GetLatencyScore();
719
720
139k
        if (it->m_reconsider) {
  Branch (720:13): [True: 13.1k, False: 126k]
721
13.1k
            auto [_, added] = reconstructed_reconsiderable_wtxids.insert(it->m_tx->GetWitnessHash());
722
            // Check that there is only ever 1 announcement per wtxid with m_reconsider set.
723
13.1k
            assert(added);
  Branch (723:13): [True: 13.1k, False: 0]
724
13.1k
        }
725
139k
    }
726
5.14k
    assert(reconstructed_peer_info.size() == m_peer_orphanage_info.size());
  Branch (726:5): [True: 5.14k, False: 0]
727
728
    // Recalculated per-peer stats are identical to m_peer_orphanage_info
729
5.14k
    assert(reconstructed_peer_info == m_peer_orphanage_info);
  Branch (729:5): [True: 5.14k, False: 0]
730
731
    // Recalculated set of reconsiderable wtxids must match.
732
5.14k
    assert(m_reconsiderable_wtxids == reconstructed_reconsiderable_wtxids);
  Branch (732:5): [True: 5.14k, False: 0]
733
734
    // All outpoints exist in m_outpoint_to_orphan_wtxids, all keys in m_outpoint_to_orphan_wtxids correspond to some
735
    // 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.
737
5.14k
    assert(all_outpoints.size() == m_outpoint_to_orphan_wtxids.size());
  Branch (737:5): [True: 5.14k, False: 0]
738
106k
    for (const auto& [outpoint, wtxid_set] : m_outpoint_to_orphan_wtxids) {
  Branch (738:44): [True: 106k, False: 5.14k]
739
106k
        assert(all_outpoints.contains(outpoint));
  Branch (739:9): [True: 106k, False: 0]
740
235k
        for (const auto& wtxid : wtxid_set) {
  Branch (740:32): [True: 235k, False: 106k]
741
235k
            assert(unique_wtxids_to_scores.contains(wtxid));
  Branch (741:13): [True: 235k, False: 0]
742
235k
        }
743
106k
    }
744
745
    // Cached m_unique_orphans value is correct.
746
5.14k
    assert(m_orphans.size() >= m_unique_orphans);
  Branch (746:5): [True: 5.14k, False: 0]
747
5.14k
    assert(m_orphans.size() <= m_peer_orphanage_info.size() * m_unique_orphans);
  Branch (747:5): [True: 5.14k, False: 0]
748
5.14k
    assert(unique_wtxids_to_scores.size() == m_unique_orphans);
  Branch (748:5): [True: 5.14k, False: 0]
749
750
5.14k
    const auto calculated_dedup_usage = std::accumulate(unique_wtxids_to_scores.begin(), unique_wtxids_to_scores.end(),
751
39.9k
        TxOrphanage::Usage{0}, [](TxOrphanage::Usage sum, const auto pair) { return sum + pair.second.first; });
752
5.14k
    assert(calculated_dedup_usage == m_unique_orphan_usage);
  Branch (752:5): [True: 5.14k, False: 0]
753
754
    // Global usage is deduplicated, should be less than or equal to the sum of all per-peer usages.
755
5.14k
    const auto summed_peer_usage = std::accumulate(m_peer_orphanage_info.begin(), m_peer_orphanage_info.end(),
756
39.2k
        TxOrphanage::Usage{0}, [](TxOrphanage::Usage sum, const auto pair) { return sum + pair.second.m_total_usage; });
757
5.14k
    assert(summed_peer_usage >= m_unique_orphan_usage);
  Branch (757:5): [True: 5.14k, False: 0]
758
759
    // Cached m_unique_rounded_input_scores value is correct.
760
5.14k
    const auto calculated_total_latency_score = std::accumulate(unique_wtxids_to_scores.begin(), unique_wtxids_to_scores.end(),
761
39.9k
        TxOrphanage::Count{0}, [](TxOrphanage::Count sum, const auto pair) { return sum + pair.second.second; });
762
5.14k
    assert(calculated_total_latency_score == m_unique_rounded_input_scores);
  Branch (762:5): [True: 5.14k, False: 0]
763
764
    // Global latency score is deduplicated, should be less than or equal to the sum of all per-peer latency scores.
765
5.14k
    const auto summed_peer_latency_score = std::accumulate(m_peer_orphanage_info.begin(), m_peer_orphanage_info.end(),
766
39.2k
        TxOrphanage::Count{0}, [](TxOrphanage::Count sum, const auto pair) { return sum + pair.second.m_total_latency_score; });
767
5.14k
    assert(summed_peer_latency_score >= m_unique_rounded_input_scores + m_orphans.size());
  Branch (767:5): [True: 5.14k, False: 0]
768
769
5.14k
    assert(!NeedsTrim());
  Branch (769:5): [True: 5.14k, False: 0]
770
5.14k
}
771
772
3.49M
TxOrphanage::Count TxOrphanageImpl::MaxGlobalLatencyScore() const { return m_max_global_latency_score; }
773
3.34M
TxOrphanage::Count TxOrphanageImpl::TotalLatencyScore() const { return m_unique_rounded_input_scores + m_orphans.size(); }
774
135k
TxOrphanage::Usage TxOrphanageImpl::ReservedPeerUsage() const { return m_reserved_usage_per_peer; }
775
135k
TxOrphanage::Count TxOrphanageImpl::MaxPeerLatencyScore() const { return m_max_global_latency_score / std::max<unsigned int>(m_peer_orphanage_info.size(), 1); }
776
3.06M
TxOrphanage::Usage TxOrphanageImpl::MaxGlobalUsage() const { return m_reserved_usage_per_peer * std::max<int64_t>(m_peer_orphanage_info.size(), 1); }
777
778
bool TxOrphanageImpl::NeedsTrim() const
779
3.20M
{
780
3.20M
    return TotalLatencyScore() > MaxGlobalLatencyScore() || TotalOrphanUsage() > MaxGlobalUsage();
  Branch (780:12): [True: 279k, False: 2.92M]
  Branch (780:61): [True: 172k, False: 2.75M]
781
3.20M
}
782
std::unique_ptr<TxOrphanage> MakeTxOrphanage() noexcept
783
24.7k
{
784
24.7k
    return std::make_unique<TxOrphanageImpl>();
785
24.7k
}
786
std::unique_ptr<TxOrphanage> MakeTxOrphanage(TxOrphanage::Count max_global_latency_score, TxOrphanage::Usage reserved_peer_usage) noexcept
787
2.21k
{
788
2.21k
    return std::make_unique<TxOrphanageImpl>(max_global_latency_score, reserved_peer_usage);
789
2.21k
}
790
} // namespace node