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 }