Coverage Report

Created: 2025-09-19 18:22

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