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 % 504 503
Test Date: 2025-08-01 04:15:35 Functions: 100.0 % 32 32
Branches: 74.6 % 642 479

             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   [ +  -  +  - ]:           4 :     static const auto testing_setup = MakeNoLogFileContext();
      36         [ +  - ]:           4 : }
      37                 :             : 
      38         [ +  - ]:        1188 : 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   [ +  -  +  +  :       94490 :     LIMITED_WHILE(outpoints.size() < 200'000 && fuzzed_data_provider.ConsumeBool(), 1000)
                   +  + ]
      59                 :             :     {
      60                 :             :         // construct transaction
      61                 :       93022 :         const CTransactionRef tx = [&] {
      62                 :       46511 :             CMutableTransaction tx_mut;
      63                 :       46511 :             const auto num_in = fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(1, outpoints.size());
      64                 :       46511 :             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         [ +  - ]:       46511 :             tx_mut.vin.reserve(num_in);
      68         [ +  + ]:     5873647 :             for (uint32_t i = 0; i < num_in; i++) {
      69                 :     5827136 :                 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         [ +  - ]:    11654272 :                 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         [ +  - ]:       46511 :             tx_mut.vout.reserve(num_out);
      75         [ +  + ]:     4736703 :             for (uint32_t i = 0; i < num_out; i++) {
      76         [ +  - ]:     9380384 :                 tx_mut.vout.emplace_back(CAmount{0}, CScript{});
      77                 :             :             }
      78         [ +  - ]:       46511 :             auto new_tx = MakeTransactionRef(tx_mut);
      79                 :             :             // add newly constructed outpoints to the coin pool
      80         [ +  + ]:     4736703 :             for (uint32_t i = 0; i < num_out; i++) {
      81         [ +  - ]:     4690192 :                 outpoints.emplace_back(new_tx->GetHash(), i);
      82                 :             :             }
      83                 :       46511 :             return new_tx;
      84         [ +  - ]:       93022 :         }();
      85                 :             : 
      86         [ +  - ]:       46511 :         tx_history.push_back(tx);
      87                 :             : 
      88         [ +  + ]:       46511 :         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         [ +  + ]:       46511 :         if (ptx_potential_parent) {
      93                 :             :             // Set up future GetTxToReconsider call.
      94         [ +  - ]:       45818 :             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                 :       45818 :             NodeId peer_id = fuzzed_data_provider.ConsumeIntegral<NodeId>();
      98   [ +  -  +  + ]:      263781 :             for (const auto& child : orphanage->GetChildrenFromSamePeer(ptx_potential_parent, peer_id)) {
      99         [ -  + ]:     1029306 :                 assert(std::any_of(child->vin.cbegin(), child->vin.cend(), [&](const auto& input) {
     100                 :             :                     return input.prevout.hash == ptx_potential_parent->GetHash();
     101                 :             :                 }));
     102                 :       45818 :             }
     103                 :             :         }
     104                 :             : 
     105                 :             :         // trigger orphanage functions
     106   [ +  +  +  + ]:     2712473 :         LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 1000)
     107                 :             :         {
     108                 :     2665962 :             NodeId peer_id = fuzzed_data_provider.ConsumeIntegral<NodeId>();
     109         [ +  - ]:     2665962 :             const auto total_bytes_start{orphanage->TotalOrphanUsage()};
     110         [ +  - ]:     2665962 :             const auto total_peer_bytes_start{orphanage->UsageByPeer(peer_id)};
     111                 :     2665962 :             const auto tx_weight{GetTransactionWeight(*tx)};
     112                 :             : 
     113         [ +  - ]:     2665962 :             CallOneOf(
     114                 :             :                 fuzzed_data_provider,
     115                 :        7773 :                 [&] {
     116                 :        7773 :                     {
     117                 :        7773 :                         CTransactionRef ref = orphanage->GetTxToReconsider(peer_id);
     118         [ +  + ]:        7773 :                         if (ref) {
     119   [ +  -  +  - ]:        1642 :                             Assert(orphanage->HaveTx(ref->GetWitnessHash()));
     120                 :             :                         }
     121                 :        7773 :                     }
     122                 :        7773 :                 },
     123                 :     2498855 :                 [&] {
     124                 :     2498855 :                     bool have_tx = orphanage->HaveTx(tx->GetWitnessHash());
     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                 :     2498855 :                     {
     128                 :     2498855 :                         bool add_tx = orphanage->AddTx(tx, peer_id);
     129                 :             :                         // have_tx == true -> add_tx == false
     130                 :     2498855 :                         Assert(!have_tx || !add_tx);
     131                 :             : 
     132         [ +  + ]:     2498855 :                         if (add_tx) {
     133                 :       52602 :                             Assert(orphanage->UsageByPeer(peer_id) == tx_weight + total_peer_bytes_start);
     134                 :       52602 :                             Assert(orphanage->TotalOrphanUsage() == tx_weight + total_bytes_start);
     135                 :       52602 :                             Assert(tx_weight <= MAX_STANDARD_TX_WEIGHT);
     136                 :             :                         } else {
     137                 :             :                             // Peer may have been added as an announcer.
     138         [ +  + ]:     2446253 :                             if (orphanage->UsageByPeer(peer_id) == tx_weight + total_peer_bytes_start) {
     139                 :       37226 :                                 Assert(orphanage->HaveTxFromPeer(wtxid, peer_id));
     140                 :             :                             } else {
     141                 :             :                                 // Otherwise, there must not be any change to the peer byte count.
     142                 :     2409027 :                                 Assert(orphanage->UsageByPeer(peer_id) == total_peer_bytes_start);
     143                 :             :                             }
     144                 :             : 
     145                 :             :                             // Regardless, total bytes should not have changed.
     146                 :     2446253 :                             Assert(orphanage->TotalOrphanUsage() == total_bytes_start);
     147                 :             :                         }
     148                 :             :                     }
     149                 :     2498855 :                     have_tx = orphanage->HaveTx(tx->GetWitnessHash());
     150                 :     2498855 :                     {
     151                 :     2498855 :                         bool add_tx = orphanage->AddTx(tx, peer_id);
     152                 :             :                         // if have_tx is still false, it must be too big
     153                 :     2498855 :                         Assert(!have_tx == (tx_weight > MAX_STANDARD_TX_WEIGHT));
     154                 :     2498855 :                         Assert(!have_tx || !add_tx);
     155                 :             :                     }
     156                 :     2498855 :                 },
     157                 :       52498 :                 [&] {
     158                 :       52498 :                     bool have_tx = orphanage->HaveTx(tx->GetWitnessHash());
     159                 :       52498 :                     bool have_tx_and_peer = orphanage->HaveTxFromPeer(tx->GetWitnessHash(), peer_id);
     160                 :             :                     // AddAnnouncer should return false if tx doesn't exist or we already HaveTxFromPeer.
     161                 :       52498 :                     {
     162                 :       52498 :                         bool added_announcer = orphanage->AddAnnouncer(tx->GetWitnessHash(), peer_id);
     163                 :             :                         // have_tx == false -> added_announcer == false
     164                 :       52498 :                         Assert(have_tx || !added_announcer);
     165                 :             :                         // have_tx_and_peer == true -> added_announcer == false
     166                 :       52498 :                         Assert(!have_tx_and_peer || !added_announcer);
     167                 :             : 
     168                 :             :                         // Total bytes should not have changed. If peer was added as announcer, byte
     169                 :             :                         // accounting must have been updated.
     170                 :       52498 :                         Assert(orphanage->TotalOrphanUsage() == total_bytes_start);
     171         [ +  + ]:       52498 :                         if (added_announcer) {
     172                 :        3181 :                             Assert(orphanage->UsageByPeer(peer_id) == tx_weight + total_peer_bytes_start);
     173                 :             :                         } else {
     174                 :       49317 :                             Assert(orphanage->UsageByPeer(peer_id) == total_peer_bytes_start);
     175                 :             :                         }
     176                 :             :                     }
     177                 :       52498 :                 },
     178                 :       74788 :                 [&] {
     179                 :       74788 :                     bool have_tx = orphanage->HaveTx(tx->GetWitnessHash());
     180                 :       74788 :                     bool have_tx_and_peer{orphanage->HaveTxFromPeer(wtxid, peer_id)};
     181                 :             :                     // EraseTx should return 0 if m_orphans doesn't have the tx
     182                 :       74788 :                     {
     183                 :       74788 :                         auto bytes_from_peer_before{orphanage->UsageByPeer(peer_id)};
     184                 :       74788 :                         Assert(have_tx == orphanage->EraseTx(tx->GetWitnessHash()));
     185         [ +  + ]:       74788 :                         if (have_tx) {
     186                 :       29105 :                             Assert(orphanage->TotalOrphanUsage() == total_bytes_start - tx_weight);
     187         [ +  + ]:       29105 :                             if (have_tx_and_peer) {
     188                 :         328 :                                 Assert(orphanage->UsageByPeer(peer_id) == bytes_from_peer_before - tx_weight);
     189                 :             :                             } else {
     190                 :       28777 :                                 Assert(orphanage->UsageByPeer(peer_id) == bytes_from_peer_before);
     191                 :             :                             }
     192                 :             :                         } else {
     193                 :       45683 :                             Assert(orphanage->TotalOrphanUsage() == total_bytes_start);
     194                 :             :                         }
     195                 :             :                     }
     196                 :       74788 :                     have_tx = orphanage->HaveTx(tx->GetWitnessHash());
     197                 :       74788 :                     have_tx_and_peer = orphanage->HaveTxFromPeer(wtxid, peer_id);
     198                 :             :                     // have_tx should be false and EraseTx should fail
     199                 :       74788 :                     {
     200   [ +  -  -  + ]:       74788 :                         Assert(!have_tx && !have_tx_and_peer && !orphanage->EraseTx(wtxid));
     201                 :             :                     }
     202                 :       74788 :                 },
     203                 :       10991 :                 [&] {
     204                 :       10991 :                     orphanage->EraseForPeer(peer_id);
     205                 :       10991 :                     Assert(!orphanage->HaveTxFromPeer(tx->GetWitnessHash(), peer_id));
     206                 :       10991 :                     Assert(orphanage->UsageByPeer(peer_id) == 0);
     207                 :       10991 :                 },
     208                 :       12190 :                 [&] {
     209                 :             :                     // Make a block out of txs and then EraseForBlock
     210                 :       12190 :                     CBlock block;
     211                 :       12190 :                     int num_txs = fuzzed_data_provider.ConsumeIntegralInRange<unsigned int>(0, 1000);
     212         [ +  + ]:     1297797 :                     for (int i{0}; i < num_txs; ++i) {
     213                 :     1285607 :                         auto& tx_to_remove = PickValue(fuzzed_data_provider, tx_history);
     214         [ +  - ]:     1285607 :                         block.vtx.push_back(tx_to_remove);
     215                 :             :                     }
     216         [ +  - ]:       12190 :                     orphanage->EraseForBlock(block);
     217         [ +  + ]:     1297797 :                     for (const auto& tx_removed : block.vtx) {
     218   [ +  -  +  - ]:     1285607 :                         Assert(!orphanage->HaveTx(tx_removed->GetWitnessHash()));
     219   [ +  -  +  - ]:     1285607 :                         Assert(!orphanage->HaveTxFromPeer(tx_removed->GetWitnessHash(), peer_id));
     220                 :             :                     }
     221                 :       12190 :                 },
     222                 :        8867 :                 [&] {
     223                 :             :                     // test mocktime and expiry
     224                 :        8867 :                     SetMockTime(ConsumeTime(fuzzed_data_provider));
     225                 :        8867 :                     orphanage->LimitOrphans();
     226                 :        8867 :                 });
     227                 :             : 
     228                 :             :         }
     229                 :             : 
     230                 :             :         // Set tx as potential parent to be used for future GetChildren() calls.
     231   [ +  +  +  + ]:       92329 :         if (!ptx_potential_parent || fuzzed_data_provider.ConsumeBool()) {
     232                 :       26351 :             ptx_potential_parent = tx;
     233                 :             :         }
     234                 :             : 
     235         [ +  - ]:       46511 :         const bool have_tx{orphanage->HaveTx(tx->GetWitnessHash())};
     236   [ +  -  +  + ]:       46511 :         const bool get_tx_nonnull{orphanage->GetTx(tx->GetWitnessHash()) != nullptr};
     237         [ +  - ]:       46511 :         Assert(have_tx == get_tx_nonnull);
     238                 :       46511 :     }
     239         [ +  - ]:         734 :     orphanage->SanityCheck();
     240         [ +  + ]:        1427 : }
     241                 :             : 
     242         [ +  - ]:         773 : FUZZ_TARGET(txorphan_protected, .init = initialize_orphanage)
     243                 :             : {
     244                 :         319 :     SeedRandomStateForTest(SeedRand::ZEROS);
     245                 :         319 :     FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
     246                 :         319 :     FastRandomContext orphanage_rng{ConsumeUInt256(fuzzed_data_provider)};
     247         [ +  - ]:         319 :     SetMockTime(ConsumeTime(fuzzed_data_provider));
     248                 :             : 
     249                 :             :     // We have num_peers peers. Some subset of them will never exceed their reserved weight or announcement count, and
     250                 :             :     // should therefore never have any orphans evicted.
     251                 :         319 :     const unsigned int MAX_PEERS = 125;
     252                 :         319 :     const unsigned int num_peers = fuzzed_data_provider.ConsumeIntegralInRange<unsigned int>(1, MAX_PEERS);
     253                 :             :     // Generate a vector of bools for whether each peer is protected from eviction
     254                 :         319 :     std::bitset<MAX_PEERS> protected_peers;
     255         [ +  + ]:        6603 :     for (unsigned int i = 0; i < num_peers; i++) {
     256         [ +  - ]:        6284 :         protected_peers.set(i, fuzzed_data_provider.ConsumeBool());
     257                 :             :     }
     258                 :             : 
     259                 :             :     // Params for orphanage.
     260                 :         319 :     const unsigned int global_latency_score_limit = fuzzed_data_provider.ConsumeIntegralInRange<unsigned int>(num_peers, 6'000);
     261                 :         319 :     const int64_t per_peer_weight_reservation = fuzzed_data_provider.ConsumeIntegralInRange<int64_t>(1, 4'040'000);
     262                 :         319 :     auto orphanage = node::MakeTxOrphanage(global_latency_score_limit, per_peer_weight_reservation);
     263                 :             : 
     264                 :             :     // The actual limit, MaxPeerLatencyScore(), may be higher, since TxOrphanage only counts peers
     265                 :             :     // that have announced an orphan. The honest peer will not experience evictions if it never
     266                 :             :     // exceeds this.
     267                 :         319 :     const unsigned int honest_latency_limit = global_latency_score_limit / num_peers;
     268                 :             :     // Honest peer will not experience evictions if it never exceeds this.
     269                 :         319 :     const int64_t honest_mem_limit = per_peer_weight_reservation;
     270                 :             : 
     271                 :         319 :     std::vector<COutPoint> outpoints; // Duplicates are tolerated
     272         [ +  - ]:         319 :     outpoints.reserve(400);
     273                 :             : 
     274                 :             :     // initial outpoints used to construct transactions later
     275         [ +  + ]:        1595 :     for (uint8_t i = 0; i < 4; i++) {
     276         [ +  - ]:        1276 :         outpoints.emplace_back(Txid::FromUint256(uint256{i}), 0);
     277                 :             :     }
     278                 :             : 
     279                 :             :     // These are honest peer's live announcements. We expect them to be protected from eviction.
     280                 :         319 :     std::set<Wtxid> protected_wtxids;
     281                 :             : 
     282   [ +  +  +  +  :        2135 :     LIMITED_WHILE(outpoints.size() < 400 && fuzzed_data_provider.ConsumeBool(), 1000)
                   -  + ]
     283                 :             :     {
     284                 :             :         // construct transaction
     285                 :        1498 :         const CTransactionRef tx = [&] {
     286                 :         749 :             CMutableTransaction tx_mut;
     287                 :         749 :             const auto num_in = fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(1, outpoints.size());
     288                 :         749 :             const auto num_out = fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(1, 256);
     289                 :             :             // pick outpoints from outpoints as input. We allow input duplicates on purpose, given we are not
     290                 :             :             // running any transaction validation logic before adding transactions to the orphanage
     291         [ +  - ]:         749 :             tx_mut.vin.reserve(num_in);
     292         [ +  + ]:        3204 :             for (uint32_t i = 0; i < num_in; i++) {
     293                 :        2455 :                 auto& prevout = PickValue(fuzzed_data_provider, outpoints);
     294                 :             :                 // try making transactions unique by setting a random nSequence, but allow duplicate transactions if they happen
     295         [ +  - ]:        4910 :                 tx_mut.vin.emplace_back(prevout, CScript{}, fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(0, CTxIn::SEQUENCE_FINAL));
     296                 :             :             }
     297                 :             :             // output amount or spendability will not affect txorphanage
     298         [ +  - ]:         749 :             tx_mut.vout.reserve(num_out);
     299         [ +  + ]:       15880 :             for (uint32_t i = 0; i < num_out; i++) {
     300                 :       15131 :                 const auto payload_size = fuzzed_data_provider.ConsumeIntegralInRange<unsigned int>(0, 100000);
     301         [ +  + ]:       15131 :                 if (payload_size) {
     302   [ +  -  +  -  :       10108 :                     tx_mut.vout.emplace_back(0, CScript() << OP_RETURN << std::vector<unsigned char>(payload_size));
                   +  - ]
     303                 :             :                 } else {
     304         [ +  - ]:       20154 :                     tx_mut.vout.emplace_back(0, CScript{});
     305                 :             :                 }
     306                 :             :             }
     307         [ +  - ]:         749 :             auto new_tx = MakeTransactionRef(tx_mut);
     308                 :             :             // add newly constructed outpoints to the coin pool
     309         [ +  + ]:       15880 :             for (uint32_t i = 0; i < num_out; i++) {
     310         [ +  - ]:       15131 :                 outpoints.emplace_back(new_tx->GetHash(), i);
     311                 :             :             }
     312                 :         749 :             return new_tx;
     313         [ +  - ]:        1498 :         }();
     314                 :             : 
     315                 :         749 :         const auto wtxid{tx->GetWitnessHash()};
     316                 :             : 
     317                 :             :         // orphanage functions
     318   [ +  +  +  + ]:       32760 :         LIMITED_WHILE(fuzzed_data_provider.remaining_bytes(), 10 * global_latency_score_limit)
     319                 :             :         {
     320                 :       32011 :             NodeId peer_id = fuzzed_data_provider.ConsumeIntegralInRange<NodeId>(0, num_peers - 1);
     321                 :       32011 :             const auto tx_weight{GetTransactionWeight(*tx)};
     322                 :             : 
     323                 :             :             // This protected peer will never send orphans that would
     324                 :             :             // exceed their own personal allotment, so is never evicted.
     325         [ +  - ]:       32011 :             const bool peer_is_protected{protected_peers[peer_id]};
     326                 :             : 
     327         [ +  - ]:       32011 :             CallOneOf(
     328                 :             :                 fuzzed_data_provider,
     329                 :       15554 :                 [&] { // AddTx
     330                 :       15554 :                     bool have_tx_and_peer = orphanage->HaveTxFromPeer(wtxid, peer_id);
     331   [ +  +  +  + ]:       15554 :                     if (peer_is_protected && !have_tx_and_peer &&
     332   [ +  +  +  + ]:        3722 :                         (orphanage->UsageByPeer(peer_id) + tx_weight > honest_mem_limit ||
     333         [ +  + ]:        1652 :                         orphanage->LatencyScoreFromPeer(peer_id) + (tx->vin.size() / 10) + 1 > honest_latency_limit)) {
     334                 :             :                         // We never want our protected peer oversized or over-announced
     335                 :             :                     } else {
     336                 :       15034 :                         orphanage->AddTx(tx, peer_id);
     337   [ +  +  +  + ]:       15034 :                         if (peer_is_protected && orphanage->HaveTxFromPeer(wtxid, peer_id)) {
     338                 :        4380 :                             protected_wtxids.insert(wtxid);
     339                 :             :                         }
     340                 :             :                     }
     341                 :       15554 :                 },
     342                 :        5642 :                 [&] { // AddAnnouncer
     343                 :        5642 :                     bool have_tx_and_peer = orphanage->HaveTxFromPeer(tx->GetWitnessHash(), peer_id);
     344                 :             :                     // AddAnnouncer should return false if tx doesn't exist or we already HaveTxFromPeer.
     345                 :        5642 :                     {
     346   [ +  +  +  + ]:        5642 :                         if (peer_is_protected && !have_tx_and_peer &&
     347   [ +  +  +  + ]:        2452 :                             (orphanage->UsageByPeer(peer_id) + tx_weight > honest_mem_limit ||
     348         [ +  + ]:        1082 :                             orphanage->LatencyScoreFromPeer(peer_id) + (tx->vin.size()) + 1 > honest_latency_limit)) {
     349                 :             :                             // We never want our protected peer oversized
     350                 :             :                         } else {
     351                 :        5041 :                             orphanage->AddAnnouncer(tx->GetWitnessHash(), peer_id);
     352   [ +  +  +  + ]:        5041 :                             if (peer_is_protected && orphanage->HaveTxFromPeer(wtxid, peer_id)) {
     353                 :        1078 :                                 protected_wtxids.insert(wtxid);
     354                 :             :                             }
     355                 :             :                         }
     356                 :             :                     }
     357                 :        5642 :                 },
     358                 :        3223 :                 [&] { // EraseTx
     359         [ +  + ]:        3223 :                     if (protected_wtxids.count(tx->GetWitnessHash())) {
     360                 :         720 :                         protected_wtxids.erase(wtxid);
     361                 :             :                     }
     362                 :        3223 :                     orphanage->EraseTx(wtxid);
     363                 :        3223 :                     Assert(!orphanage->HaveTx(wtxid));
     364                 :        3223 :                 },
     365                 :        4747 :                 [&] { // EraseForPeer
     366         [ +  + ]:        4747 :                     if (!protected_peers[peer_id]) {
     367                 :        3517 :                         orphanage->EraseForPeer(peer_id);
     368                 :        3517 :                         Assert(orphanage->UsageByPeer(peer_id) == 0);
     369                 :        3517 :                         Assert(orphanage->LatencyScoreFromPeer(peer_id) == 0);
     370                 :        3517 :                         Assert(orphanage->AnnouncementsFromPeer(peer_id) == 0);
     371                 :             :                     }
     372                 :        4747 :                 },
     373                 :        2845 :                 [&] { // LimitOrphans
     374                 :             :                     // Assert that protected peers are never affected by LimitOrphans.
     375                 :        2845 :                     unsigned int protected_count = 0;
     376                 :        2845 :                     unsigned int protected_bytes = 0;
     377         [ +  + ]:       57813 :                     for (unsigned int peer = 0; peer < num_peers; ++peer) {
     378         [ +  + ]:       54968 :                         if (protected_peers[peer]) {
     379                 :       27328 :                             protected_count += orphanage->LatencyScoreFromPeer(peer);
     380                 :       27328 :                             protected_bytes += orphanage->UsageByPeer(peer);
     381                 :             :                         }
     382                 :             :                     }
     383                 :        2845 :                     orphanage->LimitOrphans();
     384                 :        2845 :                     Assert(orphanage->TotalLatencyScore() <= global_latency_score_limit);
     385                 :        2845 :                     Assert(orphanage->TotalOrphanUsage() <= per_peer_weight_reservation * num_peers);
     386                 :             : 
     387                 :             :                     // Number of announcements and usage should never differ before and after since
     388                 :             :                     // we've never exceeded the per-peer reservations.
     389         [ +  + ]:       57813 :                     for (unsigned int peer = 0; peer < num_peers; ++peer) {
     390         [ +  + ]:       54968 :                         if (protected_peers[peer]) {
     391                 :       27328 :                             protected_count -= orphanage->LatencyScoreFromPeer(peer);
     392                 :       27328 :                             protected_bytes -= orphanage->UsageByPeer(peer);
     393                 :             :                         }
     394                 :             :                     }
     395                 :        2845 :                     Assert(protected_count == 0);
     396                 :        2845 :                     Assert(protected_bytes == 0);
     397                 :        2845 :                 });
     398                 :             : 
     399                 :             :         }
     400                 :         749 :     }
     401                 :             : 
     402         [ +  - ]:         319 :     orphanage->SanityCheck();
     403                 :             :     // All of the honest peer's announcements are still present.
     404         [ +  + ]:         451 :     for (const auto& wtxid : protected_wtxids) {
     405   [ +  -  +  - ]:         132 :         Assert(orphanage->HaveTx(wtxid));
     406                 :             :     }
     407                 :         319 : }
     408                 :             : 
     409         [ +  - ]:         965 : FUZZ_TARGET(txorphanage_sim)
     410                 :             : {
     411                 :         511 :     SeedRandomStateForTest(SeedRand::ZEROS);
     412                 :             :     // This is a comphehensive simulation fuzz test, which runs through a scenario involving up to
     413                 :             :     // 16 transactions (which may have simple or complex topology, and may have duplicate txids
     414                 :             :     // with distinct wtxids, and up to 16 peers. The scenario is performed both on a real
     415                 :             :     // TxOrphanage object and the behavior is compared with a naive reimplementation (just a vector
     416                 :             :     // of announcements) where possible, and tested for desired properties where not possible.
     417                 :             : 
     418                 :             :     //
     419                 :             :     // 1. Setup.
     420                 :             :     //
     421                 :             : 
     422                 :             :     /** The total number of transactions this simulation uses (not all of which will necessarily
     423                 :             :      *  be present in the orphanage at once). */
     424                 :         511 :     static constexpr unsigned NUM_TX = 16;
     425                 :             :     /** The number of peers this simulation uses (not all of which will necessarily be present in
     426                 :             :      *  the orphanage at once). */
     427                 :         511 :     static constexpr unsigned NUM_PEERS = 16;
     428                 :             :     /** The maximum number of announcements this simulation uses (which may be higher than the
     429                 :             :      *  number permitted inside the orphanage). */
     430                 :         511 :     static constexpr unsigned MAX_ANN = 64;
     431                 :             : 
     432                 :         511 :     FuzzedDataProvider provider(buffer.data(), buffer.size());
     433                 :             :     /** Local RNG. Only used for topology/sizes of the transaction set, the order of transactions
     434                 :             :      *  in EraseForBlock, and for the randomized passed to AddChildrenToWorkSet. */
     435                 :         511 :     InsecureRandomContext rng(provider.ConsumeIntegral<uint64_t>());
     436                 :             : 
     437                 :             :     //
     438                 :             :     // 2. Construct an interesting set of 16 transactions.
     439                 :             :     //
     440                 :             : 
     441                 :             :     // - Pick a topological order among the transactions.
     442                 :         511 :     std::vector<unsigned> txorder(NUM_TX);
     443                 :         511 :     std::iota(txorder.begin(), txorder.end(), unsigned{0});
     444                 :         511 :     std::shuffle(txorder.begin(), txorder.end(), rng);
     445                 :             :     // - Pick a set of dependencies (pair<child_index, parent_index>).
     446                 :         511 :     std::vector<std::pair<unsigned, unsigned>> deps;
     447         [ +  - ]:         511 :     deps.reserve((NUM_TX * (NUM_TX - 1)) / 2);
     448         [ +  + ]:        8176 :     for (unsigned p = 0; p < NUM_TX - 1; ++p) {
     449         [ +  + ]:       68985 :         for (unsigned c = p + 1; c < NUM_TX; ++c) {
     450         [ +  - ]:       61320 :             deps.emplace_back(c, p);
     451                 :             :         }
     452                 :             :     }
     453                 :         511 :     std::shuffle(deps.begin(), deps.end(), rng);
     454         [ +  - ]:         511 :     deps.resize(provider.ConsumeIntegralInRange<unsigned>(0, NUM_TX * 4 - 1));
     455                 :             :     // - Construct the actual transactions.
     456         [ +  - ]:         511 :     std::set<Wtxid> wtxids;
     457         [ +  - ]:         511 :     std::vector<CTransactionRef> txn(NUM_TX);
     458                 :             :     node::TxOrphanage::Usage total_usage{0};
     459         [ +  + ]:        8687 :     for (unsigned t = 0; t < NUM_TX; ++t) {
     460         [ +  - ]:        8176 :         CMutableTransaction tx;
     461   [ +  +  +  + ]:        8176 :         if (t > 0 && rng.randrange(4) == 0) {
     462                 :             :             // Occasionally duplicate the previous transaction, so that repetitions of the same
     463                 :             :             // txid are possible (with different wtxid).
     464         [ +  - ]:        6126 :             tx = CMutableTransaction(*txn[txorder[t - 1]]);
     465                 :             :         } else {
     466                 :        5113 :             tx.version = 1;
     467                 :        5113 :             tx.nLockTime = 0xffffffff;
     468                 :             :             // Construct 1 to 16 outputs.
     469                 :        5113 :             auto num_outputs = rng.randrange<unsigned>(1 << rng.randrange<unsigned>(5)) + 1;
     470         [ +  + ]:       21903 :             for (unsigned output = 0; output < num_outputs; ++output) {
     471                 :       16790 :                 CScript scriptpubkey;
     472                 :       16790 :                 scriptpubkey.resize(provider.ConsumeIntegralInRange<unsigned>(20, 34));
     473         [ +  - ]:       16790 :                 tx.vout.emplace_back(CAmount{0}, std::move(scriptpubkey));
     474                 :       16790 :             }
     475                 :             :             // Construct inputs (one for each dependency).
     476   [ +  +  +  + ]:      153448 :             for (auto& [child, parent] : deps) {
     477         [ +  + ]:      148335 :                 if (child == t) {
     478         [ -  + ]:        8998 :                     auto& partx = txn[txorder[parent]];
     479         [ -  + ]:        8998 :                     assert(partx->version == 1);
     480         [ +  - ]:        8998 :                     COutPoint outpoint(partx->GetHash(), rng.randrange<size_t>(partx->vout.size()));
     481         [ +  - ]:        8998 :                     tx.vin.emplace_back(outpoint);
     482                 :        8998 :                     tx.vin.back().scriptSig.resize(provider.ConsumeIntegralInRange<unsigned>(16, 200));
     483                 :             :                 }
     484                 :             :             }
     485                 :             :             // Construct fallback input in case there are no dependencies.
     486         [ +  + ]:        5113 :             if (tx.vin.empty()) {
     487         [ +  - ]:        2423 :                 COutPoint outpoint(Txid::FromUint256(rng.rand256()), rng.randrange<size_t>(16));
     488         [ +  - ]:        2423 :                 tx.vin.emplace_back(outpoint);
     489                 :        2423 :                 tx.vin.back().scriptSig.resize(provider.ConsumeIntegralInRange<unsigned>(16, 200));
     490                 :             :             }
     491                 :             :         }
     492                 :             :         // Optionally modify the witness (allowing wtxid != txid), and certainly when the wtxid
     493                 :             :         // already exists.
     494   [ +  -  +  +  :       37124 :         while (wtxids.contains(CTransaction(tx).GetWitnessHash()) || rng.randrange(4) == 0) {
             +  +  +  + ]
     495                 :       10386 :             auto& input = tx.vin[rng.randrange(tx.vin.size())];
     496         [ +  + ]:       10386 :             if (rng.randbool()) {
     497         [ +  - ]:        4633 :                 input.scriptWitness.stack.resize(1);
     498         [ +  - ]:        4633 :                 input.scriptWitness.stack[0].resize(rng.randrange(100));
     499                 :             :             } else {
     500         [ +  - ]:        5753 :                 input.scriptWitness.stack.resize(0);
     501                 :             :             }
     502                 :             :         }
     503                 :             :         // Convert to CTransactionRef.
     504   [ +  -  -  + ]:       16352 :         txn[txorder[t]] = MakeTransactionRef(std::move(tx));
     505         [ +  - ]:        8176 :         wtxids.insert(txn[txorder[t]]->GetWitnessHash());
     506                 :        8176 :         auto weight = GetTransactionWeight(*txn[txorder[t]]);
     507         [ -  + ]:        8176 :         assert(weight < MAX_STANDARD_TX_WEIGHT);
     508                 :        8176 :         total_usage += GetTransactionWeight(*txn[txorder[t]]);
     509                 :        8176 :     }
     510                 :             : 
     511                 :             :     //
     512                 :             :     // 3. Initialize real orphanage
     513                 :             :     //
     514                 :             : 
     515                 :         511 :     auto max_global_ann = provider.ConsumeIntegralInRange<node::TxOrphanage::Count>(NUM_PEERS, MAX_ANN);
     516                 :         511 :     auto reserved_peer_usage = provider.ConsumeIntegralInRange<node::TxOrphanage::Usage>(1, total_usage);
     517                 :         511 :     auto real = node::MakeTxOrphanage(max_global_ann, reserved_peer_usage);
     518                 :             : 
     519                 :             :     //
     520                 :             :     // 4. Functions and data structures for the simulation.
     521                 :             :     //
     522                 :             : 
     523                 :             :     /** Data structure representing one announcement (pair of (tx, peer), plus whether it's
     524                 :             :      *  reconsiderable or not. */
     525                 :         511 :     struct SimAnnouncement
     526                 :             :     {
     527                 :             :         unsigned tx;
     528                 :             :         NodeId announcer;
     529                 :             :         bool reconsider{false};
     530                 :       21549 :         SimAnnouncement(unsigned tx_in, NodeId announcer_in, bool reconsider_in) noexcept :
     531                 :       21549 :             tx(tx_in), announcer(announcer_in), reconsider(reconsider_in) {}
     532                 :             :     };
     533                 :             :     /** The entire simulated orphanage is represented by this list of announcements, in
     534                 :             :      *  announcement order (unlike TxOrphanageImpl which uses a sequence number to represent
     535                 :             :      *  announcement order). New announcements are added to the back. */
     536                 :         511 :     std::vector<SimAnnouncement> sim_announcements;
     537                 :             : 
     538                 :             :     /** Consume a transaction (index into txn) from provider. */
     539                 :        9619 :     auto read_tx_fn = [&]() -> unsigned { return provider.ConsumeIntegralInRange<unsigned>(0, NUM_TX - 1); };
     540                 :             :     /** Consume a NodeId from provider. */
     541                 :       15979 :     auto read_peer_fn = [&]() -> NodeId { return provider.ConsumeIntegralInRange<unsigned>(0, NUM_PEERS - 1); };
     542                 :             :     /** Consume both a transaction (index into txn) and a NodeId from provider. */
     543                 :       35549 :     auto read_tx_peer_fn = [&]() -> std::pair<unsigned, NodeId> {
     544                 :       35038 :         auto code = provider.ConsumeIntegralInRange<unsigned>(0, NUM_TX * NUM_PEERS - 1);
     545                 :       35038 :         return {code % NUM_TX, code / NUM_TX};
     546                 :         511 :     };
     547                 :             :     /** Determine if we have any announcements of the given transaction in the simulation. */
     548                 :      342617 :     auto have_tx_fn = [&](unsigned tx) -> bool {
     549         [ +  + ]:     2145745 :         for (auto& ann : sim_announcements) {
     550         [ +  + ]:     1919961 :             if (ann.tx == tx) return true;
     551                 :             :         }
     552                 :             :         return false;
     553                 :         511 :     };
     554                 :             :     /** Count the number of peers in the simulation. */
     555                 :       21377 :     auto count_peers_fn = [&]() -> unsigned {
     556                 :       20866 :         std::bitset<NUM_PEERS> mask;
     557         [ +  + ]:      190987 :         for (auto& ann : sim_announcements) {
     558                 :      170121 :             mask.set(ann.announcer);
     559                 :             :         }
     560                 :       20866 :         return mask.count();
     561                 :         511 :     };
     562                 :             :     /** Determine if we have any reconsiderable announcements of a given transaction. */
     563                 :       15705 :     auto have_reconsiderable_fn = [&](unsigned tx) -> bool {
     564         [ +  + ]:      136333 :         for (auto& ann : sim_announcements) {
     565   [ +  +  +  + ]:      132352 :             if (ann.reconsider && ann.tx == tx) return true;
     566                 :             :         }
     567                 :             :         return false;
     568                 :         511 :     };
     569                 :             :     /** Determine if a peer has any transactions to reconsider. */
     570                 :       11976 :     auto have_reconsider_fn = [&](NodeId peer) -> bool {
     571         [ +  + ]:       86957 :         for (auto& ann : sim_announcements) {
     572   [ +  +  +  + ]:       76182 :             if (ann.reconsider && ann.announcer == peer) return true;
     573                 :             :         }
     574                 :             :         return false;
     575                 :         511 :     };
     576                 :             :     /** Get an iterator to an existing (wtxid, peer) pair in the simulation. */
     577                 :        6734 :     auto find_announce_wtxid_fn = [&](const Wtxid& wtxid, NodeId peer) -> std::vector<SimAnnouncement>::iterator {
     578         [ +  - ]:       51115 :         for (auto it = sim_announcements.begin(); it != sim_announcements.end(); ++it) {
     579   [ +  +  +  + ]:       51115 :             if (txn[it->tx]->GetWitnessHash() == wtxid && it->announcer == peer) return it;
     580                 :             :         }
     581                 :           0 :         return sim_announcements.end();
     582                 :         511 :     };
     583                 :             :     /** Get an iterator to an existing (tx, peer) pair in the simulation. */
     584                 :      166365 :     auto find_announce_fn = [&](unsigned tx, NodeId peer) {
     585         [ +  + ]:     1397409 :         for (auto it = sim_announcements.begin(); it != sim_announcements.end(); ++it) {
     586   [ +  +  +  + ]:     1246479 :             if (it->tx == tx && it->announcer == peer) return it;
     587                 :             :         }
     588                 :      150930 :         return sim_announcements.end();
     589                 :         511 :     };
     590                 :             :     /** Compute a peer's DoS score according to simulation data. */
     591                 :       94799 :     auto dos_score_fn = [&](NodeId peer, int32_t max_count, int32_t max_usage) -> FeeFrac {
     592                 :       94288 :         int64_t count{0};
     593                 :       94288 :         int64_t usage{0};
     594         [ +  + ]:      857760 :         for (auto& ann : sim_announcements) {
     595         [ +  + ]:      763472 :             if (ann.announcer != peer) continue;
     596                 :       47717 :             count += 1 + (txn[ann.tx]->vin.size() / 10);
     597                 :       47717 :             usage += GetTransactionWeight(*txn[ann.tx]);
     598                 :             :         }
     599                 :       94288 :         return std::max(FeeFrac{count, max_count}, FeeFrac{usage, max_usage});
     600                 :         511 :     };
     601                 :             : 
     602                 :             :     //
     603                 :             :     // 5. Run through a scenario of mutators on both real and simulated orphanage.
     604                 :             :     //
     605                 :             : 
     606   [ +  +  +  + ]:       62966 :     LIMITED_WHILE(provider.remaining_bytes() > 0, 200) {
     607                 :       62455 :         int command = provider.ConsumeIntegralInRange<uint8_t>(0, 15);
     608                 :       87564 :         while (true) {
     609   [ +  +  +  + ]:       87564 :             if (sim_announcements.size() < MAX_ANN && command-- == 0) {
     610                 :             :                 // AddTx
     611         [ +  - ]:       29856 :                 auto [tx, peer] = read_tx_peer_fn();
     612         [ +  - ]:       29856 :                 bool added = real->AddTx(txn[tx], peer);
     613                 :       29856 :                 bool sim_have_tx = have_tx_fn(tx);
     614         [ -  + ]:       29856 :                 assert(added == !sim_have_tx);
     615         [ +  + ]:       29856 :                 if (find_announce_fn(tx, peer) == sim_announcements.end()) {
     616         [ +  - ]:       20444 :                     sim_announcements.emplace_back(tx, peer, false);
     617                 :             :                 }
     618                 :             :                 break;
     619   [ +  +  +  + ]:       57708 :             } else if (sim_announcements.size() < MAX_ANN && command-- == 0) {
     620                 :             :                 // AddAnnouncer
     621         [ +  - ]:        5182 :                 auto [tx, peer] = read_tx_peer_fn();
     622         [ +  - ]:        5182 :                 bool added = real->AddAnnouncer(txn[tx]->GetWitnessHash(), peer);
     623                 :        5182 :                 bool sim_have_tx = have_tx_fn(tx);
     624                 :        5182 :                 auto sim_it = find_announce_fn(tx, peer);
     625   [ +  +  +  +  :        9259 :                 assert(added == (sim_it == sim_announcements.end() && sim_have_tx));
                   -  + ]
     626         [ +  + ]:        5182 :                 if (added) {
     627         [ +  - ]:        1105 :                     sim_announcements.emplace_back(tx, peer, false);
     628                 :             :                 }
     629                 :             :                 break;
     630   [ +  +  +  +  :       52526 :             } else if (command-- == 0) {
                   +  + ]
     631                 :             :                 // EraseTx
     632                 :        3788 :                 auto tx = read_tx_fn();
     633         [ +  - ]:        3788 :                 bool erased = real->EraseTx(txn[tx]->GetWitnessHash());
     634                 :        3788 :                 bool sim_have = have_tx_fn(tx);
     635         [ -  + ]:        3788 :                 assert(erased == sim_have);
     636   [ +  +  +  +  :       16523 :                 std::erase_if(sim_announcements, [&](auto& ann) { return ann.tx == tx; });
          +  +  +  +  +  
          +  +  +  +  +  
                   +  + ]
     637                 :             :                 break;
     638                 :             :            } else if (command-- == 0) {
     639                 :             :                 // EraseForPeer
     640                 :        3084 :                 auto peer = read_peer_fn();
     641         [ +  - ]:        3084 :                 real->EraseForPeer(peer);
     642   [ +  +  +  +  :       11633 :                 std::erase_if(sim_announcements, [&](auto& ann) { return ann.announcer == peer; });
          +  +  +  +  +  
          +  +  +  +  +  
                   +  + ]
     643                 :             :                 break;
     644                 :             :             } else if (command-- == 0) {
     645                 :             :                 // EraseForBlock
     646                 :        3344 :                 auto pattern = provider.ConsumeIntegralInRange<uint64_t>(0, (uint64_t{1} << NUM_TX) - 1);
     647                 :        3344 :                 CBlock block;
     648                 :        3344 :                 std::set<COutPoint> spent;
     649         [ +  + ]:       56848 :                 for (unsigned tx = 0; tx < NUM_TX; ++tx) {
     650         [ +  + ]:       53504 :                     if ((pattern >> tx) & 1) {
     651         [ +  - ]:       16999 :                         block.vtx.emplace_back(txn[tx]);
     652         [ +  + ]:       53641 :                         for (auto& txin : block.vtx.back()->vin) {
     653         [ +  - ]:       36642 :                             spent.insert(txin.prevout);
     654                 :             :                         }
     655                 :             :                     }
     656                 :             :                 }
     657                 :        3344 :                 std::shuffle(block.vtx.begin(), block.vtx.end(), rng);
     658         [ +  - ]:        3344 :                 real->EraseForBlock(block);
     659                 :       18979 :                 std::erase_if(sim_announcements, [&](auto& ann) {
     660         [ +  + ]:       30517 :                     for (auto& txin : txn[ann.tx]->vin) {
     661         [ +  + ]:       22895 :                         if (spent.count(txin.prevout)) return true;
     662                 :             :                     }
     663                 :             :                     return false;
     664                 :             :                 });
     665                 :        3344 :                 break;
     666                 :        3344 :             } else if (command-- == 0) {
     667                 :             :                 // AddChildrenToWorkSet
     668                 :        5320 :                 auto tx = read_tx_fn();
     669                 :        5320 :                 FastRandomContext rand_ctx(rng.rand256());
     670         [ +  - ]:        5320 :                 auto added = real->AddChildrenToWorkSet(*txn[tx], rand_ctx);
     671                 :             :                 /** Map of all child wtxids, with value whether they already have a reconsiderable
     672                 :             :                     announcement from some peer. */
     673                 :        5320 :                 std::map<Wtxid, bool> child_wtxids;
     674         [ +  + ]:       90440 :                 for (unsigned child_tx = 0; child_tx < NUM_TX; ++child_tx) {
     675         [ +  + ]:       85120 :                     if (!have_tx_fn(child_tx)) continue;
     676                 :       34378 :                     bool child_of = false;
     677         [ +  + ]:       87956 :                     for (auto& txin : txn[child_tx]->vin) {
     678         [ +  + ]:       68772 :                         if (txin.prevout.hash == txn[tx]->GetHash()) {
     679                 :             :                             child_of = true;
     680                 :             :                             break;
     681                 :             :                         }
     682                 :             :                     }
     683         [ +  + ]:       34378 :                     if (child_of) {
     684         [ +  - ]:       15194 :                         child_wtxids[txn[child_tx]->GetWitnessHash()] = have_reconsiderable_fn(child_tx);
     685                 :             :                     }
     686                 :             :                 }
     687         [ +  + ]:       10182 :                 for (auto& [wtxid, peer] : added) {
     688                 :             :                     // Wtxid must be a child of tx.
     689                 :        4862 :                     auto child_wtxid_it = child_wtxids.find(wtxid);
     690         [ -  + ]:        4862 :                     assert(child_wtxid_it != child_wtxids.end());
     691                 :             :                     // Announcement must exist.
     692                 :        4862 :                     auto sim_ann_it = find_announce_wtxid_fn(wtxid, peer);
     693         [ -  + ]:        4862 :                     assert(sim_ann_it != sim_announcements.end());
     694                 :             :                     // Announcement must not yet be reconsiderable.
     695         [ -  + ]:        4862 :                     assert(sim_ann_it->reconsider == false);
     696                 :             :                     // Make reconsiderable.
     697                 :        4862 :                     sim_ann_it->reconsider = true;
     698                 :             :                 }
     699         [ +  + ]:       10182 :                 for (auto& [wtxid, peer] : added) {
     700                 :             :                     // Remove from child_wtxids map, so we can check that only already-reconsiderable
     701                 :             :                     // ones are missing from the result.
     702                 :        4862 :                     child_wtxids.erase(wtxid);
     703                 :             :                 }
     704                 :             :                 // Verify that AddChildrenToWorkSet does not select announcements that were already reconsiderable:
     705                 :             :                 // Check all child wtxids which did not occur at least once in the result were already reconsiderable
     706                 :             :                 // due to a previous AddChildrenToWorkSet.
     707   [ -  +  +  + ]:       15848 :                 for (auto& [wtxid, already_reconsider] : child_wtxids) {
     708         [ -  + ]:       10528 :                     assert(already_reconsider);
     709                 :             :                 }
     710                 :        5320 :                 break;
     711                 :        5320 :             } else if (command-- == 0) {
     712                 :             :                 // GetTxToReconsider.
     713                 :        4650 :                 auto peer = read_peer_fn();
     714         [ +  - ]:        4650 :                 auto result = real->GetTxToReconsider(peer);
     715         [ +  + ]:        4650 :                 if (result) {
     716                 :             :                     // A transaction was found. It must have a corresponding reconsiderable
     717                 :             :                     // announcement from peer.
     718                 :        1361 :                     auto sim_ann_it = find_announce_wtxid_fn(result->GetWitnessHash(), peer);
     719         [ -  + ]:        1361 :                     assert(sim_ann_it != sim_announcements.end());
     720         [ -  + ]:        1361 :                     assert(sim_ann_it->announcer == peer);
     721         [ -  + ]:        1361 :                     assert(sim_ann_it->reconsider);
     722                 :             :                     // Make it non-reconsiderable.
     723                 :        1361 :                     sim_ann_it->reconsider = false;
     724                 :             :                 } else {
     725                 :             :                     // No reconsiderable transaction was found from peer. Verify that it does not
     726                 :             :                     // have any.
     727         [ -  + ]:        3289 :                     assert(!have_reconsider_fn(peer));
     728                 :             :                 }
     729         [ +  + ]:        4650 :                 break;
     730         [ +  + ]:       36990 :             } else if (command-- == 0) {
     731                 :             :                 // LimitOrphans
     732   [ +  -  +  + ]:        7231 :                 const auto max_ann = max_global_ann / std::max<unsigned>(1, count_peers_fn());
     733                 :        7231 :                 const auto max_mem = reserved_peer_usage;
     734                 :       13124 :                 while (true) {
     735                 :             :                     // Count global usage and number of peers.
     736                 :       13124 :                     node::TxOrphanage::Usage total_usage{0};
     737                 :       13124 :                     node::TxOrphanage::Count total_latency_score = sim_announcements.size();
     738         [ +  + ]:      223108 :                     for (unsigned tx = 0; tx < NUM_TX; ++tx) {
     739         [ +  + ]:      209984 :                         if (have_tx_fn(tx)) {
     740                 :       60111 :                             total_usage += GetTransactionWeight(*txn[tx]);
     741                 :       60111 :                             total_latency_score += txn[tx]->vin.size() / 10;
     742                 :             :                         }
     743                 :             :                     }
     744         [ +  - ]:       13124 :                     auto num_peers = count_peers_fn();
     745   [ +  +  +  + ]:       21379 :                     bool oversized = (total_usage > reserved_peer_usage * num_peers) ||
     746         [ +  - ]:        8255 :                                      (total_latency_score > real->MaxGlobalLatencyScore());
     747                 :       13124 :                     if (!oversized) break;
     748                 :             :                     // Find worst peer.
     749                 :        5893 :                     FeeFrac worst_dos_score{0, 1};
     750                 :        5893 :                     unsigned worst_peer = unsigned(-1);
     751         [ +  + ]:      100181 :                     for (unsigned peer = 0; peer < NUM_PEERS; ++peer) {
     752                 :       94288 :                         auto dos_score = dos_score_fn(peer, max_ann, max_mem);
     753                 :             :                         // Use >= so that the more recent peer (higher NodeId) wins in case of
     754                 :             :                         // ties.
     755         [ +  + ]:       94288 :                         if (dos_score >= worst_dos_score) {
     756                 :       14659 :                             worst_dos_score = dos_score;
     757                 :       14659 :                             worst_peer = peer;
     758                 :             :                         }
     759                 :             :                     }
     760         [ -  + ]:        5893 :                     assert(worst_peer != unsigned(-1));
     761         [ -  + ]:        5893 :                     assert(worst_dos_score >> FeeFrac(1, 1));
     762                 :             :                     // Find oldest announcement from worst_peer, preferring non-reconsiderable ones.
     763                 :             :                     bool done{false};
     764         [ +  - ]:        6137 :                     for (int reconsider = 0; reconsider < 2; ++reconsider) {
     765         [ +  + ]:       31551 :                         for (auto it = sim_announcements.begin(); it != sim_announcements.end(); ++it) {
     766   [ +  +  +  + ]:       31307 :                             if (it->announcer != worst_peer || it->reconsider != reconsider) continue;
     767                 :        5893 :                             sim_announcements.erase(it);
     768                 :        5893 :                             done = true;
     769                 :        5893 :                             break;
     770                 :             :                         }
     771                 :        6137 :                         if (done) break;
     772                 :             :                     }
     773         [ +  - ]:        5893 :                     assert(done);
     774                 :             :                 }
     775         [ +  - ]:        7231 :                 real->LimitOrphans();
     776                 :             :                 // We must now be within limits, otherwise LimitOrphans should have continued further).
     777                 :             :                 // We don't check the contents of the orphanage until the end to make fuzz runs faster.
     778   [ +  -  +  -  :        7231 :                 assert(real->TotalLatencyScore() <= real->MaxGlobalLatencyScore());
                   -  + ]
     779   [ +  -  +  -  :        7231 :                 assert(real->TotalOrphanUsage() <= real->MaxGlobalUsage());
                   -  + ]
     780                 :             :                 break;
     781                 :             :             }
     782                 :             :         }
     783                 :             :     }
     784                 :             : 
     785                 :             :     //
     786                 :             :     // 6. Perform a full comparison between the real orphanage's inspectors and the simulation.
     787                 :             :     //
     788                 :             : 
     789         [ +  - ]:         511 :     real->SanityCheck();
     790                 :             : 
     791                 :             : 
     792         [ +  - ]:         511 :     auto all_orphans = real->GetOrphanTransactions();
     793                 :         511 :     node::TxOrphanage::Usage orphan_usage{0};
     794         [ +  - ]:         511 :     std::vector<node::TxOrphanage::Usage> usage_by_peer(NUM_PEERS);
     795                 :         511 :     node::TxOrphanage::Count unique_orphans{0};
     796         [ +  - ]:         511 :     std::vector<node::TxOrphanage::Count> count_by_peer(NUM_PEERS);
     797                 :         511 :     node::TxOrphanage::Count total_latency_score = sim_announcements.size();
     798         [ +  + ]:        8687 :     for (unsigned tx = 0; tx < NUM_TX; ++tx) {
     799                 :        8176 :         bool sim_have_tx = have_tx_fn(tx);
     800         [ +  + ]:        8176 :         if (sim_have_tx) {
     801                 :        2088 :             orphan_usage += GetTransactionWeight(*txn[tx]);
     802                 :        2088 :             total_latency_score += txn[tx]->vin.size() / 10;
     803                 :             :         }
     804                 :        8176 :         unique_orphans += sim_have_tx;
     805         [ -  + ]:       33076 :         auto orphans_it = std::find_if(all_orphans.begin(), all_orphans.end(), [&](auto& orph) { return orph.tx->GetWitnessHash() == txn[tx]->GetWitnessHash(); });
     806                 :             :         // GetOrphanTransactions (OrphanBase existence)
     807         [ -  + ]:        8176 :         assert((orphans_it != all_orphans.end()) == sim_have_tx);
     808                 :             :         // HaveTx
     809         [ +  - ]:        8176 :         bool have_tx = real->HaveTx(txn[tx]->GetWitnessHash());
     810         [ -  + ]:        8176 :         assert(have_tx == sim_have_tx);
     811                 :             :         // GetTx
     812         [ +  - ]:        8176 :         auto txref = real->GetTx(txn[tx]->GetWitnessHash());
     813         [ -  + ]:        8176 :         assert(!!txref == sim_have_tx);
     814   [ +  +  -  + ]:        8176 :         if (sim_have_tx) assert(txref->GetWitnessHash() == txn[tx]->GetWitnessHash());
     815                 :             : 
     816         [ +  + ]:      138992 :         for (NodeId peer = 0; peer < NUM_PEERS; ++peer) {
     817                 :      130816 :             auto it_sim_ann = find_announce_fn(tx, peer);
     818         [ +  + ]:      130816 :             bool sim_have_ann = it_sim_ann != sim_announcements.end();
     819         [ +  + ]:      130816 :             if (sim_have_ann) usage_by_peer[peer] += GetTransactionWeight(*txn[tx]);
     820         [ +  + ]:      130816 :             count_by_peer[peer] += sim_have_ann;
     821                 :             :             // GetOrphanTransactions (announcers presence)
     822   [ +  +  -  + ]:      130816 :             if (sim_have_ann) assert(sim_have_tx);
     823   [ +  +  -  + ]:      130816 :             if (sim_have_tx) assert(orphans_it->announcers.count(peer) == sim_have_ann);
     824                 :             :             // HaveTxFromPeer
     825         [ +  - ]:      130816 :             bool have_ann = real->HaveTxFromPeer(txn[tx]->GetWitnessHash(), peer);
     826         [ -  + ]:      130816 :             assert(sim_have_ann == have_ann);
     827                 :             :             // GetChildrenFromSamePeer
     828         [ +  - ]:      130816 :             auto children_from_peer = real->GetChildrenFromSamePeer(txn[tx], peer);
     829                 :      130816 :             auto it = children_from_peer.rbegin();
     830         [ +  + ]:      392448 :             for (int phase = 0; phase < 2; ++phase) {
     831                 :             :                 // First expect all children which have reconsiderable announcement from peer, then the others.
     832         [ +  + ]:     2307072 :                 for (auto& ann : sim_announcements) {
     833         [ +  + ]:     2045440 :                     if (ann.announcer != peer) continue;
     834         [ +  + ]:      127840 :                     if (ann.reconsider != (phase == 1)) continue;
     835                 :       63920 :                     bool matching_parent{false};
     836         [ +  + ]:      198752 :                     for (const auto& vin : txn[ann.tx]->vin) {
     837         [ +  + ]:      134832 :                         if (vin.prevout.hash == txn[tx]->GetHash()) matching_parent = true;
     838                 :             :                     }
     839         [ +  + ]:       63920 :                     if (!matching_parent) continue;
     840                 :             :                     // Found an announcement from peer which is a child of txn[tx].
     841         [ -  + ]:       10158 :                     assert(it != children_from_peer.rend());
     842         [ -  + ]:       10158 :                     assert((*it)->GetWitnessHash() == txn[ann.tx]->GetWitnessHash());
     843                 :       10158 :                     ++it;
     844                 :             :                 }
     845                 :             :             }
     846         [ -  + ]:      130816 :             assert(it == children_from_peer.rend());
     847                 :      130816 :         }
     848                 :        8176 :     }
     849                 :             :     // TotalOrphanUsage
     850   [ +  -  -  + ]:         511 :     assert(orphan_usage == real->TotalOrphanUsage());
     851         [ +  + ]:        8687 :     for (NodeId peer = 0; peer < NUM_PEERS; ++peer) {
     852                 :        8176 :         bool sim_have_reconsider = have_reconsider_fn(peer);
     853                 :             :         // HaveTxToReconsider
     854         [ +  - ]:        8176 :         bool have_reconsider = real->HaveTxToReconsider(peer);
     855         [ -  + ]:        8176 :         assert(have_reconsider == sim_have_reconsider);
     856                 :             :         // UsageByPeer
     857   [ +  -  -  + ]:        8176 :         assert(usage_by_peer[peer] == real->UsageByPeer(peer));
     858                 :             :         // AnnouncementsFromPeer
     859   [ +  -  -  + ]:        8176 :         assert(count_by_peer[peer] == real->AnnouncementsFromPeer(peer));
     860                 :             :     }
     861                 :             :     // CountAnnouncements
     862   [ +  -  -  + ]:         511 :     assert(sim_announcements.size() == real->CountAnnouncements());
     863                 :             :     // CountUniqueOrphans
     864   [ +  -  -  + ]:         511 :     assert(unique_orphans == real->CountUniqueOrphans());
     865                 :             :     // MaxGlobalLatencyScore
     866   [ +  -  -  + ]:         511 :     assert(max_global_ann == real->MaxGlobalLatencyScore());
     867                 :             :     // ReservedPeerUsage
     868   [ +  -  -  + ]:         511 :     assert(reserved_peer_usage == real->ReservedPeerUsage());
     869                 :             :     // MaxPeerLatencyScore
     870         [ +  - ]:         511 :     auto present_peers = count_peers_fn();
     871   [ +  +  +  -  :         720 :     assert(max_global_ann / std::max<unsigned>(1, present_peers) == real->MaxPeerLatencyScore());
                   -  + ]
     872                 :             :     // MaxGlobalUsage
     873   [ +  +  +  -  :         720 :     assert(reserved_peer_usage * std::max<unsigned>(1, present_peers) == real->MaxGlobalUsage());
                   -  + ]
     874                 :             :     // TotalLatencyScore.
     875   [ +  -  -  + ]:         511 :     assert(real->TotalLatencyScore() == total_latency_score);
     876                 :         511 : }
        

Generated by: LCOV version 2.0-1