/root/bitcoin/src/test/fuzz/block_index.cpp
Line | Count | Source (jump to first uncovered line) |
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 | } |