utxo_snapshot.cpp
1 // Copyright (c) 2021-2022 The Bitcoin Core developers 2 // Distributed under the MIT software license, see the accompanying 3 // file COPYING or http://www.opensource.org/licenses/mit-license.php. 4 5 #include <chainparams.h> 6 #include <consensus/validation.h> 7 #include <node/utxo_snapshot.h> 8 #include <test/fuzz/FuzzedDataProvider.h> 9 #include <test/fuzz/fuzz.h> 10 #include <test/fuzz/util.h> 11 #include <test/util/mining.h> 12 #include <test/util/setup_common.h> 13 #include <util/chaintype.h> 14 #include <util/fs.h> 15 #include <validation.h> 16 #include <validationinterface.h> 17 18 using node::SnapshotMetadata; 19 20 namespace { 21 22 const std::vector<std::shared_ptr<CBlock>>* g_chain; 23 24 void initialize_chain() 25 { 26 const auto params{CreateChainParams(ArgsManager{}, ChainType::REGTEST)}; 27 static const auto chain{CreateBlockChain(2 * COINBASE_MATURITY, *params)}; 28 g_chain = &chain; 29 } 30 31 FUZZ_TARGET(utxo_snapshot, .init = initialize_chain) 32 { 33 FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); 34 std::unique_ptr<const TestingSetup> setup{MakeNoLogFileContext<const TestingSetup>()}; 35 const auto& node = setup->m_node; 36 auto& chainman{*node.chainman}; 37 38 const auto snapshot_path = gArgs.GetDataDirNet() / "fuzzed_snapshot.dat"; 39 40 Assert(!chainman.SnapshotBlockhash()); 41 42 { 43 AutoFile outfile{fsbridge::fopen(snapshot_path, "wb")}; 44 const auto file_data{ConsumeRandomLengthByteVector(fuzzed_data_provider)}; 45 outfile << Span{file_data}; 46 } 47 48 const auto ActivateFuzzedSnapshot{[&] { 49 AutoFile infile{fsbridge::fopen(snapshot_path, "rb")}; 50 SnapshotMetadata metadata; 51 try { 52 infile >> metadata; 53 } catch (const std::ios_base::failure&) { 54 return false; 55 } 56 return chainman.ActivateSnapshot(infile, metadata, /*in_memory=*/true); 57 }}; 58 59 if (fuzzed_data_provider.ConsumeBool()) { 60 for (const auto& block : *g_chain) { 61 BlockValidationState dummy; 62 bool processed{chainman.ProcessNewBlockHeaders({*block}, true, dummy)}; 63 Assert(processed); 64 const auto* index{WITH_LOCK(::cs_main, return chainman.m_blockman.LookupBlockIndex(block->GetHash()))}; 65 Assert(index); 66 } 67 } 68 69 if (ActivateFuzzedSnapshot()) { 70 LOCK(::cs_main); 71 Assert(!chainman.ActiveChainstate().m_from_snapshot_blockhash->IsNull()); 72 Assert(*chainman.ActiveChainstate().m_from_snapshot_blockhash == 73 *chainman.SnapshotBlockhash()); 74 const auto& coinscache{chainman.ActiveChainstate().CoinsTip()}; 75 int64_t chain_tx{}; 76 for (const auto& block : *g_chain) { 77 Assert(coinscache.HaveCoin(COutPoint{block->vtx.at(0)->GetHash(), 0})); 78 const auto* index{chainman.m_blockman.LookupBlockIndex(block->GetHash())}; 79 const auto num_tx{Assert(index)->nTx}; 80 Assert(num_tx == 1); 81 chain_tx += num_tx; 82 } 83 Assert(g_chain->size() == coinscache.GetCacheSize()); 84 Assert(chain_tx == chainman.ActiveTip()->nChainTx); 85 } else { 86 Assert(!chainman.SnapshotBlockhash()); 87 Assert(!chainman.ActiveChainstate().m_from_snapshot_blockhash); 88 } 89 // Snapshot should refuse to load a second time regardless of validity 90 Assert(!ActivateFuzzedSnapshot()); 91 } 92 } // namespace