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 % 477 476
Test Date: 2026-08-29 05:53:03 Functions: 100.0 % 30 30
Branches: 71.2 % 676 481

             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 <test/util/time.h>
      19                 :             : #include <uint256.h>
      20                 :             : #include <util/check.h>
      21                 :             : #include <util/feefrac.h>
      22                 :             : #include <util/time.h>
      23                 :             : 
      24                 :             : #include <algorithm>
      25                 :             : #include <bitset>
      26                 :             : #include <cmath>
      27                 :             : #include <cstdint>
      28                 :             : #include <iostream>
      29                 :             : #include <memory>
      30                 :             : #include <set>
      31                 :             : #include <utility>
      32                 :             : #include <vector>
      33                 :             : 
      34                 :           2 : void initialize_orphanage()
      35                 :             : {
      36   [ +  -  +  -  :           2 :     static const auto testing_setup = MakeNoLogFileContext();
                   +  - ]
      37                 :           2 : }
      38                 :             : 
      39         [ +  - ]:        1417 : FUZZ_TARGET(txorphan, .init = initialize_orphanage)
      40                 :             : {
      41                 :         941 :     SeedRandomStateForTest(SeedRand::ZEROS);
      42                 :         941 :     FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
      43                 :         941 :     FastRandomContext orphanage_rng{ConsumeUInt256(fuzzed_data_provider)};
      44         [ +  - ]:         941 :     FakeNodeClock clock{ConsumeTime(fuzzed_data_provider)};
      45                 :             : 
      46                 :         941 :     auto orphanage = node::MakeTxOrphanage();
      47                 :         941 :     std::vector<COutPoint> outpoints; // Duplicates are tolerated
      48         [ +  - ]:         941 :     outpoints.reserve(200'000);
      49                 :             : 
      50                 :             :     // initial outpoints used to construct transactions later
      51         [ +  + ]:        4705 :     for (uint8_t i = 0; i < 4; i++) {
      52         [ +  - ]:        3764 :         outpoints.emplace_back(Txid::FromUint256(uint256{i}), 0);
      53                 :             :     }
      54                 :             : 
      55                 :         941 :     CTransactionRef ptx_potential_parent = nullptr;
      56                 :             : 
      57                 :         941 :     std::vector<CTransactionRef> tx_history;
      58                 :             : 
      59   [ -  +  +  -  :      189876 :     LIMITED_WHILE (outpoints.size() < 200'000 && fuzzed_data_provider.ConsumeBool(), 1000) {
             +  +  +  + ]
      60                 :             :         // construct transaction
      61                 :      187994 :         const CTransactionRef tx = [&] {
      62                 :       93997 :             CMutableTransaction tx_mut;
      63         [ -  + ]:       93997 :             const auto num_in = fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(1, outpoints.size());
      64                 :       93997 :             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         [ +  - ]:       93997 :             tx_mut.vin.reserve(num_in);
      68         [ +  + ]:    17600398 :             for (uint32_t i = 0; i < num_in; i++) {
      69                 :    17506401 :                 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         [ +  - ]:    35012802 :                 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         [ +  - ]:       93997 :             tx_mut.vout.reserve(num_out);
      75         [ +  + ]:     7219617 :             for (uint32_t i = 0; i < num_out; i++) {
      76         [ +  - ]:    14251240 :                 tx_mut.vout.emplace_back(CAmount{0}, CScript{});
      77                 :             :             }
      78         [ +  - ]:       93997 :             auto new_tx = MakeTransactionRef(tx_mut);
      79                 :             :             // add newly constructed outpoints to the coin pool
      80         [ +  + ]:     7219617 :             for (uint32_t i = 0; i < num_out; i++) {
      81         [ +  - ]:     7125620 :                 outpoints.emplace_back(new_tx->GetHash(), i);
      82                 :             :             }
      83                 :       93997 :             return new_tx;
      84         [ +  - ]:      187994 :         }();
      85                 :             : 
      86         [ +  - ]:       93997 :         tx_history.push_back(tx);
      87                 :             : 
      88         [ +  + ]:       93997 :         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         [ +  + ]:       93997 :         if (ptx_potential_parent) {
      93                 :             :             // Set up future GetTxToReconsider call.
      94         [ +  - ]:       93067 :             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                 :       93067 :             NodeId peer_id = fuzzed_data_provider.ConsumeIntegral<NodeId>();
      98   [ +  -  +  + ]:      186289 :             for (const auto& child : orphanage->GetChildrenFromSamePeer(ptx_potential_parent, peer_id)) {
      99 [ -  + ][ +  + ]:     1757544 :                 assert(std::any_of(child->vin.cbegin(), child->vin.cend(), [&](const auto& input) {
     100                 :             :                     return input.prevout.hash == ptx_potential_parent->GetHash();
     101                 :             :                 }));
     102                 :       93067 :             }
     103                 :             :         }
     104                 :             : 
     105                 :             :         // trigger orphanage functions
     106   [ +  +  +  + ]:     3538189 :         LIMITED_WHILE (fuzzed_data_provider.ConsumeBool(), 1000) {
     107                 :     3444192 :             NodeId peer_id = fuzzed_data_provider.ConsumeIntegral<NodeId>();
     108         [ +  - ]:     3444192 :             const auto total_bytes_start{orphanage->TotalOrphanUsage()};
     109         [ +  - ]:     3444192 :             const auto total_peer_bytes_start{orphanage->UsageByPeer(peer_id)};
     110                 :     3444192 :             const auto tx_weight{GetTransactionWeight(*tx)};
     111                 :             : 
     112         [ +  - ]:     3444192 :             CallOneOf(
     113                 :             :                 fuzzed_data_provider,
     114                 :       23690 :                 [&] {
     115                 :       23690 :                     {
     116                 :       23690 :                         CTransactionRef ref = orphanage->GetTxToReconsider(peer_id);
     117         [ +  + ]:       23690 :                         if (ref) {
     118   [ +  -  -  +  :       23690 :                             Assert(orphanage->HaveTx(ref->GetWitnessHash()));
                   +  + ]
     119                 :             :                         }
     120                 :       23690 :                     }
     121                 :       23690 :                 },
     122                 :     3135411 :                 [&] {
     123                 :     3135411 :                     bool have_tx = orphanage->HaveTx(tx->GetWitnessHash());
     124                 :     3135411 :                     bool have_tx_and_peer = orphanage->HaveTxFromPeer(wtxid, peer_id);
     125                 :             :                     // AddTx should return false if tx is too big or already have it
     126                 :             :                     // tx weight is unknown, we only check when tx is already in orphanage
     127                 :     3135411 :                     {
     128                 :     3135411 :                         bool add_tx = orphanage->AddTx(tx, peer_id);
     129                 :             :                         // have_tx == true -> add_tx == false
     130         [ -  + ]:     3135411 :                         Assert(!have_tx || !add_tx);
     131                 :             :                         // have_tx_and_peer == true -> add_tx == false
     132         [ -  + ]:     3135411 :                         Assert(!have_tx_and_peer || !add_tx);
     133                 :             :                         // After AddTx, the orphanage may trim itself, so the peer's usage may have gone up or down.
     134                 :             : 
     135         [ +  + ]:     3135411 :                         if (add_tx) {
     136         [ -  + ]:      178830 :                             Assert(tx_weight <= MAX_STANDARD_TX_WEIGHT);
     137                 :             :                         } else {
     138                 :             :                             // Peer may have been added as an announcer.
     139         [ +  + ]:     2956581 :                             if (orphanage->UsageByPeer(peer_id) > total_peer_bytes_start) {
     140         [ -  + ]:      445139 :                                 Assert(orphanage->HaveTxFromPeer(wtxid, peer_id));
     141                 :             :                             }
     142                 :             : 
     143                 :             :                             // If announcement was added, total bytes does not increase.
     144                 :             :                             // However, if eviction was triggered, the value may decrease.
     145         [ -  + ]:     2956581 :                             Assert(orphanage->TotalOrphanUsage() <= total_bytes_start);
     146                 :             :                         }
     147                 :             :                     }
     148                 :             :                     // We are not guaranteed to have_tx after AddTx. There are a few possible reasons:
     149                 :             :                     // - tx itself exceeds the per-peer memory usage limit, so LimitOrphans had to remove it immediately
     150                 :             :                     // - tx itself exceeds the per-peer latency score limit, so LimitOrphans had to remove it immediately
     151                 :             :                     // - the orphanage needed trim and all other announcements from this peer are reconsiderable
     152                 :     3135411 :                 },
     153                 :        9959 :                 [&] {
     154                 :        9959 :                     bool have_tx = orphanage->HaveTx(tx->GetWitnessHash());
     155                 :        9959 :                     bool have_tx_and_peer = orphanage->HaveTxFromPeer(tx->GetWitnessHash(), peer_id);
     156                 :             :                     // AddAnnouncer should return false if tx doesn't exist or we already HaveTxFromPeer.
     157                 :        9959 :                     {
     158                 :        9959 :                         bool added_announcer = orphanage->AddAnnouncer(tx->GetWitnessHash(), peer_id);
     159                 :             :                         // have_tx == false -> added_announcer == false
     160         [ -  + ]:        9959 :                         Assert(have_tx || !added_announcer);
     161                 :             :                         // have_tx_and_peer == true -> added_announcer == false
     162         [ -  + ]:        9959 :                         Assert(!have_tx_and_peer || !added_announcer);
     163                 :             : 
     164                 :             :                         // If announcement was added, total bytes does not increase.
     165                 :             :                         // However, if eviction was triggered, the value may decrease.
     166         [ -  + ]:        9959 :                         Assert(orphanage->TotalOrphanUsage() <= total_bytes_start);
     167                 :             :                     }
     168                 :        9959 :                 },
     169                 :      243413 :                 [&] {
     170                 :      243413 :                     bool have_tx = orphanage->HaveTx(tx->GetWitnessHash());
     171                 :      243413 :                     bool have_tx_and_peer{orphanage->HaveTxFromPeer(wtxid, peer_id)};
     172                 :             :                     // EraseTx should return 0 if m_orphans doesn't have the tx
     173                 :      243413 :                     {
     174                 :      243413 :                         auto bytes_from_peer_before{orphanage->UsageByPeer(peer_id)};
     175         [ -  + ]:      243413 :                         Assert(have_tx == orphanage->EraseTx(tx->GetWitnessHash()));
     176                 :             :                         // After EraseTx, the orphanage may trim itself, so any peer's usage may decrease.
     177         [ +  + ]:      243413 :                         if (!have_tx) {
     178         [ -  + ]:      137084 :                             Assert(orphanage->UsageByPeer(peer_id) == bytes_from_peer_before);
     179         [ +  + ]:      106329 :                         } else if (have_tx_and_peer) {
     180         [ -  + ]:        5909 :                             Assert(orphanage->UsageByPeer(peer_id) <= bytes_from_peer_before - tx_weight);
     181                 :             :                         } else {
     182         [ -  + ]:      100420 :                             Assert(orphanage->UsageByPeer(peer_id) <= bytes_from_peer_before);
     183                 :             :                         }
     184                 :             :                     }
     185                 :      243413 :                     have_tx = orphanage->HaveTx(tx->GetWitnessHash());
     186                 :      243413 :                     have_tx_and_peer = orphanage->HaveTxFromPeer(wtxid, peer_id);
     187                 :             :                     // have_tx should be false and EraseTx should fail
     188                 :      243413 :                     {
     189   [ +  -  -  +  :      243413 :                         Assert(!have_tx && !have_tx_and_peer && !orphanage->EraseTx(wtxid));
                   -  + ]
     190                 :             :                     }
     191                 :      243413 :                 },
     192                 :       18738 :                 [&] {
     193                 :       18738 :                     orphanage->EraseForPeer(peer_id);
     194         [ -  + ]:       18738 :                     Assert(!orphanage->HaveTxFromPeer(tx->GetWitnessHash(), peer_id));
     195         [ -  + ]:       18738 :                     Assert(orphanage->UsageByPeer(peer_id) == 0);
     196                 :       18738 :                 },
     197                 :       12981 :                 [&] {
     198                 :             :                     // Make a block out of txs and then EraseForBlock
     199                 :       12981 :                     CBlock block;
     200                 :       12981 :                     int64_t block_weight{0};
     201                 :       12981 :                     int num_txs = fuzzed_data_provider.ConsumeIntegralInRange<unsigned int>(0, 1000);
     202         [ +  + ]:     1694472 :                     for (int i{0}; i < num_txs; ++i) {
     203                 :     1685299 :                         auto& tx_to_remove = PickValue(fuzzed_data_provider, tx_history);
     204                 :     1685299 :                         const auto tx_weight = GetTransactionWeight(*tx_to_remove);
     205         [ +  + ]:     1685299 :                         if (block_weight + tx_weight > MAX_BLOCK_WEIGHT) break;
     206                 :     1681491 :                         block_weight += tx_weight;
     207         [ +  - ]:     1681491 :                         block.vtx.push_back(tx_to_remove);
     208                 :             :                     }
     209         [ +  - ]:       12981 :                     orphanage->EraseForBlock(block);
     210         [ +  + ]:     1694472 :                     for (const auto& tx_removed : block.vtx) {
     211   [ +  -  -  + ]:     1681491 :                         Assert(!orphanage->HaveTx(tx_removed->GetWitnessHash()));
     212   [ +  -  -  + ]:     1681491 :                         Assert(!orphanage->HaveTxFromPeer(tx_removed->GetWitnessHash(), peer_id));
     213                 :             :                     }
     214                 :       12981 :                 }
     215                 :             :             );
     216                 :             :         }
     217                 :             : 
     218                 :             :         // Set tx as potential parent to be used for future GetChildren() calls.
     219   [ +  +  +  + ]:      187064 :         if (!ptx_potential_parent || fuzzed_data_provider.ConsumeBool()) {
     220                 :       60871 :             ptx_potential_parent = tx;
     221                 :             :         }
     222                 :             : 
     223         [ +  - ]:       93997 :         const bool have_tx{orphanage->HaveTx(tx->GetWitnessHash())};
     224   [ +  -  +  + ]:       93997 :         const bool get_tx_nonnull{orphanage->GetTx(tx->GetWitnessHash()) != nullptr};
     225   [ -  +  +  - ]:       93997 :         Assert(have_tx == get_tx_nonnull);
     226                 :       93997 :     }
     227         [ +  - ]:         941 :     orphanage->SanityCheck();
     228         [ +  + ]:        1871 : }
     229                 :             : 
     230         [ +  - ]:        1252 : FUZZ_TARGET(txorphan_protected, .init = initialize_orphanage)
     231                 :             : {
     232                 :         776 :     SeedRandomStateForTest(SeedRand::ZEROS);
     233                 :         776 :     FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
     234                 :         776 :     FastRandomContext orphanage_rng{ConsumeUInt256(fuzzed_data_provider)};
     235         [ +  - ]:         776 :     FakeNodeClock clock{ConsumeTime(fuzzed_data_provider)};
     236                 :             : 
     237                 :             :     // We have num_peers peers. Some subset of them will never exceed their reserved weight or announcement count, and
     238                 :             :     // should therefore never have any orphans evicted.
     239                 :         776 :     const unsigned int MAX_PEERS = 125;
     240                 :         776 :     const unsigned int num_peers = fuzzed_data_provider.ConsumeIntegralInRange<unsigned int>(1, MAX_PEERS);
     241                 :             :     // Generate a vector of bools for whether each peer is protected from eviction
     242                 :         776 :     std::bitset<MAX_PEERS> protected_peers;
     243         [ +  + ]:       18417 :     for (unsigned int i = 0; i < num_peers; i++) {
     244         [ +  - ]:       17641 :         protected_peers.set(i, fuzzed_data_provider.ConsumeBool());
     245                 :             :     }
     246                 :             : 
     247                 :             :     // Params for orphanage.
     248                 :         776 :     const unsigned int global_latency_score_limit = fuzzed_data_provider.ConsumeIntegralInRange<unsigned int>(num_peers, 6'000);
     249                 :         776 :     const int64_t per_peer_weight_reservation = fuzzed_data_provider.ConsumeIntegralInRange<int64_t>(1, 4'040'000);
     250                 :         776 :     auto orphanage = node::MakeTxOrphanage(global_latency_score_limit, per_peer_weight_reservation);
     251                 :             : 
     252                 :             :     // The actual limit, MaxPeerLatencyScore(), may be higher, since TxOrphanage only counts peers
     253                 :             :     // that have announced an orphan. The honest peer will not experience evictions if it never
     254                 :             :     // exceeds this.
     255                 :         776 :     const unsigned int honest_latency_limit = global_latency_score_limit / num_peers;
     256                 :             :     // Honest peer will not experience evictions if it never exceeds this.
     257                 :         776 :     const int64_t honest_mem_limit = per_peer_weight_reservation;
     258                 :             : 
     259                 :         776 :     std::vector<COutPoint> outpoints; // Duplicates are tolerated
     260         [ +  - ]:         776 :     outpoints.reserve(400);
     261                 :             : 
     262                 :             :     // initial outpoints used to construct transactions later
     263         [ +  + ]:        3880 :     for (uint8_t i = 0; i < 4; i++) {
     264         [ +  - ]:        3104 :         outpoints.emplace_back(Txid::FromUint256(uint256{i}), 0);
     265                 :             :     }
     266                 :             : 
     267                 :             :     // These are honest peer's live announcements. We expect them to be protected from eviction.
     268                 :         776 :     std::set<Wtxid> protected_wtxids;
     269                 :             : 
     270   [ -  +  +  +  :       17698 :     LIMITED_WHILE (outpoints.size() < 400 && fuzzed_data_provider.ConsumeBool(), 1000) {
             +  +  -  + ]
     271                 :             :         // construct transaction
     272                 :       16172 :         const CTransactionRef tx = [&] {
     273                 :        8086 :             CMutableTransaction tx_mut;
     274         [ -  + ]:        8086 :             const auto num_in = fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(1, outpoints.size());
     275                 :        8086 :             const auto num_out = fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(1, 256);
     276                 :             :             // pick outpoints from outpoints as input. We allow input duplicates on purpose, given we are not
     277                 :             :             // running any transaction validation logic before adding transactions to the orphanage
     278         [ +  - ]:        8086 :             tx_mut.vin.reserve(num_in);
     279         [ +  + ]:      120192 :             for (uint32_t i = 0; i < num_in; i++) {
     280                 :      112106 :                 auto& prevout = PickValue(fuzzed_data_provider, outpoints);
     281                 :             :                 // try making transactions unique by setting a random nSequence, but allow duplicate transactions if they happen
     282         [ +  - ]:      224212 :                 tx_mut.vin.emplace_back(prevout, CScript{}, fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(0, CTxIn::SEQUENCE_FINAL));
     283                 :             :             }
     284                 :             :             // output amount or spendability will not affect txorphanage
     285         [ +  - ]:        8086 :             tx_mut.vout.reserve(num_out);
     286         [ +  + ]:       77152 :             for (uint32_t i = 0; i < num_out; i++) {
     287                 :       69066 :                 const auto payload_size = fuzzed_data_provider.ConsumeIntegralInRange<unsigned int>(0, 100000);
     288         [ +  + ]:       69066 :                 if (payload_size) {
     289   [ +  -  +  -  :      152898 :                     tx_mut.vout.emplace_back(0, CScript() << OP_RETURN << std::vector<unsigned char>(payload_size));
                   +  - ]
     290                 :             :                 } else {
     291         [ +  - ]:       36200 :                     tx_mut.vout.emplace_back(0, CScript{});
     292                 :             :                 }
     293                 :             :             }
     294         [ +  - ]:        8086 :             auto new_tx = MakeTransactionRef(tx_mut);
     295                 :             :             // add newly constructed outpoints to the coin pool
     296         [ +  + ]:       77152 :             for (uint32_t i = 0; i < num_out; i++) {
     297         [ +  - ]:       69066 :                 outpoints.emplace_back(new_tx->GetHash(), i);
     298                 :             :             }
     299                 :        8086 :             return new_tx;
     300         [ +  - ]:       16172 :         }();
     301                 :             : 
     302                 :        8086 :         const auto wtxid{tx->GetWitnessHash()};
     303                 :             : 
     304                 :             :         // orphanage functions
     305   [ +  +  +  + ]:     3569803 :         LIMITED_WHILE (fuzzed_data_provider.remaining_bytes(), 10 * global_latency_score_limit) {
     306                 :     3561717 :             NodeId peer_id = fuzzed_data_provider.ConsumeIntegralInRange<NodeId>(0, num_peers - 1);
     307                 :     3561717 :             const auto tx_weight{GetTransactionWeight(*tx)};
     308                 :             : 
     309                 :             :             // This protected peer will never send orphans that would
     310                 :             :             // exceed their own personal allotment, so is never evicted.
     311         [ +  - ]:     3561717 :             const bool peer_is_protected{protected_peers[peer_id]};
     312                 :             : 
     313         [ +  - ]:     3561717 :             CallOneOf(
     314                 :             :                 fuzzed_data_provider,
     315                 :     1091324 :                 [&] { // AddTx
     316                 :     1091324 :                     bool have_tx_and_peer = orphanage->HaveTxFromPeer(wtxid, peer_id);
     317   [ +  +  +  + ]:     1091324 :                     if (peer_is_protected && !have_tx_and_peer &&
     318         [ +  + ]:      273889 :                         (orphanage->UsageByPeer(peer_id) + tx_weight > honest_mem_limit ||
     319   [ -  +  +  + ]:      157032 :                         orphanage->LatencyScoreFromPeer(peer_id) + (tx->vin.size() / 10) + 1 > honest_latency_limit)) {
     320                 :             :                         // We never want our protected peer oversized or over-announced
     321                 :             :                     } else {
     322                 :      962902 :                         orphanage->AddTx(tx, peer_id);
     323   [ +  +  +  + ]:      962902 :                         if (peer_is_protected && orphanage->HaveTxFromPeer(wtxid, peer_id)) {
     324                 :      313198 :                             protected_wtxids.insert(wtxid);
     325                 :             :                         }
     326                 :             :                     }
     327                 :     1091324 :                 },
     328                 :     1310614 :                 [&] { // AddAnnouncer
     329                 :     1310614 :                     bool have_tx_and_peer = orphanage->HaveTxFromPeer(tx->GetWitnessHash(), peer_id);
     330                 :             :                     // AddAnnouncer should return false if tx doesn't exist or we already HaveTxFromPeer.
     331                 :     1310614 :                     {
     332   [ +  +  +  + ]:     1310614 :                         if (peer_is_protected && !have_tx_and_peer &&
     333         [ +  + ]:      551529 :                             (orphanage->UsageByPeer(peer_id) + tx_weight > honest_mem_limit ||
     334   [ -  +  +  + ]:      305106 :                             orphanage->LatencyScoreFromPeer(peer_id) + (tx->vin.size() / 10) + 1 > honest_latency_limit)) {
     335                 :             :                             // We never want our protected peer oversized
     336                 :             :                         } else {
     337                 :     1054721 :                             orphanage->AddAnnouncer(tx->GetWitnessHash(), peer_id);
     338   [ +  +  +  + ]:     1054721 :                             if (peer_is_protected && orphanage->HaveTxFromPeer(wtxid, peer_id)) {
     339                 :      285285 :                                 protected_wtxids.insert(wtxid);
     340                 :             :                             }
     341                 :             :                         }
     342                 :             :                     }
     343                 :     1310614 :                 },
     344                 :      443901 :                 [&] { // EraseTx
     345         [ +  + ]:      443901 :                     if (protected_wtxids.contains(tx->GetWitnessHash())) {
     346                 :       96666 :                         protected_wtxids.erase(wtxid);
     347                 :             :                     }
     348                 :      443901 :                     orphanage->EraseTx(wtxid);
     349         [ -  + ]:      443901 :                     Assert(!orphanage->HaveTx(wtxid));
     350                 :      443901 :                 },
     351                 :      715878 :                 [&] { // EraseForPeer
     352         [ +  + ]:      715878 :                     if (!protected_peers[peer_id]) {
     353                 :      315481 :                         orphanage->EraseForPeer(peer_id);
     354         [ -  + ]:      315481 :                         Assert(orphanage->UsageByPeer(peer_id) == 0);
     355         [ -  + ]:      315481 :                         Assert(orphanage->LatencyScoreFromPeer(peer_id) == 0);
     356         [ -  + ]:      315481 :                         Assert(orphanage->AnnouncementsFromPeer(peer_id) == 0);
     357                 :             :                     }
     358                 :      715878 :                 }
     359                 :             :             );
     360                 :             :         }
     361                 :        8086 :     }
     362                 :             : 
     363         [ +  - ]:         776 :     orphanage->SanityCheck();
     364                 :             :     // All of the honest peer's announcements are still present.
     365         [ +  + ]:        1453 :     for (const auto& wtxid : protected_wtxids) {
     366   [ +  -  -  + ]:         677 :         Assert(orphanage->HaveTx(wtxid));
     367                 :             :     }
     368                 :         776 : }
     369                 :             : 
     370         [ +  - ]:        1940 : FUZZ_TARGET(txorphanage_sim)
     371                 :             : {
     372                 :        1464 :     SeedRandomStateForTest(SeedRand::ZEROS);
     373                 :             :     // This is a comprehensive simulation fuzz test, which runs through a scenario involving up to
     374                 :             :     // 16 transactions (which may have simple or complex topology, and may have duplicate txids
     375                 :             :     // with distinct wtxids, and up to 16 peers. The scenario is performed both on a real
     376                 :             :     // TxOrphanage object and the behavior is compared with a naive reimplementation (just a vector
     377                 :             :     // of announcements) where possible, and tested for desired properties where not possible.
     378                 :             : 
     379                 :             :     //
     380                 :             :     // 1. Setup.
     381                 :             :     //
     382                 :             : 
     383                 :             :     /** The total number of transactions this simulation uses (not all of which will necessarily
     384                 :             :      *  be present in the orphanage at once). */
     385                 :        1464 :     static constexpr unsigned NUM_TX = 16;
     386                 :             :     /** The number of peers this simulation uses (not all of which will necessarily be present in
     387                 :             :      *  the orphanage at once). */
     388                 :        1464 :     static constexpr unsigned NUM_PEERS = 16;
     389                 :             :     /** The maximum number of announcements this simulation uses (which may be higher than the
     390                 :             :      *  number permitted inside the orphanage). */
     391                 :        1464 :     static constexpr unsigned MAX_ANN = 64;
     392                 :             : 
     393                 :        1464 :     FuzzedDataProvider provider(buffer.data(), buffer.size());
     394                 :             :     /** Local RNG. Only used for topology/sizes of the transaction set, the order of transactions
     395                 :             :      *  in EraseForBlock, and for the randomized passed to AddChildrenToWorkSet. */
     396                 :        1464 :     InsecureRandomContext rng(provider.ConsumeIntegral<uint64_t>());
     397                 :             : 
     398                 :             :     //
     399                 :             :     // 2. Construct an interesting set of 16 transactions.
     400                 :             :     //
     401                 :             : 
     402                 :             :     // - Pick a topological order among the transactions.
     403                 :        1464 :     std::vector<unsigned> txorder(NUM_TX);
     404                 :        1464 :     std::iota(txorder.begin(), txorder.end(), unsigned{0});
     405                 :        1464 :     std::shuffle(txorder.begin(), txorder.end(), rng);
     406                 :             :     // - Pick a set of dependencies (pair<child_index, parent_index>).
     407                 :        1464 :     std::vector<std::pair<unsigned, unsigned>> deps;
     408         [ +  - ]:        1464 :     deps.reserve((NUM_TX * (NUM_TX - 1)) / 2);
     409         [ +  + ]:       23424 :     for (unsigned p = 0; p < NUM_TX - 1; ++p) {
     410         [ +  + ]:      197640 :         for (unsigned c = p + 1; c < NUM_TX; ++c) {
     411         [ +  - ]:      175680 :             deps.emplace_back(c, p);
     412                 :             :         }
     413                 :             :     }
     414                 :        1464 :     std::shuffle(deps.begin(), deps.end(), rng);
     415         [ +  - ]:        1464 :     deps.resize(provider.ConsumeIntegralInRange<unsigned>(0, NUM_TX * 4 - 1));
     416                 :             :     // - Construct the actual transactions.
     417         [ +  - ]:        1464 :     std::set<Wtxid> wtxids;
     418         [ +  - ]:        1464 :     std::vector<CTransactionRef> txn(NUM_TX);
     419                 :             :     node::TxOrphanage::Usage total_usage{0};
     420         [ +  + ]:       24888 :     for (unsigned t = 0; t < NUM_TX; ++t) {
     421         [ +  - ]:       23424 :         CMutableTransaction tx;
     422   [ +  +  +  + ]:       23424 :         if (t > 0 && rng.randrange(4) == 0) {
     423                 :             :             // Occasionally duplicate the previous transaction, so that repetitions of the same
     424                 :             :             // txid are possible (with different wtxid).
     425         [ +  - ]:       22264 :             tx = CMutableTransaction(*txn[txorder[t - 1]]);
     426                 :             :         } else {
     427                 :       12292 :             tx.version = 1;
     428                 :       12292 :             tx.nLockTime = 0xffffffff;
     429                 :             :             // Construct 1 to 16 outputs.
     430                 :       12292 :             auto num_outputs = rng.randrange<unsigned>(1 << rng.randrange<unsigned>(5)) + 1;
     431         [ +  + ]:       49744 :             for (unsigned output = 0; output < num_outputs; ++output) {
     432                 :       37452 :                 CScript scriptpubkey;
     433                 :       37452 :                 scriptpubkey.resize(provider.ConsumeIntegralInRange<unsigned>(20, 34));
     434         [ +  - ]:       37452 :                 tx.vout.emplace_back(CAmount{0}, std::move(scriptpubkey));
     435                 :       37452 :             }
     436                 :             :             // Construct inputs (one for each dependency).
     437   [ +  +  +  + ]:      410081 :             for (auto& [child, parent] : deps) {
     438         [ +  + ]:      397789 :                 if (child == t) {
     439         [ -  + ]:       23041 :                     auto& partx = txn[txorder[parent]];
     440         [ -  + ]:       23041 :                     assert(partx->version == 1);
     441   [ -  +  +  - ]:       23041 :                     COutPoint outpoint(partx->GetHash(), rng.randrange<size_t>(partx->vout.size()));
     442         [ +  - ]:       23041 :                     tx.vin.emplace_back(outpoint);
     443                 :       23041 :                     tx.vin.back().scriptSig.resize(provider.ConsumeIntegralInRange<unsigned>(16, 200));
     444                 :             :                 }
     445                 :             :             }
     446                 :             :             // Construct fallback input in case there are no dependencies.
     447         [ +  + ]:       12292 :             if (tx.vin.empty()) {
     448         [ +  - ]:        5453 :                 COutPoint outpoint(Txid::FromUint256(rng.rand256()), rng.randrange<size_t>(16));
     449         [ +  - ]:        5453 :                 tx.vin.emplace_back(outpoint);
     450                 :        5453 :                 tx.vin.back().scriptSig.resize(provider.ConsumeIntegralInRange<unsigned>(16, 200));
     451                 :             :             }
     452                 :             :         }
     453                 :             :         // Optionally modify the witness (allowing wtxid != txid), and certainly when the wtxid
     454                 :             :         // already exists.
     455   [ +  -  +  +  :      117550 :         while (wtxids.contains(CTransaction(tx).GetWitnessHash()) || rng.randrange(4) == 0) {
             +  +  +  + ]
     456         [ -  + ]:       35351 :             auto& input = tx.vin[rng.randrange(tx.vin.size())];
     457         [ +  + ]:       35351 :             if (rng.randbool()) {
     458         [ +  - ]:       16639 :                 input.scriptWitness.stack.resize(1);
     459         [ +  - ]:       16639 :                 input.scriptWitness.stack[0].resize(rng.randrange(100));
     460                 :             :             } else {
     461         [ +  - ]:       18712 :                 input.scriptWitness.stack.resize(0);
     462                 :             :             }
     463                 :             :         }
     464                 :             :         // Convert to CTransactionRef.
     465   [ +  -  -  + ]:       46848 :         txn[txorder[t]] = MakeTransactionRef(std::move(tx));
     466         [ +  - ]:       23424 :         wtxids.insert(txn[txorder[t]]->GetWitnessHash());
     467                 :       23424 :         auto weight = GetTransactionWeight(*txn[txorder[t]]);
     468         [ -  + ]:       23424 :         assert(weight < MAX_STANDARD_TX_WEIGHT);
     469                 :       23424 :         total_usage += GetTransactionWeight(*txn[txorder[t]]);
     470                 :       23424 :     }
     471                 :             : 
     472                 :             :     //
     473                 :             :     // 3. Initialize real orphanage
     474                 :             :     //
     475                 :             : 
     476                 :        1464 :     auto max_global_latency_score = provider.ConsumeIntegralInRange<node::TxOrphanage::Count>(NUM_PEERS, MAX_ANN);
     477                 :        1464 :     auto reserved_peer_usage = provider.ConsumeIntegralInRange<node::TxOrphanage::Usage>(1, total_usage);
     478                 :        1464 :     auto real = node::MakeTxOrphanage(max_global_latency_score, reserved_peer_usage);
     479                 :             : 
     480                 :             :     //
     481                 :             :     // 4. Functions and data structures for the simulation.
     482                 :             :     //
     483                 :             : 
     484                 :             :     /** Data structure representing one announcement (pair of (tx, peer), plus whether it's
     485                 :             :      *  reconsiderable or not. */
     486                 :        1464 :     struct SimAnnouncement
     487                 :             :     {
     488                 :             :         unsigned tx;
     489                 :             :         NodeId announcer;
     490                 :             :         bool reconsider{false};
     491                 :       62582 :         SimAnnouncement(unsigned tx_in, NodeId announcer_in, bool reconsider_in) noexcept :
     492                 :       62582 :             tx(tx_in), announcer(announcer_in), reconsider(reconsider_in) {}
     493                 :             :     };
     494                 :             :     /** The entire simulated orphanage is represented by this list of announcements, in
     495                 :             :      *  announcement order (unlike TxOrphanageImpl which uses a sequence number to represent
     496                 :             :      *  announcement order). New announcements are added to the back. */
     497                 :        1464 :     std::vector<SimAnnouncement> sim_announcements;
     498                 :             : 
     499                 :             :     /** Consume a transaction (index into txn) from provider. */
     500                 :       22073 :     auto read_tx_fn = [&]() -> unsigned { return provider.ConsumeIntegralInRange<unsigned>(0, NUM_TX - 1); };
     501                 :             :     /** Consume a NodeId from provider. */
     502                 :       46896 :     auto read_peer_fn = [&]() -> NodeId { return provider.ConsumeIntegralInRange<unsigned>(0, NUM_PEERS - 1); };
     503                 :             :     /** Consume both a transaction (index into txn) and a NodeId from provider. */
     504                 :       89923 :     auto read_tx_peer_fn = [&]() -> std::pair<unsigned, NodeId> {
     505                 :       88459 :         auto code = provider.ConsumeIntegralInRange<unsigned>(0, NUM_TX * NUM_PEERS - 1);
     506                 :       88459 :         return {code % NUM_TX, code / NUM_TX};
     507                 :        1464 :     };
     508                 :             :     /** Determine if we have any announcements of the given transaction in the simulation. */
     509                 :     2984285 :     auto have_tx_fn = [&](unsigned tx) -> bool {
     510         [ +  + ]:    17871521 :         for (auto& ann : sim_announcements) {
     511         [ +  + ]:    15830870 :             if (ann.tx == tx) return true;
     512                 :             :         }
     513                 :             :         return false;
     514                 :        1464 :     };
     515                 :             :     /** Count the number of peers in the simulation. */
     516                 :      311724 :     auto count_peers_fn = [&]() -> unsigned {
     517                 :      310260 :         std::bitset<NUM_PEERS> mask;
     518         [ +  + ]:     2762698 :         for (auto& ann : sim_announcements) {
     519                 :     2452438 :             mask.set(ann.announcer);
     520                 :             :         }
     521                 :      310260 :         return mask.count();
     522                 :        1464 :     };
     523                 :             :     /** Determine if we have any reconsiderable announcements of a given transaction. */
     524                 :       67380 :     auto have_reconsiderable_fn = [&](unsigned tx) -> bool {
     525         [ +  + ]:      792543 :         for (auto& ann : sim_announcements) {
     526   [ +  +  +  + ]:      749684 :             if (ann.reconsider && ann.tx == tx) return true;
     527                 :             :         }
     528                 :             :         return false;
     529                 :        1464 :     };
     530                 :             :     /** Determine if a peer has any transactions to reconsider. */
     531                 :       31396 :     auto have_reconsider_fn = [&](NodeId peer) -> bool {
     532         [ +  + ]:      232959 :         for (auto& ann : sim_announcements) {
     533   [ +  +  +  + ]:      204656 :             if (ann.reconsider && ann.announcer == peer) return true;
     534                 :             :         }
     535                 :             :         return false;
     536                 :        1464 :     };
     537                 :             :     /** Get an iterator to an existing (wtxid, peer) pair in the simulation. */
     538                 :       25266 :     auto find_announce_wtxid_fn = [&](const Wtxid& wtxid, NodeId peer) -> std::vector<SimAnnouncement>::iterator {
     539         [ +  - ]:      134706 :         for (auto it = sim_announcements.begin(); it != sim_announcements.end(); ++it) {
     540   [ +  +  +  + ]:      134706 :             if (txn[it->tx]->GetWitnessHash() == wtxid && it->announcer == peer) return it;
     541                 :             :         }
     542                 :           0 :         return sim_announcements.end();
     543                 :        1464 :     };
     544                 :             :     /** Get an iterator to an existing (tx, peer) pair in the simulation. */
     545                 :      464707 :     auto find_announce_fn = [&](unsigned tx, NodeId peer) {
     546         [ +  + ]:     3763863 :         for (auto it = sim_announcements.begin(); it != sim_announcements.end(); ++it) {
     547   [ +  +  +  + ]:     3329016 :             if (it->tx == tx && it->announcer == peer) return it;
     548                 :             :         }
     549                 :      434847 :         return sim_announcements.end();
     550                 :        1464 :     };
     551                 :             :     /** Compute a peer's DoS score according to simulation data. */
     552                 :      382936 :     auto dos_score_fn = [&](NodeId peer, int32_t max_count, int32_t max_usage) -> FeeFrac {
     553                 :      381472 :         int64_t count{0};
     554                 :      381472 :         int64_t usage{0};
     555         [ +  + ]:     3346400 :         for (auto& ann : sim_announcements) {
     556         [ +  + ]:     2964928 :             if (ann.announcer != peer) continue;
     557         [ -  + ]:      185308 :             count += 1 + (txn[ann.tx]->vin.size() / 10);
     558                 :      185308 :             usage += GetTransactionWeight(*txn[ann.tx]);
     559                 :             :         }
     560                 :      381472 :         return std::max<ByRatioNegSize<FeeFrac>>(FeeFrac{count, max_count}, FeeFrac{usage, max_usage});
     561                 :        1464 :     };
     562                 :             : 
     563                 :             :     //
     564                 :             :     // 5. Run through a scenario of mutators on both real and simulated orphanage.
     565                 :             :     //
     566                 :             : 
     567   [ +  +  +  + ]:      143941 :     LIMITED_WHILE (provider.remaining_bytes() > 0, 200) {
     568                 :      142477 :         int command = provider.ConsumeIntegralInRange<uint8_t>(0, 15);
     569                 :      201951 :         while (true) {
     570   [ -  +  +  +  :      201951 :             if (sim_announcements.size() < MAX_ANN && command-- == 0) {
                   +  + ]
     571                 :             :                 // AddTx
     572         [ +  - ]:       70900 :                 auto [tx, peer] = read_tx_peer_fn();
     573         [ +  - ]:       70900 :                 bool added = real->AddTx(txn[tx], peer);
     574                 :       70900 :                 bool sim_have_tx = have_tx_fn(tx);
     575         [ -  + ]:       70900 :                 assert(added == !sim_have_tx);
     576         [ +  + ]:       70900 :                 if (find_announce_fn(tx, peer) == sim_announcements.end()) {
     577         [ +  - ]:       57838 :                     sim_announcements.emplace_back(tx, peer, false);
     578                 :             :                 }
     579                 :             :                 break;
     580   [ +  +  +  + ]:      131051 :             } else if (sim_announcements.size() < MAX_ANN && command-- == 0) {
     581                 :             :                 // AddAnnouncer
     582         [ +  - ]:       17559 :                 auto [tx, peer] = read_tx_peer_fn();
     583         [ +  - ]:       17559 :                 bool added = real->AddAnnouncer(txn[tx]->GetWitnessHash(), peer);
     584                 :       17559 :                 bool sim_have_tx = have_tx_fn(tx);
     585                 :       17559 :                 auto sim_it = find_announce_fn(tx, peer);
     586   [ +  +  +  +  :       30374 :                 assert(added == (sim_it == sim_announcements.end() && sim_have_tx));
                   -  + ]
     587         [ +  + ]:       17559 :                 if (added) {
     588         [ +  - ]:        4744 :                     sim_announcements.emplace_back(tx, peer, false);
     589                 :             :                 }
     590                 :             :                 break;
     591         [ +  + ]:      113492 :             } else if (command-- == 0) {
     592                 :             :                 // EraseTx
     593                 :        7994 :                 auto tx = read_tx_fn();
     594         [ +  - ]:        7994 :                 bool erased = real->EraseTx(txn[tx]->GetWitnessHash());
     595                 :        7994 :                 bool sim_have = have_tx_fn(tx);
     596         [ -  + ]:        7994 :                 assert(erased == sim_have);
     597   [ +  +  +  + ]:       66373 :                 std::erase_if(sim_announcements, [&](auto& ann) { return ann.tx == tx; });
     598                 :             :                 break;
     599         [ +  + ]:      105498 :             } else if (command-- == 0) {
     600                 :             :                 // EraseForPeer
     601                 :        7666 :                 auto peer = read_peer_fn();
     602         [ +  - ]:        7666 :                 real->EraseForPeer(peer);
     603   [ +  +  +  + ]:       51071 :                 std::erase_if(sim_announcements, [&](auto& ann) { return ann.announcer == peer; });
     604                 :             :                 break;
     605         [ +  + ]:       97832 :             } else if (command-- == 0) {
     606                 :             :                 // EraseForBlock
     607                 :       10693 :                 auto pattern = provider.ConsumeIntegralInRange<uint64_t>(0, (uint64_t{1} << NUM_TX) - 1);
     608                 :       10693 :                 CBlock block;
     609                 :       10693 :                 std::set<COutPoint> spent;
     610         [ +  + ]:      181781 :                 for (unsigned tx = 0; tx < NUM_TX; ++tx) {
     611         [ +  + ]:      171088 :                     if ((pattern >> tx) & 1) {
     612         [ +  - ]:       53003 :                         block.vtx.emplace_back(txn[tx]);
     613         [ +  + ]:      175136 :                         for (auto& txin : block.vtx.back()->vin) {
     614         [ +  - ]:      122133 :                             spent.insert(txin.prevout);
     615                 :             :                         }
     616                 :             :                     }
     617                 :             :                 }
     618                 :       10693 :                 std::shuffle(block.vtx.begin(), block.vtx.end(), rng);
     619         [ +  - ]:       10693 :                 real->EraseForBlock(block);
     620                 :       52611 :                 std::erase_if(sim_announcements, [&](auto& ann) {
     621         [ +  + ]:       86914 :                     for (auto& txin : txn[ann.tx]->vin) {
     622         [ +  + ]:       64701 :                         if (spent.contains(txin.prevout)) return true;
     623                 :             :                     }
     624                 :             :                     return false;
     625                 :             :                 });
     626                 :       10693 :                 break;
     627         [ +  + ]:       97832 :             } else if (command-- == 0) {
     628                 :             :                 // AddChildrenToWorkSet
     629                 :       12615 :                 auto tx = read_tx_fn();
     630                 :       12615 :                 FastRandomContext rand_ctx(rng.rand256());
     631         [ +  - ]:       12615 :                 auto added = real->AddChildrenToWorkSet(*txn[tx], rand_ctx);
     632                 :             :                 /** Set of not-already-reconsiderable child wtxids. */
     633                 :       12615 :                 std::set<Wtxid> child_wtxids;
     634         [ +  + ]:      214455 :                 for (unsigned child_tx = 0; child_tx < NUM_TX; ++child_tx) {
     635         [ +  + ]:      201840 :                     if (!have_tx_fn(child_tx)) continue;
     636         [ +  + ]:       65916 :                     if (have_reconsiderable_fn(child_tx)) continue;
     637                 :       42859 :                     bool child_of = false;
     638         [ +  + ]:      104805 :                     for (auto& txin : txn[child_tx]->vin) {
     639         [ +  + ]:       77206 :                         if (txin.prevout.hash == txn[tx]->GetHash()) {
     640                 :             :                             child_of = true;
     641                 :             :                             break;
     642                 :             :                         }
     643                 :             :                     }
     644         [ +  + ]:       42859 :                     if (child_of) {
     645         [ +  - ]:       15260 :                         child_wtxids.insert(txn[child_tx]->GetWitnessHash());
     646                 :             :                     }
     647                 :             :                 }
     648         [ +  + ]:       27875 :                 for (auto& [wtxid, peer] : added) {
     649                 :             :                     // Wtxid must be a child of tx that is not yet reconsiderable.
     650                 :       15260 :                     auto child_wtxid_it = child_wtxids.find(wtxid);
     651         [ -  + ]:       15260 :                     assert(child_wtxid_it != child_wtxids.end());
     652                 :             :                     // Announcement must exist.
     653                 :       15260 :                     auto sim_ann_it = find_announce_wtxid_fn(wtxid, peer);
     654         [ -  + ]:       15260 :                     assert(sim_ann_it != sim_announcements.end());
     655                 :             :                     // Announcement must not yet be reconsiderable.
     656         [ -  + ]:       15260 :                     assert(sim_ann_it->reconsider == false);
     657                 :             :                     // Make reconsiderable.
     658                 :       15260 :                     sim_ann_it->reconsider = true;
     659                 :             :                     // Remove from child_wtxids map, to disallow it being reported a second time in added.
     660                 :       15260 :                     child_wtxids.erase(wtxid);
     661                 :             :                 }
     662                 :             :                 // Verify that AddChildrenToWorkSet does not select announcements that were already reconsiderable:
     663                 :             :                 // Check all child wtxids which did not occur at least once in the result were already reconsiderable
     664                 :             :                 // due to a previous AddChildrenToWorkSet.
     665         [ -  + ]:       12615 :                 assert(child_wtxids.empty());
     666                 :       12615 :                 break;
     667         [ +  + ]:       87139 :             } else if (command-- == 0) {
     668                 :             :                 // GetTxToReconsider.
     669                 :       15050 :                 auto peer = read_peer_fn();
     670         [ +  - ]:       15050 :                 auto result = real->GetTxToReconsider(peer);
     671         [ +  + ]:       15050 :                 if (result) {
     672                 :             :                     // A transaction was found. It must have a corresponding reconsiderable
     673                 :             :                     // announcement from peer.
     674                 :        8542 :                     auto sim_ann_it = find_announce_wtxid_fn(result->GetWitnessHash(), peer);
     675         [ -  + ]:        8542 :                     assert(sim_ann_it != sim_announcements.end());
     676         [ -  + ]:        8542 :                     assert(sim_ann_it->announcer == peer);
     677         [ -  + ]:        8542 :                     assert(sim_ann_it->reconsider);
     678                 :             :                     // Make it non-reconsiderable.
     679                 :        8542 :                     sim_ann_it->reconsider = false;
     680                 :             :                 } else {
     681                 :             :                     // No reconsiderable transaction was found from peer. Verify that it does not
     682                 :             :                     // have any.
     683         [ -  + ]:        6508 :                     assert(!have_reconsider_fn(peer));
     684                 :             :                 }
     685         [ +  + ]:       15050 :                 break;
     686                 :       15050 :             }
     687                 :             :         }
     688                 :             :         // Always trim after each command if needed.
     689   [ +  -  +  + ]:      142477 :         const auto max_ann = max_global_latency_score / std::max<unsigned>(1, count_peers_fn());
     690                 :      142477 :         const auto max_mem = reserved_peer_usage;
     691                 :      166319 :         while (true) {
     692                 :             :             // Count global usage and number of peers.
     693                 :      166319 :             node::TxOrphanage::Usage total_usage{0};
     694         [ -  + ]:      166319 :             node::TxOrphanage::Count total_latency_score = sim_announcements.size();
     695         [ +  + ]:     2827423 :             for (unsigned tx = 0; tx < NUM_TX; ++tx) {
     696         [ +  + ]:     2661104 :                 if (have_tx_fn(tx)) {
     697                 :      831834 :                     total_usage += GetTransactionWeight(*txn[tx]);
     698         [ -  + ]:      831834 :                     total_latency_score += txn[tx]->vin.size() / 10;
     699                 :             :                 }
     700                 :             :             }
     701         [ +  - ]:      166319 :             auto num_peers = count_peers_fn();
     702   [ +  +  +  + ]:      313298 :             bool oversized = (total_usage > reserved_peer_usage * num_peers) ||
     703         [ +  - ]:      146979 :                                 (total_latency_score > real->MaxGlobalLatencyScore());
     704                 :      166319 :             if (!oversized) break;
     705                 :             :             // Find worst peer.
     706                 :       23842 :             FeeFrac worst_dos_score{0, 1};
     707                 :       23842 :             unsigned worst_peer = unsigned(-1);
     708         [ +  + ]:      405314 :             for (unsigned peer = 0; peer < NUM_PEERS; ++peer) {
     709                 :      381472 :                 auto dos_score = dos_score_fn(peer, max_ann, max_mem);
     710                 :             :                 // Use >= so that the more recent peer (higher NodeId) wins in case of
     711                 :             :                 // ties.
     712         [ +  + ]:      381472 :                 if (ByRatioNegSize{dos_score} >= ByRatioNegSize{worst_dos_score}) {
     713                 :       44972 :                     worst_dos_score = dos_score;
     714                 :       44972 :                     worst_peer = peer;
     715                 :             :                 }
     716                 :             :             }
     717         [ -  + ]:       23842 :             assert(worst_peer != unsigned(-1));
     718         [ -  + ]:       23842 :             assert(ByRatio{worst_dos_score} > ByRatio{FeeFrac(1, 1)});
     719                 :             :             // Find oldest announcement from worst_peer, preferring non-reconsiderable ones.
     720                 :             :             bool done{false};
     721         [ +  - ]:       24163 :             for (int reconsider = 0; reconsider < 2; ++reconsider) {
     722         [ +  + ]:      140624 :                 for (auto it = sim_announcements.begin(); it != sim_announcements.end(); ++it) {
     723   [ +  +  +  + ]:      140303 :                     if (it->announcer != worst_peer || it->reconsider != reconsider) continue;
     724                 :       23842 :                     sim_announcements.erase(it);
     725                 :       23842 :                     done = true;
     726                 :       23842 :                     break;
     727                 :             :                 }
     728                 :       24163 :                 if (done) break;
     729                 :             :             }
     730         [ +  - ]:       23842 :             assert(done);
     731                 :             :         }
     732                 :             :         // We must now be within limits, otherwise LimitOrphans should have continued further.
     733                 :             :         // We don't check the contents of the orphanage until the end to make fuzz runs faster.
     734   [ +  -  +  -  :      142477 :         assert(real->TotalLatencyScore() <= real->MaxGlobalLatencyScore());
                   -  + ]
     735   [ +  -  +  -  :      142477 :         assert(real->TotalOrphanUsage() <= real->MaxGlobalUsage());
                   -  + ]
     736                 :             :     }
     737                 :             : 
     738                 :             :     //
     739                 :             :     // 6. Perform a full comparison between the real orphanage's inspectors and the simulation.
     740                 :             :     //
     741                 :             : 
     742         [ +  - ]:        1464 :     real->SanityCheck();
     743                 :             : 
     744                 :             : 
     745         [ +  - ]:        1464 :     auto all_orphans = real->GetOrphanTransactions();
     746                 :        1464 :     node::TxOrphanage::Usage orphan_usage{0};
     747         [ +  - ]:        1464 :     std::vector<node::TxOrphanage::Usage> usage_by_peer(NUM_PEERS);
     748                 :        1464 :     node::TxOrphanage::Count unique_orphans{0};
     749         [ +  - ]:        1464 :     std::vector<node::TxOrphanage::Count> count_by_peer(NUM_PEERS);
     750         [ -  + ]:        1464 :     node::TxOrphanage::Count total_latency_score = sim_announcements.size();
     751         [ +  + ]:       24888 :     for (unsigned tx = 0; tx < NUM_TX; ++tx) {
     752                 :       23424 :         bool sim_have_tx = have_tx_fn(tx);
     753         [ +  + ]:       23424 :         if (sim_have_tx) {
     754                 :        6041 :             orphan_usage += GetTransactionWeight(*txn[tx]);
     755         [ -  + ]:        6041 :             total_latency_score += txn[tx]->vin.size() / 10;
     756                 :             :         }
     757                 :       23424 :         unique_orphans += sim_have_tx;
     758         [ +  + ]:       95989 :         auto orphans_it = std::find_if(all_orphans.begin(), all_orphans.end(), [&](auto& orph) { return orph.tx->GetWitnessHash() == txn[tx]->GetWitnessHash(); });
     759                 :             :         // GetOrphanTransactions (OrphanBase existence)
     760         [ -  + ]:       23424 :         assert((orphans_it != all_orphans.end()) == sim_have_tx);
     761                 :             :         // HaveTx
     762         [ +  - ]:       23424 :         bool have_tx = real->HaveTx(txn[tx]->GetWitnessHash());
     763         [ -  + ]:       23424 :         assert(have_tx == sim_have_tx);
     764                 :             :         // GetTx
     765         [ +  - ]:       23424 :         auto txref = real->GetTx(txn[tx]->GetWitnessHash());
     766         [ -  + ]:       23424 :         assert(!!txref == sim_have_tx);
     767   [ +  +  -  + ]:       23424 :         if (sim_have_tx) assert(txref->GetWitnessHash() == txn[tx]->GetWitnessHash());
     768                 :             : 
     769         [ +  + ]:      398208 :         for (NodeId peer = 0; peer < NUM_PEERS; ++peer) {
     770                 :      374784 :             auto it_sim_ann = find_announce_fn(tx, peer);
     771         [ +  + ]:      374784 :             bool sim_have_ann = it_sim_ann != sim_announcements.end();
     772         [ +  + ]:      374784 :             if (sim_have_ann) usage_by_peer[peer] += GetTransactionWeight(*txn[tx]);
     773         [ +  + ]:      374784 :             count_by_peer[peer] += sim_have_ann;
     774                 :             :             // GetOrphanTransactions (announcers presence)
     775   [ +  +  -  + ]:      374784 :             if (sim_have_ann) assert(sim_have_tx);
     776   [ +  +  -  + ]:      374784 :             if (sim_have_tx) assert(orphans_it->announcers.count(peer) == sim_have_ann);
     777                 :             :             // HaveTxFromPeer
     778         [ +  - ]:      374784 :             bool have_ann = real->HaveTxFromPeer(txn[tx]->GetWitnessHash(), peer);
     779         [ -  + ]:      374784 :             assert(sim_have_ann == have_ann);
     780                 :             :             // GetChildrenFromSamePeer
     781         [ +  - ]:      374784 :             auto children_from_peer = real->GetChildrenFromSamePeer(txn[tx], peer);
     782                 :      374784 :             auto it = children_from_peer.rbegin();
     783         [ +  + ]:     1124352 :             for (int phase = 0; phase < 2; ++phase) {
     784                 :             :                 // First expect all children which have reconsiderable announcement from peer, then the others.
     785         [ +  + ]:     6375424 :                 for (auto& ann : sim_announcements) {
     786         [ +  + ]:     5625856 :                     if (ann.announcer != peer) continue;
     787         [ +  + ]:      351616 :                     if (ann.reconsider != (phase == 1)) continue;
     788                 :      175808 :                     bool matching_parent{false};
     789         [ +  + ]:      602416 :                     for (const auto& vin : txn[ann.tx]->vin) {
     790         [ +  + ]:      426608 :                         if (vin.prevout.hash == txn[tx]->GetHash()) matching_parent = true;
     791                 :             :                     }
     792         [ +  + ]:      175808 :                     if (!matching_parent) continue;
     793                 :             :                     // Found an announcement from peer which is a child of txn[tx].
     794         [ -  + ]:       37756 :                     assert(it != children_from_peer.rend());
     795         [ -  + ]:       37756 :                     assert((*it)->GetWitnessHash() == txn[ann.tx]->GetWitnessHash());
     796                 :       37756 :                     ++it;
     797                 :             :                 }
     798                 :             :             }
     799         [ -  + ]:      374784 :             assert(it == children_from_peer.rend());
     800                 :      374784 :         }
     801                 :       23424 :     }
     802                 :             :     // TotalOrphanUsage
     803   [ +  -  -  + ]:        1464 :     assert(orphan_usage == real->TotalOrphanUsage());
     804         [ +  + ]:       24888 :     for (NodeId peer = 0; peer < NUM_PEERS; ++peer) {
     805                 :       23424 :         bool sim_have_reconsider = have_reconsider_fn(peer);
     806                 :             :         // HaveTxToReconsider
     807         [ +  - ]:       23424 :         bool have_reconsider = real->HaveTxToReconsider(peer);
     808         [ -  + ]:       23424 :         assert(have_reconsider == sim_have_reconsider);
     809                 :             :         // UsageByPeer
     810   [ +  -  -  + ]:       23424 :         assert(usage_by_peer[peer] == real->UsageByPeer(peer));
     811                 :             :         // AnnouncementsFromPeer
     812   [ +  -  -  + ]:       23424 :         assert(count_by_peer[peer] == real->AnnouncementsFromPeer(peer));
     813                 :             :     }
     814                 :             :     // CountAnnouncements
     815   [ -  +  +  -  :        1464 :     assert(sim_announcements.size() == real->CountAnnouncements());
                   -  + ]
     816                 :             :     // CountUniqueOrphans
     817   [ +  -  -  + ]:        1464 :     assert(unique_orphans == real->CountUniqueOrphans());
     818                 :             :     // MaxGlobalLatencyScore
     819   [ +  -  -  + ]:        1464 :     assert(max_global_latency_score == real->MaxGlobalLatencyScore());
     820                 :             :     // ReservedPeerUsage
     821   [ +  -  -  + ]:        1464 :     assert(reserved_peer_usage == real->ReservedPeerUsage());
     822                 :             :     // MaxPeerLatencyScore
     823         [ +  - ]:        1464 :     auto present_peers = count_peers_fn();
     824   [ +  +  +  -  :        2035 :     assert(max_global_latency_score / std::max<unsigned>(1, present_peers) == real->MaxPeerLatencyScore());
                   -  + ]
     825                 :             :     // MaxGlobalUsage
     826   [ +  +  +  -  :        2035 :     assert(reserved_peer_usage * std::max<unsigned>(1, present_peers) == real->MaxGlobalUsage());
                   -  + ]
     827                 :             :     // TotalLatencyScore.
     828   [ +  -  -  + ]:        1464 :     assert(real->TotalLatencyScore() == total_latency_score);
     829                 :        1464 : }
        

Generated by: LCOV version 2.5.0-full