Coverage Report

Created: 2026-08-25 19:02

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/root/bitcoin/src/test/fuzz/txorphan.cpp
Line
Count
Source
1
// Copyright (c) 2022-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 <consensus/amount.h>
6
#include <consensus/validation.h>
7
#include <net_processing.h>
8
#include <node/eviction.h>
9
#include <node/txorphanage.h>
10
#include <policy/policy.h>
11
#include <primitives/transaction.h>
12
#include <script/script.h>
13
#include <sync.h>
14
#include <test/fuzz/FuzzedDataProvider.h>
15
#include <test/fuzz/fuzz.h>
16
#include <test/fuzz/util.h>
17
#include <test/util/setup_common.h>
18
#include <test/util/time.h>
19
#include <uint256.h>
20
#include <util/check.h>
21
#include <util/feefrac.h>
22
#include <util/time.h>
23
24
#include <algorithm>
25
#include <bitset>
26
#include <cmath>
27
#include <cstdint>
28
#include <iostream>
29
#include <memory>
30
#include <set>
31
#include <utility>
32
#include <vector>
33
34
void initialize_orphanage()
35
0
{
36
0
    static const auto testing_setup = MakeNoLogFileContext();
37
0
}
38
39
FUZZ_TARGET(txorphan, .init = initialize_orphanage)
40
940
{
41
940
    SeedRandomStateForTest(SeedRand::ZEROS);
42
940
    FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
43
940
    FastRandomContext orphanage_rng{ConsumeUInt256(fuzzed_data_provider)};
44
940
    FakeNodeClock clock{ConsumeTime(fuzzed_data_provider)};
45
46
940
    auto orphanage = node::MakeTxOrphanage();
47
940
    std::vector<COutPoint> outpoints; // Duplicates are tolerated
48
940
    outpoints.reserve(200'000);
49
50
    // initial outpoints used to construct transactions later
51
4.70k
    for (uint8_t i = 0; i < 4; i++) {
  Branch (51:25): [True: 3.76k, False: 940]
52
3.76k
        outpoints.emplace_back(Txid::FromUint256(uint256{i}), 0);
53
3.76k
    }
54
55
940
    CTransactionRef ptx_potential_parent = nullptr;
56
57
940
    std::vector<CTransactionRef> tx_history;
58
59
93.7k
    LIMITED_WHILE (outpoints.size() < 200'000 && fuzzed_data_provider.ConsumeBool(), 1000) {
60
        // construct transaction
61
93.7k
        const CTransactionRef tx = [&] {
62
93.7k
            CMutableTransaction tx_mut;
63
93.7k
            const auto num_in = fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(1, outpoints.size());
64
93.7k
            const auto num_out = fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(1, 256);
65
            // pick outpoints from outpoints as input. We allow input duplicates on purpose, given we are not
66
            // running any transaction validation logic before adding transactions to the orphanage
67
93.7k
            tx_mut.vin.reserve(num_in);
68
17.5M
            for (uint32_t i = 0; i < num_in; i++) {
  Branch (68:34): [True: 17.5M, False: 93.7k]
69
17.5M
                auto& prevout = PickValue(fuzzed_data_provider, outpoints);
70
                // try making transactions unique by setting a random nSequence, but allow duplicate transactions if they happen
71
17.5M
                tx_mut.vin.emplace_back(prevout, CScript{}, fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(0, CTxIn::SEQUENCE_FINAL));
72
17.5M
            }
73
            // output amount will not affect txorphanage
74
93.7k
            tx_mut.vout.reserve(num_out);
75
7.21M
            for (uint32_t i = 0; i < num_out; i++) {
  Branch (75:34): [True: 7.12M, False: 93.7k]
76
7.12M
                tx_mut.vout.emplace_back(CAmount{0}, CScript{});
77
7.12M
            }
78
93.7k
            auto new_tx = MakeTransactionRef(tx_mut);
79
            // add newly constructed outpoints to the coin pool
80
7.21M
            for (uint32_t i = 0; i < num_out; i++) {
  Branch (80:34): [True: 7.12M, False: 93.7k]
81
7.12M
                outpoints.emplace_back(new_tx->GetHash(), i);
82
7.12M
            }
83
93.7k
            return new_tx;
84
93.7k
        }();
85
86
93.7k
        tx_history.push_back(tx);
87
88
93.7k
        const auto wtxid{tx->GetWitnessHash()};
89
90
        // Trigger orphanage functions that are called using parents. ptx_potential_parent is a tx we constructed in a
91
        // previous loop and potentially the parent of this tx.
92
93.7k
        if (ptx_potential_parent) {
  Branch (92:13): [True: 92.8k, False: 929]
93
            // Set up future GetTxToReconsider call.
94
92.8k
            orphanage->AddChildrenToWorkSet(*ptx_potential_parent, orphanage_rng);
95
96
            // Check that all txns returned from GetChildrenFrom* are indeed a direct child of this tx.
97
92.8k
            NodeId peer_id = fuzzed_data_provider.ConsumeIntegral<NodeId>();
98
93.2k
            for (const auto& child : orphanage->GetChildrenFromSamePeer(ptx_potential_parent, peer_id)) {
  Branch (98:36): [True: 93.2k, False: 92.8k]
99
93.2k
                assert(std::any_of(child->vin.cbegin(), child->vin.cend(), [&](const auto& input) {
  Branch (99:17): [True: 93.2k, False: 0]
100
93.2k
                    return input.prevout.hash == ptx_potential_parent->GetHash();
101
93.2k
                }));
102
93.2k
            }
103
92.8k
        }
104
105
        // trigger orphanage functions
106
3.44M
        LIMITED_WHILE (fuzzed_data_provider.ConsumeBool(), 1000) {
107
3.44M
            NodeId peer_id = fuzzed_data_provider.ConsumeIntegral<NodeId>();
108
3.44M
            const auto total_bytes_start{orphanage->TotalOrphanUsage()};
109
3.44M
            const auto total_peer_bytes_start{orphanage->UsageByPeer(peer_id)};
110
3.44M
            const auto tx_weight{GetTransactionWeight(*tx)};
111
112
3.44M
            CallOneOf(
113
3.44M
                fuzzed_data_provider,
114
3.44M
                [&] {
115
23.4k
                    {
116
23.4k
                        CTransactionRef ref = orphanage->GetTxToReconsider(peer_id);
117
23.4k
                        if (ref) {
  Branch (117:29): [True: 4.94k, False: 18.5k]
118
4.94k
                            Assert(orphanage->HaveTx(ref->GetWitnessHash()));
119
4.94k
                        }
120
23.4k
                    }
121
23.4k
                },
122
3.44M
                [&] {
123
3.13M
                    bool have_tx = orphanage->HaveTx(tx->GetWitnessHash());
124
3.13M
                    bool have_tx_and_peer = orphanage->HaveTxFromPeer(wtxid, peer_id);
125
                    // AddTx should return false if tx is too big or already have it
126
                    // tx weight is unknown, we only check when tx is already in orphanage
127
3.13M
                    {
128
3.13M
                        bool add_tx = orphanage->AddTx(tx, peer_id);
129
                        // have_tx == true -> add_tx == false
130
3.13M
                        Assert(!have_tx || !add_tx);
131
                        // have_tx_and_peer == true -> add_tx == false
132
3.13M
                        Assert(!have_tx_and_peer || !add_tx);
133
                        // After AddTx, the orphanage may trim itself, so the peer's usage may have gone up or down.
134
135
3.13M
                        if (add_tx) {
  Branch (135:29): [True: 178k, False: 2.95M]
136
178k
                            Assert(tx_weight <= MAX_STANDARD_TX_WEIGHT);
137
2.95M
                        } else {
138
                            // Peer may have been added as an announcer.
139
2.95M
                            if (orphanage->UsageByPeer(peer_id) > total_peer_bytes_start) {
  Branch (139:33): [True: 445k, False: 2.51M]
140
445k
                                Assert(orphanage->HaveTxFromPeer(wtxid, peer_id));
141
445k
                            }
142
143
                            // If announcement was added, total bytes does not increase.
144
                            // However, if eviction was triggered, the value may decrease.
145
2.95M
                            Assert(orphanage->TotalOrphanUsage() <= total_bytes_start);
146
2.95M
                        }
147
3.13M
                    }
148
                    // We are not guaranteed to have_tx after AddTx. There are a few possible reasons:
149
                    // - tx itself exceeds the per-peer memory usage limit, so LimitOrphans had to remove it immediately
150
                    // - tx itself exceeds the per-peer latency score limit, so LimitOrphans had to remove it immediately
151
                    // - the orphanage needed trim and all other announcements from this peer are reconsiderable
152
3.13M
                },
153
3.44M
                [&] {
154
9.95k
                    bool have_tx = orphanage->HaveTx(tx->GetWitnessHash());
155
9.95k
                    bool have_tx_and_peer = orphanage->HaveTxFromPeer(tx->GetWitnessHash(), peer_id);
156
                    // AddAnnouncer should return false if tx doesn't exist or we already HaveTxFromPeer.
157
9.95k
                    {
158
9.95k
                        bool added_announcer = orphanage->AddAnnouncer(tx->GetWitnessHash(), peer_id);
159
                        // have_tx == false -> added_announcer == false
160
9.95k
                        Assert(have_tx || !added_announcer);
161
                        // have_tx_and_peer == true -> added_announcer == false
162
9.95k
                        Assert(!have_tx_and_peer || !added_announcer);
163
164
                        // If announcement was added, total bytes does not increase.
165
                        // However, if eviction was triggered, the value may decrease.
166
9.95k
                        Assert(orphanage->TotalOrphanUsage() <= total_bytes_start);
167
9.95k
                    }
168
9.95k
                },
169
3.44M
                [&] {
170
243k
                    bool have_tx = orphanage->HaveTx(tx->GetWitnessHash());
171
243k
                    bool have_tx_and_peer{orphanage->HaveTxFromPeer(wtxid, peer_id)};
172
                    // EraseTx should return 0 if m_orphans doesn't have the tx
173
243k
                    {
174
243k
                        auto bytes_from_peer_before{orphanage->UsageByPeer(peer_id)};
175
243k
                        Assert(have_tx == orphanage->EraseTx(tx->GetWitnessHash()));
176
                        // After EraseTx, the orphanage may trim itself, so any peer's usage may decrease.
177
243k
                        if (!have_tx) {
  Branch (177:29): [True: 137k, False: 106k]
178
137k
                            Assert(orphanage->UsageByPeer(peer_id) == bytes_from_peer_before);
179
137k
                        } else if (have_tx_and_peer) {
  Branch (179:36): [True: 5.90k, False: 100k]
180
5.90k
                            Assert(orphanage->UsageByPeer(peer_id) <= bytes_from_peer_before - tx_weight);
181
100k
                        } else {
182
100k
                            Assert(orphanage->UsageByPeer(peer_id) <= bytes_from_peer_before);
183
100k
                        }
184
243k
                    }
185
243k
                    have_tx = orphanage->HaveTx(tx->GetWitnessHash());
186
243k
                    have_tx_and_peer = orphanage->HaveTxFromPeer(wtxid, peer_id);
187
                    // have_tx should be false and EraseTx should fail
188
243k
                    {
189
243k
                        Assert(!have_tx && !have_tx_and_peer && !orphanage->EraseTx(wtxid));
190
243k
                    }
191
243k
                },
192
3.44M
                [&] {
193
18.7k
                    orphanage->EraseForPeer(peer_id);
194
18.7k
                    Assert(!orphanage->HaveTxFromPeer(tx->GetWitnessHash(), peer_id));
195
18.7k
                    Assert(orphanage->UsageByPeer(peer_id) == 0);
196
18.7k
                },
197
3.44M
                [&] {
198
                    // Make a block out of txs and then EraseForBlock
199
12.9k
                    CBlock block;
200
12.9k
                    int64_t block_weight{0};
201
12.9k
                    int num_txs = fuzzed_data_provider.ConsumeIntegralInRange<unsigned int>(0, 1000);
202
1.69M
                    for (int i{0}; i < num_txs; ++i) {
  Branch (202:36): [True: 1.68M, False: 9.16k]
203
1.68M
                        auto& tx_to_remove = PickValue(fuzzed_data_provider, tx_history);
204
1.68M
                        const auto tx_weight = GetTransactionWeight(*tx_to_remove);
205
1.68M
                        if (block_weight + tx_weight > MAX_BLOCK_WEIGHT) break;
  Branch (205:29): [True: 3.80k, False: 1.68M]
206
1.68M
                        block_weight += tx_weight;
207
1.68M
                        block.vtx.push_back(tx_to_remove);
208
1.68M
                    }
209
12.9k
                    orphanage->EraseForBlock(block);
210
1.68M
                    for (const auto& tx_removed : block.vtx) {
  Branch (210:49): [True: 1.68M, False: 12.9k]
211
1.68M
                        Assert(!orphanage->HaveTx(tx_removed->GetWitnessHash()));
212
1.68M
                        Assert(!orphanage->HaveTxFromPeer(tx_removed->GetWitnessHash(), peer_id));
213
1.68M
                    }
214
12.9k
                }
215
3.44M
            );
216
3.44M
        }
217
218
        // Set tx as potential parent to be used for future GetChildren() calls.
219
93.7k
        if (!ptx_potential_parent || fuzzed_data_provider.ConsumeBool()) {
  Branch (219:13): [True: 929, False: 92.8k]
  Branch (219:38): [True: 59.9k, False: 32.8k]
220
60.8k
            ptx_potential_parent = tx;
221
60.8k
        }
222
223
93.7k
        const bool have_tx{orphanage->HaveTx(tx->GetWitnessHash())};
224
93.7k
        const bool get_tx_nonnull{orphanage->GetTx(tx->GetWitnessHash()) != nullptr};
225
93.7k
        Assert(have_tx == get_tx_nonnull);
226
93.7k
    }
227
940
    orphanage->SanityCheck();
228
940
}
229
230
FUZZ_TARGET(txorphan_protected, .init = initialize_orphanage)
231
771
{
232
771
    SeedRandomStateForTest(SeedRand::ZEROS);
233
771
    FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
234
771
    FastRandomContext orphanage_rng{ConsumeUInt256(fuzzed_data_provider)};
235
771
    FakeNodeClock clock{ConsumeTime(fuzzed_data_provider)};
236
237
    // We have num_peers peers. Some subset of them will never exceed their reserved weight or announcement count, and
238
    // should therefore never have any orphans evicted.
239
771
    const unsigned int MAX_PEERS = 125;
240
771
    const unsigned int num_peers = fuzzed_data_provider.ConsumeIntegralInRange<unsigned int>(1, MAX_PEERS);
241
    // Generate a vector of bools for whether each peer is protected from eviction
242
771
    std::bitset<MAX_PEERS> protected_peers;
243
18.3k
    for (unsigned int i = 0; i < num_peers; i++) {
  Branch (243:30): [True: 17.5k, False: 771]
244
17.5k
        protected_peers.set(i, fuzzed_data_provider.ConsumeBool());
245
17.5k
    }
246
247
    // Params for orphanage.
248
771
    const unsigned int global_latency_score_limit = fuzzed_data_provider.ConsumeIntegralInRange<unsigned int>(num_peers, 6'000);
249
771
    const int64_t per_peer_weight_reservation = fuzzed_data_provider.ConsumeIntegralInRange<int64_t>(1, 4'040'000);
250
771
    auto orphanage = node::MakeTxOrphanage(global_latency_score_limit, per_peer_weight_reservation);
251
252
    // The actual limit, MaxPeerLatencyScore(), may be higher, since TxOrphanage only counts peers
253
    // that have announced an orphan. The honest peer will not experience evictions if it never
254
    // exceeds this.
255
771
    const unsigned int honest_latency_limit = global_latency_score_limit / num_peers;
256
    // Honest peer will not experience evictions if it never exceeds this.
257
771
    const int64_t honest_mem_limit = per_peer_weight_reservation;
258
259
771
    std::vector<COutPoint> outpoints; // Duplicates are tolerated
260
771
    outpoints.reserve(400);
261
262
    // initial outpoints used to construct transactions later
263
3.85k
    for (uint8_t i = 0; i < 4; i++) {
  Branch (263:25): [True: 3.08k, False: 771]
264
3.08k
        outpoints.emplace_back(Txid::FromUint256(uint256{i}), 0);
265
3.08k
    }
266
267
    // These are honest peer's live announcements. We expect them to be protected from eviction.
268
771
    std::set<Wtxid> protected_wtxids;
269
270
7.64k
    LIMITED_WHILE (outpoints.size() < 400 && fuzzed_data_provider.ConsumeBool(), 1000) {
271
        // construct transaction
272
7.64k
        const CTransactionRef tx = [&] {
273
7.64k
            CMutableTransaction tx_mut;
274
7.64k
            const auto num_in = fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(1, outpoints.size());
275
7.64k
            const auto num_out = fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(1, 256);
276
            // pick outpoints from outpoints as input. We allow input duplicates on purpose, given we are not
277
            // running any transaction validation logic before adding transactions to the orphanage
278
7.64k
            tx_mut.vin.reserve(num_in);
279
118k
            for (uint32_t i = 0; i < num_in; i++) {
  Branch (279:34): [True: 110k, False: 7.64k]
280
110k
                auto& prevout = PickValue(fuzzed_data_provider, outpoints);
281
                // try making transactions unique by setting a random nSequence, but allow duplicate transactions if they happen
282
110k
                tx_mut.vin.emplace_back(prevout, CScript{}, fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(0, CTxIn::SEQUENCE_FINAL));
283
110k
            }
284
            // output amount or spendability will not affect txorphanage
285
7.64k
            tx_mut.vout.reserve(num_out);
286
76.0k
            for (uint32_t i = 0; i < num_out; i++) {
  Branch (286:34): [True: 68.3k, False: 7.64k]
287
68.3k
                const auto payload_size = fuzzed_data_provider.ConsumeIntegralInRange<unsigned int>(0, 100000);
288
68.3k
                if (payload_size) {
  Branch (288:21): [True: 50.5k, False: 17.8k]
289
50.5k
                    tx_mut.vout.emplace_back(0, CScript() << OP_RETURN << std::vector<unsigned char>(payload_size));
290
50.5k
                } else {
291
17.8k
                    tx_mut.vout.emplace_back(0, CScript{});
292
17.8k
                }
293
68.3k
            }
294
7.64k
            auto new_tx = MakeTransactionRef(tx_mut);
295
            // add newly constructed outpoints to the coin pool
296
76.0k
            for (uint32_t i = 0; i < num_out; i++) {
  Branch (296:34): [True: 68.3k, False: 7.64k]
297
68.3k
                outpoints.emplace_back(new_tx->GetHash(), i);
298
68.3k
            }
299
7.64k
            return new_tx;
300
7.64k
        }();
301
302
7.64k
        const auto wtxid{tx->GetWitnessHash()};
303
304
        // orphanage functions
305
3.54M
        LIMITED_WHILE (fuzzed_data_provider.remaining_bytes(), 10 * global_latency_score_limit) {
306
3.54M
            NodeId peer_id = fuzzed_data_provider.ConsumeIntegralInRange<NodeId>(0, num_peers - 1);
307
3.54M
            const auto tx_weight{GetTransactionWeight(*tx)};
308
309
            // This protected peer will never send orphans that would
310
            // exceed their own personal allotment, so is never evicted.
311
3.54M
            const bool peer_is_protected{protected_peers[peer_id]};
312
313
3.54M
            CallOneOf(
314
3.54M
                fuzzed_data_provider,
315
3.54M
                [&] { // AddTx
316
1.07M
                    bool have_tx_and_peer = orphanage->HaveTxFromPeer(wtxid, peer_id);
317
1.07M
                    if (peer_is_protected && !have_tx_and_peer &&
  Branch (317:25): [True: 436k, False: 642k]
  Branch (317:46): [True: 266k, False: 169k]
318
1.07M
                        (orphanage->UsageByPeer(peer_id) + tx_weight > honest_mem_limit ||
  Branch (318:26): [True: 109k, False: 156k]
319
266k
                        orphanage->LatencyScoreFromPeer(peer_id) + (tx->vin.size() / 10) + 1 > honest_latency_limit)) {
  Branch (319:25): [True: 11.5k, False: 145k]
320
                        // We never want our protected peer oversized or over-announced
321
957k
                    } else {
322
957k
                        orphanage->AddTx(tx, peer_id);
323
957k
                        if (peer_is_protected && orphanage->HaveTxFromPeer(wtxid, peer_id)) {
  Branch (323:29): [True: 314k, False: 642k]
  Branch (323:50): [True: 312k, False: 2.03k]
324
312k
                            protected_wtxids.insert(wtxid);
325
312k
                        }
326
957k
                    }
327
1.07M
                },
328
3.54M
                [&] { // AddAnnouncer
329
1.30M
                    bool have_tx_and_peer = orphanage->HaveTxFromPeer(tx->GetWitnessHash(), peer_id);
330
                    // AddAnnouncer should return false if tx doesn't exist or we already HaveTxFromPeer.
331
1.30M
                    {
332
1.30M
                        if (peer_is_protected && !have_tx_and_peer &&
  Branch (332:29): [True: 768k, False: 539k]
  Branch (332:50): [True: 550k, False: 218k]
333
1.30M
                            (orphanage->UsageByPeer(peer_id) + tx_weight > honest_mem_limit ||
  Branch (333:30): [True: 245k, False: 305k]
334
550k
                            orphanage->LatencyScoreFromPeer(peer_id) + (tx->vin.size() / 10) + 1 > honest_latency_limit)) {
  Branch (334:29): [True: 9.47k, False: 295k]
335
                            // We never want our protected peer oversized
336
1.05M
                        } else {
337
1.05M
                            orphanage->AddAnnouncer(tx->GetWitnessHash(), peer_id);
338
1.05M
                            if (peer_is_protected && orphanage->HaveTxFromPeer(wtxid, peer_id)) {
  Branch (338:33): [True: 513k, False: 539k]
  Branch (338:54): [True: 285k, False: 228k]
339
285k
                                protected_wtxids.insert(wtxid);
340
285k
                            }
341
1.05M
                        }
342
1.30M
                    }
343
1.30M
                },
344
3.54M
                [&] { // EraseTx
345
442k
                    if (protected_wtxids.contains(tx->GetWitnessHash())) {
  Branch (345:25): [True: 96.6k, False: 346k]
346
96.6k
                        protected_wtxids.erase(wtxid);
347
96.6k
                    }
348
442k
                    orphanage->EraseTx(wtxid);
349
442k
                    Assert(!orphanage->HaveTx(wtxid));
350
442k
                },
351
3.54M
                [&] { // EraseForPeer
352
712k
                    if (!protected_peers[peer_id]) {
  Branch (352:25): [True: 313k, False: 399k]
353
313k
                        orphanage->EraseForPeer(peer_id);
354
313k
                        Assert(orphanage->UsageByPeer(peer_id) == 0);
355
313k
                        Assert(orphanage->LatencyScoreFromPeer(peer_id) == 0);
356
313k
                        Assert(orphanage->AnnouncementsFromPeer(peer_id) == 0);
357
313k
                    }
358
712k
                }
359
3.54M
            );
360
3.54M
        }
361
7.64k
    }
362
363
771
    orphanage->SanityCheck();
364
    // All of the honest peer's announcements are still present.
365
771
    for (const auto& wtxid : protected_wtxids) {
  Branch (365:28): [True: 674, False: 771]
366
674
        Assert(orphanage->HaveTx(wtxid));
367
674
    }
368
771
}
369
370
FUZZ_TARGET(txorphanage_sim)
371
1.43k
{
372
1.43k
    SeedRandomStateForTest(SeedRand::ZEROS);
373
    // This is a comprehensive simulation fuzz test, which runs through a scenario involving up to
374
    // 16 transactions (which may have simple or complex topology, and may have duplicate txids
375
    // with distinct wtxids, and up to 16 peers. The scenario is performed both on a real
376
    // TxOrphanage object and the behavior is compared with a naive reimplementation (just a vector
377
    // of announcements) where possible, and tested for desired properties where not possible.
378
379
    //
380
    // 1. Setup.
381
    //
382
383
    /** The total number of transactions this simulation uses (not all of which will necessarily
384
     *  be present in the orphanage at once). */
385
1.43k
    static constexpr unsigned NUM_TX = 16;
386
    /** The number of peers this simulation uses (not all of which will necessarily be present in
387
     *  the orphanage at once). */
388
1.43k
    static constexpr unsigned NUM_PEERS = 16;
389
    /** The maximum number of announcements this simulation uses (which may be higher than the
390
     *  number permitted inside the orphanage). */
391
1.43k
    static constexpr unsigned MAX_ANN = 64;
392
393
1.43k
    FuzzedDataProvider provider(buffer.data(), buffer.size());
394
    /** Local RNG. Only used for topology/sizes of the transaction set, the order of transactions
395
     *  in EraseForBlock, and for the randomized passed to AddChildrenToWorkSet. */
396
1.43k
    InsecureRandomContext rng(provider.ConsumeIntegral<uint64_t>());
397
398
    //
399
    // 2. Construct an interesting set of 16 transactions.
400
    //
401
402
    // - Pick a topological order among the transactions.
403
1.43k
    std::vector<unsigned> txorder(NUM_TX);
404
1.43k
    std::iota(txorder.begin(), txorder.end(), unsigned{0});
405
1.43k
    std::shuffle(txorder.begin(), txorder.end(), rng);
406
    // - Pick a set of dependencies (pair<child_index, parent_index>).
407
1.43k
    std::vector<std::pair<unsigned, unsigned>> deps;
408
1.43k
    deps.reserve((NUM_TX * (NUM_TX - 1)) / 2);
409
23.0k
    for (unsigned p = 0; p < NUM_TX - 1; ++p) {
  Branch (409:26): [True: 21.5k, False: 1.43k]
410
194k
        for (unsigned c = p + 1; c < NUM_TX; ++c) {
  Branch (410:34): [True: 172k, False: 21.5k]
411
172k
            deps.emplace_back(c, p);
412
172k
        }
413
21.5k
    }
414
1.43k
    std::shuffle(deps.begin(), deps.end(), rng);
415
1.43k
    deps.resize(provider.ConsumeIntegralInRange<unsigned>(0, NUM_TX * 4 - 1));
416
    // - Construct the actual transactions.
417
1.43k
    std::set<Wtxid> wtxids;
418
1.43k
    std::vector<CTransactionRef> txn(NUM_TX);
419
1.43k
    node::TxOrphanage::Usage total_usage{0};
420
24.4k
    for (unsigned t = 0; t < NUM_TX; ++t) {
  Branch (420:26): [True: 23.0k, False: 1.43k]
421
23.0k
        CMutableTransaction tx;
422
23.0k
        if (t > 0 && rng.randrange(4) == 0) {
  Branch (422:13): [True: 21.5k, False: 1.43k]
  Branch (422:22): [True: 11.2k, False: 10.3k]
423
            // Occasionally duplicate the previous transaction, so that repetitions of the same
424
            // txid are possible (with different wtxid).
425
11.2k
            tx = CMutableTransaction(*txn[txorder[t - 1]]);
426
11.7k
        } else {
427
11.7k
            tx.version = 1;
428
11.7k
            tx.nLockTime = 0xffffffff;
429
            // Construct 1 to 16 outputs.
430
11.7k
            auto num_outputs = rng.randrange<unsigned>(1 << rng.randrange<unsigned>(5)) + 1;
431
47.1k
            for (unsigned output = 0; output < num_outputs; ++output) {
  Branch (431:39): [True: 35.4k, False: 11.7k]
432
35.4k
                CScript scriptpubkey;
433
35.4k
                scriptpubkey.resize(provider.ConsumeIntegralInRange<unsigned>(20, 34));
434
35.4k
                tx.vout.emplace_back(CAmount{0}, std::move(scriptpubkey));
435
35.4k
            }
436
            // Construct inputs (one for each dependency).
437
386k
            for (auto& [child, parent] : deps) {
  Branch (437:40): [True: 386k, False: 11.7k]
438
386k
                if (child == t) {
  Branch (438:21): [True: 22.2k, False: 363k]
439
22.2k
                    auto& partx = txn[txorder[parent]];
440
22.2k
                    assert(partx->version == 1);
  Branch (440:21): [True: 22.2k, False: 0]
441
22.2k
                    COutPoint outpoint(partx->GetHash(), rng.randrange<size_t>(partx->vout.size()));
442
22.2k
                    tx.vin.emplace_back(outpoint);
443
22.2k
                    tx.vin.back().scriptSig.resize(provider.ConsumeIntegralInRange<unsigned>(16, 200));
444
22.2k
                }
445
386k
            }
446
            // Construct fallback input in case there are no dependencies.
447
11.7k
            if (tx.vin.empty()) {
  Branch (447:17): [True: 5.15k, False: 6.62k]
448
5.15k
                COutPoint outpoint(Txid::FromUint256(rng.rand256()), rng.randrange<size_t>(16));
449
5.15k
                tx.vin.emplace_back(outpoint);
450
5.15k
                tx.vin.back().scriptSig.resize(provider.ConsumeIntegralInRange<unsigned>(16, 200));
451
5.15k
            }
452
11.7k
        }
453
        // Optionally modify the witness (allowing wtxid != txid), and certainly when the wtxid
454
        // already exists.
455
58.3k
        while (wtxids.contains(CTransaction(tx).GetWitnessHash()) || rng.randrange(4) == 0) {
  Branch (455:16): [True: 26.5k, False: 31.7k]
  Branch (455:16): [True: 35.2k, False: 23.0k]
  Branch (455:70): [True: 8.68k, False: 23.0k]
456
35.2k
            auto& input = tx.vin[rng.randrange(tx.vin.size())];
457
35.2k
            if (rng.randbool()) {
  Branch (457:17): [True: 16.6k, False: 18.6k]
458
16.6k
                input.scriptWitness.stack.resize(1);
459
16.6k
                input.scriptWitness.stack[0].resize(rng.randrange(100));
460
18.6k
            } else {
461
18.6k
                input.scriptWitness.stack.resize(0);
462
18.6k
            }
463
35.2k
        }
464
        // Convert to CTransactionRef.
465
23.0k
        txn[txorder[t]] = MakeTransactionRef(std::move(tx));
466
23.0k
        wtxids.insert(txn[txorder[t]]->GetWitnessHash());
467
23.0k
        auto weight = GetTransactionWeight(*txn[txorder[t]]);
468
23.0k
        assert(weight < MAX_STANDARD_TX_WEIGHT);
  Branch (468:9): [True: 23.0k, False: 0]
469
23.0k
        total_usage += GetTransactionWeight(*txn[txorder[t]]);
470
23.0k
    }
471
472
    //
473
    // 3. Initialize real orphanage
474
    //
475
476
1.43k
    auto max_global_latency_score = provider.ConsumeIntegralInRange<node::TxOrphanage::Count>(NUM_PEERS, MAX_ANN);
477
1.43k
    auto reserved_peer_usage = provider.ConsumeIntegralInRange<node::TxOrphanage::Usage>(1, total_usage);
478
1.43k
    auto real = node::MakeTxOrphanage(max_global_latency_score, reserved_peer_usage);
479
480
    //
481
    // 4. Functions and data structures for the simulation.
482
    //
483
484
    /** Data structure representing one announcement (pair of (tx, peer), plus whether it's
485
     *  reconsiderable or not. */
486
1.43k
    struct SimAnnouncement
487
1.43k
    {
488
1.43k
        unsigned tx;
489
1.43k
        NodeId announcer;
490
1.43k
        bool reconsider{false};
491
1.43k
        SimAnnouncement(unsigned tx_in, NodeId announcer_in, bool reconsider_in) noexcept :
492
61.3k
            tx(tx_in), announcer(announcer_in), reconsider(reconsider_in) {}
493
1.43k
    };
494
    /** The entire simulated orphanage is represented by this list of announcements, in
495
     *  announcement order (unlike TxOrphanageImpl which uses a sequence number to represent
496
     *  announcement order). New announcements are added to the back. */
497
1.43k
    std::vector<SimAnnouncement> sim_announcements;
498
499
    /** Consume a transaction (index into txn) from provider. */
500
20.3k
    auto read_tx_fn = [&]() -> unsigned { return provider.ConsumeIntegralInRange<unsigned>(0, NUM_TX - 1); };
501
    /** Consume a NodeId from provider. */
502
22.4k
    auto read_peer_fn = [&]() -> NodeId { return provider.ConsumeIntegralInRange<unsigned>(0, NUM_PEERS - 1); };
503
    /** Consume both a transaction (index into txn) and a NodeId from provider. */
504
86.0k
    auto read_tx_peer_fn = [&]() -> std::pair<unsigned, NodeId> {
505
86.0k
        auto code = provider.ConsumeIntegralInRange<unsigned>(0, NUM_TX * NUM_PEERS - 1);
506
86.0k
        return {code % NUM_TX, code / NUM_TX};
507
86.0k
    };
508
    /** Determine if we have any announcements of the given transaction in the simulation. */
509
2.92M
    auto have_tx_fn = [&](unsigned tx) -> bool {
510
15.9M
        for (auto& ann : sim_announcements) {
  Branch (510:24): [True: 15.9M, False: 1.97M]
511
15.9M
            if (ann.tx == tx) return true;
  Branch (511:17): [True: 946k, False: 14.9M]
512
15.9M
        }
513
1.97M
        return false;
514
2.92M
    };
515
    /** Count the number of peers in the simulation. */
516
303k
    auto count_peers_fn = [&]() -> unsigned {
517
303k
        std::bitset<NUM_PEERS> mask;
518
2.44M
        for (auto& ann : sim_announcements) {
  Branch (518:24): [True: 2.44M, False: 303k]
519
2.44M
            mask.set(ann.announcer);
520
2.44M
        }
521
303k
        return mask.count();
522
303k
    };
523
    /** Determine if we have any reconsiderable announcements of a given transaction. */
524
65.1k
    auto have_reconsiderable_fn = [&](unsigned tx) -> bool {
525
736k
        for (auto& ann : sim_announcements) {
  Branch (525:24): [True: 736k, False: 42.3k]
526
736k
            if (ann.reconsider && ann.tx == tx) return true;
  Branch (526:17): [True: 157k, False: 579k]
  Branch (526:35): [True: 22.7k, False: 134k]
527
736k
        }
528
42.3k
        return false;
529
65.1k
    };
530
    /** Determine if a peer has any transactions to reconsider. */
531
29.4k
    auto have_reconsider_fn = [&](NodeId peer) -> bool {
532
204k
        for (auto& ann : sim_announcements) {
  Branch (532:24): [True: 204k, False: 27.8k]
533
204k
            if (ann.reconsider && ann.announcer == peer) return true;
  Branch (533:17): [True: 34.6k, False: 170k]
  Branch (533:35): [True: 1.60k, False: 33.0k]
534
204k
        }
535
27.8k
        return false;
536
29.4k
    };
537
    /** Get an iterator to an existing (wtxid, peer) pair in the simulation. */
538
23.3k
    auto find_announce_wtxid_fn = [&](const Wtxid& wtxid, NodeId peer) -> std::vector<SimAnnouncement>::iterator {
539
129k
        for (auto it = sim_announcements.begin(); it != sim_announcements.end(); ++it) {
  Branch (539:51): [True: 129k, False: 0]
540
129k
            if (txn[it->tx]->GetWitnessHash() == wtxid && it->announcer == peer) return it;
  Branch (540:17): [True: 26.3k, False: 103k]
  Branch (540:59): [True: 23.3k, False: 3.03k]
541
129k
        }
542
0
        return sim_announcements.end();
543
23.3k
    };
544
    /** Get an iterator to an existing (tx, peer) pair in the simulation. */
545
454k
    auto find_announce_fn = [&](unsigned tx, NodeId peer) {
546
3.77M
        for (auto it = sim_announcements.begin(); it != sim_announcements.end(); ++it) {
  Branch (546:51): [True: 3.34M, False: 427k]
547
3.34M
            if (it->tx == tx && it->announcer == peer) return it;
  Branch (547:17): [True: 223k, False: 3.12M]
  Branch (547:33): [True: 27.3k, False: 195k]
548
3.34M
        }
549
427k
        return sim_announcements.end();
550
454k
    };
551
    /** Compute a peer's DoS score according to simulation data. */
552
383k
    auto dos_score_fn = [&](NodeId peer, int32_t max_count, int32_t max_usage) -> FeeFrac {
553
383k
        int64_t count{0};
554
383k
        int64_t usage{0};
555
3.23M
        for (auto& ann : sim_announcements) {
  Branch (555:24): [True: 3.23M, False: 383k]
556
3.23M
            if (ann.announcer != peer) continue;
  Branch (556:17): [True: 3.03M, False: 202k]
557
202k
            count += 1 + (txn[ann.tx]->vin.size() / 10);
558
202k
            usage += GetTransactionWeight(*txn[ann.tx]);
559
202k
        }
560
383k
        return std::max<ByRatioNegSize<FeeFrac>>(FeeFrac{count, max_count}, FeeFrac{usage, max_usage});
561
383k
    };
562
563
    //
564
    // 5. Run through a scenario of mutators on both real and simulated orphanage.
565
    //
566
567
139k
    LIMITED_WHILE (provider.remaining_bytes() > 0, 200) {
568
139k
        int command = provider.ConsumeIntegralInRange<uint8_t>(0, 15);
569
196k
        while (true) {
  Branch (569:16): [Folded - Ignored]
570
196k
            if (sim_announcements.size() < MAX_ANN && command-- == 0) {
  Branch (570:17): [True: 195k, False: 678]
  Branch (570:55): [True: 68.6k, False: 126k]
571
                // AddTx
572
68.6k
                auto [tx, peer] = read_tx_peer_fn();
573
68.6k
                bool added = real->AddTx(txn[tx], peer);
574
68.6k
                bool sim_have_tx = have_tx_fn(tx);
575
68.6k
                assert(added == !sim_have_tx);
  Branch (575:17): [True: 68.6k, False: 0]
576
68.6k
                if (find_announce_fn(tx, peer) == sim_announcements.end()) {
  Branch (576:21): [True: 56.4k, False: 12.1k]
577
56.4k
                    sim_announcements.emplace_back(tx, peer, false);
578
56.4k
                }
579
68.6k
                break;
580
127k
            } else if (sim_announcements.size() < MAX_ANN && command-- == 0) {
  Branch (580:24): [True: 126k, False: 678]
  Branch (580:62): [True: 17.3k, False: 109k]
581
                // AddAnnouncer
582
17.3k
                auto [tx, peer] = read_tx_peer_fn();
583
17.3k
                bool added = real->AddAnnouncer(txn[tx]->GetWitnessHash(), peer);
584
17.3k
                bool sim_have_tx = have_tx_fn(tx);
585
17.3k
                auto sim_it = find_announce_fn(tx, peer);
586
17.3k
                assert(added == (sim_it == sim_announcements.end() && sim_have_tx));
  Branch (586:17): [True: 13.2k, False: 4.14k]
  Branch (586:17): [True: 4.88k, False: 8.34k]
  Branch (586:17): [True: 17.3k, False: 0]
587
17.3k
                if (added) {
  Branch (587:21): [True: 4.88k, False: 12.4k]
588
4.88k
                    sim_announcements.emplace_back(tx, peer, false);
589
4.88k
                }
590
17.3k
                break;
591
110k
            } else if (command-- == 0) {
  Branch (591:24): [True: 7.84k, False: 102k]
592
                // EraseTx
593
7.84k
                auto tx = read_tx_fn();
594
7.84k
                bool erased = real->EraseTx(txn[tx]->GetWitnessHash());
595
7.84k
                bool sim_have = have_tx_fn(tx);
596
7.84k
                assert(erased == sim_have);
  Branch (596:17): [True: 7.84k, False: 0]
597
58.3k
                std::erase_if(sim_announcements, [&](auto& ann) { return ann.tx == tx; });
598
7.84k
                break;
599
102k
            } else if (command-- == 0) {
  Branch (599:24): [True: 7.49k, False: 94.8k]
600
                // EraseForPeer
601
7.49k
                auto peer = read_peer_fn();
602
7.49k
                real->EraseForPeer(peer);
603
43.1k
                std::erase_if(sim_announcements, [&](auto& ann) { return ann.announcer == peer; });
604
7.49k
                break;
605
94.8k
            } else if (command-- == 0) {
  Branch (605:24): [True: 10.2k, False: 84.5k]
606
                // EraseForBlock
607
10.2k
                auto pattern = provider.ConsumeIntegralInRange<uint64_t>(0, (uint64_t{1} << NUM_TX) - 1);
608
10.2k
                CBlock block;
609
10.2k
                std::set<COutPoint> spent;
610
174k
                for (unsigned tx = 0; tx < NUM_TX; ++tx) {
  Branch (610:39): [True: 163k, False: 10.2k]
611
163k
                    if ((pattern >> tx) & 1) {
  Branch (611:25): [True: 51.2k, False: 112k]
612
51.2k
                        block.vtx.emplace_back(txn[tx]);
613
117k
                        for (auto& txin : block.vtx.back()->vin) {
  Branch (613:41): [True: 117k, False: 51.2k]
614
117k
                            spent.insert(txin.prevout);
615
117k
                        }
616
51.2k
                    }
617
163k
                }
618
10.2k
                std::shuffle(block.vtx.begin(), block.vtx.end(), rng);
619
10.2k
                real->EraseForBlock(block);
620
42.6k
                std::erase_if(sim_announcements, [&](auto& ann) {
621
63.7k
                    for (auto& txin : txn[ann.tx]->vin) {
  Branch (621:37): [True: 63.7k, False: 23.8k]
622
63.7k
                        if (spent.contains(txin.prevout)) return true;
  Branch (622:29): [True: 18.7k, False: 45.0k]
623
63.7k
                    }
624
23.8k
                    return false;
625
42.6k
                });
626
10.2k
                break;
627
84.5k
            } else if (command-- == 0) {
  Branch (627:24): [True: 12.4k, False: 72.0k]
628
                // AddChildrenToWorkSet
629
12.4k
                auto tx = read_tx_fn();
630
12.4k
                FastRandomContext rand_ctx(rng.rand256());
631
12.4k
                auto added = real->AddChildrenToWorkSet(*txn[tx], rand_ctx);
632
                /** Set of not-already-reconsiderable child wtxids. */
633
12.4k
                std::set<Wtxid> child_wtxids;
634
212k
                for (unsigned child_tx = 0; child_tx < NUM_TX; ++child_tx) {
  Branch (634:45): [True: 199k, False: 12.4k]
635
199k
                    if (!have_tx_fn(child_tx)) continue;
  Branch (635:25): [True: 134k, False: 65.1k]
636
65.1k
                    if (have_reconsiderable_fn(child_tx)) continue;
  Branch (636:25): [True: 22.7k, False: 42.3k]
637
42.3k
                    bool child_of = false;
638
75.0k
                    for (auto& txin : txn[child_tx]->vin) {
  Branch (638:37): [True: 75.0k, False: 27.4k]
639
75.0k
                        if (txin.prevout.hash == txn[tx]->GetHash()) {
  Branch (639:29): [True: 14.8k, False: 60.2k]
640
14.8k
                            child_of = true;
641
14.8k
                            break;
642
14.8k
                        }
643
75.0k
                    }
644
42.3k
                    if (child_of) {
  Branch (644:25): [True: 14.8k, False: 27.4k]
645
14.8k
                        child_wtxids.insert(txn[child_tx]->GetWitnessHash());
646
14.8k
                    }
647
42.3k
                }
648
14.8k
                for (auto& [wtxid, peer] : added) {
  Branch (648:42): [True: 14.8k, False: 12.4k]
649
                    // Wtxid must be a child of tx that is not yet reconsiderable.
650
14.8k
                    auto child_wtxid_it = child_wtxids.find(wtxid);
651
14.8k
                    assert(child_wtxid_it != child_wtxids.end());
  Branch (651:21): [True: 14.8k, False: 0]
652
                    // Announcement must exist.
653
14.8k
                    auto sim_ann_it = find_announce_wtxid_fn(wtxid, peer);
654
14.8k
                    assert(sim_ann_it != sim_announcements.end());
  Branch (654:21): [True: 14.8k, False: 0]
655
                    // Announcement must not yet be reconsiderable.
656
14.8k
                    assert(sim_ann_it->reconsider == false);
  Branch (656:21): [True: 14.8k, False: 0]
657
                    // Make reconsiderable.
658
14.8k
                    sim_ann_it->reconsider = true;
659
                    // Remove from child_wtxids map, to disallow it being reported a second time in added.
660
14.8k
                    child_wtxids.erase(wtxid);
661
14.8k
                }
662
                // Verify that AddChildrenToWorkSet does not select announcements that were already reconsiderable:
663
                // Check all child wtxids which did not occur at least once in the result were already reconsiderable
664
                // due to a previous AddChildrenToWorkSet.
665
12.4k
                assert(child_wtxids.empty());
  Branch (665:17): [True: 12.4k, False: 0]
666
12.4k
                break;
667
72.0k
            } else if (command-- == 0) {
  Branch (667:24): [True: 14.9k, False: 57.1k]
668
                // GetTxToReconsider.
669
14.9k
                auto peer = read_peer_fn();
670
14.9k
                auto result = real->GetTxToReconsider(peer);
671
14.9k
                if (result) {
  Branch (671:21): [True: 8.49k, False: 6.46k]
672
                    // A transaction was found. It must have a corresponding reconsiderable
673
                    // announcement from peer.
674
8.49k
                    auto sim_ann_it = find_announce_wtxid_fn(result->GetWitnessHash(), peer);
675
8.49k
                    assert(sim_ann_it != sim_announcements.end());
  Branch (675:21): [True: 8.49k, False: 0]
676
8.49k
                    assert(sim_ann_it->announcer == peer);
  Branch (676:21): [True: 8.49k, False: 0]
677
8.49k
                    assert(sim_ann_it->reconsider);
  Branch (677:21): [True: 8.49k, False: 0]
678
                    // Make it non-reconsiderable.
679
8.49k
                    sim_ann_it->reconsider = false;
680
8.49k
                } else {
681
                    // No reconsiderable transaction was found from peer. Verify that it does not
682
                    // have any.
683
6.46k
                    assert(!have_reconsider_fn(peer));
  Branch (683:21): [True: 6.46k, False: 0]
684
6.46k
                }
685
14.9k
                break;
686
14.9k
            }
687
196k
        }
688
        // Always trim after each command if needed.
689
139k
        const auto max_ann = max_global_latency_score / std::max<unsigned>(1, count_peers_fn());
690
139k
        const auto max_mem = reserved_peer_usage;
691
163k
        while (true) {
  Branch (691:16): [Folded - Ignored]
692
            // Count global usage and number of peers.
693
163k
            node::TxOrphanage::Usage total_usage{0};
694
163k
            node::TxOrphanage::Count total_latency_score = sim_announcements.size();
695
2.77M
            for (unsigned tx = 0; tx < NUM_TX; ++tx) {
  Branch (695:35): [True: 2.60M, False: 163k]
696
2.60M
                if (have_tx_fn(tx)) {
  Branch (696:21): [True: 837k, False: 1.77M]
697
837k
                    total_usage += GetTransactionWeight(*txn[tx]);
698
837k
                    total_latency_score += txn[tx]->vin.size() / 10;
699
837k
                }
700
2.60M
            }
701
163k
            auto num_peers = count_peers_fn();
702
163k
            bool oversized = (total_usage > reserved_peer_usage * num_peers) ||
  Branch (702:30): [True: 19.2k, False: 143k]
703
163k
                                (total_latency_score > real->MaxGlobalLatencyScore());
  Branch (703:33): [True: 4.70k, False: 139k]
704
163k
            if (!oversized) break;
  Branch (704:17): [True: 139k, False: 23.9k]
705
            // Find worst peer.
706
23.9k
            FeeFrac worst_dos_score{0, 1};
707
23.9k
            unsigned worst_peer = unsigned(-1);
708
407k
            for (unsigned peer = 0; peer < NUM_PEERS; ++peer) {
  Branch (708:37): [True: 383k, False: 23.9k]
709
383k
                auto dos_score = dos_score_fn(peer, max_ann, max_mem);
710
                // Use >= so that the more recent peer (higher NodeId) wins in case of
711
                // ties.
712
383k
                if (ByRatioNegSize{dos_score} >= ByRatioNegSize{worst_dos_score}) {
  Branch (712:21): [True: 48.6k, False: 334k]
713
48.6k
                    worst_dos_score = dos_score;
714
48.6k
                    worst_peer = peer;
715
48.6k
                }
716
383k
            }
717
23.9k
            assert(worst_peer != unsigned(-1));
  Branch (717:13): [True: 23.9k, False: 0]
718
23.9k
            assert(ByRatio{worst_dos_score} > ByRatio{FeeFrac(1, 1)});
  Branch (718:13): [True: 23.9k, False: 0]
719
            // Find oldest announcement from worst_peer, preferring non-reconsiderable ones.
720
23.9k
            bool done{false};
721
24.2k
            for (int reconsider = 0; reconsider < 2; ++reconsider) {
  Branch (721:38): [True: 24.2k, False: 0]
722
154k
                for (auto it = sim_announcements.begin(); it != sim_announcements.end(); ++it) {
  Branch (722:59): [True: 154k, False: 322]
723
154k
                    if (it->announcer != worst_peer || it->reconsider != reconsider) continue;
  Branch (723:25): [True: 122k, False: 31.9k]
  Branch (723:56): [True: 7.94k, False: 23.9k]
724
23.9k
                    sim_announcements.erase(it);
725
23.9k
                    done = true;
726
23.9k
                    break;
727
154k
                }
728
24.2k
                if (done) break;
  Branch (728:21): [True: 23.9k, False: 322]
729
24.2k
            }
730
23.9k
            assert(done);
  Branch (730:13): [True: 23.9k, False: 0]
731
23.9k
        }
732
        // We must now be within limits, otherwise LimitOrphans should have continued further.
733
        // We don't check the contents of the orphanage until the end to make fuzz runs faster.
734
139k
        assert(real->TotalLatencyScore() <= real->MaxGlobalLatencyScore());
  Branch (734:9): [True: 139k, False: 0]
735
139k
        assert(real->TotalOrphanUsage() <= real->MaxGlobalUsage());
  Branch (735:9): [True: 139k, False: 0]
736
139k
    }
737
738
    //
739
    // 6. Perform a full comparison between the real orphanage's inspectors and the simulation.
740
    //
741
742
1.43k
    real->SanityCheck();
743
744
745
1.43k
    auto all_orphans = real->GetOrphanTransactions();
746
1.43k
    node::TxOrphanage::Usage orphan_usage{0};
747
1.43k
    std::vector<node::TxOrphanage::Usage> usage_by_peer(NUM_PEERS);
748
1.43k
    node::TxOrphanage::Count unique_orphans{0};
749
1.43k
    std::vector<node::TxOrphanage::Count> count_by_peer(NUM_PEERS);
750
1.43k
    node::TxOrphanage::Count total_latency_score = sim_announcements.size();
751
24.4k
    for (unsigned tx = 0; tx < NUM_TX; ++tx) {
  Branch (751:27): [True: 23.0k, False: 1.43k]
752
23.0k
        bool sim_have_tx = have_tx_fn(tx);
753
23.0k
        if (sim_have_tx) {
  Branch (753:13): [True: 6.05k, False: 16.9k]
754
6.05k
            orphan_usage += GetTransactionWeight(*txn[tx]);
755
6.05k
            total_latency_score += txn[tx]->vin.size() / 10;
756
6.05k
        }
757
23.0k
        unique_orphans += sim_have_tx;
758
73.0k
        auto orphans_it = std::find_if(all_orphans.begin(), all_orphans.end(), [&](auto& orph) { return orph.tx->GetWitnessHash() == txn[tx]->GetWitnessHash(); });
759
        // GetOrphanTransactions (OrphanBase existence)
760
23.0k
        assert((orphans_it != all_orphans.end()) == sim_have_tx);
  Branch (760:9): [True: 23.0k, False: 0]
761
        // HaveTx
762
23.0k
        bool have_tx = real->HaveTx(txn[tx]->GetWitnessHash());
763
23.0k
        assert(have_tx == sim_have_tx);
  Branch (763:9): [True: 23.0k, False: 0]
764
        // GetTx
765
23.0k
        auto txref = real->GetTx(txn[tx]->GetWitnessHash());
766
23.0k
        assert(!!txref == sim_have_tx);
  Branch (766:9): [True: 23.0k, False: 0]
767
23.0k
        if (sim_have_tx) assert(txref->GetWitnessHash() == txn[tx]->GetWitnessHash());
  Branch (767:13): [True: 6.05k, False: 16.9k]
  Branch (767:26): [True: 6.05k, False: 0]
768
769
391k
        for (NodeId peer = 0; peer < NUM_PEERS; ++peer) {
  Branch (769:31): [True: 368k, False: 23.0k]
770
368k
            auto it_sim_ann = find_announce_fn(tx, peer);
771
368k
            bool sim_have_ann = it_sim_ann != sim_announcements.end();
772
368k
            if (sim_have_ann) usage_by_peer[peer] += GetTransactionWeight(*txn[tx]);
  Branch (772:17): [True: 11.0k, False: 357k]
773
368k
            count_by_peer[peer] += sim_have_ann;
774
            // GetOrphanTransactions (announcers presence)
775
368k
            if (sim_have_ann) assert(sim_have_tx);
  Branch (775:17): [True: 11.0k, False: 357k]
  Branch (775:31): [True: 11.0k, False: 0]
776
368k
            if (sim_have_tx) assert(orphans_it->announcers.count(peer) == sim_have_ann);
  Branch (776:17): [True: 96.9k, False: 271k]
  Branch (776:30): [True: 96.9k, False: 0]
777
            // HaveTxFromPeer
778
368k
            bool have_ann = real->HaveTxFromPeer(txn[tx]->GetWitnessHash(), peer);
779
368k
            assert(sim_have_ann == have_ann);
  Branch (779:13): [True: 368k, False: 0]
780
            // GetChildrenFromSamePeer
781
368k
            auto children_from_peer = real->GetChildrenFromSamePeer(txn[tx], peer);
782
368k
            auto it = children_from_peer.rbegin();
783
1.10M
            for (int phase = 0; phase < 2; ++phase) {
  Branch (783:33): [True: 736k, False: 368k]
784
                // First expect all children which have reconsiderable announcement from peer, then the others.
785
5.66M
                for (auto& ann : sim_announcements) {
  Branch (785:32): [True: 5.66M, False: 736k]
786
5.66M
                    if (ann.announcer != peer) continue;
  Branch (786:25): [True: 5.31M, False: 354k]
787
354k
                    if (ann.reconsider != (phase == 1)) continue;
  Branch (787:25): [True: 177k, False: 177k]
788
177k
                    bool matching_parent{false};
789
423k
                    for (const auto& vin : txn[ann.tx]->vin) {
  Branch (789:42): [True: 423k, False: 177k]
790
423k
                        if (vin.prevout.hash == txn[tx]->GetHash()) matching_parent = true;
  Branch (790:29): [True: 61.4k, False: 361k]
791
423k
                    }
792
177k
                    if (!matching_parent) continue;
  Branch (792:25): [True: 139k, False: 37.2k]
793
                    // Found an announcement from peer which is a child of txn[tx].
794
177k
                    assert(it != children_from_peer.rend());
  Branch (794:21): [True: 37.2k, False: 0]
795
37.2k
                    assert((*it)->GetWitnessHash() == txn[ann.tx]->GetWitnessHash());
  Branch (795:21): [True: 37.2k, False: 0]
796
37.2k
                    ++it;
797
37.2k
                }
798
736k
            }
799
368k
            assert(it == children_from_peer.rend());
  Branch (799:13): [True: 368k, False: 0]
800
368k
        }
801
23.0k
    }
802
    // TotalOrphanUsage
803
1.43k
    assert(orphan_usage == real->TotalOrphanUsage());
  Branch (803:5): [True: 1.43k, False: 0]
804
24.4k
    for (NodeId peer = 0; peer < NUM_PEERS; ++peer) {
  Branch (804:27): [True: 23.0k, False: 1.43k]
805
23.0k
        bool sim_have_reconsider = have_reconsider_fn(peer);
806
        // HaveTxToReconsider
807
23.0k
        bool have_reconsider = real->HaveTxToReconsider(peer);
808
23.0k
        assert(have_reconsider == sim_have_reconsider);
  Branch (808:9): [True: 23.0k, False: 0]
809
        // UsageByPeer
810
23.0k
        assert(usage_by_peer[peer] == real->UsageByPeer(peer));
  Branch (810:9): [True: 23.0k, False: 0]
811
        // AnnouncementsFromPeer
812
23.0k
        assert(count_by_peer[peer] == real->AnnouncementsFromPeer(peer));
  Branch (812:9): [True: 23.0k, False: 0]
813
23.0k
    }
814
    // CountAnnouncements
815
1.43k
    assert(sim_announcements.size() == real->CountAnnouncements());
  Branch (815:5): [True: 1.43k, False: 0]
816
    // CountUniqueOrphans
817
1.43k
    assert(unique_orphans == real->CountUniqueOrphans());
  Branch (817:5): [True: 1.43k, False: 0]
818
    // MaxGlobalLatencyScore
819
1.43k
    assert(max_global_latency_score == real->MaxGlobalLatencyScore());
  Branch (819:5): [True: 1.43k, False: 0]
820
    // ReservedPeerUsage
821
1.43k
    assert(reserved_peer_usage == real->ReservedPeerUsage());
  Branch (821:5): [True: 1.43k, False: 0]
822
    // MaxPeerLatencyScore
823
1.43k
    auto present_peers = count_peers_fn();
824
1.43k
    assert(max_global_latency_score / std::max<unsigned>(1, present_peers) == real->MaxPeerLatencyScore());
  Branch (824:5): [True: 1.43k, False: 0]
825
    // MaxGlobalUsage
826
1.43k
    assert(reserved_peer_usage * std::max<unsigned>(1, present_peers) == real->MaxGlobalUsage());
  Branch (826:5): [True: 1.43k, False: 0]
827
    // TotalLatencyScore.
828
1.43k
    assert(real->TotalLatencyScore() == total_latency_score);
  Branch (828:5): [True: 1.43k, False: 0]
829
1.43k
}