LCOV - code coverage report
Current view: top level - src/test/fuzz - txorphan.cpp (source / functions) Coverage Total Hit
Test: fuzz_coverage.info Lines: 99.8 % 471 470
Test Date: 2025-08-28 04:05:06 Functions: 100.0 % 30 30
Branches: 71.8 % 628 451

             Branch data     Line data    Source code
       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 <uint256.h>
      19                 :             : #include <util/check.h>
      20                 :             : #include <util/feefrac.h>
      21                 :             : #include <util/time.h>
      22                 :             : 
      23                 :             : #include <algorithm>
      24                 :             : #include <bitset>
      25                 :             : #include <cmath>
      26                 :             : #include <cstdint>
      27                 :             : #include <iostream>
      28                 :             : #include <memory>
      29                 :             : #include <set>
      30                 :             : #include <utility>
      31                 :             : #include <vector>
      32                 :             : 
      33                 :           2 : void initialize_orphanage()
      34                 :             : {
      35   [ +  -  +  -  :           2 :     static const auto testing_setup = MakeNoLogFileContext();
                   +  - ]
      36                 :           2 : }
      37                 :             : 
      38         [ +  - ]:        1190 : FUZZ_TARGET(txorphan, .init = initialize_orphanage)
      39                 :             : {
      40                 :         734 :     SeedRandomStateForTest(SeedRand::ZEROS);
      41                 :         734 :     FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
      42                 :         734 :     FastRandomContext orphanage_rng{ConsumeUInt256(fuzzed_data_provider)};
      43         [ +  - ]:         734 :     SetMockTime(ConsumeTime(fuzzed_data_provider));
      44                 :             : 
      45                 :         734 :     auto orphanage = node::MakeTxOrphanage();
      46                 :         734 :     std::vector<COutPoint> outpoints; // Duplicates are tolerated
      47         [ +  - ]:         734 :     outpoints.reserve(200'000);
      48                 :             : 
      49                 :             :     // initial outpoints used to construct transactions later
      50         [ +  + ]:        3670 :     for (uint8_t i = 0; i < 4; i++) {
      51         [ +  - ]:        2936 :         outpoints.emplace_back(Txid::FromUint256(uint256{i}), 0);
      52                 :             :     }
      53                 :             : 
      54                 :         734 :     CTransactionRef ptx_potential_parent = nullptr;
      55                 :             : 
      56                 :         734 :     std::vector<CTransactionRef> tx_history;
      57                 :             : 
      58   [ -  +  +  -  :       70826 :     LIMITED_WHILE(outpoints.size() < 200'000 && fuzzed_data_provider.ConsumeBool(), 1000)
             +  +  +  + ]
      59                 :             :     {
      60                 :             :         // construct transaction
      61                 :       69358 :         const CTransactionRef tx = [&] {
      62                 :       34679 :             CMutableTransaction tx_mut;
      63         [ -  + ]:       34679 :             const auto num_in = fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(1, outpoints.size());
      64                 :       34679 :             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         [ +  - ]:       34679 :             tx_mut.vin.reserve(num_in);
      68         [ +  + ]:     5129283 :             for (uint32_t i = 0; i < num_in; i++) {
      69                 :     5094604 :                 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         [ +  - ]:    10189208 :                 tx_mut.vin.emplace_back(prevout, CScript{}, fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(0, CTxIn::SEQUENCE_FINAL));
      72                 :             :             }
      73                 :             :             // output amount will not affect txorphanage
      74         [ +  - ]:       34679 :             tx_mut.vout.reserve(num_out);
      75         [ +  + ]:     4046081 :             for (uint32_t i = 0; i < num_out; i++) {
      76         [ +  - ]:     8022804 :                 tx_mut.vout.emplace_back(CAmount{0}, CScript{});
      77                 :             :             }
      78         [ +  - ]:       34679 :             auto new_tx = MakeTransactionRef(tx_mut);
      79                 :             :             // add newly constructed outpoints to the coin pool
      80         [ +  + ]:     4046081 :             for (uint32_t i = 0; i < num_out; i++) {
      81         [ +  - ]:     4011402 :                 outpoints.emplace_back(new_tx->GetHash(), i);
      82                 :             :             }
      83                 :       34679 :             return new_tx;
      84         [ +  - ]:       69358 :         }();
      85                 :             : 
      86         [ +  - ]:       34679 :         tx_history.push_back(tx);
      87                 :             : 
      88         [ +  + ]:       34679 :         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         [ +  + ]:       34679 :         if (ptx_potential_parent) {
      93                 :             :             // Set up future GetTxToReconsider call.
      94         [ +  - ]:       33986 :             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                 :       33986 :             NodeId peer_id = fuzzed_data_provider.ConsumeIntegral<NodeId>();
      98   [ +  -  +  + ]:       96269 :             for (const auto& child : orphanage->GetChildrenFromSamePeer(ptx_potential_parent, peer_id)) {
      99         [ -  + ]:      309253 :                 assert(std::any_of(child->vin.cbegin(), child->vin.cend(), [&](const auto& input) {
     100                 :             :                     return input.prevout.hash == ptx_potential_parent->GetHash();
     101                 :             :                 }));
     102                 :       33986 :             }
     103                 :             :         }
     104                 :             : 
     105                 :             :         // trigger orphanage functions
     106   [ +  +  +  + ]:     2372864 :         LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 1000)
     107                 :             :         {
     108                 :     2338185 :             NodeId peer_id = fuzzed_data_provider.ConsumeIntegral<NodeId>();
     109         [ +  - ]:     2338185 :             const auto total_bytes_start{orphanage->TotalOrphanUsage()};
     110         [ +  - ]:     2338185 :             const auto total_peer_bytes_start{orphanage->UsageByPeer(peer_id)};
     111                 :     2338185 :             const auto tx_weight{GetTransactionWeight(*tx)};
     112                 :             : 
     113         [ +  - ]:     2338185 :             CallOneOf(
     114                 :             :                 fuzzed_data_provider,
     115                 :        2657 :                 [&] {
     116                 :        2657 :                     {
     117                 :        2657 :                         CTransactionRef ref = orphanage->GetTxToReconsider(peer_id);
     118         [ +  + ]:        2657 :                         if (ref) {
     119   [ +  -  +  - ]:          57 :                             Assert(orphanage->HaveTx(ref->GetWitnessHash()));
     120                 :             :                         }
     121                 :        2657 :                     }
     122                 :        2657 :                 },
     123                 :     2238360 :                 [&] {
     124                 :     2238360 :                     bool have_tx = orphanage->HaveTx(tx->GetWitnessHash());
     125                 :     2238360 :                     bool have_tx_and_peer = orphanage->HaveTxFromPeer(wtxid, peer_id);
     126                 :             :                     // AddTx should return false if tx is too big or already have it
     127                 :             :                     // tx weight is unknown, we only check when tx is already in orphanage
     128                 :     2238360 :                     {
     129                 :     2238360 :                         bool add_tx = orphanage->AddTx(tx, peer_id);
     130                 :             :                         // have_tx == true -> add_tx == false
     131                 :     2238360 :                         Assert(!have_tx || !add_tx);
     132                 :             :                         // have_tx_and_peer == true -> add_tx == false
     133                 :     2238360 :                         Assert(!have_tx_and_peer || !add_tx);
     134                 :             :                         // After AddTx, the orphanage may trim itself, so the peer's usage may have gone up or down.
     135                 :             : 
     136         [ +  + ]:     2238360 :                         if (add_tx) {
     137                 :       58160 :                             Assert(tx_weight <= MAX_STANDARD_TX_WEIGHT);
     138                 :             :                         } else {
     139                 :             :                             // Peer may have been added as an announcer.
     140         [ +  + ]:     2180200 :                             if (orphanage->UsageByPeer(peer_id) > total_peer_bytes_start) {
     141                 :       27929 :                                 Assert(orphanage->HaveTxFromPeer(wtxid, peer_id));
     142                 :             :                             }
     143                 :             : 
     144                 :             :                             // If announcement was added, total bytes does not increase.
     145                 :             :                             // However, if eviction was triggered, the value may decrease.
     146                 :     2180200 :                             Assert(orphanage->TotalOrphanUsage() <= total_bytes_start);
     147                 :             :                         }
     148                 :             :                     }
     149                 :             :                     // We are not guaranteed to have_tx after AddTx. There are a few possible reasons:
     150                 :             :                     // - tx itself exceeds the per-peer memory usage limit, so LimitOrphans had to remove it immediately
     151                 :             :                     // - tx itself exceeds the per-peer latency score limit, so LimitOrphans had to remove it immediately
     152                 :             :                     // - the orphanage needed trim and all other announcements from this peer are reconsiderable
     153                 :     2238360 :                 },
     154                 :        1228 :                 [&] {
     155                 :        1228 :                     bool have_tx = orphanage->HaveTx(tx->GetWitnessHash());
     156                 :        1228 :                     bool have_tx_and_peer = orphanage->HaveTxFromPeer(tx->GetWitnessHash(), peer_id);
     157                 :             :                     // AddAnnouncer should return false if tx doesn't exist or we already HaveTxFromPeer.
     158                 :        1228 :                     {
     159                 :        1228 :                         bool added_announcer = orphanage->AddAnnouncer(tx->GetWitnessHash(), peer_id);
     160                 :             :                         // have_tx == false -> added_announcer == false
     161                 :        1228 :                         Assert(have_tx || !added_announcer);
     162                 :             :                         // have_tx_and_peer == true -> added_announcer == false
     163                 :        1228 :                         Assert(!have_tx_and_peer || !added_announcer);
     164                 :             : 
     165                 :             :                         // If announcement was added, total bytes does not increase.
     166                 :             :                         // However, if eviction was triggered, the value may decrease.
     167                 :        1228 :                         Assert(orphanage->TotalOrphanUsage() <= total_bytes_start);
     168                 :             :                     }
     169                 :        1228 :                 },
     170                 :       79223 :                 [&] {
     171                 :       79223 :                     bool have_tx = orphanage->HaveTx(tx->GetWitnessHash());
     172                 :       79223 :                     bool have_tx_and_peer{orphanage->HaveTxFromPeer(wtxid, peer_id)};
     173                 :             :                     // EraseTx should return 0 if m_orphans doesn't have the tx
     174                 :       79223 :                     {
     175                 :       79223 :                         auto bytes_from_peer_before{orphanage->UsageByPeer(peer_id)};
     176                 :       79223 :                         Assert(have_tx == orphanage->EraseTx(tx->GetWitnessHash()));
     177                 :             :                         // After EraseTx, the orphanage may trim itself, so all peers' usage may have gone up or down.
     178         [ +  + ]:       79223 :                         if (have_tx) {
     179         [ +  + ]:       38437 :                             if (!have_tx_and_peer) {
     180                 :       37985 :                                 Assert(orphanage->UsageByPeer(peer_id) == bytes_from_peer_before);
     181                 :             :                             }
     182                 :             :                         }
     183                 :             :                     }
     184                 :       79223 :                     have_tx = orphanage->HaveTx(tx->GetWitnessHash());
     185                 :       79223 :                     have_tx_and_peer = orphanage->HaveTxFromPeer(wtxid, peer_id);
     186                 :             :                     // have_tx should be false and EraseTx should fail
     187                 :       79223 :                     {
     188   [ +  -  -  + ]:       79223 :                         Assert(!have_tx && !have_tx_and_peer && !orphanage->EraseTx(wtxid));
     189                 :             :                     }
     190                 :       79223 :                 },
     191                 :        1054 :                 [&] {
     192                 :        1054 :                     orphanage->EraseForPeer(peer_id);
     193                 :        1054 :                     Assert(!orphanage->HaveTxFromPeer(tx->GetWitnessHash(), peer_id));
     194                 :        1054 :                     Assert(orphanage->UsageByPeer(peer_id) == 0);
     195                 :        1054 :                 },
     196                 :       15663 :                 [&] {
     197                 :             :                     // Make a block out of txs and then EraseForBlock
     198                 :       15663 :                     CBlock block;
     199                 :       15663 :                     int num_txs = fuzzed_data_provider.ConsumeIntegralInRange<unsigned int>(0, 1000);
     200         [ +  + ]:     2095806 :                     for (int i{0}; i < num_txs; ++i) {
     201                 :     2080143 :                         auto& tx_to_remove = PickValue(fuzzed_data_provider, tx_history);
     202         [ +  - ]:     2080143 :                         block.vtx.push_back(tx_to_remove);
     203                 :             :                     }
     204         [ +  - ]:       15663 :                     orphanage->EraseForBlock(block);
     205         [ +  + ]:     2095806 :                     for (const auto& tx_removed : block.vtx) {
     206   [ +  -  +  - ]:     2080143 :                         Assert(!orphanage->HaveTx(tx_removed->GetWitnessHash()));
     207   [ +  -  +  - ]:     2080143 :                         Assert(!orphanage->HaveTxFromPeer(tx_removed->GetWitnessHash(), peer_id));
     208                 :             :                     }
     209                 :       15663 :                 }
     210                 :             :             );
     211                 :             :         }
     212                 :             : 
     213                 :             :         // Set tx as potential parent to be used for future GetChildren() calls.
     214   [ +  +  +  + ]:       68665 :         if (!ptx_potential_parent || fuzzed_data_provider.ConsumeBool()) {
     215                 :       17763 :             ptx_potential_parent = tx;
     216                 :             :         }
     217                 :             : 
     218         [ +  - ]:       34679 :         const bool have_tx{orphanage->HaveTx(tx->GetWitnessHash())};
     219   [ +  -  +  + ]:       34679 :         const bool get_tx_nonnull{orphanage->GetTx(tx->GetWitnessHash()) != nullptr};
     220         [ +  - ]:       34679 :         Assert(have_tx == get_tx_nonnull);
     221                 :       34679 :     }
     222         [ +  - ]:         734 :     orphanage->SanityCheck();
     223         [ +  + ]:        1427 : }
     224                 :             : 
     225         [ +  - ]:         775 : FUZZ_TARGET(txorphan_protected, .init = initialize_orphanage)
     226                 :             : {
     227                 :         319 :     SeedRandomStateForTest(SeedRand::ZEROS);
     228                 :         319 :     FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
     229                 :         319 :     FastRandomContext orphanage_rng{ConsumeUInt256(fuzzed_data_provider)};
     230         [ +  - ]:         319 :     SetMockTime(ConsumeTime(fuzzed_data_provider));
     231                 :             : 
     232                 :             :     // We have num_peers peers. Some subset of them will never exceed their reserved weight or announcement count, and
     233                 :             :     // should therefore never have any orphans evicted.
     234                 :         319 :     const unsigned int MAX_PEERS = 125;
     235                 :         319 :     const unsigned int num_peers = fuzzed_data_provider.ConsumeIntegralInRange<unsigned int>(1, MAX_PEERS);
     236                 :             :     // Generate a vector of bools for whether each peer is protected from eviction
     237                 :         319 :     std::bitset<MAX_PEERS> protected_peers;
     238         [ +  + ]:        6603 :     for (unsigned int i = 0; i < num_peers; i++) {
     239         [ +  - ]:        6284 :         protected_peers.set(i, fuzzed_data_provider.ConsumeBool());
     240                 :             :     }
     241                 :             : 
     242                 :             :     // Params for orphanage.
     243                 :         319 :     const unsigned int global_latency_score_limit = fuzzed_data_provider.ConsumeIntegralInRange<unsigned int>(num_peers, 6'000);
     244                 :         319 :     const int64_t per_peer_weight_reservation = fuzzed_data_provider.ConsumeIntegralInRange<int64_t>(1, 4'040'000);
     245                 :         319 :     auto orphanage = node::MakeTxOrphanage(global_latency_score_limit, per_peer_weight_reservation);
     246                 :             : 
     247                 :             :     // The actual limit, MaxPeerLatencyScore(), may be higher, since TxOrphanage only counts peers
     248                 :             :     // that have announced an orphan. The honest peer will not experience evictions if it never
     249                 :             :     // exceeds this.
     250                 :         319 :     const unsigned int honest_latency_limit = global_latency_score_limit / num_peers;
     251                 :             :     // Honest peer will not experience evictions if it never exceeds this.
     252                 :         319 :     const int64_t honest_mem_limit = per_peer_weight_reservation;
     253                 :             : 
     254                 :         319 :     std::vector<COutPoint> outpoints; // Duplicates are tolerated
     255         [ +  - ]:         319 :     outpoints.reserve(400);
     256                 :             : 
     257                 :             :     // initial outpoints used to construct transactions later
     258         [ +  + ]:        1595 :     for (uint8_t i = 0; i < 4; i++) {
     259         [ +  - ]:        1276 :         outpoints.emplace_back(Txid::FromUint256(uint256{i}), 0);
     260                 :             :     }
     261                 :             : 
     262                 :             :     // These are honest peer's live announcements. We expect them to be protected from eviction.
     263                 :         319 :     std::set<Wtxid> protected_wtxids;
     264                 :             : 
     265   [ -  +  +  +  :        2135 :     LIMITED_WHILE(outpoints.size() < 400 && fuzzed_data_provider.ConsumeBool(), 1000)
             +  +  -  + ]
     266                 :             :     {
     267                 :             :         // construct transaction
     268                 :        1498 :         const CTransactionRef tx = [&] {
     269                 :         749 :             CMutableTransaction tx_mut;
     270         [ -  + ]:         749 :             const auto num_in = fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(1, outpoints.size());
     271                 :         749 :             const auto num_out = fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(1, 256);
     272                 :             :             // pick outpoints from outpoints as input. We allow input duplicates on purpose, given we are not
     273                 :             :             // running any transaction validation logic before adding transactions to the orphanage
     274         [ +  - ]:         749 :             tx_mut.vin.reserve(num_in);
     275         [ +  + ]:        3204 :             for (uint32_t i = 0; i < num_in; i++) {
     276                 :        2455 :                 auto& prevout = PickValue(fuzzed_data_provider, outpoints);
     277                 :             :                 // try making transactions unique by setting a random nSequence, but allow duplicate transactions if they happen
     278         [ +  - ]:        4910 :                 tx_mut.vin.emplace_back(prevout, CScript{}, fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(0, CTxIn::SEQUENCE_FINAL));
     279                 :             :             }
     280                 :             :             // output amount or spendability will not affect txorphanage
     281         [ +  - ]:         749 :             tx_mut.vout.reserve(num_out);
     282         [ +  + ]:       15880 :             for (uint32_t i = 0; i < num_out; i++) {
     283                 :       15131 :                 const auto payload_size = fuzzed_data_provider.ConsumeIntegralInRange<unsigned int>(0, 100000);
     284         [ +  + ]:       15131 :                 if (payload_size) {
     285   [ +  -  +  -  :       15162 :                     tx_mut.vout.emplace_back(0, CScript() << OP_RETURN << std::vector<unsigned char>(payload_size));
                   +  - ]
     286                 :             :                 } else {
     287         [ +  - ]:       20154 :                     tx_mut.vout.emplace_back(0, CScript{});
     288                 :             :                 }
     289                 :             :             }
     290         [ +  - ]:         749 :             auto new_tx = MakeTransactionRef(tx_mut);
     291                 :             :             // add newly constructed outpoints to the coin pool
     292         [ +  + ]:       15880 :             for (uint32_t i = 0; i < num_out; i++) {
     293         [ +  - ]:       15131 :                 outpoints.emplace_back(new_tx->GetHash(), i);
     294                 :             :             }
     295                 :         749 :             return new_tx;
     296         [ +  - ]:        1498 :         }();
     297                 :             : 
     298                 :         749 :         const auto wtxid{tx->GetWitnessHash()};
     299                 :             : 
     300                 :             :         // orphanage functions
     301   [ +  +  +  + ]:       32760 :         LIMITED_WHILE(fuzzed_data_provider.remaining_bytes(), 10 * global_latency_score_limit)
     302                 :             :         {
     303                 :       32011 :             NodeId peer_id = fuzzed_data_provider.ConsumeIntegralInRange<NodeId>(0, num_peers - 1);
     304                 :       32011 :             const auto tx_weight{GetTransactionWeight(*tx)};
     305                 :             : 
     306                 :             :             // This protected peer will never send orphans that would
     307                 :             :             // exceed their own personal allotment, so is never evicted.
     308         [ +  - ]:       32011 :             const bool peer_is_protected{protected_peers[peer_id]};
     309                 :             : 
     310         [ +  - ]:       32011 :             CallOneOf(
     311                 :             :                 fuzzed_data_provider,
     312                 :       12627 :                 [&] { // AddTx
     313                 :       12627 :                     bool have_tx_and_peer = orphanage->HaveTxFromPeer(wtxid, peer_id);
     314   [ +  +  +  + ]:       12627 :                     if (peer_is_protected && !have_tx_and_peer &&
     315         [ +  + ]:        1975 :                         (orphanage->UsageByPeer(peer_id) + tx_weight > honest_mem_limit ||
     316   [ -  +  +  + ]:        1321 :                         orphanage->LatencyScoreFromPeer(peer_id) + (tx->vin.size() / 10) + 1 > honest_latency_limit)) {
     317                 :             :                         // We never want our protected peer oversized or over-announced
     318                 :             :                     } else {
     319                 :       11907 :                         orphanage->AddTx(tx, peer_id);
     320   [ +  +  +  + ]:       11907 :                         if (peer_is_protected && orphanage->HaveTxFromPeer(wtxid, peer_id)) {
     321                 :        2719 :                             protected_wtxids.insert(wtxid);
     322                 :             :                         }
     323                 :             :                     }
     324                 :       12627 :                 },
     325                 :        5786 :                 [&] { // AddAnnouncer
     326                 :        5786 :                     bool have_tx_and_peer = orphanage->HaveTxFromPeer(tx->GetWitnessHash(), peer_id);
     327                 :             :                     // AddAnnouncer should return false if tx doesn't exist or we already HaveTxFromPeer.
     328                 :        5786 :                     {
     329   [ +  +  +  + ]:        5786 :                         if (peer_is_protected && !have_tx_and_peer &&
     330         [ +  + ]:        1666 :                             (orphanage->UsageByPeer(peer_id) + tx_weight > honest_mem_limit ||
     331   [ -  +  +  + ]:         992 :                             orphanage->LatencyScoreFromPeer(peer_id) + (tx->vin.size() / 10) + 1 > honest_latency_limit)) {
     332                 :             :                             // We never want our protected peer oversized
     333                 :             :                         } else {
     334                 :        5081 :                             orphanage->AddAnnouncer(tx->GetWitnessHash(), peer_id);
     335   [ +  +  +  + ]:        5081 :                             if (peer_is_protected && orphanage->HaveTxFromPeer(wtxid, peer_id)) {
     336                 :         685 :                                 protected_wtxids.insert(wtxid);
     337                 :             :                             }
     338                 :             :                         }
     339                 :             :                     }
     340                 :        5786 :                 },
     341                 :        4037 :                 [&] { // EraseTx
     342         [ +  + ]:        4037 :                     if (protected_wtxids.count(tx->GetWitnessHash())) {
     343                 :         737 :                         protected_wtxids.erase(wtxid);
     344                 :             :                     }
     345                 :        4037 :                     orphanage->EraseTx(wtxid);
     346                 :        4037 :                     Assert(!orphanage->HaveTx(wtxid));
     347                 :        4037 :                 },
     348                 :        9561 :                 [&] { // EraseForPeer
     349         [ +  + ]:        9561 :                     if (!protected_peers[peer_id]) {
     350                 :        6252 :                         orphanage->EraseForPeer(peer_id);
     351                 :        6252 :                         Assert(orphanage->UsageByPeer(peer_id) == 0);
     352                 :        6252 :                         Assert(orphanage->LatencyScoreFromPeer(peer_id) == 0);
     353                 :        6252 :                         Assert(orphanage->AnnouncementsFromPeer(peer_id) == 0);
     354                 :             :                     }
     355                 :        9561 :                 }
     356                 :             :             );
     357                 :             :         }
     358                 :         749 :     }
     359                 :             : 
     360         [ +  - ]:         319 :     orphanage->SanityCheck();
     361                 :             :     // All of the honest peer's announcements are still present.
     362         [ +  + ]:         424 :     for (const auto& wtxid : protected_wtxids) {
     363   [ +  -  +  - ]:         105 :         Assert(orphanage->HaveTx(wtxid));
     364                 :             :     }
     365                 :         319 : }
     366                 :             : 
     367         [ +  - ]:         967 : FUZZ_TARGET(txorphanage_sim)
     368                 :             : {
     369                 :         511 :     SeedRandomStateForTest(SeedRand::ZEROS);
     370                 :             :     // This is a comprehensive simulation fuzz test, which runs through a scenario involving up to
     371                 :             :     // 16 transactions (which may have simple or complex topology, and may have duplicate txids
     372                 :             :     // with distinct wtxids, and up to 16 peers. The scenario is performed both on a real
     373                 :             :     // TxOrphanage object and the behavior is compared with a naive reimplementation (just a vector
     374                 :             :     // of announcements) where possible, and tested for desired properties where not possible.
     375                 :             : 
     376                 :             :     //
     377                 :             :     // 1. Setup.
     378                 :             :     //
     379                 :             : 
     380                 :             :     /** The total number of transactions this simulation uses (not all of which will necessarily
     381                 :             :      *  be present in the orphanage at once). */
     382                 :         511 :     static constexpr unsigned NUM_TX = 16;
     383                 :             :     /** The number of peers this simulation uses (not all of which will necessarily be present in
     384                 :             :      *  the orphanage at once). */
     385                 :         511 :     static constexpr unsigned NUM_PEERS = 16;
     386                 :             :     /** The maximum number of announcements this simulation uses (which may be higher than the
     387                 :             :      *  number permitted inside the orphanage). */
     388                 :         511 :     static constexpr unsigned MAX_ANN = 64;
     389                 :             : 
     390                 :         511 :     FuzzedDataProvider provider(buffer.data(), buffer.size());
     391                 :             :     /** Local RNG. Only used for topology/sizes of the transaction set, the order of transactions
     392                 :             :      *  in EraseForBlock, and for the randomized passed to AddChildrenToWorkSet. */
     393                 :         511 :     InsecureRandomContext rng(provider.ConsumeIntegral<uint64_t>());
     394                 :             : 
     395                 :             :     //
     396                 :             :     // 2. Construct an interesting set of 16 transactions.
     397                 :             :     //
     398                 :             : 
     399                 :             :     // - Pick a topological order among the transactions.
     400                 :         511 :     std::vector<unsigned> txorder(NUM_TX);
     401                 :         511 :     std::iota(txorder.begin(), txorder.end(), unsigned{0});
     402                 :         511 :     std::shuffle(txorder.begin(), txorder.end(), rng);
     403                 :             :     // - Pick a set of dependencies (pair<child_index, parent_index>).
     404                 :         511 :     std::vector<std::pair<unsigned, unsigned>> deps;
     405         [ +  - ]:         511 :     deps.reserve((NUM_TX * (NUM_TX - 1)) / 2);
     406         [ +  + ]:        8176 :     for (unsigned p = 0; p < NUM_TX - 1; ++p) {
     407         [ +  + ]:       68985 :         for (unsigned c = p + 1; c < NUM_TX; ++c) {
     408         [ +  - ]:       61320 :             deps.emplace_back(c, p);
     409                 :             :         }
     410                 :             :     }
     411                 :         511 :     std::shuffle(deps.begin(), deps.end(), rng);
     412         [ +  - ]:         511 :     deps.resize(provider.ConsumeIntegralInRange<unsigned>(0, NUM_TX * 4 - 1));
     413                 :             :     // - Construct the actual transactions.
     414         [ +  - ]:         511 :     std::set<Wtxid> wtxids;
     415         [ +  - ]:         511 :     std::vector<CTransactionRef> txn(NUM_TX);
     416                 :             :     node::TxOrphanage::Usage total_usage{0};
     417         [ +  + ]:        8687 :     for (unsigned t = 0; t < NUM_TX; ++t) {
     418         [ +  - ]:        8176 :         CMutableTransaction tx;
     419   [ +  +  +  + ]:        8176 :         if (t > 0 && rng.randrange(4) == 0) {
     420                 :             :             // Occasionally duplicate the previous transaction, so that repetitions of the same
     421                 :             :             // txid are possible (with different wtxid).
     422         [ +  - ]:        6126 :             tx = CMutableTransaction(*txn[txorder[t - 1]]);
     423                 :             :         } else {
     424                 :        5113 :             tx.version = 1;
     425                 :        5113 :             tx.nLockTime = 0xffffffff;
     426                 :             :             // Construct 1 to 16 outputs.
     427                 :        5113 :             auto num_outputs = rng.randrange<unsigned>(1 << rng.randrange<unsigned>(5)) + 1;
     428         [ +  + ]:       21903 :             for (unsigned output = 0; output < num_outputs; ++output) {
     429                 :       16790 :                 CScript scriptpubkey;
     430                 :       16790 :                 scriptpubkey.resize(provider.ConsumeIntegralInRange<unsigned>(20, 34));
     431         [ +  - ]:       16790 :                 tx.vout.emplace_back(CAmount{0}, std::move(scriptpubkey));
     432                 :       16790 :             }
     433                 :             :             // Construct inputs (one for each dependency).
     434   [ +  +  +  + ]:      153448 :             for (auto& [child, parent] : deps) {
     435         [ +  + ]:      148335 :                 if (child == t) {
     436         [ -  + ]:        8998 :                     auto& partx = txn[txorder[parent]];
     437         [ -  + ]:        8998 :                     assert(partx->version == 1);
     438   [ -  +  +  - ]:        8998 :                     COutPoint outpoint(partx->GetHash(), rng.randrange<size_t>(partx->vout.size()));
     439         [ +  - ]:        8998 :                     tx.vin.emplace_back(outpoint);
     440                 :        8998 :                     tx.vin.back().scriptSig.resize(provider.ConsumeIntegralInRange<unsigned>(16, 200));
     441                 :             :                 }
     442                 :             :             }
     443                 :             :             // Construct fallback input in case there are no dependencies.
     444         [ +  + ]:        5113 :             if (tx.vin.empty()) {
     445         [ +  - ]:        2423 :                 COutPoint outpoint(Txid::FromUint256(rng.rand256()), rng.randrange<size_t>(16));
     446         [ +  - ]:        2423 :                 tx.vin.emplace_back(outpoint);
     447                 :        2423 :                 tx.vin.back().scriptSig.resize(provider.ConsumeIntegralInRange<unsigned>(16, 200));
     448                 :             :             }
     449                 :             :         }
     450                 :             :         // Optionally modify the witness (allowing wtxid != txid), and certainly when the wtxid
     451                 :             :         // already exists.
     452   [ +  -  +  +  :       37124 :         while (wtxids.contains(CTransaction(tx).GetWitnessHash()) || rng.randrange(4) == 0) {
             +  +  +  + ]
     453         [ -  + ]:       10386 :             auto& input = tx.vin[rng.randrange(tx.vin.size())];
     454         [ +  + ]:       10386 :             if (rng.randbool()) {
     455         [ +  - ]:        4633 :                 input.scriptWitness.stack.resize(1);
     456         [ +  - ]:        4633 :                 input.scriptWitness.stack[0].resize(rng.randrange(100));
     457                 :             :             } else {
     458         [ +  - ]:        5753 :                 input.scriptWitness.stack.resize(0);
     459                 :             :             }
     460                 :             :         }
     461                 :             :         // Convert to CTransactionRef.
     462   [ +  -  -  + ]:       16352 :         txn[txorder[t]] = MakeTransactionRef(std::move(tx));
     463         [ +  - ]:        8176 :         wtxids.insert(txn[txorder[t]]->GetWitnessHash());
     464                 :        8176 :         auto weight = GetTransactionWeight(*txn[txorder[t]]);
     465         [ -  + ]:        8176 :         assert(weight < MAX_STANDARD_TX_WEIGHT);
     466                 :        8176 :         total_usage += GetTransactionWeight(*txn[txorder[t]]);
     467                 :        8176 :     }
     468                 :             : 
     469                 :             :     //
     470                 :             :     // 3. Initialize real orphanage
     471                 :             :     //
     472                 :             : 
     473                 :         511 :     auto max_global_latency_score = provider.ConsumeIntegralInRange<node::TxOrphanage::Count>(NUM_PEERS, MAX_ANN);
     474                 :         511 :     auto reserved_peer_usage = provider.ConsumeIntegralInRange<node::TxOrphanage::Usage>(1, total_usage);
     475                 :         511 :     auto real = node::MakeTxOrphanage(max_global_latency_score, reserved_peer_usage);
     476                 :             : 
     477                 :             :     //
     478                 :             :     // 4. Functions and data structures for the simulation.
     479                 :             :     //
     480                 :             : 
     481                 :             :     /** Data structure representing one announcement (pair of (tx, peer), plus whether it's
     482                 :             :      *  reconsiderable or not. */
     483                 :         511 :     struct SimAnnouncement
     484                 :             :     {
     485                 :             :         unsigned tx;
     486                 :             :         NodeId announcer;
     487                 :             :         bool reconsider{false};
     488                 :       19562 :         SimAnnouncement(unsigned tx_in, NodeId announcer_in, bool reconsider_in) noexcept :
     489                 :       19562 :             tx(tx_in), announcer(announcer_in), reconsider(reconsider_in) {}
     490                 :             :     };
     491                 :             :     /** The entire simulated orphanage is represented by this list of announcements, in
     492                 :             :      *  announcement order (unlike TxOrphanageImpl which uses a sequence number to represent
     493                 :             :      *  announcement order). New announcements are added to the back. */
     494                 :         511 :     std::vector<SimAnnouncement> sim_announcements;
     495                 :             : 
     496                 :             :     /** Consume a transaction (index into txn) from provider. */
     497                 :        8102 :     auto read_tx_fn = [&]() -> unsigned { return provider.ConsumeIntegralInRange<unsigned>(0, NUM_TX - 1); };
     498                 :             :     /** Consume a NodeId from provider. */
     499                 :       16289 :     auto read_peer_fn = [&]() -> NodeId { return provider.ConsumeIntegralInRange<unsigned>(0, NUM_PEERS - 1); };
     500                 :             :     /** Consume both a transaction (index into txn) and a NodeId from provider. */
     501                 :       41220 :     auto read_tx_peer_fn = [&]() -> std::pair<unsigned, NodeId> {
     502                 :       40709 :         auto code = provider.ConsumeIntegralInRange<unsigned>(0, NUM_TX * NUM_PEERS - 1);
     503                 :       40709 :         return {code % NUM_TX, code / NUM_TX};
     504                 :         511 :     };
     505                 :             :     /** Determine if we have any announcements of the given transaction in the simulation. */
     506                 :     1187195 :     auto have_tx_fn = [&](unsigned tx) -> bool {
     507         [ +  + ]:     5235204 :         for (auto& ann : sim_announcements) {
     508         [ +  + ]:     4284725 :             if (ann.tx == tx) return true;
     509                 :             :         }
     510                 :             :         return false;
     511                 :         511 :     };
     512                 :             :     /** Count the number of peers in the simulation. */
     513                 :      128194 :     auto count_peers_fn = [&]() -> unsigned {
     514                 :      127683 :         std::bitset<NUM_PEERS> mask;
     515         [ +  + ]:      739640 :         for (auto& ann : sim_announcements) {
     516                 :      611957 :             mask.set(ann.announcer);
     517                 :             :         }
     518                 :      127683 :         return mask.count();
     519                 :         511 :     };
     520                 :             :     /** Determine if we have any reconsiderable announcements of a given transaction. */
     521                 :       11068 :     auto have_reconsiderable_fn = [&](unsigned tx) -> bool {
     522         [ +  + ]:       93504 :         for (auto& ann : sim_announcements) {
     523   [ +  +  +  + ]:       84539 :             if (ann.reconsider && ann.tx == tx) return true;
     524                 :             :         }
     525                 :             :         return false;
     526                 :         511 :     };
     527                 :             :     /** Determine if a peer has any transactions to reconsider. */
     528                 :       12915 :     auto have_reconsider_fn = [&](NodeId peer) -> bool {
     529         [ +  + ]:       72729 :         for (auto& ann : sim_announcements) {
     530   [ +  +  +  + ]:       60543 :             if (ann.reconsider && ann.announcer == peer) return true;
     531                 :             :         }
     532                 :             :         return false;
     533                 :         511 :     };
     534                 :             :     /** Get an iterator to an existing (wtxid, peer) pair in the simulation. */
     535                 :        2245 :     auto find_announce_wtxid_fn = [&](const Wtxid& wtxid, NodeId peer) -> std::vector<SimAnnouncement>::iterator {
     536         [ +  - ]:       11891 :         for (auto it = sim_announcements.begin(); it != sim_announcements.end(); ++it) {
     537   [ +  +  +  + ]:       11891 :             if (txn[it->tx]->GetWitnessHash() == wtxid && it->announcer == peer) return it;
     538                 :             :         }
     539                 :           0 :         return sim_announcements.end();
     540                 :         511 :     };
     541                 :             :     /** Get an iterator to an existing (tx, peer) pair in the simulation. */
     542                 :      172036 :     auto find_announce_fn = [&](unsigned tx, NodeId peer) {
     543         [ +  + ]:      939360 :         for (auto it = sim_announcements.begin(); it != sim_announcements.end(); ++it) {
     544   [ +  +  +  + ]:      781671 :             if (it->tx == tx && it->announcer == peer) return it;
     545                 :             :         }
     546                 :      157689 :         return sim_announcements.end();
     547                 :         511 :     };
     548                 :             :     /** Compute a peer's DoS score according to simulation data. */
     549                 :      113375 :     auto dos_score_fn = [&](NodeId peer, int32_t max_count, int32_t max_usage) -> FeeFrac {
     550                 :      112864 :         int64_t count{0};
     551                 :      112864 :         int64_t usage{0};
     552         [ +  + ]:      364112 :         for (auto& ann : sim_announcements) {
     553         [ +  + ]:      251248 :             if (ann.announcer != peer) continue;
     554         [ -  + ]:       15703 :             count += 1 + (txn[ann.tx]->vin.size() / 10);
     555                 :       15703 :             usage += GetTransactionWeight(*txn[ann.tx]);
     556                 :             :         }
     557                 :      112864 :         return std::max(FeeFrac{count, max_count}, FeeFrac{usage, max_usage});
     558                 :         511 :     };
     559                 :             : 
     560                 :             :     //
     561                 :             :     // 5. Run through a scenario of mutators on both real and simulated orphanage.
     562                 :             :     //
     563                 :             : 
     564   [ +  +  +  + ]:       60570 :     LIMITED_WHILE(provider.remaining_bytes() > 0, 200) {
     565                 :       60059 :         int command = provider.ConsumeIntegralInRange<uint8_t>(0, 15);
     566                 :       93845 :         while (true) {
     567   [ -  +  +  -  :       93845 :             if (sim_announcements.size() < MAX_ANN && command-- == 0) {
                   +  + ]
     568                 :             :                 // AddTx
     569         [ +  - ]:       26294 :                 auto [tx, peer] = read_tx_peer_fn();
     570         [ +  - ]:       26294 :                 bool added = real->AddTx(txn[tx], peer);
     571                 :       26294 :                 bool sim_have_tx = have_tx_fn(tx);
     572         [ -  + ]:       26294 :                 assert(added == !sim_have_tx);
     573         [ +  + ]:       26294 :                 if (find_announce_fn(tx, peer) == sim_announcements.end()) {
     574         [ +  - ]:       18370 :                     sim_announcements.emplace_back(tx, peer, false);
     575                 :             :                 }
     576                 :             :                 break;
     577   [ +  -  +  + ]:       67551 :             } else if (sim_announcements.size() < MAX_ANN && command-- == 0) {
     578                 :             :                 // AddAnnouncer
     579         [ +  - ]:       14415 :                 auto [tx, peer] = read_tx_peer_fn();
     580         [ +  - ]:       14415 :                 bool added = real->AddAnnouncer(txn[tx]->GetWitnessHash(), peer);
     581                 :       14415 :                 bool sim_have_tx = have_tx_fn(tx);
     582                 :       14415 :                 auto sim_it = find_announce_fn(tx, peer);
     583   [ +  +  +  +  :       27638 :                 assert(added == (sim_it == sim_announcements.end() && sim_have_tx));
                   -  + ]
     584         [ +  + ]:       14415 :                 if (added) {
     585         [ +  - ]:        1192 :                     sim_announcements.emplace_back(tx, peer, false);
     586                 :             :                 }
     587                 :             :                 break;
     588         [ +  + ]:       53136 :             } else if (command-- == 0) {
     589                 :             :                 // EraseTx
     590                 :        3831 :                 auto tx = read_tx_fn();
     591         [ +  - ]:        3831 :                 bool erased = real->EraseTx(txn[tx]->GetWitnessHash());
     592                 :        3831 :                 bool sim_have = have_tx_fn(tx);
     593         [ -  + ]:        3831 :                 assert(erased == sim_have);
     594   [ +  +  +  + ]:       19917 :                 std::erase_if(sim_announcements, [&](auto& ann) { return ann.tx == tx; });
     595                 :             :                 break;
     596         [ +  + ]:       49305 :            } else if (command-- == 0) {
     597                 :             :                 // EraseForPeer
     598                 :        3551 :                 auto peer = read_peer_fn();
     599         [ +  - ]:        3551 :                 real->EraseForPeer(peer);
     600   [ +  +  +  + ]:       17745 :                 std::erase_if(sim_announcements, [&](auto& ann) { return ann.announcer == peer; });
     601                 :             :                 break;
     602         [ +  + ]:       45754 :             } else if (command-- == 0) {
     603                 :             :                 // EraseForBlock
     604                 :        3870 :                 auto pattern = provider.ConsumeIntegralInRange<uint64_t>(0, (uint64_t{1} << NUM_TX) - 1);
     605                 :        3870 :                 CBlock block;
     606                 :        3870 :                 std::set<COutPoint> spent;
     607         [ +  + ]:       65790 :                 for (unsigned tx = 0; tx < NUM_TX; ++tx) {
     608         [ +  + ]:       61920 :                     if ((pattern >> tx) & 1) {
     609         [ +  - ]:       20708 :                         block.vtx.emplace_back(txn[tx]);
     610         [ +  + ]:       67227 :                         for (auto& txin : block.vtx.back()->vin) {
     611         [ +  - ]:       46519 :                             spent.insert(txin.prevout);
     612                 :             :                         }
     613                 :             :                     }
     614                 :             :                 }
     615                 :        3870 :                 std::shuffle(block.vtx.begin(), block.vtx.end(), rng);
     616         [ +  - ]:        3870 :                 real->EraseForBlock(block);
     617                 :       17200 :                 std::erase_if(sim_announcements, [&](auto& ann) {
     618         [ +  + ]:       23703 :                     for (auto& txin : txn[ann.tx]->vin) {
     619         [ +  + ]:       17703 :                         if (spent.count(txin.prevout)) return true;
     620                 :             :                     }
     621                 :             :                     return false;
     622                 :             :                 });
     623                 :        3870 :                 break;
     624         [ +  + ]:       45754 :             } else if (command-- == 0) {
     625                 :             :                 // AddChildrenToWorkSet
     626                 :        3760 :                 auto tx = read_tx_fn();
     627                 :        3760 :                 FastRandomContext rand_ctx(rng.rand256());
     628         [ +  - ]:        3760 :                 auto added = real->AddChildrenToWorkSet(*txn[tx], rand_ctx);
     629                 :             :                 /** Set of not-already-reconsiderable child wtxids. */
     630                 :        3760 :                 std::set<Wtxid> child_wtxids;
     631         [ +  + ]:       63920 :                 for (unsigned child_tx = 0; child_tx < NUM_TX; ++child_tx) {
     632         [ +  + ]:       60160 :                     if (!have_tx_fn(child_tx)) continue;
     633         [ +  + ]:       10557 :                     if (have_reconsiderable_fn(child_tx)) continue;
     634                 :        8965 :                     bool child_of = false;
     635         [ +  + ]:       23575 :                     for (auto& txin : txn[child_tx]->vin) {
     636         [ +  + ]:       16234 :                         if (txin.prevout.hash == txn[tx]->GetHash()) {
     637                 :             :                             child_of = true;
     638                 :             :                             break;
     639                 :             :                         }
     640                 :             :                     }
     641         [ +  + ]:        8965 :                     if (child_of) {
     642         [ +  - ]:        1624 :                         child_wtxids.insert(txn[child_tx]->GetWitnessHash());
     643                 :             :                     }
     644                 :             :                 }
     645         [ +  + ]:        5384 :                 for (auto& [wtxid, peer] : added) {
     646                 :             :                     // Wtxid must be a child of tx that is not yet reconsiderable.
     647                 :        1624 :                     auto child_wtxid_it = child_wtxids.find(wtxid);
     648         [ -  + ]:        1624 :                     assert(child_wtxid_it != child_wtxids.end());
     649                 :             :                     // Announcement must exist.
     650                 :        1624 :                     auto sim_ann_it = find_announce_wtxid_fn(wtxid, peer);
     651         [ -  + ]:        1624 :                     assert(sim_ann_it != sim_announcements.end());
     652                 :             :                     // Announcement must not yet be reconsiderable.
     653         [ -  + ]:        1624 :                     assert(sim_ann_it->reconsider == false);
     654                 :             :                     // Make reconsiderable.
     655                 :        1624 :                     sim_ann_it->reconsider = true;
     656                 :             :                     // Remove from child_wtxids map, to disallow it being reported a second time in added.
     657                 :        1624 :                     child_wtxids.erase(wtxid);
     658                 :             :                 }
     659                 :             :                 // Verify that AddChildrenToWorkSet does not select announcements that were already reconsiderable:
     660                 :             :                 // Check all child wtxids which did not occur at least once in the result were already reconsiderable
     661                 :             :                 // due to a previous AddChildrenToWorkSet.
     662         [ -  + ]:        3760 :                 assert(child_wtxids.empty());
     663                 :        3760 :                 break;
     664         [ +  + ]:       41884 :             } else if (command-- == 0) {
     665                 :             :                 // GetTxToReconsider.
     666                 :        4338 :                 auto peer = read_peer_fn();
     667         [ +  - ]:        4338 :                 auto result = real->GetTxToReconsider(peer);
     668         [ +  + ]:        4338 :                 if (result) {
     669                 :             :                     // A transaction was found. It must have a corresponding reconsiderable
     670                 :             :                     // announcement from peer.
     671                 :         110 :                     auto sim_ann_it = find_announce_wtxid_fn(result->GetWitnessHash(), peer);
     672         [ -  + ]:         110 :                     assert(sim_ann_it != sim_announcements.end());
     673         [ -  + ]:         110 :                     assert(sim_ann_it->announcer == peer);
     674         [ -  + ]:         110 :                     assert(sim_ann_it->reconsider);
     675                 :             :                     // Make it non-reconsiderable.
     676                 :         110 :                     sim_ann_it->reconsider = false;
     677                 :             :                 } else {
     678                 :             :                     // No reconsiderable transaction was found from peer. Verify that it does not
     679                 :             :                     // have any.
     680         [ -  + ]:        4228 :                     assert(!have_reconsider_fn(peer));
     681                 :             :                 }
     682         [ +  + ]:        4338 :                 break;
     683                 :        4338 :             }
     684                 :             :         }
     685                 :             :         // Always trim after each command if needed.
     686   [ +  -  +  + ]:       60059 :         const auto max_ann = max_global_latency_score / std::max<unsigned>(1, count_peers_fn());
     687                 :       60059 :         const auto max_mem = reserved_peer_usage;
     688                 :       67113 :         while (true) {
     689                 :             :             // Count global usage and number of peers.
     690                 :       67113 :             node::TxOrphanage::Usage total_usage{0};
     691         [ -  + ]:       67113 :             node::TxOrphanage::Count total_latency_score = sim_announcements.size();
     692         [ +  + ]:     1140921 :             for (unsigned tx = 0; tx < NUM_TX; ++tx) {
     693         [ +  + ]:     1073808 :                 if (have_tx_fn(tx)) {
     694                 :      207013 :                     total_usage += GetTransactionWeight(*txn[tx]);
     695         [ -  + ]:      207013 :                     total_latency_score += txn[tx]->vin.size() / 10;
     696                 :             :                 }
     697                 :             :             }
     698         [ +  - ]:       67113 :             auto num_peers = count_peers_fn();
     699   [ +  +  +  + ]:      127381 :             bool oversized = (total_usage > reserved_peer_usage * num_peers) ||
     700         [ +  - ]:       60268 :                                 (total_latency_score > real->MaxGlobalLatencyScore());
     701                 :       67113 :             if (!oversized) break;
     702                 :             :             // Find worst peer.
     703                 :        7054 :             FeeFrac worst_dos_score{0, 1};
     704                 :        7054 :             unsigned worst_peer = unsigned(-1);
     705         [ +  + ]:      119918 :             for (unsigned peer = 0; peer < NUM_PEERS; ++peer) {
     706                 :      112864 :                 auto dos_score = dos_score_fn(peer, max_ann, max_mem);
     707                 :             :                 // Use >= so that the more recent peer (higher NodeId) wins in case of
     708                 :             :                 // ties.
     709         [ +  + ]:      112864 :                 if (dos_score >= worst_dos_score) {
     710                 :       15442 :                     worst_dos_score = dos_score;
     711                 :       15442 :                     worst_peer = peer;
     712                 :             :                 }
     713                 :             :             }
     714         [ -  + ]:        7054 :             assert(worst_peer != unsigned(-1));
     715         [ -  + ]:        7054 :             assert(worst_dos_score >> FeeFrac(1, 1));
     716                 :             :             // Find oldest announcement from worst_peer, preferring non-reconsiderable ones.
     717                 :             :             bool done{false};
     718         [ +  - ]:        7060 :             for (int reconsider = 0; reconsider < 2; ++reconsider) {
     719         [ +  + ]:       12169 :                 for (auto it = sim_announcements.begin(); it != sim_announcements.end(); ++it) {
     720   [ +  +  +  + ]:       12163 :                     if (it->announcer != worst_peer || it->reconsider != reconsider) continue;
     721                 :        7054 :                     sim_announcements.erase(it);
     722                 :        7054 :                     done = true;
     723                 :        7054 :                     break;
     724                 :             :                 }
     725                 :        7060 :                 if (done) break;
     726                 :             :             }
     727         [ +  - ]:        7054 :             assert(done);
     728                 :             :         }
     729                 :             :         // We must now be within limits, otherwise LimitOrphans should have continued further.
     730                 :             :         // We don't check the contents of the orphanage until the end to make fuzz runs faster.
     731   [ +  -  +  -  :       60059 :         assert(real->TotalLatencyScore() <= real->MaxGlobalLatencyScore());
                   -  + ]
     732   [ +  -  +  -  :       60059 :         assert(real->TotalOrphanUsage() <= real->MaxGlobalUsage());
                   -  + ]
     733                 :             :     }
     734                 :             : 
     735                 :             :     //
     736                 :             :     // 6. Perform a full comparison between the real orphanage's inspectors and the simulation.
     737                 :             :     //
     738                 :             : 
     739         [ +  - ]:         511 :     real->SanityCheck();
     740                 :             : 
     741                 :             : 
     742         [ +  - ]:         511 :     auto all_orphans = real->GetOrphanTransactions();
     743                 :         511 :     node::TxOrphanage::Usage orphan_usage{0};
     744         [ +  - ]:         511 :     std::vector<node::TxOrphanage::Usage> usage_by_peer(NUM_PEERS);
     745                 :         511 :     node::TxOrphanage::Count unique_orphans{0};
     746         [ +  - ]:         511 :     std::vector<node::TxOrphanage::Count> count_by_peer(NUM_PEERS);
     747         [ -  + ]:         511 :     node::TxOrphanage::Count total_latency_score = sim_announcements.size();
     748         [ +  + ]:        8687 :     for (unsigned tx = 0; tx < NUM_TX; ++tx) {
     749                 :        8176 :         bool sim_have_tx = have_tx_fn(tx);
     750         [ +  + ]:        8176 :         if (sim_have_tx) {
     751                 :        1555 :             orphan_usage += GetTransactionWeight(*txn[tx]);
     752         [ -  + ]:        1555 :             total_latency_score += txn[tx]->vin.size() / 10;
     753                 :             :         }
     754                 :        8176 :         unique_orphans += sim_have_tx;
     755                 :       28510 :         auto orphans_it = std::find_if(all_orphans.begin(), all_orphans.end(), [&](auto& orph) { return orph.tx->GetWitnessHash() == txn[tx]->GetWitnessHash(); });
     756                 :             :         // GetOrphanTransactions (OrphanBase existence)
     757         [ -  + ]:        8176 :         assert((orphans_it != all_orphans.end()) == sim_have_tx);
     758                 :             :         // HaveTx
     759         [ +  - ]:        8176 :         bool have_tx = real->HaveTx(txn[tx]->GetWitnessHash());
     760         [ -  + ]:        8176 :         assert(have_tx == sim_have_tx);
     761                 :             :         // GetTx
     762         [ +  - ]:        8176 :         auto txref = real->GetTx(txn[tx]->GetWitnessHash());
     763         [ -  + ]:        8176 :         assert(!!txref == sim_have_tx);
     764   [ +  +  -  + ]:        8176 :         if (sim_have_tx) assert(txref->GetWitnessHash() == txn[tx]->GetWitnessHash());
     765                 :             : 
     766         [ +  + ]:      138992 :         for (NodeId peer = 0; peer < NUM_PEERS; ++peer) {
     767                 :      130816 :             auto it_sim_ann = find_announce_fn(tx, peer);
     768         [ +  + ]:      130816 :             bool sim_have_ann = it_sim_ann != sim_announcements.end();
     769         [ +  + ]:      130816 :             if (sim_have_ann) usage_by_peer[peer] += GetTransactionWeight(*txn[tx]);
     770         [ +  + ]:      130816 :             count_by_peer[peer] += sim_have_ann;
     771                 :             :             // GetOrphanTransactions (announcers presence)
     772   [ +  +  -  + ]:      130816 :             if (sim_have_ann) assert(sim_have_tx);
     773   [ +  +  -  + ]:      130816 :             if (sim_have_tx) assert(orphans_it->announcers.count(peer) == sim_have_ann);
     774                 :             :             // HaveTxFromPeer
     775         [ +  - ]:      130816 :             bool have_ann = real->HaveTxFromPeer(txn[tx]->GetWitnessHash(), peer);
     776         [ -  + ]:      130816 :             assert(sim_have_ann == have_ann);
     777                 :             :             // GetChildrenFromSamePeer
     778         [ +  - ]:      130816 :             auto children_from_peer = real->GetChildrenFromSamePeer(txn[tx], peer);
     779                 :      130816 :             auto it = children_from_peer.rbegin();
     780         [ +  + ]:      392448 :             for (int phase = 0; phase < 2; ++phase) {
     781                 :             :                 // First expect all children which have reconsiderable announcement from peer, then the others.
     782         [ +  + ]:     1509888 :                 for (auto& ann : sim_announcements) {
     783         [ +  + ]:     1248256 :                     if (ann.announcer != peer) continue;
     784         [ +  + ]:       78016 :                     if (ann.reconsider != (phase == 1)) continue;
     785                 :       39008 :                     bool matching_parent{false};
     786         [ +  + ]:      115136 :                     for (const auto& vin : txn[ann.tx]->vin) {
     787         [ +  + ]:       76128 :                         if (vin.prevout.hash == txn[tx]->GetHash()) matching_parent = true;
     788                 :             :                     }
     789         [ +  + ]:       39008 :                     if (!matching_parent) continue;
     790                 :             :                     // Found an announcement from peer which is a child of txn[tx].
     791         [ -  + ]:        5488 :                     assert(it != children_from_peer.rend());
     792         [ -  + ]:        5488 :                     assert((*it)->GetWitnessHash() == txn[ann.tx]->GetWitnessHash());
     793                 :        5488 :                     ++it;
     794                 :             :                 }
     795                 :             :             }
     796         [ -  + ]:      130816 :             assert(it == children_from_peer.rend());
     797                 :      130816 :         }
     798                 :        8176 :     }
     799                 :             :     // TotalOrphanUsage
     800   [ +  -  -  + ]:         511 :     assert(orphan_usage == real->TotalOrphanUsage());
     801         [ +  + ]:        8687 :     for (NodeId peer = 0; peer < NUM_PEERS; ++peer) {
     802                 :        8176 :         bool sim_have_reconsider = have_reconsider_fn(peer);
     803                 :             :         // HaveTxToReconsider
     804         [ +  - ]:        8176 :         bool have_reconsider = real->HaveTxToReconsider(peer);
     805         [ -  + ]:        8176 :         assert(have_reconsider == sim_have_reconsider);
     806                 :             :         // UsageByPeer
     807   [ +  -  -  + ]:        8176 :         assert(usage_by_peer[peer] == real->UsageByPeer(peer));
     808                 :             :         // AnnouncementsFromPeer
     809   [ +  -  -  + ]:        8176 :         assert(count_by_peer[peer] == real->AnnouncementsFromPeer(peer));
     810                 :             :     }
     811                 :             :     // CountAnnouncements
     812   [ -  +  +  -  :         511 :     assert(sim_announcements.size() == real->CountAnnouncements());
                   -  + ]
     813                 :             :     // CountUniqueOrphans
     814   [ +  -  -  + ]:         511 :     assert(unique_orphans == real->CountUniqueOrphans());
     815                 :             :     // MaxGlobalLatencyScore
     816   [ +  -  -  + ]:         511 :     assert(max_global_latency_score == real->MaxGlobalLatencyScore());
     817                 :             :     // ReservedPeerUsage
     818   [ +  -  -  + ]:         511 :     assert(reserved_peer_usage == real->ReservedPeerUsage());
     819                 :             :     // MaxPeerLatencyScore
     820         [ +  - ]:         511 :     auto present_peers = count_peers_fn();
     821   [ +  +  +  -  :         742 :     assert(max_global_latency_score / std::max<unsigned>(1, present_peers) == real->MaxPeerLatencyScore());
                   -  + ]
     822                 :             :     // MaxGlobalUsage
     823   [ +  +  +  -  :         742 :     assert(reserved_peer_usage * std::max<unsigned>(1, present_peers) == real->MaxGlobalUsage());
                   -  + ]
     824                 :             :     // TotalLatencyScore.
     825   [ +  -  -  + ]:         511 :     assert(real->TotalLatencyScore() == total_latency_score);
     826                 :         511 : }
        

Generated by: LCOV version 2.0-1