Skip to content

Commit ee57737

Browse files
committed
Merge bitcoin/bitcoin#28209: fuzz: a target for the block index database
86b3852 qa: a fuzz target for the block index database (Antoine Poinsot) Pull request description: This introduces a small fuzz target for `CBlockTreeDB` which asserts a few invariants by using an in-memory LevelDb. ACKs for top commit: achow101: ACK 86b3852 TheCharlatan: Re-ACK 86b3852 maflcko: review ACK 86b3852 🥒 brunoerg: utACK 86b3852 Tree-SHA512: ab75b4ae1c7e0a4b15f8a6ceffdf509fbc79833e6ea073ecef68558d53b83663d1b30362aaa2d77c22b8890a572f5b1d4b1c5abbca483c8c8f9b1fb5b276a59a
2 parents 5b0059f + 86b3852 commit ee57737

File tree

2 files changed

+134
-0
lines changed

2 files changed

+134
-0
lines changed

src/Makefile.test.include

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,7 @@ test_fuzz_fuzz_SOURCES = \
299299
test/fuzz/bitset.cpp \
300300
test/fuzz/block.cpp \
301301
test/fuzz/block_header.cpp \
302+
test/fuzz/block_index.cpp \
302303
test/fuzz/blockfilter.cpp \
303304
test/fuzz/bloom_filter.cpp \
304305
test/fuzz/buffered_file.cpp \

src/test/fuzz/block_index.cpp

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
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+
{
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+
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+
CBlockFileInfo info;
97+
for (const auto& [n, file_info]: files_info) {
98+
assert(block_index.ReadBlockFileInfo(n, info));
99+
assert(info == *file_info);
100+
}
101+
102+
// We should be able to read the last block file number. Its value should be consistent.
103+
int last_block_file;
104+
assert(block_index.ReadLastBlockFile(last_block_file));
105+
assert(last_block_file == files_count - 1);
106+
107+
// We should be able to flip and read the reindexing flag.
108+
bool reindexing;
109+
block_index.WriteReindexing(true);
110+
block_index.ReadReindexing(reindexing);
111+
assert(reindexing);
112+
block_index.WriteReindexing(false);
113+
block_index.ReadReindexing(reindexing);
114+
assert(!reindexing);
115+
116+
// We should be able to set and read the value of any random flag.
117+
const std::string flag_name = fuzzed_data_provider.ConsumeRandomLengthString(100);
118+
bool flag_value;
119+
block_index.WriteFlag(flag_name, true);
120+
block_index.ReadFlag(flag_name, flag_value);
121+
assert(flag_value);
122+
block_index.WriteFlag(flag_name, false);
123+
block_index.ReadFlag(flag_name, flag_value);
124+
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+
const auto params{Params().GetConsensus()};
129+
const auto inserter = [&](const uint256&) {
130+
return blocks.back().get();
131+
};
132+
WITH_LOCK(::cs_main, assert(block_index.LoadBlockIndexGuts(params, inserter, g_setup->m_interrupt)));
133+
}

0 commit comments

Comments
 (0)