LCOV - code coverage report
Current view: top level - src/kernel - coinstats.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 105 108 97.2 %
Date: 2026-06-25 07:23:43 Functions: 20 20 100.0 %

          Line data    Source code
       1             : // Copyright (c) 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 <kernel/coinstats.h>
       6             : 
       7             : #include <coins.h>
       8             : #include <crypto/muhash.h>
       9             : #include <hash.h>
      10             : #include <serialize.h>
      11             : #include <uint256.h>
      12             : #include <util/check.h>
      13             : #include <util/overflow.h>
      14             : #include <util/system.h>
      15             : #include <validation.h>
      16             : 
      17             : #include <map>
      18             : 
      19             : namespace kernel {
      20             : 
      21         406 : CCoinsStats::CCoinsStats(int block_height, const uint256& block_hash)
      22         203 :     : nHeight(block_height),
      23         406 :       hashBlock(block_hash) {}
      24             : 
      25             : // Database-independent metric indicating the UTXO set size
      26       27507 : uint64_t GetBogoSize(const CScript& script_pub_key)
      27             : {
      28       27507 :     return 32 /* txid */ +
      29             :            4 /* vout index */ +
      30             :            4 /* height + coinbase */ +
      31             :            8 /* amount */ +
      32       27507 :            2 /* scriptPubKey len */ +
      33       27507 :            script_pub_key.size() /* scriptPubKey */;
      34             : }
      35             : 
      36        3067 : CDataStream TxOutSer(const COutPoint& outpoint, const Coin& coin) {
      37        3067 :     CDataStream ss(SER_DISK, PROTOCOL_VERSION);
      38        3067 :     ss << outpoint;
      39        3067 :     ss << static_cast<uint32_t>(coin.nHeight * 2 + coin.fCoinBase);
      40        3067 :     ss << coin.out;
      41        3067 :     return ss;
      42        3067 : }
      43             : 
      44             : //! Warning: be very careful when changing this! assumeutxo and UTXO snapshot
      45             : //! validation commitments are reliant on the hash constructed by this
      46             : //! function.
      47             : //!
      48             : //! If the construction of this hash is changed, it will invalidate
      49             : //! existing UTXO snapshots. This will not result in any kind of consensus
      50             : //! failure, but it will force clients that were expecting to make use of
      51             : //! assumeutxo to do traditional IBD instead.
      52             : //!
      53             : //! It is also possible, though very unlikely, that a change in this
      54             : //! construction could cause a previously invalid (and potentially malicious)
      55             : //! UTXO snapshot to be considered valid.
      56       15761 : static void ApplyHash(HashWriter& ss, const uint256& hash, const std::map<uint32_t, Coin>& outputs)
      57             : {
      58       39197 :     for (auto it = outputs.begin(); it != outputs.end(); ++it) {
      59       23436 :         if (it == outputs.begin()) {
      60       15761 :             ss << hash;
      61       15761 :             ss << VARINT(it->second.nHeight * 2 + it->second.fCoinBase ? 1u : 0u);
      62       15761 :         }
      63             : 
      64       23436 :         ss << VARINT(it->first + 1);
      65       23436 :         ss << it->second.out.scriptPubKey;
      66       23436 :         ss << VARINT_MODE(it->second.out.nValue, VarIntMode::NONNEGATIVE_SIGNED);
      67             : 
      68       23436 :         if (it == std::prev(outputs.end())) {
      69       15761 :             ss << VARINT(0u);
      70       15761 :         }
      71       23436 :     }
      72       15761 : }
      73             : 
      74        1004 : static void ApplyHash(std::nullptr_t, const uint256& hash, const std::map<uint32_t, Coin>& outputs) {}
      75             : 
      76        1868 : static void ApplyHash(MuHash3072& muhash, const uint256& hash, const std::map<uint32_t, Coin>& outputs)
      77             : {
      78        3742 :     for (auto it = outputs.begin(); it != outputs.end(); ++it) {
      79        1874 :         COutPoint outpoint = COutPoint(hash, it->first);
      80        1874 :         Coin coin = it->second;
      81        1874 :         muhash.Insert(MakeUCharSpan(TxOutSer(outpoint, coin)));
      82        1874 :     }
      83        1868 : }
      84             : 
      85       18633 : static void ApplyStats(CCoinsStats& stats, const uint256& hash, const std::map<uint32_t, Coin>& outputs)
      86             : {
      87       18633 :     assert(!outputs.empty());
      88       18633 :     stats.nTransactions++;
      89       44947 :     for (auto it = outputs.begin(); it != outputs.end(); ++it) {
      90       26314 :         stats.nTransactionOutputs++;
      91       26314 :         if (stats.total_amount.has_value()) {
      92       26314 :             stats.total_amount = CheckedAdd(*stats.total_amount, it->second.out.nValue);
      93       26314 :         }
      94       26314 :         stats.nBogoSize += GetBogoSize(it->second.out.scriptPubKey);
      95       26314 :     }
      96       18633 : }
      97             : 
      98             : //! Calculate statistics about the unspent transaction output set
      99             : template <typename T>
     100         111 : static bool ComputeUTXOStats(CCoinsView* view, CCoinsStats& stats, T hash_obj, const std::function<void()>& interruption_point)
     101             : {
     102         111 :     std::unique_ptr<CCoinsViewCursor> pcursor(view->Cursor());
     103         111 :     assert(pcursor);
     104             : 
     105         111 :     PrepareHash(hash_obj, stats);
     106             : 
     107         111 :     uint256 prevkey;
     108         111 :     std::map<uint32_t, Coin> outputs;
     109       26425 :     while (pcursor->Valid()) {
     110       26314 :         if (interruption_point) interruption_point();
     111       26314 :         COutPoint key;
     112       26314 :         Coin coin;
     113       26314 :         if (pcursor->GetKey(key) && pcursor->GetValue(coin)) {
     114       26314 :             if (!outputs.empty() && key.hash != prevkey) {
     115       18526 :                 ApplyStats(stats, prevkey, outputs);
     116       18526 :                 ApplyHash(hash_obj, prevkey, outputs);
     117       18526 :                 outputs.clear();
     118       18526 :             }
     119       26314 :             prevkey = key.hash;
     120       26314 :             outputs[key.n] = std::move(coin);
     121       26314 :             stats.coins_count++;
     122       26314 :         } else {
     123           0 :             return error("%s: unable to read value", __func__);
     124             :         }
     125       26314 :         pcursor->Next();
     126       26314 :     }
     127         111 :     if (!outputs.empty()) {
     128         107 :         ApplyStats(stats, prevkey, outputs);
     129         107 :         ApplyHash(hash_obj, prevkey, outputs);
     130         107 :     }
     131             : 
     132         111 :     FinalizeHash(hash_obj, stats);
     133             : 
     134         111 :     stats.nDiskSize = view->EstimateSize();
     135             : 
     136         111 :     return true;
     137         111 : }
     138             : 
     139         111 : std::optional<CCoinsStats> ComputeUTXOStats(CoinStatsHashType hash_type, CCoinsView* view, node::BlockManager& blockman, const std::function<void()>& interruption_point)
     140             : {
     141         222 :     CBlockIndex* pindex = WITH_LOCK(::cs_main, return blockman.LookupBlockIndex(view->GetBestBlock()));
     142         111 :     CCoinsStats stats{Assert(pindex)->nHeight, pindex->GetBlockHash()};
     143             : 
     144         222 :     bool success = [&]() -> bool {
     145         111 :         switch (hash_type) {
     146             :         case(CoinStatsHashType::HASH_SERIALIZED): {
     147          91 :             HashWriter ss{};
     148          91 :             return ComputeUTXOStats(view, stats, ss, interruption_point);
     149             :         }
     150             :         case(CoinStatsHashType::MUHASH): {
     151          14 :             MuHash3072 muhash;
     152          14 :             return ComputeUTXOStats(view, stats, muhash, interruption_point);
     153             :         }
     154             :         case(CoinStatsHashType::NONE): {
     155           6 :             return ComputeUTXOStats(view, stats, nullptr, interruption_point);
     156             :         }
     157             :         } // no default case, so the compiler can warn about missing cases
     158           0 :         assert(false);
     159         111 :     }();
     160             : 
     161         111 :     if (!success) {
     162           0 :         return std::nullopt;
     163             :     }
     164         111 :     return stats;
     165         111 : }
     166             : 
     167             : // The legacy hash serializes the hashBlock
     168          91 : static void PrepareHash(HashWriter& ss, const CCoinsStats& stats)
     169             : {
     170          91 :     ss << stats.hashBlock;
     171          91 : }
     172             : // MuHash does not need the prepare step
     173          14 : static void PrepareHash(MuHash3072& muhash, CCoinsStats& stats) {}
     174           6 : static void PrepareHash(std::nullptr_t, CCoinsStats& stats) {}
     175             : 
     176          91 : static void FinalizeHash(HashWriter& ss, CCoinsStats& stats)
     177             : {
     178          91 :     stats.hashSerialized = ss.GetHash();
     179          91 : }
     180          14 : static void FinalizeHash(MuHash3072& muhash, CCoinsStats& stats)
     181             : {
     182          14 :     uint256 out;
     183          14 :     muhash.Finalize(out);
     184          14 :     stats.hashSerialized = out;
     185          14 : }
     186           6 : static void FinalizeHash(std::nullptr_t, CCoinsStats& stats) {}
     187             : 
     188             : } // namespace kernel

Generated by: LCOV version 1.16