block_index.cpp
1 // Copyright (c) 2023 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 <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 assert(block_index.WriteBatchSync(files_info, files_count - 1, blocks_info)); 93 94 // We should be able to read every block file info we stored. Its value should correspond to 95 // what we stored above. 96 CBlockFileInfo info; 97 for (const auto& [n, file_info]: files_info) { 98 assert(block_index.ReadBlockFileInfo(n, info)); 99 assert(info == *file_info); 100 } 101 102 // We should be able to read the last block file number. Its value should be consistent. 103 int last_block_file; 104 assert(block_index.ReadLastBlockFile(last_block_file)); 105 assert(last_block_file == files_count - 1); 106 107 // We should be able to flip and read the reindexing flag. 108 bool reindexing; 109 block_index.WriteReindexing(true); 110 block_index.ReadReindexing(reindexing); 111 assert(reindexing); 112 block_index.WriteReindexing(false); 113 block_index.ReadReindexing(reindexing); 114 assert(!reindexing); 115 116 // We should be able to set and read the value of any random flag. 117 const std::string flag_name = fuzzed_data_provider.ConsumeRandomLengthString(100); 118 bool flag_value; 119 block_index.WriteFlag(flag_name, true); 120 block_index.ReadFlag(flag_name, flag_value); 121 assert(flag_value); 122 block_index.WriteFlag(flag_name, false); 123 block_index.ReadFlag(flag_name, flag_value); 124 assert(!flag_value); 125 126 // We should be able to load everything we've previously stored. Note to assert on the 127 // return value we need to make sure all blocks pass the pow check. 128 const auto params{Params().GetConsensus()}; 129 const auto inserter = [&](const uint256&) { 130 return blocks.back().get(); 131 }; 132 WITH_LOCK(::cs_main, assert(block_index.LoadBlockIndexGuts(params, inserter, g_setup->m_interrupt))); 133 }