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