Compare commits

...

4 Commits

Author SHA1 Message Date
Antoine Poinsot
5c7f4c3508
Merge 63b534f97e591d4e107fd5148909852eb2965d27 into cd8089c20baaaee329cbf7951195953a9f86d7cf 2025-03-16 17:15:40 +01:00
Antoine Poinsot
63b534f97e fuzz: sanity check hardcoded snapshot in utxo_snapshot target
The assumeutxo data for the fuzz target could change and invalidate the hash silently, preventing
the fuzz target from reaching some code paths.

Fix this by sanity checking the snapshot values during initialization.
2025-02-21 20:55:01 -05:00
Antoine Poinsot
3b85eba83a test util: split up ConnectBlock from MineBlock 2025-02-21 20:55:01 -05:00
Antoine Poinsot
d1527f6b88 qa: correct off-by-one in utxo snapshot fuzz target
The chain starts at block 1, not genesis.
2025-02-19 16:12:35 -05:00
4 changed files with 42 additions and 3 deletions

View File

@ -589,7 +589,7 @@ public:
{
// For use by fuzz target src/test/fuzz/utxo_snapshot.cpp
.height = 200,
.hash_serialized = AssumeutxoHash{uint256{"4f34d431c3e482f6b0d67b64609ece3964dc8d7976d02ac68dd7c9c1421738f2"}},
.hash_serialized = AssumeutxoHash{uint256{"7e3b7780fbd2fa479a01f66950dc8f728dc1b11f03d06d5bf223168520df3a48"}},
.m_chain_tx_count = 201,
.blockhash = consteval_ctor(uint256{"5e93653318f294fb5aa339d00bbf8cf1c3515488ad99412c37608b139ea63b27"}),
},

View File

@ -7,6 +7,7 @@
#include <coins.h>
#include <consensus/consensus.h>
#include <consensus/validation.h>
#include <kernel/coinstats.h>
#include <node/blockstorage.h>
#include <node/utxo_snapshot.h>
#include <primitives/block.h>
@ -39,7 +40,31 @@ using node::SnapshotMetadata;
namespace {
const std::vector<std::shared_ptr<CBlock>>* g_chain;
TestingSetup* g_setup;
TestingSetup* g_setup{nullptr};
/** Sanity check the assumeutxo values hardcoded in chainparams for the fuzz target. */
void sanity_check_snapshot()
{
Assert(g_chain && g_setup == nullptr);
// Create a temporary chainstate manager to connect the chain to.
const auto tmp_setup{MakeNoLogFileContext<TestingSetup>(ChainType::REGTEST, TestOpts{.setup_net = false})};
const auto& node{tmp_setup->m_node};
for (auto& block: *g_chain) {
ProcessBlock(node, block);
}
// Connect the chain to the tmp chainman and sanity check the chainparams snapshot values.
LOCK(cs_main);
auto& cs{node.chainman->ActiveChainstate()};
cs.ForceFlushStateToDisk();
const auto stats{*Assert(kernel::ComputeUTXOStats(kernel::CoinStatsHashType::HASH_SERIALIZED, &cs.CoinsDB(), node.chainman->m_blockman))};
const auto cp_au_data{*Assert(node.chainman->GetParams().AssumeutxoForHeight(2 * COINBASE_MATURITY))};
Assert(stats.nHeight == cp_au_data.height);
Assert(stats.nTransactions + 1 == cp_au_data.m_chain_tx_count); // +1 for the genesis tx.
Assert(stats.hashBlock == cp_au_data.blockhash);
Assert(AssumeutxoHash{stats.hashSerialized} == cp_au_data.hash_serialized);
}
template <bool INVALID>
void initialize_chain()
@ -47,6 +72,10 @@ void initialize_chain()
const auto params{CreateChainParams(ArgsManager{}, ChainType::REGTEST)};
static const auto chain{CreateBlockChain(2 * COINBASE_MATURITY, *params)};
g_chain = &chain;
// Make sure we can generate a valid snapshot.
sanity_check_snapshot();
static const auto setup{
MakeNoLogFileContext<TestingSetup>(ChainType::REGTEST,
TestOpts{
@ -101,7 +130,7 @@ void utxo_snapshot_fuzz(FuzzBufferType buffer)
std::vector<uint8_t> file_data{ConsumeRandomLengthByteVector(fuzzed_data_provider)};
outfile << Span{file_data};
} else {
int height{0};
int height{1};
for (const auto& block : *g_chain) {
auto coinbase{block->vtx.at(0)};
outfile << coinbase->GetHash();

View File

@ -92,6 +92,11 @@ COutPoint MineBlock(const NodeContext& node, std::shared_ptr<CBlock>& block)
assert(block->nNonce);
}
return ProcessBlock(node, block);
}
COutPoint ProcessBlock(const NodeContext& node, const std::shared_ptr<CBlock>& block)
{
auto& chainman{*Assert(node.chainman)};
const auto old_height = WITH_LOCK(chainman.GetMutex(), return chainman.ActiveHeight());
bool new_block;

View File

@ -32,6 +32,11 @@ COutPoint MineBlock(const node::NodeContext&,
**/
COutPoint MineBlock(const node::NodeContext&, std::shared_ptr<CBlock>& block);
/**
* Returns the generated coin (or Null if the block was invalid).
*/
COutPoint ProcessBlock(const node::NodeContext&, const std::shared_ptr<CBlock>& block);
/** Prepare a block to be mined */
std::shared_ptr<CBlock> PrepareBlock(const node::NodeContext&);
std::shared_ptr<CBlock> PrepareBlock(const node::NodeContext& node,