/ src / test / fuzz / txorphan.cpp
txorphan.cpp
  1  // Copyright (c) 2022 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 <policy/policy.h>
 10  #include <primitives/transaction.h>
 11  #include <script/script.h>
 12  #include <sync.h>
 13  #include <test/fuzz/FuzzedDataProvider.h>
 14  #include <test/fuzz/fuzz.h>
 15  #include <test/fuzz/util.h>
 16  #include <test/util/setup_common.h>
 17  #include <txorphanage.h>
 18  #include <uint256.h>
 19  #include <util/check.h>
 20  #include <util/time.h>
 21  
 22  #include <cstdint>
 23  #include <memory>
 24  #include <set>
 25  #include <utility>
 26  #include <vector>
 27  
 28  void initialize_orphanage()
 29  {
 30      static const auto testing_setup = MakeNoLogFileContext();
 31  }
 32  
 33  FUZZ_TARGET(txorphan, .init = initialize_orphanage)
 34  {
 35      FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
 36      FastRandomContext limit_orphans_rng{/*fDeterministic=*/true};
 37      SetMockTime(ConsumeTime(fuzzed_data_provider));
 38  
 39      TxOrphanage orphanage;
 40      std::vector<COutPoint> outpoints;
 41      // initial outpoints used to construct transactions later
 42      for (uint8_t i = 0; i < 4; i++) {
 43          outpoints.emplace_back(Txid::FromUint256(uint256{i}), 0);
 44      }
 45      // if true, allow duplicate input when constructing tx
 46      const bool duplicate_input = fuzzed_data_provider.ConsumeBool();
 47  
 48      LIMITED_WHILE(outpoints.size() < 200'000 && fuzzed_data_provider.ConsumeBool(), 10 * DEFAULT_MAX_ORPHAN_TRANSACTIONS)
 49      {
 50          // construct transaction
 51          const CTransactionRef tx = [&] {
 52              CMutableTransaction tx_mut;
 53              const auto num_in = fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(1, outpoints.size());
 54              const auto num_out = fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(1, outpoints.size());
 55              // pick unique outpoints from outpoints as input
 56              for (uint32_t i = 0; i < num_in; i++) {
 57                  auto& prevout = PickValue(fuzzed_data_provider, outpoints);
 58                  tx_mut.vin.emplace_back(prevout);
 59                  // pop the picked outpoint if duplicate input is not allowed
 60                  if (!duplicate_input) {
 61                      std::swap(prevout, outpoints.back());
 62                      outpoints.pop_back();
 63                  }
 64              }
 65              // output amount will not affect txorphanage
 66              for (uint32_t i = 0; i < num_out; i++) {
 67                  tx_mut.vout.emplace_back(CAmount{0}, CScript{});
 68              }
 69              // restore previously popped outpoints
 70              for (auto& in : tx_mut.vin) {
 71                  outpoints.push_back(in.prevout);
 72              }
 73              auto new_tx = MakeTransactionRef(tx_mut);
 74              // add newly constructed transaction to outpoints
 75              for (uint32_t i = 0; i < num_out; i++) {
 76                  outpoints.emplace_back(new_tx->GetHash(), i);
 77              }
 78              return new_tx;
 79          }();
 80  
 81          // trigger orphanage functions
 82          LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 10 * DEFAULT_MAX_ORPHAN_TRANSACTIONS)
 83          {
 84              NodeId peer_id = fuzzed_data_provider.ConsumeIntegral<NodeId>();
 85  
 86              CallOneOf(
 87                  fuzzed_data_provider,
 88                  [&] {
 89                      orphanage.AddChildrenToWorkSet(*tx);
 90                  },
 91                  [&] {
 92                      {
 93                          CTransactionRef ref = orphanage.GetTxToReconsider(peer_id);
 94                          if (ref) {
 95                              bool have_tx = orphanage.HaveTx(GenTxid::Txid(ref->GetHash())) || orphanage.HaveTx(GenTxid::Wtxid(ref->GetWitnessHash()));
 96                              Assert(have_tx);
 97                          }
 98                      }
 99                  },
100                  [&] {
101                      bool have_tx = orphanage.HaveTx(GenTxid::Txid(tx->GetHash())) || orphanage.HaveTx(GenTxid::Wtxid(tx->GetWitnessHash()));
102                      // AddTx should return false if tx is too big or already have it
103                      // tx weight is unknown, we only check when tx is already in orphanage
104                      {
105                          bool add_tx = orphanage.AddTx(tx, peer_id);
106                          // have_tx == true -> add_tx == false
107                          Assert(!have_tx || !add_tx);
108                      }
109                      have_tx = orphanage.HaveTx(GenTxid::Txid(tx->GetHash())) || orphanage.HaveTx(GenTxid::Wtxid(tx->GetWitnessHash()));
110                      {
111                          bool add_tx = orphanage.AddTx(tx, peer_id);
112                          // if have_tx is still false, it must be too big
113                          Assert(!have_tx == (GetTransactionWeight(*tx) > MAX_STANDARD_TX_WEIGHT));
114                          Assert(!have_tx || !add_tx);
115                      }
116                  },
117                  [&] {
118                      bool have_tx = orphanage.HaveTx(GenTxid::Txid(tx->GetHash())) || orphanage.HaveTx(GenTxid::Wtxid(tx->GetWitnessHash()));
119                      // EraseTx should return 0 if m_orphans doesn't have the tx
120                      {
121                          Assert(have_tx == orphanage.EraseTx(tx->GetHash()));
122                      }
123                      have_tx = orphanage.HaveTx(GenTxid::Txid(tx->GetHash())) || orphanage.HaveTx(GenTxid::Wtxid(tx->GetWitnessHash()));
124                      // have_tx should be false and EraseTx should fail
125                      {
126                          Assert(!have_tx && !orphanage.EraseTx(tx->GetHash()));
127                      }
128                  },
129                  [&] {
130                      orphanage.EraseForPeer(peer_id);
131                  },
132                  [&] {
133                      // test mocktime and expiry
134                      SetMockTime(ConsumeTime(fuzzed_data_provider));
135                      auto limit = fuzzed_data_provider.ConsumeIntegral<unsigned int>();
136                      orphanage.LimitOrphans(limit, limit_orphans_rng);
137                      Assert(orphanage.Size() <= limit);
138                  });
139          }
140      }
141  }