Coverage Report

Created: 2025-06-10 13:21

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/bitcoin/src/kernel/coinstats.cpp
Line
Count
Source
1
// Copyright (c) 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 <kernel/coinstats.h>
6
7
#include <chain.h>
8
#include <coins.h>
9
#include <crypto/muhash.h>
10
#include <hash.h>
11
#include <logging.h>
12
#include <node/blockstorage.h>
13
#include <primitives/transaction.h>
14
#include <script/script.h>
15
#include <serialize.h>
16
#include <span.h>
17
#include <streams.h>
18
#include <sync.h>
19
#include <tinyformat.h>
20
#include <uint256.h>
21
#include <util/check.h>
22
#include <util/overflow.h>
23
#include <validation.h>
24
25
#include <cassert>
26
#include <iosfwd>
27
#include <iterator>
28
#include <map>
29
#include <memory>
30
#include <string>
31
#include <utility>
32
33
namespace kernel {
34
35
CCoinsStats::CCoinsStats(int block_height, const uint256& block_hash)
36
0
    : nHeight(block_height),
37
0
      hashBlock(block_hash) {}
38
39
// Database-independent metric indicating the UTXO set size
40
uint64_t GetBogoSize(const CScript& script_pub_key)
41
0
{
42
0
    return 32 /* txid */ +
43
0
           4 /* vout index */ +
44
0
           4 /* height + coinbase */ +
45
0
           8 /* amount */ +
46
0
           2 /* scriptPubKey len */ +
47
0
           script_pub_key.size() /* scriptPubKey */;
48
0
}
49
50
template <typename T>
51
static void TxOutSer(T& ss, const COutPoint& outpoint, const Coin& coin)
52
0
{
53
0
    ss << outpoint;
54
0
    ss << static_cast<uint32_t>((coin.nHeight << 1) + coin.fCoinBase);
55
0
    ss << coin.out;
56
0
}
Unexecuted instantiation: coinstats.cpp:void kernel::TxOutSer<HashWriter>(HashWriter&, COutPoint const&, Coin const&)
Unexecuted instantiation: coinstats.cpp:void kernel::TxOutSer<DataStream>(DataStream&, COutPoint const&, Coin const&)
57
58
static void ApplyCoinHash(HashWriter& ss, const COutPoint& outpoint, const Coin& coin)
59
0
{
60
0
    TxOutSer(ss, outpoint, coin);
61
0
}
62
63
void ApplyCoinHash(MuHash3072& muhash, const COutPoint& outpoint, const Coin& coin)
64
0
{
65
0
    DataStream ss{};
66
0
    TxOutSer(ss, outpoint, coin);
67
0
    muhash.Insert(MakeUCharSpan(ss));
68
0
}
69
70
void RemoveCoinHash(MuHash3072& muhash, const COutPoint& outpoint, const Coin& coin)
71
0
{
72
0
    DataStream ss{};
73
0
    TxOutSer(ss, outpoint, coin);
74
0
    muhash.Remove(MakeUCharSpan(ss));
75
0
}
76
77
0
static void ApplyCoinHash(std::nullptr_t, const COutPoint& outpoint, const Coin& coin) {}
78
79
//! Warning: be very careful when changing this! assumeutxo and UTXO snapshot
80
//! validation commitments are reliant on the hash constructed by this
81
//! function.
82
//!
83
//! If the construction of this hash is changed, it will invalidate
84
//! existing UTXO snapshots. This will not result in any kind of consensus
85
//! failure, but it will force clients that were expecting to make use of
86
//! assumeutxo to do traditional IBD instead.
87
//!
88
//! It is also possible, though very unlikely, that a change in this
89
//! construction could cause a previously invalid (and potentially malicious)
90
//! UTXO snapshot to be considered valid.
91
template <typename T>
92
static void ApplyHash(T& hash_obj, const Txid& hash, const std::map<uint32_t, Coin>& outputs)
93
0
{
94
0
    for (auto it = outputs.begin(); it != outputs.end(); ++it) {
  Branch (94:37): [True: 0, False: 0]
  Branch (94:37): [True: 0, False: 0]
  Branch (94:37): [True: 0, False: 0]
95
0
        COutPoint outpoint = COutPoint(hash, it->first);
96
0
        Coin coin = it->second;
97
0
        ApplyCoinHash(hash_obj, outpoint, coin);
98
0
    }
99
0
}
Unexecuted instantiation: coinstats.cpp:void kernel::ApplyHash<HashWriter>(HashWriter&, transaction_identifier<false> const&, std::map<unsigned int, Coin, std::less<unsigned int>, std::allocator<std::pair<unsigned int const, Coin> > > const&)
Unexecuted instantiation: coinstats.cpp:void kernel::ApplyHash<MuHash3072>(MuHash3072&, transaction_identifier<false> const&, std::map<unsigned int, Coin, std::less<unsigned int>, std::allocator<std::pair<unsigned int const, Coin> > > const&)
Unexecuted instantiation: coinstats.cpp:void kernel::ApplyHash<decltype(nullptr)>(decltype(nullptr)&, transaction_identifier<false> const&, std::map<unsigned int, Coin, std::less<unsigned int>, std::allocator<std::pair<unsigned int const, Coin> > > const&)
100
101
static void ApplyStats(CCoinsStats& stats, const uint256& hash, const std::map<uint32_t, Coin>& outputs)
102
0
{
103
0
    assert(!outputs.empty());
  Branch (103:5): [True: 0, False: 0]
104
0
    stats.nTransactions++;
105
0
    for (auto it = outputs.begin(); it != outputs.end(); ++it) {
  Branch (105:37): [True: 0, False: 0]
106
0
        stats.nTransactionOutputs++;
107
0
        if (stats.total_amount.has_value()) {
  Branch (107:13): [True: 0, False: 0]
108
0
            stats.total_amount = CheckedAdd(*stats.total_amount, it->second.out.nValue);
109
0
        }
110
0
        stats.nBogoSize += GetBogoSize(it->second.out.scriptPubKey);
111
0
    }
112
0
}
113
114
//! Calculate statistics about the unspent transaction output set
115
template <typename T>
116
static bool ComputeUTXOStats(CCoinsView* view, CCoinsStats& stats, T hash_obj, const std::function<void()>& interruption_point)
117
0
{
118
0
    std::unique_ptr<CCoinsViewCursor> pcursor(view->Cursor());
119
0
    assert(pcursor);
  Branch (119:5): [True: 0, False: 0]
  Branch (119:5): [True: 0, False: 0]
  Branch (119:5): [True: 0, False: 0]
120
121
0
    Txid prevkey;
122
0
    std::map<uint32_t, Coin> outputs;
123
0
    while (pcursor->Valid()) {
  Branch (123:12): [True: 0, False: 0]
  Branch (123:12): [True: 0, False: 0]
  Branch (123:12): [True: 0, False: 0]
124
0
        if (interruption_point) interruption_point();
  Branch (124:13): [True: 0, False: 0]
  Branch (124:13): [True: 0, False: 0]
  Branch (124:13): [True: 0, False: 0]
125
0
        COutPoint key;
126
0
        Coin coin;
127
0
        if (pcursor->GetKey(key) && pcursor->GetValue(coin)) {
  Branch (127:13): [True: 0, False: 0]
  Branch (127:37): [True: 0, False: 0]
  Branch (127:13): [True: 0, False: 0]
  Branch (127:37): [True: 0, False: 0]
  Branch (127:13): [True: 0, False: 0]
  Branch (127:37): [True: 0, False: 0]
128
0
            if (!outputs.empty() && key.hash != prevkey) {
  Branch (128:17): [True: 0, False: 0]
  Branch (128:37): [True: 0, False: 0]
  Branch (128:17): [True: 0, False: 0]
  Branch (128:37): [True: 0, False: 0]
  Branch (128:17): [True: 0, False: 0]
  Branch (128:37): [True: 0, False: 0]
129
0
                ApplyStats(stats, prevkey, outputs);
130
0
                ApplyHash(hash_obj, prevkey, outputs);
131
0
                outputs.clear();
132
0
            }
133
0
            prevkey = key.hash;
134
0
            outputs[key.n] = std::move(coin);
135
0
            stats.coins_count++;
136
0
        } else {
137
0
            LogError("%s: unable to read value\n", __func__);
138
0
            return false;
139
0
        }
140
0
        pcursor->Next();
141
0
    }
142
0
    if (!outputs.empty()) {
  Branch (142:9): [True: 0, False: 0]
  Branch (142:9): [True: 0, False: 0]
  Branch (142:9): [True: 0, False: 0]
143
0
        ApplyStats(stats, prevkey, outputs);
144
0
        ApplyHash(hash_obj, prevkey, outputs);
145
0
    }
146
147
0
    FinalizeHash(hash_obj, stats);
148
149
0
    stats.nDiskSize = view->EstimateSize();
150
151
0
    return true;
152
0
}
Unexecuted instantiation: coinstats.cpp:bool kernel::ComputeUTXOStats<HashWriter>(CCoinsView*, kernel::CCoinsStats&, HashWriter, std::function<void ()> const&)
Unexecuted instantiation: coinstats.cpp:bool kernel::ComputeUTXOStats<MuHash3072>(CCoinsView*, kernel::CCoinsStats&, MuHash3072, std::function<void ()> const&)
Unexecuted instantiation: coinstats.cpp:bool kernel::ComputeUTXOStats<decltype(nullptr)>(CCoinsView*, kernel::CCoinsStats&, decltype(nullptr), std::function<void ()> const&)
153
154
std::optional<CCoinsStats> ComputeUTXOStats(CoinStatsHashType hash_type, CCoinsView* view, node::BlockManager& blockman, const std::function<void()>& interruption_point)
155
0
{
156
0
    CBlockIndex* pindex = WITH_LOCK(::cs_main, return blockman.LookupBlockIndex(view->GetBestBlock()));
157
0
    CCoinsStats stats{Assert(pindex)->nHeight, pindex->GetBlockHash()};
158
159
0
    bool success = [&]() -> bool {
160
0
        switch (hash_type) {
  Branch (160:17): [True: 0, False: 0]
161
0
        case(CoinStatsHashType::HASH_SERIALIZED): {
  Branch (161:9): [True: 0, False: 0]
162
0
            HashWriter ss{};
163
0
            return ComputeUTXOStats(view, stats, ss, interruption_point);
164
0
        }
165
0
        case(CoinStatsHashType::MUHASH): {
  Branch (165:9): [True: 0, False: 0]
166
0
            MuHash3072 muhash;
167
0
            return ComputeUTXOStats(view, stats, muhash, interruption_point);
168
0
        }
169
0
        case(CoinStatsHashType::NONE): {
  Branch (169:9): [True: 0, False: 0]
170
0
            return ComputeUTXOStats(view, stats, nullptr, interruption_point);
171
0
        }
172
0
        } // no default case, so the compiler can warn about missing cases
173
0
        assert(false);
  Branch (173:9): [Folded - Ignored]
174
0
    }();
175
176
0
    if (!success) {
  Branch (176:9): [True: 0, False: 0]
177
0
        return std::nullopt;
178
0
    }
179
0
    return stats;
180
0
}
181
182
static void FinalizeHash(HashWriter& ss, CCoinsStats& stats)
183
0
{
184
0
    stats.hashSerialized = ss.GetHash();
185
0
}
186
static void FinalizeHash(MuHash3072& muhash, CCoinsStats& stats)
187
0
{
188
0
    uint256 out;
189
0
    muhash.Finalize(out);
190
0
    stats.hashSerialized = out;
191
0
}
192
0
static void FinalizeHash(std::nullptr_t, CCoinsStats& stats) {}
193
194
} // namespace kernel