LCOV - code coverage report
Current view: top level - src/test/fuzz - utxo_snapshot.cpp (source / functions) Coverage Total Hit
Test: fuzz_coverage.info Lines: 99.0 % 97 96
Test Date: 2025-01-22 04:09:46 Functions: 100.0 % 12 12
Branches: 54.5 % 202 110

             Branch data     Line data    Source code
       1                 :             : // Copyright (c) 2021-present 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 <chain.h>
       6                 :             : #include <chainparams.h>
       7                 :             : #include <coins.h>
       8                 :             : #include <consensus/consensus.h>
       9                 :             : #include <consensus/validation.h>
      10                 :             : #include <node/blockstorage.h>
      11                 :             : #include <node/utxo_snapshot.h>
      12                 :             : #include <primitives/block.h>
      13                 :             : #include <primitives/transaction.h>
      14                 :             : #include <serialize.h>
      15                 :             : #include <span.h>
      16                 :             : #include <streams.h>
      17                 :             : #include <sync.h>
      18                 :             : #include <test/fuzz/FuzzedDataProvider.h>
      19                 :             : #include <test/fuzz/fuzz.h>
      20                 :             : #include <test/fuzz/util.h>
      21                 :             : #include <test/util/mining.h>
      22                 :             : #include <test/util/setup_common.h>
      23                 :             : #include <uint256.h>
      24                 :             : #include <util/check.h>
      25                 :             : #include <util/fs.h>
      26                 :             : #include <util/result.h>
      27                 :             : #include <util/time.h>
      28                 :             : #include <validation.h>
      29                 :             : 
      30                 :             : #include <cstdint>
      31                 :             : #include <functional>
      32                 :             : #include <ios>
      33                 :             : #include <memory>
      34                 :             : #include <optional>
      35                 :             : #include <vector>
      36                 :             : 
      37                 :             : using node::SnapshotMetadata;
      38                 :             : 
      39                 :             : namespace {
      40                 :             : 
      41                 :             : const std::vector<std::shared_ptr<CBlock>>* g_chain;
      42                 :             : TestingSetup* g_setup;
      43                 :             : 
      44                 :             : template <bool INVALID>
      45                 :           2 : void initialize_chain()
      46                 :             : {
      47         [ +  - ]:           2 :     const auto params{CreateChainParams(ArgsManager{}, ChainType::REGTEST)};
      48   [ +  -  +  -  :           4 :     static const auto chain{CreateBlockChain(2 * COINBASE_MATURITY, *params)};
                   +  - ]
      49                 :           2 :     g_chain = &chain;
      50   [ +  -  +  - ]:           4 :     static const auto setup{
      51                 :             :         MakeNoLogFileContext<TestingSetup>(ChainType::REGTEST,
      52                 :             :                                            TestOpts{
      53                 :             :                                                .setup_net = false,
      54                 :             :                                                .setup_validation_interface = false,
      55                 :             :                                                .min_validation_cache = true,
      56                 :             :                                            }),
      57                 :             :     };
      58                 :             :     if constexpr (INVALID) {
      59                 :           1 :         auto& chainman{*setup->m_node.chainman};
      60         [ +  + ]:         201 :         for (const auto& block : chain) {
      61                 :         200 :             BlockValidationState dummy;
      62         [ +  - ]:         200 :             bool processed{chainman.ProcessNewBlockHeaders({{block->GetBlockHeader()}}, true, dummy)};
      63         [ +  - ]:         200 :             Assert(processed);
      64   [ +  -  +  -  :         600 :             const auto* index{WITH_LOCK(::cs_main, return chainman.m_blockman.LookupBlockIndex(block->GetHash()))};
                   +  - ]
      65         [ +  - ]:         200 :             Assert(index);
      66                 :             :         }
      67                 :             :     }
      68                 :           2 :     g_setup = setup.get();
      69         [ +  - ]:           4 : }
      70                 :             : 
      71                 :             : template <bool INVALID>
      72                 :        2919 : void utxo_snapshot_fuzz(FuzzBufferType buffer)
      73                 :             : {
      74                 :        2919 :     SeedRandomStateForTest(SeedRand::ZEROS);
      75                 :        2919 :     FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
      76                 :        2919 :     SetMockTime(ConsumeTime(fuzzed_data_provider, /*min=*/1296688602)); // regtest genesis block timestamp
      77                 :        2919 :     auto& setup{*g_setup};
      78                 :        2919 :     bool dirty_chainman{false}; // Re-use the global chainman, but reset it when it is dirty
      79                 :        2919 :     auto& chainman{*setup.m_node.chainman};
      80                 :             : 
      81         [ +  - ]:        5838 :     const auto snapshot_path = gArgs.GetDataDirNet() / "fuzzed_snapshot.dat";
      82                 :             : 
      83   [ +  -  +  - ]:        2919 :     Assert(!chainman.SnapshotBlockhash());
      84                 :             : 
      85                 :             :     {
      86   [ +  -  +  - ]:        5838 :         AutoFile outfile{fsbridge::fopen(snapshot_path, "wb")};
      87                 :             :         // Metadata
      88         [ +  + ]:        2919 :         if (fuzzed_data_provider.ConsumeBool()) {
      89         [ +  - ]:        1598 :             std::vector<uint8_t> metadata{ConsumeRandomLengthByteVector(fuzzed_data_provider)};
      90         [ +  - ]:        3196 :             outfile << Span{metadata};
      91                 :        1598 :         } else {
      92                 :        1321 :             auto msg_start = chainman.GetParams().MessageStart();
      93                 :        1321 :             int base_blockheight{fuzzed_data_provider.ConsumeIntegralInRange<int>(1, 2 * COINBASE_MATURITY)};
      94   [ +  -  +  - ]:        1321 :             uint256 base_blockhash{g_chain->at(base_blockheight - 1)->GetHash()};
      95                 :        1321 :             uint64_t m_coins_count{fuzzed_data_provider.ConsumeIntegralInRange<uint64_t>(1, 3 * COINBASE_MATURITY)};
      96         [ +  - ]:        1321 :             SnapshotMetadata metadata{msg_start, base_blockhash, m_coins_count};
      97                 :        1321 :             outfile << metadata;
      98                 :        1321 :         }
      99                 :             :         // Coins
     100         [ +  + ]:        2919 :         if (fuzzed_data_provider.ConsumeBool()) {
     101         [ +  - ]:        1225 :             std::vector<uint8_t> file_data{ConsumeRandomLengthByteVector(fuzzed_data_provider)};
     102         [ +  - ]:        2450 :             outfile << Span{file_data};
     103                 :        1225 :         } else {
     104                 :        1694 :             int height{0};
     105   [ +  -  +  + ]:      340494 :             for (const auto& block : *g_chain) {
     106   [ +  -  +  -  :      677600 :                 auto coinbase{block->vtx.at(0)};
                   +  - ]
     107         [ +  - ]:      338800 :                 outfile << coinbase->GetHash();
     108         [ +  - ]:      338800 :                 WriteCompactSize(outfile, 1); // number of coins for the hash
     109         [ +  - ]:      338800 :                 WriteCompactSize(outfile, 0); // index of coin
     110         [ +  - ]:      677600 :                 outfile << Coin(coinbase->vout[0], height, /*fCoinBaseIn=*/1);
     111         [ +  - ]:      338800 :                 height++;
     112                 :             :             }
     113                 :             :         }
     114                 :             :         if constexpr (INVALID) {
     115                 :             :             // Append an invalid coin to ensure invalidity. This error will be
     116                 :             :             // detected late in PopulateAndValidateSnapshot, and allows the
     117                 :             :             // INVALID fuzz target to reach more potential code coverage.
     118         [ +  - ]:        1157 :             const auto& coinbase{g_chain->back()->vtx.back()};
     119         [ +  - ]:        1157 :             outfile << coinbase->GetHash();
     120         [ +  - ]:        1157 :             WriteCompactSize(outfile, 1);   // number of coins for the hash
     121         [ +  - ]:        1157 :             WriteCompactSize(outfile, 999); // index of coin
     122         [ +  - ]:        2314 :             outfile << Coin{coinbase->vout[0], /*nHeightIn=*/999, /*fCoinBaseIn=*/0};
     123                 :             :         }
     124                 :           0 :     }
     125                 :             : 
     126                 :        8757 :     const auto ActivateFuzzedSnapshot{[&] {
     127   [ +  -  +  - ]:        5838 :         AutoFile infile{fsbridge::fopen(snapshot_path, "rb")};
     128   [ +  -  +  - ]:        5838 :         auto msg_start = chainman.GetParams().MessageStart();
     129   [ +  -  +  - ]:        5838 :         SnapshotMetadata metadata{msg_start};
     130                 :             :         try {
     131                 :        2668 :             infile >> metadata;
     132   [ -  +  -  + ]:        3170 :         } catch (const std::ios_base::failure&) {
     133                 :             :             return false;
     134                 :             :         }
     135   [ +  -  +  - ]:        2668 :         return !!chainman.ActivateSnapshot(infile, metadata, /*in_memory=*/true);
     136                 :       11676 :     }};
     137                 :             : 
     138         [ +  + ]:        2919 :     if (fuzzed_data_provider.ConsumeBool()) {
     139                 :             :         // Consume the bool, but skip the code for the INVALID fuzz target
     140                 :             :         if constexpr (!INVALID) {
     141         [ +  + ]:       89646 :             for (const auto& block : *g_chain) {
     142                 :       89200 :                 BlockValidationState dummy;
     143         [ +  - ]:       89200 :                 bool processed{chainman.ProcessNewBlockHeaders({{block->GetBlockHeader()}}, true, dummy)};
     144         [ +  - ]:       89200 :                 Assert(processed);
     145   [ +  -  +  -  :      267600 :                 const auto* index{WITH_LOCK(::cs_main, return chainman.m_blockman.LookupBlockIndex(block->GetHash()))};
                   +  - ]
     146         [ +  - ]:       89200 :                 Assert(index);
     147                 :             :             }
     148                 :             :             dirty_chainman = true;
     149                 :             :         }
     150                 :             :     }
     151                 :             : 
     152   [ +  -  +  + ]:        2919 :     if (ActivateFuzzedSnapshot()) {
     153         [ +  - ]:           3 :         LOCK(::cs_main);
     154   [ +  -  +  - ]:           3 :         Assert(!chainman.ActiveChainstate().m_from_snapshot_blockhash->IsNull());
     155   [ +  -  +  -  :           3 :         Assert(*chainman.ActiveChainstate().m_from_snapshot_blockhash ==
                   +  - ]
     156                 :             :                *chainman.SnapshotBlockhash());
     157   [ +  -  +  - ]:           3 :         const auto& coinscache{chainman.ActiveChainstate().CoinsTip()};
     158         [ +  + ]:         603 :         for (const auto& block : *g_chain) {
     159   [ +  -  +  -  :         600 :             Assert(coinscache.HaveCoin(COutPoint{block->vtx.at(0)->GetHash(), 0}));
                   +  - ]
     160   [ +  -  +  - ]:         600 :             const auto* index{chainman.m_blockman.LookupBlockIndex(block->GetHash())};
     161         [ +  - ]:         600 :             Assert(index);
     162         [ +  - ]:         600 :             Assert(index->nTx == 0);
     163   [ +  -  +  - ]:         603 :             if (index->nHeight == chainman.GetSnapshotBaseHeight()) {
     164         [ +  - ]:           3 :                 auto params{chainman.GetParams().AssumeutxoForHeight(index->nHeight)};
     165         [ +  - ]:           3 :                 Assert(params.has_value());
     166         [ +  - ]:           3 :                 Assert(params.value().m_chain_tx_count == index->m_chain_tx_count);
     167                 :             :             } else {
     168         [ +  - ]:         597 :                 Assert(index->m_chain_tx_count == 0);
     169                 :             :             }
     170                 :             :         }
     171   [ +  -  +  - ]:           3 :         Assert(g_chain->size() == coinscache.GetCacheSize());
     172         [ +  - ]:           3 :         dirty_chainman = true;
     173                 :           3 :     } else {
     174   [ +  -  +  - ]:        2916 :         Assert(!chainman.SnapshotBlockhash());
     175   [ +  -  +  - ]:        2916 :         Assert(!chainman.ActiveChainstate().m_from_snapshot_blockhash);
     176                 :             :     }
     177                 :             :     // Snapshot should refuse to load a second time regardless of validity
     178   [ +  -  +  - ]:        2919 :     Assert(!ActivateFuzzedSnapshot());
     179                 :             :     if constexpr (INVALID) {
     180                 :             :         // Activating the snapshot, or any other action that makes the chainman
     181                 :             :         // "dirty" can and must not happen for the INVALID fuzz target
     182         [ +  - ]:        1157 :         Assert(!dirty_chainman);
     183                 :             :     }
     184         [ +  + ]:        2919 :     if (dirty_chainman) {
     185         [ +  - ]:         446 :         setup.m_node.chainman.reset();
     186         [ +  - ]:         446 :         setup.m_make_chainman();
     187         [ +  - ]:         446 :         setup.LoadVerifyActivateChainstate();
     188                 :             :     }
     189                 :        2919 : }
     190                 :             : 
     191                 :             : // There are two fuzz targets:
     192                 :             : //
     193                 :             : // The target 'utxo_snapshot', which allows valid snapshots, but is slow,
     194                 :             : // because it has to reset the chainstate manager on almost all fuzz inputs.
     195                 :             : // Otherwise, a dirty header tree or dirty chainstate could leak from one fuzz
     196                 :             : // input execution into the next, which makes execution non-deterministic.
     197                 :             : //
     198                 :             : // The target 'utxo_snapshot_invalid', which is fast and does not require any
     199                 :             : // expensive state to be reset.
     200         [ +  - ]:        2176 : FUZZ_TARGET(utxo_snapshot /*valid*/, .init = initialize_chain<false>) { utxo_snapshot_fuzz<false>(buffer); }
     201         [ +  - ]:        1571 : FUZZ_TARGET(utxo_snapshot_invalid, .init = initialize_chain<true>) { utxo_snapshot_fuzz<true>(buffer); }
     202                 :             : 
     203                 :             : } // namespace
        

Generated by: LCOV version 2.0-1