/ src / test / fuzz / block_index.cpp
block_index.cpp
  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  }