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