headerssync.cpp raw

   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      SeedRandomStateForTest(SeedRand::ZEROS);
  52      FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
  53      auto mock_time{ConsumeTime(fuzzed_data_provider)};
  54  
  55      CBlockHeader genesis_header{Params().GenesisBlock()};
  56      CBlockIndex start_index(genesis_header);
  57  
  58      if (mock_time < start_index.GetMedianTimePast()) return;
  59      SetMockTime(mock_time);
  60  
  61      const uint256 genesis_hash = genesis_header.GetHash();
  62      start_index.phashBlock = &genesis_hash;
  63  
  64      arith_uint256 min_work{UintToArith256(ConsumeUInt256(fuzzed_data_provider))};
  65      FuzzedHeadersSyncState headers_sync(
  66          /*commit_offset=*/fuzzed_data_provider.ConsumeIntegralInRange<unsigned>(1, 1024),
  67          /*chain_start=*/&start_index,
  68          /*minimum_required_work=*/min_work);
  69  
  70      // Store headers for potential redownload phase.
  71      std::vector<CBlockHeader> all_headers;
  72      std::vector<CBlockHeader>::const_iterator redownloaded_it;
  73      bool presync{true};
  74      bool requested_more{true};
  75  
  76      while (requested_more) {
  77          std::vector<CBlockHeader> headers;
  78  
  79          // Consume headers from fuzzer or maybe replay headers if we got to the
  80          // redownload phase.
  81          if (presync || fuzzed_data_provider.ConsumeBool()) {
  82              auto deser_headers = ConsumeDeserializable<std::vector<CBlockHeader>>(fuzzed_data_provider);
  83              if (!deser_headers || deser_headers->empty()) return;
  84  
  85              if (fuzzed_data_provider.ConsumeBool()) {
  86                  MakeHeadersContinuous(genesis_header, all_headers, *deser_headers);
  87              }
  88  
  89              headers.swap(*deser_headers);
  90          } else if (auto num_headers_left{std::distance(redownloaded_it, all_headers.cend())}; num_headers_left > 0) {
  91              // Consume some headers from the redownload buffer (At least one
  92              // header is consumed).
  93              auto begin_it{redownloaded_it};
  94              std::advance(redownloaded_it, fuzzed_data_provider.ConsumeIntegralInRange<int>(1, num_headers_left));
  95              headers.insert(headers.cend(), begin_it, redownloaded_it);
  96          }
  97  
  98          if (headers.empty()) return;
  99          auto result = headers_sync.ProcessNextHeaders(headers, fuzzed_data_provider.ConsumeBool());
 100          requested_more = result.request_more;
 101  
 102          if (result.request_more) {
 103              if (presync) {
 104                  all_headers.insert(all_headers.cend(), headers.cbegin(), headers.cend());
 105  
 106                  if (headers_sync.GetState() == HeadersSyncState::State::REDOWNLOAD) {
 107                      presync = false;
 108                      redownloaded_it = all_headers.cbegin();
 109  
 110                      // If we get to redownloading, the presynced headers need
 111                      // to have the min amount of work on them.
 112                      assert(CalculateClaimedHeadersWork(all_headers) >= min_work);
 113                  }
 114              }
 115  
 116              (void)headers_sync.NextHeadersRequestLocator();
 117          }
 118      }
 119  }
 120