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 <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 : 1 : static void initialize_headers_sync_state_fuzz()
22 : : {
23 : 1 : static const auto testing_setup = MakeNoLogFileContext<>(
24 [ + - + - : 1 : /*chain_type=*/ChainType::MAIN);
+ - ]
25 : 1 : }
26 : :
27 : 170274 : void MakeHeadersContinuous(
28 : : const CBlockHeader& genesis_header,
29 : : const std::vector<CBlockHeader>& all_headers,
30 : : std::vector<CBlockHeader>& new_headers)
31 : : {
32 [ - + ]: 170274 : Assume(!new_headers.empty());
33 : :
34 : 170274 : const CBlockHeader* prev_header{
35 [ + + ]: 170274 : all_headers.empty() ? &genesis_header : &all_headers.back()};
36 : :
37 [ + + ]: 351081 : for (auto& header : new_headers) {
38 : 180807 : header.hashPrevBlock = prev_header->GetHash();
39 : :
40 : 180807 : prev_header = &header;
41 : : }
42 : 170274 : }
43 : :
44 : 0 : class FuzzedHeadersSyncState : public HeadersSyncState
45 : : {
46 : : public:
47 : 204 : FuzzedHeadersSyncState(const HeadersSyncParams& sync_params, const size_t commit_offset,
48 : : const CBlockIndex& chain_start, const arith_uint256& minimum_required_work)
49 : 204 : : HeadersSyncState(/*id=*/0, Params().GetConsensus(), sync_params, chain_start, minimum_required_work)
50 : : {
51 : 204 : const_cast<size_t&>(m_commit_offset) = commit_offset;
52 : 204 : }
53 : : };
54 : :
55 [ + - ]: 662 : FUZZ_TARGET(headers_sync_state, .init = initialize_headers_sync_state_fuzz)
56 : : {
57 : 204 : SeedRandomStateForTest(SeedRand::ZEROS);
58 : 204 : FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
59 : :
60 : 204 : CBlockHeader genesis_header{Params().GenesisBlock()};
61 : 204 : CBlockIndex start_index(genesis_header);
62 : :
63 : 204 : NodeClockContext clock_ctx{ConsumeTime(fuzzed_data_provider, /*min=*/start_index.GetMedianTimePast())};
64 : :
65 [ + - ]: 204 : const uint256 genesis_hash = genesis_header.GetHash();
66 : 204 : start_index.phashBlock = &genesis_hash;
67 : :
68 : 204 : const HeadersSyncParams params{
69 [ + - ]: 204 : .commitment_period = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(1, Params().HeadersSync().commitment_period * 2),
70 : 204 : .redownload_buffer_size = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, Params().HeadersSync().redownload_buffer_size * 2),
71 [ + - ]: 204 : };
72 [ + - ]: 204 : arith_uint256 min_work{UintToArith256(ConsumeUInt256(fuzzed_data_provider))};
73 : 204 : FuzzedHeadersSyncState headers_sync(
74 : : params,
75 : 204 : /*commit_offset=*/fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, params.commitment_period - 1),
76 : : /*chain_start=*/start_index,
77 [ + - ]: 204 : /*minimum_required_work=*/min_work);
78 : :
79 : : // Store headers for potential redownload phase.
80 : 204 : std::vector<CBlockHeader> all_headers;
81 : 204 : std::vector<CBlockHeader>::const_iterator redownloaded_it;
82 : 204 : bool presync{true};
83 : 204 : bool requested_more{true};
84 : :
85 [ + + ]: 170696 : while (requested_more) {
86 : 170554 : std::vector<CBlockHeader> headers;
87 : :
88 : : // Consume headers from fuzzer or maybe replay headers if we got to the
89 : : // redownload phase.
90 [ + + + + ]: 170748 : if (presync || fuzzed_data_provider.ConsumeBool()) {
91 : 170382 : auto deser_headers = ConsumeDeserializable<std::vector<CBlockHeader>>(fuzzed_data_provider);
92 [ + + + + ]: 170382 : if (!deser_headers || deser_headers->empty()) return;
93 : :
94 [ + + ]: 170320 : if (fuzzed_data_provider.ConsumeBool()) {
95 [ + - ]: 170274 : MakeHeadersContinuous(genesis_header, all_headers, *deser_headers);
96 : : }
97 : :
98 : 170320 : headers.swap(*deser_headers);
99 [ + - ]: 170554 : } 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 : 172 : auto begin_it{redownloaded_it};
103 : 172 : std::advance(redownloaded_it, fuzzed_data_provider.ConsumeIntegralInRange<int>(1, num_headers_left));
104 [ + - ]: 172 : headers.insert(headers.cend(), begin_it, redownloaded_it);
105 : : }
106 : :
107 [ + - ]: 170492 : if (headers.empty()) return;
108 [ - + + - ]: 170492 : auto result = headers_sync.ProcessNextHeaders(headers, fuzzed_data_provider.ConsumeBool());
109 : 170492 : requested_more = result.request_more;
110 : :
111 [ + + ]: 170492 : if (result.request_more) {
112 [ + + ]: 170350 : if (presync) {
113 [ + - ]: 170215 : all_headers.insert(all_headers.cend(), headers.cbegin(), headers.cend());
114 : :
115 [ + + ]: 170215 : if (headers_sync.GetState() == HeadersSyncState::State::REDOWNLOAD) {
116 : 59 : presync = false;
117 [ - + ]: 59 : 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 [ - + + - : 59 : assert(CalculateClaimedHeadersWork(all_headers) >= min_work);
+ - - + ]
122 : : }
123 : : }
124 : :
125 [ + - ]: 340700 : (void)headers_sync.NextHeadersRequestLocator();
126 : : }
127 : 170554 : }
128 : 408 : }
|