fuzz coverage

Coverage Report

Created: 2025-06-01 19:34

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