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