fuzz: add inputfetcher fuzz harness

This commit is contained in:
Andrew Toth 2024-11-16 14:33:30 -05:00
parent 054b7830e5
commit e8c038c9f6
No known key found for this signature in database
GPG key ID: 60007AFC8938B018
2 changed files with 152 additions and 0 deletions

View file

@ -53,6 +53,7 @@ add_executable(fuzz
hex.cpp
http_request.cpp
i2p.cpp
inputfetcher.cpp
integer.cpp
key.cpp
key_io.cpp

View file

@ -0,0 +1,151 @@
// Copyright (c) 2024-present The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <consensus/amount.h>
#include <inputfetcher.h>
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h>
#include <util/transaction_identifier.h>
#include <cstdint>
#include <map>
#include <optional>
#include <stdexcept>
#include <utility>
using DbMap = std::map<const COutPoint, std::pair<std::optional<const Coin>, bool>>;
class DbCoinsView : public CCoinsView
{
private:
DbMap& m_map;
public:
DbCoinsView(DbMap& map) : m_map(map) {}
std::optional<Coin> GetCoin(const COutPoint& outpoint) const override
{
const auto it{m_map.find(outpoint)};
assert(it != m_map.end());
const auto [coin, err] = it->second;
if (err) {
throw std::runtime_error("database error");
}
return coin;
}
};
class NoAccessCoinsView : public CCoinsView
{
public:
std::optional<Coin> GetCoin(const COutPoint& outpoint) const override
{
abort();
}
};
FUZZ_TARGET(inputfetcher)
{
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
const auto batch_size{
fuzzed_data_provider.ConsumeIntegralInRange<int32_t>(0, 1024)};
const auto worker_threads{
fuzzed_data_provider.ConsumeIntegralInRange<int32_t>(2, 4)};
InputFetcher fetcher{batch_size, worker_threads};
LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 10000) {
CBlock block;
Txid prevhash{Txid::FromUint256(ConsumeUInt256(fuzzed_data_provider))};
DbMap db_map{};
std::map<const COutPoint, const Coin> cache_map{};
DbCoinsView db(db_map);
NoAccessCoinsView back;
CCoinsViewCache cache(&back);
LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), static_cast<uint32_t>(batch_size * worker_threads * 2)) {
CMutableTransaction tx;
LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 10) {
const auto txid{fuzzed_data_provider.ConsumeBool()
? Txid::FromUint256(ConsumeUInt256(fuzzed_data_provider))
: prevhash};
const auto index{fuzzed_data_provider.ConsumeIntegral<uint32_t>()};
const COutPoint outpoint(txid, index);
tx.vin.emplace_back(outpoint);
std::optional<Coin> maybe_coin;
if (fuzzed_data_provider.ConsumeBool()) {
Coin coin{};
coin.fCoinBase = fuzzed_data_provider.ConsumeBool();
coin.nHeight =
fuzzed_data_provider.ConsumeIntegralInRange<int32_t>(
0, std::numeric_limits<int32_t>::max());
coin.out.nValue = ConsumeMoney(fuzzed_data_provider);
maybe_coin = coin;
} else {
maybe_coin = std::nullopt;
}
db_map.try_emplace(outpoint, std::make_pair(
maybe_coin,
fuzzed_data_provider.ConsumeBool()));
// Add the coin to the cache
if (fuzzed_data_provider.ConsumeBool()) {
Coin coin{};
coin.fCoinBase = fuzzed_data_provider.ConsumeBool();
coin.nHeight =
fuzzed_data_provider.ConsumeIntegralInRange<int32_t>(
0, std::numeric_limits<int32_t>::max());
coin.out.nValue =
fuzzed_data_provider.ConsumeIntegralInRange<int64_t>(
-1, MAX_MONEY);
cache_map.try_emplace(outpoint, coin);
cache.EmplaceCoinInternalDANGER(
COutPoint(outpoint),
std::move(coin),
/*set_dirty=*/fuzzed_data_provider.ConsumeBool());
}
}
prevhash = tx.GetHash();
block.vtx.push_back(MakeTransactionRef(tx));
}
fetcher.FetchInputs(cache, db, block);
for (const auto& [outpoint, pair] : db_map) {
// Check pre-existing coins in the cache have not been updated
const auto it{cache_map.find(outpoint)};
if (it != cache_map.end()) {
const auto& cache_coin{it->second};
const auto& coin{cache.AccessCoin(outpoint)};
assert(coin.IsSpent() == cache_coin.IsSpent());
assert(coin.fCoinBase == cache_coin.fCoinBase);
assert(coin.nHeight == cache_coin.nHeight);
assert(coin.out == cache_coin.out);
continue;
}
if (!cache.HaveCoinInCache(outpoint)) {
continue;
}
const auto& [maybe_coin, err] = pair;
assert(maybe_coin && !err);
// Check any newly added coins in the cache are the same as the db
const auto& coin{cache.AccessCoin(outpoint)};
assert(!coin.IsSpent());
assert(coin.fCoinBase == (*maybe_coin).fCoinBase);
assert(coin.nHeight == (*maybe_coin).nHeight);
assert(coin.out == (*maybe_coin).out);
}
}
}