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