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