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