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