From d1527f6b88656ff4aab3c671c6d9780ea2ae986e Mon Sep 17 00:00:00 2001 From: Antoine Poinsot Date: Wed, 19 Feb 2025 15:56:45 -0500 Subject: [PATCH 1/3] qa: correct off-by-one in utxo snapshot fuzz target The chain starts at block 1, not genesis. --- src/kernel/chainparams.cpp | 2 +- src/test/fuzz/utxo_snapshot.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/kernel/chainparams.cpp b/src/kernel/chainparams.cpp index 349e14a5d6..f3046e5ff1 100644 --- a/src/kernel/chainparams.cpp +++ b/src/kernel/chainparams.cpp @@ -623,7 +623,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"}), }, diff --git a/src/test/fuzz/utxo_snapshot.cpp b/src/test/fuzz/utxo_snapshot.cpp index 5eb6a32320..5a9df1e48a 100644 --- a/src/test/fuzz/utxo_snapshot.cpp +++ b/src/test/fuzz/utxo_snapshot.cpp @@ -101,7 +101,7 @@ void utxo_snapshot_fuzz(FuzzBufferType buffer) std::vector 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(); From 3b85eba83abe561078c91f5a5c49cf26c682c19b Mon Sep 17 00:00:00 2001 From: Antoine Poinsot Date: Thu, 20 Feb 2025 10:18:21 -0500 Subject: [PATCH 2/3] test util: split up ConnectBlock from MineBlock --- src/test/util/mining.cpp | 5 +++++ src/test/util/mining.h | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/src/test/util/mining.cpp b/src/test/util/mining.cpp index 04925792dc..8baac7bba3 100644 --- a/src/test/util/mining.cpp +++ b/src/test/util/mining.cpp @@ -92,6 +92,11 @@ COutPoint MineBlock(const NodeContext& node, std::shared_ptr& block) assert(block->nNonce); } + return ProcessBlock(node, block); +} + +COutPoint ProcessBlock(const NodeContext& node, const std::shared_ptr& block) +{ auto& chainman{*Assert(node.chainman)}; const auto old_height = WITH_LOCK(chainman.GetMutex(), return chainman.ActiveHeight()); bool new_block; diff --git a/src/test/util/mining.h b/src/test/util/mining.h index 9c6e29b4d3..9ad8692276 100644 --- a/src/test/util/mining.h +++ b/src/test/util/mining.h @@ -32,6 +32,11 @@ COutPoint MineBlock(const node::NodeContext&, **/ COutPoint MineBlock(const node::NodeContext&, std::shared_ptr& block); +/** + * Returns the generated coin (or Null if the block was invalid). + */ +COutPoint ProcessBlock(const node::NodeContext&, const std::shared_ptr& block); + /** Prepare a block to be mined */ std::shared_ptr PrepareBlock(const node::NodeContext&); std::shared_ptr PrepareBlock(const node::NodeContext& node, From 63b534f97e591d4e107fd5148909852eb2965d27 Mon Sep 17 00:00:00 2001 From: Antoine Poinsot Date: Thu, 20 Feb 2025 10:19:18 -0500 Subject: [PATCH 3/3] 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. --- src/test/fuzz/utxo_snapshot.cpp | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/src/test/fuzz/utxo_snapshot.cpp b/src/test/fuzz/utxo_snapshot.cpp index 5a9df1e48a..825fada9dd 100644 --- a/src/test/fuzz/utxo_snapshot.cpp +++ b/src/test/fuzz/utxo_snapshot.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -39,7 +40,31 @@ using node::SnapshotMetadata; namespace { const std::vector>* 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(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 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(ChainType::REGTEST, TestOpts{