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