block_index.cpp raw

   1  // Copyright (c) 2023-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 <chain.h>
   6  #include <chainparams.h>
   7  #include <node/blockstorage.h>
   8  #include <test/fuzz/FuzzedDataProvider.h>
   9  #include <test/fuzz/fuzz.h>
  10  #include <test/fuzz/util.h>
  11  #include <test/util/setup_common.h>
  12  #include <txdb.h>
  13  #include <util/byte_units.h>
  14  #include <validation.h>
  15  
  16  using kernel::CBlockFileInfo;
  17  
  18  namespace {
  19  
  20  const BasicTestingSetup* g_setup;
  21  
  22  // Hardcoded block hash and nBits to make sure the blocks we store pass the pow check.
  23  uint256 g_block_hash;
  24  
  25  bool operator==(const CBlockFileInfo& a, const CBlockFileInfo& b)
  26  {
  27      return a.nBlocks == b.nBlocks &&
  28          a.nSize == b.nSize &&
  29          a.nUndoSize == b.nUndoSize &&
  30          a.nHeightFirst == b.nHeightFirst &&
  31          a.nHeightLast == b.nHeightLast &&
  32          a.nTimeFirst == b.nTimeFirst &&
  33          a.nTimeLast == b.nTimeLast;
  34  }
  35  
  36  CBlockHeader ConsumeBlockHeader(FuzzedDataProvider& provider)
  37  {
  38      CBlockHeader header;
  39      header.nVersion = provider.ConsumeIntegral<decltype(header.nVersion)>();
  40      header.hashPrevBlock = g_block_hash;
  41      header.hashMerkleRoot = g_block_hash;
  42      header.nTime = provider.ConsumeIntegral<decltype(header.nTime)>();
  43      header.nBits = Params().GenesisBlock().nBits;
  44      header.nNonce = provider.ConsumeIntegral<decltype(header.nNonce)>();
  45      return header;
  46  }
  47  
  48  } // namespace
  49  
  50  void init_block_index()
  51  {
  52      static const auto testing_setup = MakeNoLogFileContext<>(ChainType::MAIN);
  53      g_setup = testing_setup.get();
  54      g_block_hash = Params().GenesisBlock().GetHash();
  55  }
  56  
  57  FUZZ_TARGET(block_index, .init = init_block_index)
  58  {
  59      FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
  60      auto block_index = kernel::BlockTreeDB(DBParams{
  61          .path = "", // Memory only.
  62          .cache_bytes = 1_MiB,
  63          .memory_only = true,
  64      });
  65  
  66      // Generate a number of block files to be stored in the index.
  67      int files_count = fuzzed_data_provider.ConsumeIntegralInRange(1, 100);
  68      std::vector<std::unique_ptr<CBlockFileInfo>> files;
  69      files.reserve(files_count);
  70      std::vector<std::pair<int, const CBlockFileInfo*>> files_info;
  71      files_info.reserve(files_count);
  72      for (int i = 0; i < files_count; i++) {
  73          if (auto file_info = ConsumeDeserializable<CBlockFileInfo>(fuzzed_data_provider)) {
  74              files.push_back(std::make_unique<CBlockFileInfo>(std::move(*file_info)));
  75              files_info.emplace_back(i, files.back().get());
  76          } else {
  77              return;
  78          }
  79      }
  80  
  81      // Generate a number of block headers to be stored in the index.
  82      int blocks_count = fuzzed_data_provider.ConsumeIntegralInRange(files_count * 10, files_count * 100);
  83      std::vector<std::unique_ptr<CBlockIndex>> blocks;
  84      blocks.reserve(blocks_count);
  85      std::vector<const CBlockIndex*> blocks_info;
  86      blocks_info.reserve(blocks_count);
  87      for (int i = 0; i < blocks_count; i++) {
  88          CBlockHeader header{ConsumeBlockHeader(fuzzed_data_provider)};
  89          blocks.push_back(std::make_unique<CBlockIndex>(std::move(header)));
  90          blocks.back()->phashBlock = &g_block_hash;
  91          blocks_info.push_back(blocks.back().get());
  92      }
  93  
  94      // Store these files and blocks in the block index. It should not fail.
  95      block_index.WriteBatchSync(files_info, files_count - 1, blocks_info);
  96  
  97      // We should be able to read every block file info we stored. Its value should correspond to
  98      // what we stored above.
  99      CBlockFileInfo info;
 100      for (const auto& [n, file_info]: files_info) {
 101          assert(block_index.ReadBlockFileInfo(n, info));
 102          assert(info == *file_info);
 103      }
 104  
 105      // We should be able to read the last block file number. Its value should be consistent.
 106      int last_block_file;
 107      assert(block_index.ReadLastBlockFile(last_block_file));
 108      assert(last_block_file == files_count - 1);
 109  
 110      // We should be able to flip and read the reindexing flag.
 111      bool reindexing;
 112      block_index.WriteReindexing(true);
 113      block_index.ReadReindexing(reindexing);
 114      assert(reindexing);
 115      block_index.WriteReindexing(false);
 116      block_index.ReadReindexing(reindexing);
 117      assert(!reindexing);
 118  
 119      // We should be able to set and read the value of any random flag.
 120      const std::string flag_name = fuzzed_data_provider.ConsumeRandomLengthString(100);
 121      bool flag_value;
 122      block_index.WriteFlag(flag_name, true);
 123      block_index.ReadFlag(flag_name, flag_value);
 124      assert(flag_value);
 125      block_index.WriteFlag(flag_name, false);
 126      block_index.ReadFlag(flag_name, flag_value);
 127      assert(!flag_value);
 128  
 129      // We should be able to load everything we've previously stored. Note to assert on the
 130      // return value we need to make sure all blocks pass the pow check.
 131      const auto params{Params().GetConsensus()};
 132      const auto inserter = [&](const uint256&) {
 133          return blocks.back().get();
 134      };
 135      WITH_LOCK(::cs_main, assert(block_index.LoadBlockIndexGuts(params, inserter, g_setup->m_interrupt)));
 136  }
 137