headerssync.cpp raw

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