LCOV - code coverage report
Current view: top level - src/index - coinstatsindex.cpp (source / functions) Hit Total Coverage
Test: coverage.lcov Lines: 267 318 84.0 %
Date: 2022-04-21 14:51:19 Functions: 16 16 100.0 %
Legend: Modified by patch:
Lines: hit not hit | Branches: + taken - not taken # not executed

Not modified by patch:
Lines: hit not hit | Branches: + taken - not taken # not executed
Branches: 62 94 66.0 %

           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 : }

Generated by: LCOV version 0-eol-96201-ge66f56f4af6a