LCOV - code coverage report
Current view: top level - src/kernel - coinstats.cpp (source / functions) Hit Total Coverage
Test: fuzz_coverage.info Lines: 49 119 41.2 %
Date: 2023-09-26 12:08:55 Functions: 10 22 45.5 %

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

Generated by: LCOV version 1.14