Line data Source code
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 2 : 18 2 : using node::SnapshotMetadata; 19 : 20 : namespace { 21 : 22 : const std::vector<std::shared_ptr<CBlock>>* g_chain; 23 : 24 0 : void initialize_chain() 25 : { 26 0 : const auto params{CreateChainParams(ArgsManager{}, ChainType::REGTEST)}; 27 0 : static const auto chain{CreateBlockChain(2 * COINBASE_MATURITY, *params)}; 28 0 : g_chain = &chain; 29 0 : } 30 : 31 4 : FUZZ_TARGET(utxo_snapshot, .init = initialize_chain) 32 : { 33 0 : FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); 34 0 : std::unique_ptr<const TestingSetup> setup{MakeNoLogFileContext<const TestingSetup>()}; 35 0 : const auto& node = setup->m_node; 36 0 : auto& chainman{*node.chainman}; 37 : 38 0 : const auto snapshot_path = gArgs.GetDataDirNet() / "fuzzed_snapshot.dat"; 39 : 40 0 : Assert(!chainman.SnapshotBlockhash()); 41 : 42 : { 43 0 : AutoFile outfile{fsbridge::fopen(snapshot_path, "wb")}; 44 0 : const auto file_data{ConsumeRandomLengthByteVector(fuzzed_data_provider)}; 45 0 : outfile << Span{file_data}; 46 0 : } 47 : 48 0 : const auto ActivateFuzzedSnapshot{[&] { 49 0 : AutoFile infile{fsbridge::fopen(snapshot_path, "rb")}; 50 0 : SnapshotMetadata metadata; 51 2 : try { 52 0 : infile >> metadata; 53 0 : } catch (const std::ios_base::failure&) { 54 0 : return false; 55 0 : } 56 0 : return chainman.ActivateSnapshot(infile, metadata, /*in_memory=*/true); 57 0 : }}; 58 : 59 0 : if (fuzzed_data_provider.ConsumeBool()) { 60 0 : for (const auto& block : *g_chain) { 61 0 : BlockValidationState dummy; 62 0 : bool processed{chainman.ProcessNewBlockHeaders({*block}, true, dummy)}; 63 0 : Assert(processed); 64 0 : const auto* index{WITH_LOCK(::cs_main, return chainman.m_blockman.LookupBlockIndex(block->GetHash()))}; 65 0 : Assert(index); 66 0 : } 67 0 : } 68 : 69 0 : if (ActivateFuzzedSnapshot()) { 70 0 : LOCK(::cs_main); 71 0 : Assert(!chainman.ActiveChainstate().m_from_snapshot_blockhash->IsNull()); 72 0 : Assert(*chainman.ActiveChainstate().m_from_snapshot_blockhash == 73 : *chainman.SnapshotBlockhash()); 74 2 : const auto& coinscache{chainman.ActiveChainstate().CoinsTip()}; 75 0 : int64_t chain_tx{}; 76 0 : for (const auto& block : *g_chain) { 77 0 : Assert(coinscache.HaveCoin(COutPoint{block->vtx.at(0)->GetHash(), 0})); 78 0 : const auto* index{chainman.m_blockman.LookupBlockIndex(block->GetHash())}; 79 0 : const auto num_tx{Assert(index)->nTx}; 80 0 : Assert(num_tx == 1); 81 0 : chain_tx += num_tx; 82 : } 83 0 : Assert(g_chain->size() == coinscache.GetCacheSize()); 84 0 : Assert(chain_tx == chainman.ActiveTip()->nChainTx); 85 0 : } else { 86 0 : Assert(!chainman.SnapshotBlockhash()); 87 0 : Assert(!chainman.ActiveChainstate().m_from_snapshot_blockhash); 88 : } 89 : // Snapshot should refuse to load a second time regardless of validity 90 0 : Assert(!ActivateFuzzedSnapshot()); 91 0 : } 92 : } // namespace