/ src / test / fuzz / headerssync.cpp
headerssync.cpp
  1  #include <arith_uint256.h>
  2  #include <chain.h>
  3  #include <chainparams.h>
  4  #include <headerssync.h>
  5  #include <test/fuzz/fuzz.h>
  6  #include <test/fuzz/util.h>
  7  #include <test/util/setup_common.h>
  8  #include <uint256.h>
  9  #include <util/chaintype.h>
 10  #include <util/time.h>
 11  #include <validation.h>
 12  
 13  #include <iterator>
 14  #include <vector>
 15  
 16  static void initialize_headers_sync_state_fuzz()
 17  {
 18      static const auto testing_setup = MakeNoLogFileContext<>(
 19          /*chain_type=*/ChainType::MAIN);
 20  }
 21  
 22  void MakeHeadersContinuous(
 23      const CBlockHeader& genesis_header,
 24      const std::vector<CBlockHeader>& all_headers,
 25      std::vector<CBlockHeader>& new_headers)
 26  {
 27      Assume(!new_headers.empty());
 28  
 29      const CBlockHeader* prev_header{
 30          all_headers.empty() ? &genesis_header : &all_headers.back()};
 31  
 32      for (auto& header : new_headers) {
 33          header.hashPrevBlock = prev_header->GetHash();
 34  
 35          prev_header = &header;
 36      }
 37  }
 38  
 39  class FuzzedHeadersSyncState : public HeadersSyncState
 40  {
 41  public:
 42      FuzzedHeadersSyncState(const unsigned commit_offset, const CBlockIndex* chain_start, const arith_uint256& minimum_required_work)
 43          : HeadersSyncState(/*id=*/0, Params().GetConsensus(), chain_start, minimum_required_work)
 44      {
 45          const_cast<unsigned&>(m_commit_offset) = commit_offset;
 46      }
 47  };
 48  
 49  FUZZ_TARGET(headers_sync_state, .init = initialize_headers_sync_state_fuzz)
 50  {
 51      FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
 52      auto mock_time{ConsumeTime(fuzzed_data_provider)};
 53  
 54      CBlockHeader genesis_header{Params().GenesisBlock()};
 55      CBlockIndex start_index(genesis_header);
 56  
 57      if (mock_time < start_index.GetMedianTimePast()) return;
 58      SetMockTime(mock_time);
 59  
 60      const uint256 genesis_hash = genesis_header.GetHash();
 61      start_index.phashBlock = &genesis_hash;
 62  
 63      arith_uint256 min_work{UintToArith256(ConsumeUInt256(fuzzed_data_provider))};
 64      FuzzedHeadersSyncState headers_sync(
 65          /*commit_offset=*/fuzzed_data_provider.ConsumeIntegralInRange<unsigned>(1, 1024),
 66          /*chain_start=*/&start_index,
 67          /*minimum_required_work=*/min_work);
 68  
 69      // Store headers for potential redownload phase.
 70      std::vector<CBlockHeader> all_headers;
 71      std::vector<CBlockHeader>::const_iterator redownloaded_it;
 72      bool presync{true};
 73      bool requested_more{true};
 74  
 75      while (requested_more) {
 76          std::vector<CBlockHeader> headers;
 77  
 78          // Consume headers from fuzzer or maybe replay headers if we got to the
 79          // redownload phase.
 80          if (presync || fuzzed_data_provider.ConsumeBool()) {
 81              auto deser_headers = ConsumeDeserializable<std::vector<CBlockHeader>>(fuzzed_data_provider);
 82              if (!deser_headers || deser_headers->empty()) return;
 83  
 84              if (fuzzed_data_provider.ConsumeBool()) {
 85                  MakeHeadersContinuous(genesis_header, all_headers, *deser_headers);
 86              }
 87  
 88              headers.swap(*deser_headers);
 89          } else if (auto num_headers_left{std::distance(redownloaded_it, all_headers.cend())}; num_headers_left > 0) {
 90              // Consume some headers from the redownload buffer (At least one
 91              // header is consumed).
 92              auto begin_it{redownloaded_it};
 93              std::advance(redownloaded_it, fuzzed_data_provider.ConsumeIntegralInRange<int>(1, num_headers_left));
 94              headers.insert(headers.cend(), begin_it, redownloaded_it);
 95          }
 96  
 97          if (headers.empty()) return;
 98          auto result = headers_sync.ProcessNextHeaders(headers, fuzzed_data_provider.ConsumeBool());
 99          requested_more = result.request_more;
100  
101          if (result.request_more) {
102              if (presync) {
103                  all_headers.insert(all_headers.cend(), headers.cbegin(), headers.cend());
104  
105                  if (headers_sync.GetState() == HeadersSyncState::State::REDOWNLOAD) {
106                      presync = false;
107                      redownloaded_it = all_headers.cbegin();
108  
109                      // If we get to redownloading, the presynced headers need
110                      // to have the min amount of work on them.
111                      assert(CalculateClaimedHeadersWork(all_headers) >= min_work);
112                  }
113              }
114  
115              (void)headers_sync.NextHeadersRequestLocator();
116          }
117      }
118  }