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