merge bitcoin#21953: fuzz: Add utxo_snapshot target

This commit is contained in:
Konstantin Akimov 2024-07-21 14:20:05 +00:00 committed by Kittywhiskers Van Gogh
parent 8b7ea28e80
commit 6264c7b7c7
No known key found for this signature in database
GPG Key ID: 30CD0C065E5C4AAD
6 changed files with 133 additions and 6 deletions

View File

@ -331,6 +331,7 @@ test_fuzz_fuzz_SOURCES = \
test/fuzz/tx_in.cpp \ test/fuzz/tx_in.cpp \
test/fuzz/tx_out.cpp \ test/fuzz/tx_out.cpp \
test/fuzz/tx_pool.cpp \ test/fuzz/tx_pool.cpp \
test/fuzz/utxo_snapshot.cpp \
test/fuzz/validation_load_mempool.cpp \ test/fuzz/validation_load_mempool.cpp \
test/fuzz/versionbits.cpp test/fuzz/versionbits.cpp
endif # ENABLE_FUZZ_BINARY endif # ENABLE_FUZZ_BINARY

View File

@ -901,8 +901,8 @@ public:
{AssumeutxoHash{uint256S("0x9b2a277a3e3b979f1a539d57e949495d7f8247312dbc32bce6619128c192b44b")}, 110}, {AssumeutxoHash{uint256S("0x9b2a277a3e3b979f1a539d57e949495d7f8247312dbc32bce6619128c192b44b")}, 110},
}, },
{ {
210, 200,
{AssumeutxoHash{uint256S("0xd4c97d32882583b057efc3dce673e44204851435e6ffcef20346e69cddc7c91e")}, 210}, {AssumeutxoHash{uint256S("0x8a5bdd92252fc6b24663244bbe958c947bb036dc1f94ccd15439f48d8d1cb4e3")}, 200},
}, },
}; };

View File

@ -0,0 +1,88 @@
// Copyright (c) 2021 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 <chainparams.h>
#include <consensus/validation.h>
#include <node/utxo_snapshot.h>
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h>
#include <test/util/mining.h>
#include <test/util/setup_common.h>
#include <validation.h>
#include <validationinterface.h>
namespace {
const std::vector<std::shared_ptr<CBlock>>* g_chain;
void initialize_chain()
{
const auto params{CreateChainParams(ArgsManager{}, CBaseChainParams::REGTEST)};
static const auto chain{CreateBlockChain(2 * COINBASE_MATURITY, *params)};
g_chain = &chain;
}
FUZZ_TARGET_INIT(utxo_snapshot, initialize_chain)
{
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
std::unique_ptr<const TestingSetup> setup{MakeNoLogFileContext<const TestingSetup>()};
const auto& node = setup->m_node;
auto& chainman{*node.chainman};
const auto snapshot_path = GetDataDir() / "fuzzed_snapshot.dat";
Assert(!chainman.SnapshotBlockhash());
{
CAutoFile outfile{fsbridge::fopen(snapshot_path, "wb"), SER_DISK, CLIENT_VERSION};
const auto file_data{ConsumeRandomLengthByteVector(fuzzed_data_provider)};
outfile << Span<const uint8_t>{file_data};
}
const auto ActivateFuzzedSnapshot{[&] {
CAutoFile infile{fsbridge::fopen(snapshot_path, "rb"), SER_DISK, CLIENT_VERSION};
SnapshotMetadata metadata;
try {
infile >> metadata;
} catch (const std::ios_base::failure&) {
return false;
}
return chainman.ActivateSnapshot(infile, metadata, /* in_memory */ true);
}};
if (fuzzed_data_provider.ConsumeBool()) {
for (const auto& block : *g_chain) {
BlockValidationState dummy;
bool processed{chainman.ProcessNewBlockHeaders({*block}, dummy, ::Params())};
Assert(processed);
const auto* index{WITH_LOCK(::cs_main, return chainman.m_blockman.LookupBlockIndex(block->GetHash()))};
Assert(index);
}
}
if (ActivateFuzzedSnapshot()) {
LOCK(::cs_main);
Assert(!chainman.ActiveChainstate().m_from_snapshot_blockhash->IsNull());
Assert(*chainman.ActiveChainstate().m_from_snapshot_blockhash ==
*chainman.SnapshotBlockhash());
const auto& coinscache{chainman.ActiveChainstate().CoinsTip()};
int64_t chain_tx{};
for (const auto& block : *g_chain) {
Assert(coinscache.HaveCoin(COutPoint{block->vtx.at(0)->GetHash(), 0}));
const auto* index{chainman.m_blockman.LookupBlockIndex(block->GetHash())};
const auto num_tx{Assert(index)->nTx};
Assert(num_tx == 1);
chain_tx += num_tx;
}
Assert(g_chain->size() == coinscache.GetCacheSize());
Assert(chain_tx == chainman.ActiveTip()->nChainTx);
} else {
Assert(!chainman.SnapshotBlockhash());
Assert(!chainman.ActiveChainstate().m_from_snapshot_blockhash);
}
// Snapshot should refuse to load a second time regardless of validity
Assert(!ActivateFuzzedSnapshot());
}
} // namespace

