Skip to content

Commit 3153e7d

Browse files
committed
[fuzz] Add HeadersSyncState target
1 parent 53552af commit 3153e7d

File tree

2 files changed

+118
-0
lines changed

2 files changed

+118
-0
lines changed

src/Makefile.test.include

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,7 @@ test_fuzz_fuzz_SOURCES = \
269269
test/fuzz/flatfile.cpp \
270270
test/fuzz/float.cpp \
271271
test/fuzz/golomb_rice.cpp \
272+
test/fuzz/headerssync.cpp \
272273
test/fuzz/hex.cpp \
273274
test/fuzz/http_request.cpp \
274275
test/fuzz/i2p.cpp \

src/test/fuzz/headerssync.cpp

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
#include <arith_uint256.h>
2+
#include <chain.h>
3+
#include <chainparams.h>
4+
#include <headerssync.h>
5+
#include <test/fuzz/fuzz.h>
6+
#include <test/fuzz/util.h>
7+
#include <test/util/setup_common.h>
8+
#include <uint256.h>
9+
#include <util/time.h>
10+
#include <validation.h>
11+
12+
#include <iterator>
13+
#include <vector>
14+
15+
static void initialize_headers_sync_state_fuzz()
16+
{
17+
static const auto testing_setup = MakeNoLogFileContext<>(
18+
/*chain_name=*/CBaseChainParams::MAIN);
19+
}
20+
21+
void MakeHeadersContinuous(
22+
const CBlockHeader& genesis_header,
23+
const std::vector<CBlockHeader>& all_headers,
24+
std::vector<CBlockHeader>& new_headers)
25+
{
26+
Assume(!new_headers.empty());
27+
28+
const CBlockHeader* prev_header{
29+
all_headers.empty() ? &genesis_header : &all_headers.back()};
30+
31+
for (auto& header : new_headers) {
32+
header.hashPrevBlock = prev_header->GetHash();
33+
34+
prev_header = &header;
35+
}
36+
}
37+
38+
class FuzzedHeadersSyncState : public HeadersSyncState
39+
{
40+
public:
41+
FuzzedHeadersSyncState(const unsigned commit_offset, const CBlockIndex* chain_start, const arith_uint256& minimum_required_work)
42+
: HeadersSyncState(/*id=*/0, Params().GetConsensus(), chain_start, minimum_required_work)
43+
{
44+
const_cast<unsigned&>(m_commit_offset) = commit_offset;
45+
}
46+
};
47+
48+
FUZZ_TARGET_INIT(headers_sync_state, initialize_headers_sync_state_fuzz)
49+
{
50+
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
51+
auto mock_time{ConsumeTime(fuzzed_data_provider)};
52+
53+
CBlockHeader genesis_header{Params().GenesisBlock()};
54+
CBlockIndex start_index(genesis_header);
55+
56+
if (mock_time < start_index.GetMedianTimePast()) return;
57+
SetMockTime(mock_time);
58+
59+
const uint256 genesis_hash = genesis_header.GetHash();
60+
start_index.phashBlock = &genesis_hash;
61+
62+
arith_uint256 min_work{UintToArith256(ConsumeUInt256(fuzzed_data_provider))};
63+
FuzzedHeadersSyncState headers_sync(
64+
/*commit_offset=*/fuzzed_data_provider.ConsumeIntegralInRange<unsigned>(1, 1024),
65+
/*chain_start=*/&start_index,
66+
/*minimum_required_work=*/min_work);
67+
68+
// Store headers for potential redownload phase.
69+
std::vector<CBlockHeader> all_headers;
70+
std::vector<CBlockHeader>::const_iterator redownloaded_it;
71+
bool presync{true};
72+
bool requested_more{true};
73+
74+
while (requested_more) {
75+
std::vector<CBlockHeader> headers;
76+
77+
// Consume headers from fuzzer or maybe replay headers if we got to the
78+
// redownload phase.
79+
if (presync || fuzzed_data_provider.ConsumeBool()) {
80+
auto deser_headers = ConsumeDeserializable<std::vector<CBlockHeader>>(fuzzed_data_provider);
81+
if (!deser_headers || deser_headers->empty()) return;
82+
83+
if (fuzzed_data_provider.ConsumeBool()) {
84+
MakeHeadersContinuous(genesis_header, all_headers, *deser_headers);
85+
}
86+
87+
headers.swap(*deser_headers);
88+
} else if (auto num_headers_left{std::distance(redownloaded_it, all_headers.cend())}; num_headers_left > 0) {
89+
// Consume some headers from the redownload buffer (At least one
90+
// header is consumed).
91+
auto begin_it{redownloaded_it};
92+
std::advance(redownloaded_it, fuzzed_data_provider.ConsumeIntegralInRange<int>(1, num_headers_left));
93+
headers.insert(headers.cend(), begin_it, redownloaded_it);
94+
}
95+
96+
if (headers.empty()) return;
97+
auto result = headers_sync.ProcessNextHeaders(headers, fuzzed_data_provider.ConsumeBool());
98+
requested_more = result.request_more;
99+
100+
if (result.request_more) {
101+
if (presync) {
102+
all_headers.insert(all_headers.cend(), headers.cbegin(), headers.cend());
103+
104+
if (headers_sync.GetState() == HeadersSyncState::State::REDOWNLOAD) {
105+
presync = false;
106+
redownloaded_it = all_headers.cbegin();
107+
108+
// If we get to redownloading, the presynced headers need
109+
// to have the min amount of work on them.
110+
assert(CalculateHeadersWork(all_headers) >= min_work);
111+
}
112+
}
113+
114+
(void)headers_sync.NextHeadersRequestLocator();
115+
}
116+
}
117+
}

0 commit comments

Comments
 (0)