Bitcoin Core  27.99.0
P2P Digital Currency
utxo_snapshot.cpp
Go to the documentation of this file.
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>
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 
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{
35  MakeNoLogFileContext<const TestingSetup>(
37  TestOpts{
38  .setup_net = false,
39  .setup_validation_interface = false,
40  })};
41  const auto& node = setup->m_node;
42  auto& chainman{*node.chainman};
43 
44  const auto snapshot_path = gArgs.GetDataDirNet() / "fuzzed_snapshot.dat";
45 
46  Assert(!chainman.SnapshotBlockhash());
47 
48  {
49  AutoFile outfile{fsbridge::fopen(snapshot_path, "wb")};
50  // Metadata
51  if (fuzzed_data_provider.ConsumeBool()) {
52  std::vector<uint8_t> metadata{ConsumeRandomLengthByteVector(fuzzed_data_provider)};
53  outfile << Span{metadata};
54  } else {
55  DataStream data_stream{};
56  auto msg_start = chainman.GetParams().MessageStart();
57  int base_blockheight{fuzzed_data_provider.ConsumeIntegralInRange<int>(1, 2 * COINBASE_MATURITY)};
58  uint256 base_blockhash{g_chain->at(base_blockheight - 1)->GetHash()};
59  uint64_t m_coins_count{fuzzed_data_provider.ConsumeIntegralInRange<uint64_t>(1, 3 * COINBASE_MATURITY)};
60  SnapshotMetadata metadata{msg_start, base_blockhash, base_blockheight, m_coins_count};
61  outfile << metadata;
62  }
63  // Coins
64  if (fuzzed_data_provider.ConsumeBool()) {
65  std::vector<uint8_t> file_data{ConsumeRandomLengthByteVector(fuzzed_data_provider)};
66  outfile << Span{file_data};
67  } else {
68  int height{0};
69  for (const auto& block : *g_chain) {
70  auto coinbase{block->vtx.at(0)};
71  outfile << coinbase->GetHash();
72  WriteCompactSize(outfile, 1); // number of coins for the hash
73  WriteCompactSize(outfile, 0); // index of coin
74  outfile << Coin(coinbase->vout[0], height, /*fCoinBaseIn=*/1);
75  height++;
76  }
77  }
78  }
79 
80  const auto ActivateFuzzedSnapshot{[&] {
81  AutoFile infile{fsbridge::fopen(snapshot_path, "rb")};
82  auto msg_start = chainman.GetParams().MessageStart();
83  SnapshotMetadata metadata{msg_start};
84  try {
85  infile >> metadata;
86  } catch (const std::ios_base::failure&) {
87  return false;
88  }
89  return !!chainman.ActivateSnapshot(infile, metadata, /*in_memory=*/true);
90  }};
91 
92  if (fuzzed_data_provider.ConsumeBool()) {
93  for (const auto& block : *g_chain) {
95  bool processed{chainman.ProcessNewBlockHeaders({*block}, true, dummy)};
96  Assert(processed);
97  const auto* index{WITH_LOCK(::cs_main, return chainman.m_blockman.LookupBlockIndex(block->GetHash()))};
98  Assert(index);
99  }
100  }
101 
102  if (ActivateFuzzedSnapshot()) {
103  LOCK(::cs_main);
104  Assert(!chainman.ActiveChainstate().m_from_snapshot_blockhash->IsNull());
105  Assert(*chainman.ActiveChainstate().m_from_snapshot_blockhash ==
106  *chainman.SnapshotBlockhash());
107  const auto& coinscache{chainman.ActiveChainstate().CoinsTip()};
108  for (const auto& block : *g_chain) {
109  Assert(coinscache.HaveCoin(COutPoint{block->vtx.at(0)->GetHash(), 0}));
110  const auto* index{chainman.m_blockman.LookupBlockIndex(block->GetHash())};
111  Assert(index);
112  Assert(index->nTx == 0);
113  if (index->nHeight == chainman.GetSnapshotBaseHeight()) {
114  auto params{chainman.GetParams().AssumeutxoForHeight(index->nHeight)};
115  Assert(params.has_value());
116  Assert(params.value().nChainTx == index->nChainTx);
117  } else {
118  Assert(index->nChainTx == 0);
119  }
120  }
121  Assert(g_chain->size() == coinscache.GetCacheSize());
122  } else {
123  Assert(!chainman.SnapshotBlockhash());
124  Assert(!chainman.ActiveChainstate().m_from_snapshot_blockhash);
125  }
126  // Snapshot should refuse to load a second time regardless of validity
127  Assert(!ActivateFuzzedSnapshot());
128 }
129 } // namespace
ArgsManager gArgs
Definition: args.cpp:41
std::unique_ptr< const CChainParams > CreateChainParams(const ArgsManager &args, const ChainType chain)
Creates and returns a std::unique_ptr<CChainParams> of the chosen chain.
#define Assert(val)
Identity function.
Definition: check.h:77
fs::path GetDataDirNet() const
Get data directory path with appended network identifier.
Definition: args.h:232
Non-refcounted RAII wrapper for FILE*.
Definition: streams.h:389
An outpoint - a combination of a transaction hash and an index n into its vout.
Definition: transaction.h:29
A UTXO entry.
Definition: coins.h:32
Double ended buffer combining vector and stream-like interfaces.
Definition: streams.h:147
A Span is an object that can refer to a contiguous sequence of objects.
Definition: span.h:98
Metadata describing a serialized version of a UTXO set from which an assumeutxo Chainstate can be con...
Definition: utxo_snapshot.h:32
256-bit opaque blob.
Definition: uint256.h:127
static const int COINBASE_MATURITY
Coinbase transaction outputs can only be spent after this number of new blocks (network rule)
Definition: consensus.h:19
RecursiveMutex cs_main
Mutex to guard access to validation specific variables, such as reading or changing the chainstate.
Definition: cs_main.cpp:8
#define FUZZ_TARGET(...)
Definition: fuzz.h:35
FILE * fopen(const fs::path &p, const char *mode)
Definition: fs.cpp:26
Definition: messages.h:20
void WriteCompactSize(SizeComputer &os, uint64_t nSize)
Definition: serialize.h:1095
bool setup_net
Definition: setup_common.h:56
#define LOCK(cs)
Definition: sync.h:257
#define WITH_LOCK(cs, code)
Run code while locking a mutex.
Definition: sync.h:301
std::vector< B > ConsumeRandomLengthByteVector(FuzzedDataProvider &fuzzed_data_provider, const std::optional< size_t > &max_length=std::nullopt) noexcept
Definition: util.h:57
std::vector< std::shared_ptr< CBlock > > CreateBlockChain(size_t total_height, const CChainParams &params)
Create a blockchain, starting from genesis.
Definition: mining.cpp:32