LCOV - code coverage report
Current view: top level - src/test/fuzz - utxo_total_supply.cpp (source / functions) Coverage Total Hit
Test: fuzz_coverage.info Lines: 98.0 % 98 96
Test Date: 2026-03-28 03:58:45 Functions: 100.0 % 10 10
Branches: 55.2 % 154 85

             Branch data     Line data    Source code
       1                 :             : // Copyright (c) 2020-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 <chainparams.h>
       6                 :             : #include <consensus/consensus.h>
       7                 :             : #include <consensus/merkle.h>
       8                 :             : #include <kernel/coinstats.h>
       9                 :             : #include <node/miner.h>
      10                 :             : #include <script/interpreter.h>
      11                 :             : #include <streams.h>
      12                 :             : #include <test/fuzz/FuzzedDataProvider.h>
      13                 :             : #include <test/fuzz/fuzz.h>
      14                 :             : #include <test/fuzz/util.h>
      15                 :             : #include <test/util/mining.h>
      16                 :             : #include <test/util/setup_common.h>
      17                 :             : #include <test/util/time.h>
      18                 :             : #include <util/chaintype.h>
      19                 :             : #include <util/time.h>
      20                 :             : #include <validation.h>
      21                 :             : 
      22                 :             : using node::BlockAssembler;
      23                 :             : 
      24         [ +  - ]:        1264 : FUZZ_TARGET(utxo_total_supply)
      25                 :             : {
      26                 :         806 :     SeedRandomStateForTest(SeedRand::ZEROS);
      27                 :         806 :     FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
      28                 :         806 :     NodeClockContext clock_ctx{ConsumeTime(fuzzed_data_provider, /*min=*/1296688602)}; // regtest genesis block timestamp
      29                 :             :     /** The testing setup that creates a chainman only (no chainstate) */
      30                 :         806 :     ChainTestingSetup test_setup{
      31                 :             :         ChainType::REGTEST,
      32                 :             :         {
      33                 :             :             .extra_args = {
      34                 :             :                 "-testactivationheight=bip34@2",
      35                 :             :             },
      36                 :             :         },
      37   [ +  -  +  - ]:        1612 :     };
      38                 :             :     // Create chainstate
      39         [ +  - ]:         806 :     test_setup.LoadVerifyActivateChainstate();
      40                 :         806 :     auto& node{test_setup.m_node};
      41   [ -  +  +  - ]:         806 :     auto& chainman{*Assert(test_setup.m_node.chainman)};
      42                 :             : 
      43                 :       87308 :     const auto ActiveHeight = [&]() {
      44                 :       86502 :         LOCK(chainman.GetMutex());
      45   [ +  -  +  - ]:       86502 :         return chainman.ActiveHeight();
      46                 :       87308 :     };
      47         [ +  - ]:         806 :     BlockAssembler::Options options;
      48         [ +  - ]:         806 :     options.coinbase_output_script = CScript() << OP_FALSE;
      49                 :         806 :     options.include_dummy_extranonce = true;
      50                 :       60087 :     const auto PrepareNextBlock = [&]() {
      51                 :             :         // Use OP_FALSE to avoid BIP30 check from hitting early
      52                 :       59281 :         auto block = PrepareBlock(node, options);
      53                 :             :         // Replace OP_FALSE with OP_TRUE
      54                 :       59281 :         {
      55         [ +  - ]:       59281 :             CMutableTransaction tx{*block->vtx.back()};
      56                 :       59281 :             tx.nLockTime = 0; // Use the same nLockTime for all as we want to duplicate one of them.
      57   [ +  -  +  - ]:       59281 :             tx.vout.at(0).scriptPubKey = CScript{} << OP_TRUE;
      58   [ +  -  -  + ]:      118562 :             block->vtx.back() = MakeTransactionRef(tx);
      59                 :           0 :         }
      60                 :       59281 :         return block;
      61                 :         806 :     };
      62                 :             : 
      63                 :             :     /** The block template this fuzzer is working on */
      64         [ +  - ]:         806 :     auto current_block = PrepareNextBlock();
      65                 :             :     /** Append-only set of tx outpoints, entries are not removed when spent */
      66                 :         806 :     std::vector<std::pair<COutPoint, CTxOut>> txos;
      67                 :             :     /** The utxo stats at the chain tip */
      68                 :         806 :     kernel::CCoinsStats utxo_stats;
      69                 :             :     /** The total amount of coins in the utxo set */
      70                 :         806 :     CAmount circulation{0};
      71                 :             : 
      72                 :             : 
      73                 :             :     // Store the tx out in the txo map
      74                 :       84106 :     const auto StoreLastTxo = [&]() {
      75                 :             :         // get last tx
      76         [ -  + ]:       83300 :         const CTransaction& tx = *current_block->vtx.back();
      77                 :             :         // get last out
      78         [ -  + ]:       83300 :         const uint32_t i = tx.vout.size() - 1;
      79                 :             :         // store it
      80                 :       83300 :         txos.emplace_back(COutPoint{tx.GetHash(), i}, tx.vout.at(i));
      81   [ -  +  +  +  :      149643 :         if (current_block->vtx.size() == 1 && tx.vout.at(i).scriptPubKey[0] == OP_RETURN) {
             +  +  +  + ]
      82                 :             :             // also store coinbase
      83         [ -  + ]:       63337 :             const uint32_t i = tx.vout.size() - 2;
      84                 :       63337 :             txos.emplace_back(COutPoint{tx.GetHash(), i}, tx.vout.at(i));
      85                 :             :         }
      86                 :       84106 :     };
      87                 :       24825 :     const auto AppendRandomTxo = [&](CMutableTransaction& tx) {
      88         [ -  + ]:       24019 :         const auto& txo = txos.at(fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, txos.size() - 1));
      89                 :       24019 :         tx.vin.emplace_back(txo.first);
      90                 :       24019 :         tx.vout.emplace_back(txo.second.nValue, txo.second.scriptPubKey); // "Forward" coin with no fee
      91                 :       24825 :     };
      92                 :       59281 :     const auto UpdateUtxoStats = [&](bool wipe_cache) {
      93                 :       58475 :         LOCK(chainman.GetMutex());
      94   [ +  -  +  - ]:       58475 :         chainman.ActiveChainstate().ForceFlushStateToDisk(wipe_cache);
      95                 :       58475 :         utxo_stats = std::move(
      96   [ +  -  +  -  :      116950 :             *Assert(kernel::ComputeUTXOStats(kernel::CoinStatsHashType::NONE, &chainman.ActiveChainstate().CoinsDB(), chainman.m_blockman, {})));
                   +  - ]
      97                 :             :         // Check that miner can't print more money than they are allowed to
      98   [ +  -  +  - ]:      116950 :         assert(circulation == utxo_stats.total_amount);
      99                 :       59281 :     };
     100                 :             : 
     101                 :             : 
     102                 :             :     // Update internal state to chain tip
     103         [ +  - ]:         806 :     StoreLastTxo();
     104         [ +  - ]:         806 :     UpdateUtxoStats(/*wipe_cache=*/fuzzed_data_provider.ConsumeBool());
     105   [ +  -  -  + ]:         806 :     assert(ActiveHeight() == 0);
     106                 :             :     // Get at which height we duplicate the coinbase
     107                 :             :     // Assuming that the fuzzer will mine relatively short chains (less than 200 blocks), we want the duplicate coinbase to be not too high.
     108                 :             :     // Up to 300 seems reasonable.
     109                 :         806 :     int64_t duplicate_coinbase_height = fuzzed_data_provider.ConsumeIntegralInRange(0, 300);
     110                 :             :     // Always pad with OP_0 at the end to avoid bad-cb-length error
     111   [ +  -  +  - ]:         806 :     const CScript duplicate_coinbase_script = CScript() << duplicate_coinbase_height << OP_0;
     112                 :             :     // Mine the first block with this duplicate
     113   [ +  -  -  + ]:        1612 :     current_block = PrepareNextBlock();
     114         [ +  - ]:         806 :     StoreLastTxo();
     115                 :             : 
     116                 :         806 :     {
     117                 :             :         // Create duplicate (CScript should match exact format as in CreateNewBlock)
     118         [ +  - ]:         806 :         CMutableTransaction tx{*current_block->vtx.front()};
     119         [ +  - ]:         806 :         tx.vin.at(0).scriptSig = duplicate_coinbase_script;
     120                 :             : 
     121                 :             :         // Mine block and create next block template
     122   [ +  -  -  + ]:        1612 :         current_block->vtx.front() = MakeTransactionRef(tx);
     123                 :           0 :     }
     124         [ +  - ]:         806 :     current_block->hashMerkleRoot = BlockMerkleRoot(*current_block);
     125   [ +  -  -  + ]:         806 :     assert(!MineBlock(node, current_block).IsNull());
     126   [ +  -  +  -  :         806 :     circulation += GetBlockSubsidy(ActiveHeight(), Params().GetConsensus());
                   +  - ]
     127                 :             : 
     128   [ +  -  -  + ]:         806 :     assert(ActiveHeight() == 1);
     129         [ +  - ]:         806 :     UpdateUtxoStats(/*wipe_cache=*/fuzzed_data_provider.ConsumeBool());
     130   [ +  -  -  + ]:        1612 :     current_block = PrepareNextBlock();
     131         [ +  - ]:         806 :     StoreLastTxo();
     132                 :             : 
     133                 :             :     // Limit to avoid timeout, but enough to cover duplicate_coinbase_height
     134                 :             :     // and CVE-2018-17144.
     135   [ +  +  +  + ]:       81688 :     LIMITED_WHILE(fuzzed_data_provider.remaining_bytes(), 2'00)
     136                 :             :     {
     137         [ +  - ]:       80882 :         CallOneOf(
     138                 :             :             fuzzed_data_provider,
     139                 :       11041 :             [&] {
     140                 :             :                 // Append an input-output pair to the last tx in the current block
     141                 :       11041 :                 CMutableTransaction tx{*current_block->vtx.back()};
     142         [ +  - ]:       11041 :                 AppendRandomTxo(tx);
     143   [ +  -  -  + ]:       22082 :                 current_block->vtx.back() = MakeTransactionRef(tx);
     144         [ +  - ]:       11041 :                 StoreLastTxo();
     145                 :       11041 :             },
     146                 :       12978 :             [&] {
     147                 :             :                 // Append a tx to the list of txs in the current block
     148                 :       12978 :                 CMutableTransaction tx{};
     149         [ +  - ]:       12978 :                 AppendRandomTxo(tx);
     150   [ +  -  +  -  :       25956 :                 current_block->vtx.push_back(MakeTransactionRef(tx));
                   -  + ]
     151         [ +  - ]:       12978 :                 StoreLastTxo();
     152                 :       12978 :             },
     153                 :       56863 :             [&] {
     154                 :             :                 // Append the current block to the active chain
     155                 :       56863 :                 node::RegenerateCommitments(*current_block, chainman);
     156                 :       56863 :                 const bool was_valid = !MineBlock(node, current_block).IsNull();
     157                 :             : 
     158                 :       56863 :                 const uint256 prev_hash_serialized{utxo_stats.hashSerialized};
     159         [ +  + ]:       56863 :                 if (was_valid) {
     160         [ +  + ]:       42042 :                     if (duplicate_coinbase_height == ActiveHeight()) {
     161                 :             :                         // we mined the duplicate coinbase
     162         [ -  + ]:          10 :                         assert(current_block->vtx.at(0)->vin.at(0).scriptSig == duplicate_coinbase_script);
     163                 :             :                     }
     164                 :             : 
     165                 :       42042 :                     circulation += GetBlockSubsidy(ActiveHeight(), Params().GetConsensus());
     166                 :             :                 }
     167                 :             : 
     168                 :       56863 :                 UpdateUtxoStats(/*wipe_cache=*/fuzzed_data_provider.ConsumeBool());
     169                 :             : 
     170         [ +  + ]:       56863 :                 if (!was_valid) {
     171                 :             :                     // utxo stats must not change
     172         [ -  + ]:       14821 :                     assert(prev_hash_serialized == utxo_stats.hashSerialized);
     173                 :             :                 }
     174                 :             : 
     175   [ -  +  -  + ]:       56863 :                 current_block = PrepareNextBlock();
     176                 :       56863 :                 StoreLastTxo();
     177                 :       56863 :             });
     178                 :             :     }
     179   [ +  -  +  - ]:        2418 : }
        

Generated by: LCOV version 2.0-1