Branch data Line data Source code
# 1 : : // Copyright (c) 2020-2021 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 <chainparams.h>
# 6 : : #include <coins.h>
# 7 : : #include <crypto/muhash.h>
# 8 : : #include <index/coinstatsindex.h>
# 9 : : #include <node/blockstorage.h>
# 10 : : #include <serialize.h>
# 11 : : #include <txdb.h>
# 12 : : #include <undo.h>
# 13 : : #include <validation.h>
# 14 : :
# 15 : : using node::CCoinsStats;
# 16 : : using node::GetBogoSize;
# 17 : : using node::ReadBlockFromDisk;
# 18 : : using node::TxOutSer;
# 19 : : using node::UndoReadFromDisk;
# 20 : :
# 21 : : static constexpr uint8_t DB_BLOCK_HASH{'s'};
# 22 : : static constexpr uint8_t DB_BLOCK_HEIGHT{'t'};
# 23 : : static constexpr uint8_t DB_MUHASH{'M'};
# 24 : :
# 25 : : namespace {
# 26 : :
# 27 : : struct DBVal {
# 28 : : uint256 muhash;
# 29 : : uint64_t transaction_output_count;
# 30 : : uint64_t bogo_size;
# 31 : : CAmount total_amount;
# 32 : : CAmount total_subsidy;
# 33 : : CAmount total_unspendable_amount;
# 34 : : CAmount total_prevout_spent_amount;
# 35 : : CAmount total_new_outputs_ex_coinbase_amount;
# 36 : : CAmount total_coinbase_amount;
# 37 : : CAmount total_unspendables_genesis_block;
# 38 : : CAmount total_unspendables_bip30;
# 39 : : CAmount total_unspendables_scripts;
# 40 : : CAmount total_unspendables_unclaimed_rewards;
# 41 : :
# 42 : : SERIALIZE_METHODS(DBVal, obj)
# 43 : 1610 : {
# 44 : 1610 : READWRITE(obj.muhash);
# 45 : 1610 : READWRITE(obj.transaction_output_count);
# 46 : 1610 : READWRITE(obj.bogo_size);
# 47 : 1610 : READWRITE(obj.total_amount);
# 48 : 1610 : READWRITE(obj.total_subsidy);
# 49 : 1610 : READWRITE(obj.total_unspendable_amount);
# 50 : 1610 : READWRITE(obj.total_prevout_spent_amount);
# 51 : 1610 : READWRITE(obj.total_new_outputs_ex_coinbase_amount);
# 52 : 1610 : READWRITE(obj.total_coinbase_amount);
# 53 : 1610 : READWRITE(obj.total_unspendables_genesis_block);
# 54 : 1610 : READWRITE(obj.total_unspendables_bip30);
# 55 : 1610 : READWRITE(obj.total_unspendables_scripts);
# 56 : 1610 : READWRITE(obj.total_unspendables_unclaimed_rewards);
# 57 : 1610 : }
# 58 : : };
# 59 : :
# 60 : : struct DBHeightKey {
# 61 : : int height;
# 62 : :
# 63 : 1574 : explicit DBHeightKey(int height_in) : height(height_in) {}
# 64 : :
# 65 : : template <typename Stream>
# 66 : : void Serialize(Stream& s) const
# 67 : 1574 : {
# 68 : 1574 : ser_writedata8(s, DB_BLOCK_HEIGHT);
# 69 : 1574 : ser_writedata32be(s, height);
# 70 : 1574 : }
# 71 : :
# 72 : : template <typename Stream>
# 73 : : void Unserialize(Stream& s)
# 74 : 19 : {
# 75 : 19 : const uint8_t prefix{ser_readdata8(s)};
# 76 [ - + ]: 19 : if (prefix != DB_BLOCK_HEIGHT) {
# 77 : 0 : throw std::ios_base::failure("Invalid format for coinstatsindex DB height key");
# 78 : 0 : }
# 79 : 19 : height = ser_readdata32be(s);
# 80 : 19 : }
# 81 : : };
# 82 : :
# 83 : : struct DBHashKey {
# 84 : : uint256 block_hash;
# 85 : :
# 86 : 21 : explicit DBHashKey(const uint256& hash_in) : block_hash(hash_in) {}
# 87 : :
# 88 : : SERIALIZE_METHODS(DBHashKey, obj)
# 89 : 21 : {
# 90 : 21 : uint8_t prefix{DB_BLOCK_HASH};
# 91 : 21 : READWRITE(prefix);
# 92 [ - + ]: 21 : if (prefix != DB_BLOCK_HASH) {
# 93 : 0 : throw std::ios_base::failure("Invalid format for coinstatsindex DB hash key");
# 94 : 0 : }
# 95 : :
# 96 : 21 : READWRITE(obj.block_hash);
# 97 : 21 : }
# 98 : : };
# 99 : :
# 100 : : }; // namespace
# 101 : :
# 102 : : std::unique_ptr<CoinStatsIndex> g_coin_stats_index;
# 103 : :
# 104 : : CoinStatsIndex::CoinStatsIndex(size_t n_cache_size, bool f_memory, bool f_wipe)
# 105 : 19 : {
# 106 : 19 : fs::path path{gArgs.GetDataDirNet() / "indexes" / "coinstats"};
# 107 : 19 : fs::create_directories(path);
# 108 : :
# 109 : 19 : m_db = std::make_unique<CoinStatsIndex::DB>(path / "db", n_cache_size, f_memory, f_wipe);
# 110 : 19 : }
# 111 : :
# 112 : : bool CoinStatsIndex::WriteBlock(const CBlock& block, const CBlockIndex* pindex)
# 113 : 750 : {
# 114 : 750 : CBlockUndo block_undo;
# 115 : 750 : const CAmount block_subsidy{GetBlockSubsidy(pindex->nHeight, Params().GetConsensus())};
# 116 : 750 : m_total_subsidy += block_subsidy;
# 117 : :
# 118 : : // Ignore genesis block
# 119 [ + + ]: 750 : if (pindex->nHeight > 0) {
# 120 [ - + ]: 743 : if (!UndoReadFromDisk(block_undo, pindex)) {
# 121 : 0 : return false;
# 122 : 0 : }
# 123 : :
# 124 : 743 : std::pair<uint256, DBVal> read_out;
# 125 [ - + ]: 743 : if (!m_db->Read(DBHeightKey(pindex->nHeight - 1), read_out)) {
# 126 : 0 : return false;
# 127 : 0 : }
# 128 : :
# 129 : 743 : uint256 expected_block_hash{pindex->pprev->GetBlockHash()};
# 130 [ - + ]: 743 : if (read_out.first != expected_block_hash) {
# 131 : 0 : LogPrintf("WARNING: previous block header belongs to unexpected block %s; expected %s\n",
# 132 : 0 : read_out.first.ToString(), expected_block_hash.ToString());
# 133 : :
# 134 [ # # ]: 0 : if (!m_db->Read(DBHashKey(expected_block_hash), read_out)) {
# 135 : 0 : return error("%s: previous block header not found; expected %s",
# 136 : 0 : __func__, expected_block_hash.ToString());
# 137 : 0 : }
# 138 : 0 : }
# 139 : :
# 140 : : // TODO: Deduplicate BIP30 related code
# 141 [ - + ][ # # ]: 743 : bool is_bip30_block{(pindex->nHeight == 91722 && pindex->GetBlockHash() == uint256S("0x00000000000271a2dc26e7667f8419f2e15416dc6955e5a6c6cdf3f2574dd08e")) ||
# 142 [ - + ][ # # ]: 743 : (pindex->nHeight == 91812 && pindex->GetBlockHash() == uint256S("0x00000000000af0aed4792b1acee3d966af36cf5def14935db8de83d6f9306f2f"))};
# 143 : :
# 144 : : // Add the new utxos created from the block
# 145 [ + + ]: 1492 : for (size_t i = 0; i < block.vtx.size(); ++i) {
# 146 : 749 : const auto& tx{block.vtx.at(i)};
# 147 : :
# 148 : : // Skip duplicate txid coinbase transactions (BIP30).
# 149 [ - + ][ # # ]: 749 : if (is_bip30_block && tx->IsCoinBase()) {
# 150 : 0 : m_total_unspendable_amount += block_subsidy;
# 151 : 0 : m_total_unspendables_bip30 += block_subsidy;
# 152 : 0 : continue;
# 153 : 0 : }
# 154 : :
# 155 [ + + ]: 2243 : for (uint32_t j = 0; j < tx->vout.size(); ++j) {
# 156 : 1494 : const CTxOut& out{tx->vout[j]};
# 157 : 1494 : Coin coin{out, pindex->nHeight, tx->IsCoinBase()};
# 158 : 1494 : COutPoint outpoint{tx->GetHash(), j};
# 159 : :
# 160 : : // Skip unspendable coins
# 161 [ + + ]: 1494 : if (coin.out.scriptPubKey.IsUnspendable()) {
# 162 : 743 : m_total_unspendable_amount += coin.out.nValue;
# 163 : 743 : m_total_unspendables_scripts += coin.out.nValue;
# 164 : 743 : continue;
# 165 : 743 : }
# 166 : :
# 167 : 751 : m_muhash.Insert(MakeUCharSpan(TxOutSer(outpoint, coin)));
# 168 : :
# 169 [ + + ]: 751 : if (tx->IsCoinBase()) {
# 170 : 745 : m_total_coinbase_amount += coin.out.nValue;
# 171 : 745 : } else {
# 172 : 6 : m_total_new_outputs_ex_coinbase_amount += coin.out.nValue;
# 173 : 6 : }
# 174 : :
# 175 : 751 : ++m_transaction_output_count;
# 176 : 751 : m_total_amount += coin.out.nValue;
# 177 : 751 : m_bogo_size += GetBogoSize(coin.out.scriptPubKey);
# 178 : 751 : }
# 179 : :
# 180 : : // The coinbase tx has no undo data since no former output is spent
# 181 [ + + ]: 749 : if (!tx->IsCoinBase()) {
# 182 : 6 : const auto& tx_undo{block_undo.vtxundo.at(i - 1)};
# 183 : :
# 184 [ + + ]: 12 : for (size_t j = 0; j < tx_undo.vprevout.size(); ++j) {
# 185 : 6 : Coin coin{tx_undo.vprevout[j]};
# 186 : 6 : COutPoint outpoint{tx->vin[j].prevout.hash, tx->vin[j].prevout.n};
# 187 : :
# 188 : 6 : m_muhash.Remove(MakeUCharSpan(TxOutSer(outpoint, coin)));
# 189 : :
# 190 : 6 : m_total_prevout_spent_amount += coin.out.nValue;
# 191 : :
# 192 : 6 : --m_transaction_output_count;
# 193 : 6 : m_total_amount -= coin.out.nValue;
# 194 : 6 : m_bogo_size -= GetBogoSize(coin.out.scriptPubKey);
# 195 : 6 : }
# 196 : 6 : }
# 197 : 749 : }
# 198 : 743 : } else {
# 199 : : // genesis block
# 200 : 7 : m_total_unspendable_amount += block_subsidy;
# 201 : 7 : m_total_unspendables_genesis_block += block_subsidy;
# 202 : 7 : }
# 203 : :
# 204 : : // If spent prevouts + block subsidy are still a higher amount than
# 205 : : // new outputs + coinbase + current unspendable amount this means
# 206 : : // the miner did not claim the full block reward. Unclaimed block
# 207 : : // rewards are also unspendable.
# 208 : 750 : const CAmount unclaimed_rewards{(m_total_prevout_spent_amount + m_total_subsidy) - (m_total_new_outputs_ex_coinbase_amount + m_total_coinbase_amount + m_total_unspendable_amount)};
# 209 : 750 : m_total_unspendable_amount += unclaimed_rewards;
# 210 : 750 : m_total_unspendables_unclaimed_rewards += unclaimed_rewards;
# 211 : :
# 212 : 750 : std::pair<uint256, DBVal> value;
# 213 : 750 : value.first = pindex->GetBlockHash();
# 214 : 750 : value.second.transaction_output_count = m_transaction_output_count;
# 215 : 750 : value.second.bogo_size = m_bogo_size;
# 216 : 750 : value.second.total_amount = m_total_amount;
# 217 : 750 : value.second.total_subsidy = m_total_subsidy;
# 218 : 750 : value.second.total_unspendable_amount = m_total_unspendable_amount;
# 219 : 750 : value.second.total_prevout_spent_amount = m_total_prevout_spent_amount;
# 220 : 750 : value.second.total_new_outputs_ex_coinbase_amount = m_total_new_outputs_ex_coinbase_amount;
# 221 : 750 : value.second.total_coinbase_amount = m_total_coinbase_amount;
# 222 : 750 : value.second.total_unspendables_genesis_block = m_total_unspendables_genesis_block;
# 223 : 750 : value.second.total_unspendables_bip30 = m_total_unspendables_bip30;
# 224 : 750 : value.second.total_unspendables_scripts = m_total_unspendables_scripts;
# 225 : 750 : value.second.total_unspendables_unclaimed_rewards = m_total_unspendables_unclaimed_rewards;
# 226 : :
# 227 : 750 : uint256 out;
# 228 : 750 : m_muhash.Finalize(out);
# 229 : 750 : value.second.muhash = out;
# 230 : :
# 231 : : // Intentionally do not update DB_MUHASH here so it stays in sync with
# 232 : : // DB_BEST_BLOCK, and the index is not corrupted if there is an unclean shutdown.
# 233 : 750 : return m_db->Write(DBHeightKey(pindex->nHeight), value);
# 234 : 750 : }
# 235 : :
# 236 : : static bool CopyHeightIndexToHashIndex(CDBIterator& db_it, CDBBatch& batch,
# 237 : : const std::string& index_name,
# 238 : : int start_height, int stop_height)
# 239 : 2 : {
# 240 : 2 : DBHeightKey key{start_height};
# 241 : 2 : db_it.Seek(key);
# 242 : :
# 243 [ + + ]: 21 : for (int height = start_height; height <= stop_height; ++height) {
# 244 [ - + ][ - + ]: 19 : if (!db_it.GetKey(key) || key.height != height) {
# 245 : 0 : return error("%s: unexpected key in %s: expected (%c, %d)",
# 246 : 0 : __func__, index_name, DB_BLOCK_HEIGHT, height);
# 247 : 0 : }
# 248 : :
# 249 : 19 : std::pair<uint256, DBVal> value;
# 250 [ - + ]: 19 : if (!db_it.GetValue(value)) {
# 251 : 0 : return error("%s: unable to read value in %s at key (%c, %d)",
# 252 : 0 : __func__, index_name, DB_BLOCK_HEIGHT, height);
# 253 : 0 : }
# 254 : :
# 255 : 19 : batch.Write(DBHashKey(value.first), std::move(value.second));
# 256 : :
# 257 : 19 : db_it.Next();
# 258 : 19 : }
# 259 : 2 : return true;
# 260 : 2 : }
# 261 : :
# 262 : : bool CoinStatsIndex::Rewind(const CBlockIndex* current_tip, const CBlockIndex* new_tip)
# 263 : 2 : {
# 264 : 2 : assert(current_tip->GetAncestor(new_tip->nHeight) == new_tip);
# 265 : :
# 266 : 0 : CDBBatch batch(*m_db);
# 267 : 2 : std::unique_ptr<CDBIterator> db_it(m_db->NewIterator());
# 268 : :
# 269 : : // During a reorg, we need to copy all hash digests for blocks that are
# 270 : : // getting disconnected from the height index to the hash index so we can
# 271 : : // still find them when the height index entries are overwritten.
# 272 [ - + ]: 2 : if (!CopyHeightIndexToHashIndex(*db_it, batch, m_name, new_tip->nHeight, current_tip->nHeight)) {
# 273 : 0 : return false;
# 274 : 0 : }
# 275 : :
# 276 [ - + ]: 2 : if (!m_db->WriteBatch(batch)) return false;
# 277 : :
# 278 : 2 : {
# 279 : 2 : LOCK(cs_main);
# 280 : 2 : const CBlockIndex* iter_tip{m_chainstate->m_blockman.LookupBlockIndex(current_tip->GetBlockHash())};
# 281 : 2 : const auto& consensus_params{Params().GetConsensus()};
# 282 : :
# 283 : 17 : do {
# 284 : 17 : CBlock block;
# 285 : :
# 286 [ - + ]: 17 : if (!ReadBlockFromDisk(block, iter_tip, consensus_params)) {
# 287 : 0 : return error("%s: Failed to read block %s from disk",
# 288 : 0 : __func__, iter_tip->GetBlockHash().ToString());
# 289 : 0 : }
# 290 : :
# 291 : 17 : ReverseBlock(block, iter_tip);
# 292 : :
# 293 : 17 : iter_tip = iter_tip->GetAncestor(iter_tip->nHeight - 1);
# 294 [ + + ]: 17 : } while (new_tip != iter_tip);
# 295 : 2 : }
# 296 : :
# 297 : 2 : return BaseIndex::Rewind(current_tip, new_tip);
# 298 : 2 : }
# 299 : :
# 300 : : static bool LookUpOne(const CDBWrapper& db, const CBlockIndex* block_index, DBVal& result)
# 301 : 62 : {
# 302 : : // First check if the result is stored under the height index and the value
# 303 : : // there matches the block hash. This should be the case if the block is on
# 304 : : // the active chain.
# 305 : 62 : std::pair<uint256, DBVal> read_out;
# 306 [ + + ]: 62 : if (!db.Read(DBHeightKey(block_index->nHeight), read_out)) {
# 307 : 2 : return false;
# 308 : 2 : }
# 309 [ + + ]: 60 : if (read_out.first == block_index->GetBlockHash()) {
# 310 : 58 : result = std::move(read_out.second);
# 311 : 58 : return true;
# 312 : 58 : }
# 313 : :
# 314 : : // If value at the height index corresponds to an different block, the
# 315 : : // result will be stored in the hash index.
# 316 : 2 : return db.Read(DBHashKey(block_index->GetBlockHash()), result);
# 317 : 60 : }
# 318 : :
# 319 : : bool CoinStatsIndex::LookUpStats(const CBlockIndex* block_index, CCoinsStats& coins_stats) const
# 320 : 50 : {
# 321 : 50 : DBVal entry;
# 322 [ + + ]: 50 : if (!LookUpOne(*m_db, block_index, entry)) {
# 323 : 2 : return false;
# 324 : 2 : }
# 325 : :
# 326 : 48 : coins_stats.hashSerialized = entry.muhash;
# 327 : 48 : coins_stats.nTransactionOutputs = entry.transaction_output_count;
# 328 : 48 : coins_stats.nBogoSize = entry.bogo_size;
# 329 : 48 : coins_stats.total_amount = entry.total_amount;
# 330 : 48 : coins_stats.total_subsidy = entry.total_subsidy;
# 331 : 48 : coins_stats.total_unspendable_amount = entry.total_unspendable_amount;
# 332 : 48 : coins_stats.total_prevout_spent_amount = entry.total_prevout_spent_amount;
# 333 : 48 : coins_stats.total_new_outputs_ex_coinbase_amount = entry.total_new_outputs_ex_coinbase_amount;
# 334 : 48 : coins_stats.total_coinbase_amount = entry.total_coinbase_amount;
# 335 : 48 : coins_stats.total_unspendables_genesis_block = entry.total_unspendables_genesis_block;
# 336 : 48 : coins_stats.total_unspendables_bip30 = entry.total_unspendables_bip30;
# 337 : 48 : coins_stats.total_unspendables_scripts = entry.total_unspendables_scripts;
# 338 : 48 : coins_stats.total_unspendables_unclaimed_rewards = entry.total_unspendables_unclaimed_rewards;
# 339 : :
# 340 : 48 : return true;
# 341 : 50 : }
# 342 : :
# 343 : : bool CoinStatsIndex::Init()
# 344 : 19 : {
# 345 [ + + ]: 19 : if (!m_db->Read(DB_MUHASH, m_muhash)) {
# 346 : : // Check that the cause of the read failure is that the key does not
# 347 : : // exist. Any other errors indicate database corruption or a disk
# 348 : : // failure, and starting the index would cause further corruption.
# 349 [ - + ]: 7 : if (m_db->Exists(DB_MUHASH)) {
# 350 : 0 : return error("%s: Cannot read current %s state; index may be corrupted",
# 351 : 0 : __func__, GetName());
# 352 : 0 : }
# 353 : 7 : }
# 354 : :
# 355 [ - + ]: 19 : if (!BaseIndex::Init()) return false;
# 356 : :
# 357 : 19 : const CBlockIndex* pindex{CurrentIndex()};
# 358 : :
# 359 [ + + ]: 19 : if (pindex) {
# 360 : 12 : DBVal entry;
# 361 [ - + ]: 12 : if (!LookUpOne(*m_db, pindex, entry)) {
# 362 : 0 : return error("%s: Cannot read current %s state; index may be corrupted",
# 363 : 0 : __func__, GetName());
# 364 : 0 : }
# 365 : :
# 366 : 12 : uint256 out;
# 367 : 12 : m_muhash.Finalize(out);
# 368 [ - + ]: 12 : if (entry.muhash != out) {
# 369 : 0 : return error("%s: Cannot read current %s state; index may be corrupted",
# 370 : 0 : __func__, GetName());
# 371 : 0 : }
# 372 : :
# 373 : 12 : m_transaction_output_count = entry.transaction_output_count;
# 374 : 12 : m_bogo_size = entry.bogo_size;
# 375 : 12 : m_total_amount = entry.total_amount;
# 376 : 12 : m_total_subsidy = entry.total_subsidy;
# 377 : 12 : m_total_unspendable_amount = entry.total_unspendable_amount;
# 378 : 12 : m_total_prevout_spent_amount = entry.total_prevout_spent_amount;
# 379 : 12 : m_total_new_outputs_ex_coinbase_amount = entry.total_new_outputs_ex_coinbase_amount;
# 380 : 12 : m_total_coinbase_amount = entry.total_coinbase_amount;
# 381 : 12 : m_total_unspendables_genesis_block = entry.total_unspendables_genesis_block;
# 382 : 12 : m_total_unspendables_bip30 = entry.total_unspendables_bip30;
# 383 : 12 : m_total_unspendables_scripts = entry.total_unspendables_scripts;
# 384 : 12 : m_total_unspendables_unclaimed_rewards = entry.total_unspendables_unclaimed_rewards;
# 385 : 12 : }
# 386 : :
# 387 : 19 : return true;
# 388 : 19 : }
# 389 : :
# 390 : : bool CoinStatsIndex::CommitInternal(CDBBatch& batch)
# 391 : 63 : {
# 392 : : // DB_MUHASH should always be committed in a batch together with DB_BEST_BLOCK
# 393 : : // to prevent an inconsistent state of the DB.
# 394 : 63 : batch.Write(DB_MUHASH, m_muhash);
# 395 : 63 : return BaseIndex::CommitInternal(batch);
# 396 : 63 : }
# 397 : :
# 398 : : // Reverse a single block as part of a reorg
# 399 : : bool CoinStatsIndex::ReverseBlock(const CBlock& block, const CBlockIndex* pindex)
# 400 : 17 : {
# 401 : 17 : CBlockUndo block_undo;
# 402 : 17 : std::pair<uint256, DBVal> read_out;
# 403 : :
# 404 : 17 : const CAmount block_subsidy{GetBlockSubsidy(pindex->nHeight, Params().GetConsensus())};
# 405 : 17 : m_total_subsidy -= block_subsidy;
# 406 : :
# 407 : : // Ignore genesis block
# 408 [ + - ]: 17 : if (pindex->nHeight > 0) {
# 409 [ - + ]: 17 : if (!UndoReadFromDisk(block_undo, pindex)) {
# 410 : 0 : return false;
# 411 : 0 : }
# 412 : :
# 413 [ - + ]: 17 : if (!m_db->Read(DBHeightKey(pindex->nHeight - 1), read_out)) {
# 414 : 0 : return false;
# 415 : 0 : }
# 416 : :
# 417 : 17 : uint256 expected_block_hash{pindex->pprev->GetBlockHash()};
# 418 [ - + ]: 17 : if (read_out.first != expected_block_hash) {
# 419 : 0 : LogPrintf("WARNING: previous block header belongs to unexpected block %s; expected %s\n",
# 420 : 0 : read_out.first.ToString(), expected_block_hash.ToString());
# 421 : :
# 422 [ # # ]: 0 : if (!m_db->Read(DBHashKey(expected_block_hash), read_out)) {
# 423 : 0 : return error("%s: previous block header not found; expected %s",
# 424 : 0 : __func__, expected_block_hash.ToString());
# 425 : 0 : }
# 426 : 0 : }
# 427 : 17 : }
# 428 : :
# 429 : : // Remove the new UTXOs that were created from the block
# 430 [ + + ]: 37 : for (size_t i = 0; i < block.vtx.size(); ++i) {
# 431 : 20 : const auto& tx{block.vtx.at(i)};
# 432 : :
# 433 [ + + ]: 58 : for (uint32_t j = 0; j < tx->vout.size(); ++j) {
# 434 : 38 : const CTxOut& out{tx->vout[j]};
# 435 : 38 : COutPoint outpoint{tx->GetHash(), j};
# 436 : 38 : Coin coin{out, pindex->nHeight, tx->IsCoinBase()};
# 437 : :
# 438 : : // Skip unspendable coins
# 439 [ + + ]: 38 : if (coin.out.scriptPubKey.IsUnspendable()) {
# 440 : 17 : m_total_unspendable_amount -= coin.out.nValue;
# 441 : 17 : m_total_unspendables_scripts -= coin.out.nValue;
# 442 : 17 : continue;
# 443 : 17 : }
# 444 : :
# 445 : 21 : m_muhash.Remove(MakeUCharSpan(TxOutSer(outpoint, coin)));
# 446 : :
# 447 [ + + ]: 21 : if (tx->IsCoinBase()) {
# 448 : 18 : m_total_coinbase_amount -= coin.out.nValue;
# 449 : 18 : } else {
# 450 : 3 : m_total_new_outputs_ex_coinbase_amount -= coin.out.nValue;
# 451 : 3 : }
# 452 : :
# 453 : 21 : --m_transaction_output_count;
# 454 : 21 : m_total_amount -= coin.out.nValue;
# 455 : 21 : m_bogo_size -= GetBogoSize(coin.out.scriptPubKey);
# 456 : 21 : }
# 457 : :
# 458 : : // The coinbase tx has no undo data since no former output is spent
# 459 [ + + ]: 20 : if (!tx->IsCoinBase()) {
# 460 : 3 : const auto& tx_undo{block_undo.vtxundo.at(i - 1)};
# 461 : :
# 462 [ + + ]: 6 : for (size_t j = 0; j < tx_undo.vprevout.size(); ++j) {
# 463 : 3 : Coin coin{tx_undo.vprevout[j]};
# 464 : 3 : COutPoint outpoint{tx->vin[j].prevout.hash, tx->vin[j].prevout.n};
# 465 : :
# 466 : 3 : m_muhash.Insert(MakeUCharSpan(TxOutSer(outpoint, coin)));
# 467 : :
# 468 : 3 : m_total_prevout_spent_amount -= coin.out.nValue;
# 469 : :
# 470 : 3 : m_transaction_output_count++;
# 471 : 3 : m_total_amount += coin.out.nValue;
# 472 : 3 : m_bogo_size += GetBogoSize(coin.out.scriptPubKey);
# 473 : 3 : }
# 474 : 3 : }
# 475 : 20 : }
# 476 : :
# 477 : 17 : const CAmount unclaimed_rewards{(m_total_new_outputs_ex_coinbase_amount + m_total_coinbase_amount + m_total_unspendable_amount) - (m_total_prevout_spent_amount + m_total_subsidy)};
# 478 : 17 : m_total_unspendable_amount -= unclaimed_rewards;
# 479 : 17 : m_total_unspendables_unclaimed_rewards -= unclaimed_rewards;
# 480 : :
# 481 : : // Check that the rolled back internal values are consistent with the DB read out
# 482 : 17 : uint256 out;
# 483 : 17 : m_muhash.Finalize(out);
# 484 : 17 : Assert(read_out.second.muhash == out);
# 485 : :
# 486 : 17 : Assert(m_transaction_output_count == read_out.second.transaction_output_count);
# 487 : 17 : Assert(m_total_amount == read_out.second.total_amount);
# 488 : 17 : Assert(m_bogo_size == read_out.second.bogo_size);
# 489 : 17 : Assert(m_total_subsidy == read_out.second.total_subsidy);
# 490 : 17 : Assert(m_total_unspendable_amount == read_out.second.total_unspendable_amount);
# 491 : 17 : Assert(m_total_prevout_spent_amount == read_out.second.total_prevout_spent_amount);
# 492 : 17 : Assert(m_total_new_outputs_ex_coinbase_amount == read_out.second.total_new_outputs_ex_coinbase_amount);
# 493 : 17 : Assert(m_total_coinbase_amount == read_out.second.total_coinbase_amount);
# 494 : 17 : Assert(m_total_unspendables_genesis_block == read_out.second.total_unspendables_genesis_block);
# 495 : 17 : Assert(m_total_unspendables_bip30 == read_out.second.total_unspendables_bip30);
# 496 : 17 : Assert(m_total_unspendables_scripts == read_out.second.total_unspendables_scripts);
# 497 : 17 : Assert(m_total_unspendables_unclaimed_rewards == read_out.second.total_unspendables_unclaimed_rewards);
# 498 : :
# 499 : 17 : return true;
# 500 : 17 : }
|