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