/ src / test / fuzz / utxo_snapshot.cpp
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