Files
bitcoin/src/test/fuzz/headerssync.cpp
TheCharlatan ba8fc7d788 refactor: Replace string chain name constants with ChainTypes
This commit effectively moves the definition of these constants
out of the chainparamsbase to their own file.

Using the ChainType enums provides better type safety compared to
passing around strings.

The commit is part of an ongoing effort to decouple the libbitcoinkernel
library from the ArgsManager and other functionality that should not be
part of the kernel library.
2023-05-09 15:49:14 +02:00

119 lines
4.1 KiB
C++

#include <arith_uint256.h>
#include <chain.h>
#include <chainparams.h>
#include <headerssync.h>
#include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h>
#include <test/util/setup_common.h>
#include <uint256.h>
#include <util/chaintype.h>
#include <util/time.h>
#include <validation.h>
#include <iterator>
#include <vector>
static void initialize_headers_sync_state_fuzz()
{
static const auto testing_setup = MakeNoLogFileContext<>(
/*chain_type=*/ChainType::MAIN);
}
void MakeHeadersContinuous(
const CBlockHeader& genesis_header,
const std::vector<CBlockHeader>& all_headers,
std::vector<CBlockHeader>& new_headers)
{
Assume(!new_headers.empty());
const CBlockHeader* prev_header{
all_headers.empty() ? &genesis_header : &all_headers.back()};
for (auto& header : new_headers) {
header.hashPrevBlock = prev_header->GetHash();
prev_header = &header;
}
}
class FuzzedHeadersSyncState : public HeadersSyncState
{
public:
FuzzedHeadersSyncState(const unsigned commit_offset, const CBlockIndex* chain_start, const arith_uint256& minimum_required_work)
: HeadersSyncState(/*id=*/0, Params().GetConsensus(), chain_start, minimum_required_work)
{
const_cast<unsigned&>(m_commit_offset) = commit_offset;
}
};
FUZZ_TARGET_INIT(headers_sync_state, initialize_headers_sync_state_fuzz)
{
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
auto mock_time{ConsumeTime(fuzzed_data_provider)};
CBlockHeader genesis_header{Params().GenesisBlock()};
CBlockIndex start_index(genesis_header);
if (mock_time < start_index.GetMedianTimePast()) return;
SetMockTime(mock_time);
const uint256 genesis_hash = genesis_header.GetHash();
start_index.phashBlock = &genesis_hash;
arith_uint256 min_work{UintToArith256(ConsumeUInt256(fuzzed_data_provider))};
FuzzedHeadersSyncState headers_sync(
/*commit_offset=*/fuzzed_data_provider.ConsumeIntegralInRange<unsigned>(1, 1024),
/*chain_start=*/&start_index,
/*minimum_required_work=*/min_work);
// Store headers for potential redownload phase.
std::vector<CBlockHeader> all_headers;
std::vector<CBlockHeader>::const_iterator redownloaded_it;
bool presync{true};
bool requested_more{true};
while (requested_more) {
std::vector<CBlockHeader> headers;
// Consume headers from fuzzer or maybe replay headers if we got to the
// redownload phase.
if (presync || fuzzed_data_provider.ConsumeBool()) {
auto deser_headers = ConsumeDeserializable<std::vector<CBlockHeader>>(fuzzed_data_provider);
if (!deser_headers || deser_headers->empty()) return;
if (fuzzed_data_provider.ConsumeBool()) {
MakeHeadersContinuous(genesis_header, all_headers, *deser_headers);
}
headers.swap(*deser_headers);
} else if (auto num_headers_left{std::distance(redownloaded_it, all_headers.cend())}; num_headers_left > 0) {
// Consume some headers from the redownload buffer (At least one
// header is consumed).
auto begin_it{redownloaded_it};
std::advance(redownloaded_it, fuzzed_data_provider.ConsumeIntegralInRange<int>(1, num_headers_left));
headers.insert(headers.cend(), begin_it, redownloaded_it);
}
if (headers.empty()) return;
auto result = headers_sync.ProcessNextHeaders(headers, fuzzed_data_provider.ConsumeBool());
requested_more = result.request_more;
if (result.request_more) {
if (presync) {
all_headers.insert(all_headers.cend(), headers.cbegin(), headers.cend());
if (headers_sync.GetState() == HeadersSyncState::State::REDOWNLOAD) {
presync = false;
redownloaded_it = all_headers.cbegin();
// If we get to redownloading, the presynced headers need
// to have the min amount of work on them.
assert(CalculateHeadersWork(all_headers) >= min_work);
}
}
(void)headers_sync.NextHeadersRequestLocator();
}
}
}