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