View File

@ -13,8 +13,10 @@
#include <pow.h> #include <pow.h>
#include <script/standard.h> #include <script/standard.h>
#include <spork.h> #include <spork.h>
#include <test/util/script.h>
#include <util/check.h> #include <util/check.h>
#include <validation.h> #include <validation.h>
#include <versionbits.h>
CTxIn generatetoaddress(const NodeContext& node, const std::string& address) CTxIn generatetoaddress(const NodeContext& node, const std::string& address)
{ {
@ -25,6 +27,37 @@ CTxIn generatetoaddress(const NodeContext& node, const std::string& address)
return MineBlock(node, coinbase_script); return MineBlock(node, coinbase_script);
} }
std::vector<std::shared_ptr<CBlock>> CreateBlockChain(size_t total_height, const CChainParams& params)
{
std::vector<std::shared_ptr<CBlock>> ret{total_height};
auto time{params.GenesisBlock().nTime};
for (size_t height{0}; height < total_height; ++height) {
CBlock& block{*(ret.at(height) = std::make_shared<CBlock>())};
CMutableTransaction coinbase_tx;
coinbase_tx.vin.resize(1);
coinbase_tx.vin[0].prevout.SetNull();
coinbase_tx.vout.resize(1);
coinbase_tx.vout[0].scriptPubKey = P2SH_OP_TRUE;
coinbase_tx.vout[0].nValue = GetBlockSubsidyInner(params.GenesisBlock().nBits, height, params.GetConsensus(), false);
coinbase_tx.vin[0].scriptSig = CScript() << (height + 1) << OP_0;
block.vtx = {MakeTransactionRef(std::move(coinbase_tx))};
block.nVersion = VERSIONBITS_LAST_OLD_BLOCK_VERSION;
block.hashPrevBlock = (height >= 1 ? *ret.at(height - 1) : params.GenesisBlock()).GetHash();
block.hashMerkleRoot = BlockMerkleRoot(block);
block.nTime = ++time;
block.nBits = params.GenesisBlock().nBits;
block.nNonce = 0;
while (!CheckProofOfWork(block.GetHash(), block.nBits, params.GetConsensus())) {
++block.nNonce;
assert(block.nNonce);
}
}
return ret;
}
CTxIn MineBlock(const NodeContext& node, const CScript& coinbase_scriptPubKey) CTxIn MineBlock(const NodeContext& node, const CScript& coinbase_scriptPubKey)
{ {
auto block = PrepareBlock(node, coinbase_scriptPubKey); auto block = PrepareBlock(node, coinbase_scriptPubKey);

View File

@ -7,12 +7,17 @@
#include <memory> #include <memory>
#include <string> #include <string>
#include <vector>
class CBlock; class CBlock;
class CChainParams;
class CScript; class CScript;
class CTxIn; class CTxIn;
struct NodeContext; struct NodeContext;
/** Create a blockchain, starting from genesis */
std::vector<std::shared_ptr<CBlock>> CreateBlockChain(size_t total_height, const CChainParams& params);
/** Returns the generated coin */ /** Returns the generated coin */
CTxIn MineBlock(const NodeContext&, const CScript& coinbase_scriptPubKey); CTxIn MineBlock(const NodeContext&, const CScript& coinbase_scriptPubKey);

View File

@ -28,11 +28,11 @@ BOOST_AUTO_TEST_CASE(test_assumeutxo)
const auto out110 = *ExpectedAssumeutxo(110, *params); const auto out110 = *ExpectedAssumeutxo(110, *params);
BOOST_CHECK_EQUAL(out110.hash_serialized.ToString(), "9b2a277a3e3b979f1a539d57e949495d7f8247312dbc32bce6619128c192b44b"); BOOST_CHECK_EQUAL(out110.hash_serialized.ToString(), "9b2a277a3e3b979f1a539d57e949495d7f8247312dbc32bce6619128c192b44b");
BOOST_CHECK_EQUAL(out110.nChainTx, (unsigned int)110); BOOST_CHECK_EQUAL(out110.nChainTx, 110U);
const auto out210 = *ExpectedAssumeutxo(210, *params); const auto out210 = *ExpectedAssumeutxo(200, *params);
BOOST_CHECK_EQUAL(out210.hash_serialized.ToString(), "d4c97d32882583b057efc3dce673e44204851435e6ffcef20346e69cddc7c91e"); BOOST_CHECK_EQUAL(out210.hash_serialized.ToString(), "8a5bdd92252fc6b24663244bbe958c947bb036dc1f94ccd15439f48d8d1cb4e3");
BOOST_CHECK_EQUAL(out210.nChainTx, (unsigned int)210); BOOST_CHECK_EQUAL(out210.nChainTx, 200U);
} }
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()