Coverage Report

Created: 2026-04-20 22:07

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