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