block_index.cpp raw

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