LCOV - code coverage report
Current view: top level - src/kernel - coinstats.cpp (source / functions) Hit Total Coverage
Test: test_dash_coverage.info Lines: 86 108 79.6 %
Date: 2026-06-25 07:23:51 Functions: 12 20 60.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          32 : CCoinsStats::CCoinsStats(int block_height, const uint256& block_hash)
      22          16 :     : nHeight(block_height),
      23          32 :       hashBlock(block_hash) {}
      24             : 
      25             : // Database-independent metric indicating the UTXO set size
      26        1611 : uint64_t GetBogoSize(const CScript& script_pub_key)
      27             : {
      28        1611 :     return 32 /* txid */ +
      29             :            4 /* vout index */ +
      30             :            4 /* height + coinbase */ +
      31             :            8 /* amount */ +
      32        1611 :            2 /* scriptPubKey len */ +
      33        1611 :            script_pub_key.size() /* scriptPubKey */;
      34             : }
      35             : 
      36         202 : CDataStream TxOutSer(const COutPoint& outpoint, const Coin& coin) {
      37         202 :     CDataStream ss(SER_DISK, PROTOCOL_VERSION);
      38         202 :     ss << outpoint;
      39         202 :     ss << static_cast<uint32_t>(coin.nHeight * 2 + coin.fCoinBase);
      40         202 :     ss << coin.out;
      41         202 :     return ss;
      42         202 : }
      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        1409 : static void ApplyHash(HashWriter& ss, const uint256& hash, const std::map<uint32_t, Coin>& outputs)
      57             : {
      58        2818 :     for (auto it = outputs.begin(); it != outputs.end(); ++it) {
      59        1409 :         if (it == outputs.begin()) {
      60        1409 :             ss << hash;
      61        1409 :             ss << VARINT(it->second.nHeight * 2 + it->second.fCoinBase ? 1u : 0u);
      62        1409 :         }
      63             : 
      64        1409 :         ss << VARINT(it->first + 1);
      65        1409 :         ss << it->second.out.scriptPubKey;
      66        1409 :         ss << VARINT_MODE(it->second.out.nValue, VarIntMode::NONNEGATIVE_SIGNED);
      67             : 
      68        1409 :         if (it == std::prev(outputs.end())) {
      69        1409 :             ss << VARINT(0u);
      70        1409 :         }
      71        1409 :     }
      72        1409 : }
      73             : 
      74           0 : static void ApplyHash(std::nullptr_t, const uint256& hash, const std::map<uint32_t, Coin>& outputs) {}
      75             : 
      76           0 : static void ApplyHash(MuHash3072& muhash, const uint256& hash, const std::map<uint32_t, Coin>& outputs)
      77             : {
      78           0 :     for (auto it = outputs.begin(); it != outputs.end(); ++it) {
      79           0 :         COutPoint outpoint = COutPoint(hash, it->first);
      80           0 :         Coin coin = it->second;
      81           0 :         muhash.Insert(MakeUCharSpan(TxOutSer(outpoint, coin)));
      82           0 :     }
      83           0 : }
      84             : 
      85        1409 : static void ApplyStats(CCoinsStats& stats, const uint256& hash, const std::map<uint32_t, Coin>& outputs)
      86             : {
      87        1409 :     assert(!outputs.empty());
      88        1409 :     stats.nTransactions++;
      89        2818 :     for (auto it = outputs.begin(); it != outputs.end(); ++it) {
      90        1409 :         stats.nTransactionOutputs++;
      91        1409 :         if (stats.total_amount.has_value()) {
      92        1409 :             stats.total_amount = CheckedAdd(*stats.total_amount, it->second.out.nValue);
      93        1409 :         }
      94        1409 :         stats.nBogoSize += GetBogoSize(it->second.out.scriptPubKey);
      95        1409 :     }
      96        1409 : }
      97             : 
      98             : //! Calculate statistics about the unspent transaction output set
      99             : template <typename T>
     100          12 : static bool ComputeUTXOStats(CCoinsView* view, CCoinsStats& stats, T hash_obj, const std::function<void()>& interruption_point)
     101             : {
     102          12 :     std::unique_ptr<CCoinsViewCursor> pcursor(view->Cursor());
     103          12 :     assert(pcursor);
     104             : 
     105          12 :     PrepareHash(hash_obj, stats);
     106             : 
     107          12 :     uint256 prevkey;
     108          12 :     std::map<uint32_t, Coin> outputs;
     109        1421 :     while (pcursor->Valid()) {
     110        1409 :         if (interruption_point) interruption_point();
     111        1409 :         COutPoint key;
     112        1409 :         Coin coin;
     113        1409 :         if (pcursor->GetKey(key) && pcursor->GetValue(coin)) {
     114        1409 :             if (!outputs.empty() && key.hash != prevkey) {
     115        1397 :                 ApplyStats(stats, prevkey, outputs);
     116        1397 :                 ApplyHash(hash_obj, prevkey, outputs);
     117        1397 :                 outputs.clear();
     118        1397 :             }
     119        1409 :             prevkey = key.hash;
     120        1409 :             outputs[key.n] = std::move(coin);
     121        1409 :             stats.coins_count++;
     122        1409 :         } else {
     123           0 :             return error("%s: unable to read value", __func__);
     124             :         }
     125        1409 :         pcursor->Next();
     126        1409 :     }
     127          12 :     if (!outputs.empty()) {
     128          12 :         ApplyStats(stats, prevkey, outputs);
     129          12 :         ApplyHash(hash_obj, prevkey, outputs);
     130          12 :     }
     131             : 
     132          12 :     FinalizeHash(hash_obj, stats);
     133             : 
     134          12 :     stats.nDiskSize = view->EstimateSize();
     135             : 
     136          12 :     return true;
     137          12 : }
     138             : 
     139          12 : std::optional<CCoinsStats> ComputeUTXOStats(CoinStatsHashType hash_type, CCoinsView* view, node::BlockManager& blockman, const std::function<void()>& interruption_point)
     140             : {
     141          24 :     CBlockIndex* pindex = WITH_LOCK(::cs_main, return blockman.LookupBlockIndex(view->GetBestBlock()));
     142          12 :     CCoinsStats stats{Assert(pindex)->nHeight, pindex->GetBlockHash()};
     143             : 
     144          24 :     bool success = [&]() -> bool {
     145          12 :         switch (hash_type) {
     146             :         case(CoinStatsHashType::HASH_SERIALIZED): {
     147          12 :             HashWriter ss{};
     148          12 :             return ComputeUTXOStats(view, stats, ss, interruption_point);
     149             :         }
     150             :         case(CoinStatsHashType::MUHASH): {
     151           0 :             MuHash3072 muhash;
     152           0 :             return ComputeUTXOStats(view, stats, muhash, interruption_point);
     153             :         }
     154             :         case(CoinStatsHashType::NONE): {
     155           0 :             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          12 :     }();
     160             : 
     161          12 :     if (!success) {
     162           0 :         return std::nullopt;
     163             :     }
     164          12 :     return stats;
     165          12 : }
     166             : 
     167             : // The legacy hash serializes the hashBlock
     168          12 : static void PrepareHash(HashWriter& ss, const CCoinsStats& stats)
     169             : {
     170          12 :     ss << stats.hashBlock;
     171          12 : }
     172             : // MuHash does not need the prepare step
     173           0 : static void PrepareHash(MuHash3072& muhash, CCoinsStats& stats) {}
     174           0 : static void PrepareHash(std::nullptr_t, CCoinsStats& stats) {}
     175             : 
     176          12 : static void FinalizeHash(HashWriter& ss, CCoinsStats& stats)
     177             : {
     178          12 :     stats.hashSerialized = ss.GetHash();
     179          12 : }
     180           0 : static void FinalizeHash(MuHash3072& muhash, CCoinsStats& stats)
     181             : {
     182           0 :     uint256 out;
     183           0 :     muhash.Finalize(out);
     184           0 :     stats.hashSerialized = out;
     185           0 : }
     186           0 : static void FinalizeHash(std::nullptr_t, CCoinsStats& stats) {}
     187             : 
     188             : } // namespace kernel

Generated by: LCOV version 1.16