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