Coverage Report

Created: 2026-06-01 18:35

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