LCOV - code coverage report
Current view: top level - src/test/fuzz - utxo_snapshot.cpp (source / functions) Coverage Total Hit
Test: fuzz_coverage.info Lines: 84.4 % 109 92
Test Date: 2024-09-01 05:20:30 Functions: 75.0 % 12 9
Branches: 40.3 % 380 153

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

Generated by: LCOV version 2.0-1