Skip to content

Commit 1352e41

Browse files
committed
coinstats: Separate hasher/index lookup codepaths
Split out ComputeUTXOStats and LookupUTXOStatsWithIndex from GetUTXOStats, since the hashing and indexing codepaths are quite disparate in practice. Also allow add a constructor to CCoinsStats for it to be constructed from a a block height and hash. This is used in both codepaths. Also add a note in GetUTXOStats documenting a behaviour quirk that predates this patchset. [META] This allows the hashing codepath to be moved to a separate file in a future commit, decoupling callers that only rely on the hashing codepath from the indexing one. This is key for libbitcoinkernel, which needs to have the hashing codepath for AssumeUTXO, but does not wish to be coupled with indexes.
1 parent 524463d commit 1352e41

File tree

2 files changed

+55
-19
lines changed

2 files changed

+55
-19
lines changed

src/node/coinstats.cpp

Lines changed: 52 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@
1919
#include <map>
2020

2121
namespace node {
22+
23+
CCoinsStats::CCoinsStats(int block_height, const uint256& block_hash)
24+
: nHeight(block_height),
25+
hashBlock(block_hash) {}
26+
2227
// Database-independent metric indicating the UTXO set size
2328
uint64_t GetBogoSize(const CScript& script_pub_key)
2429
{
@@ -94,24 +99,11 @@ static void ApplyStats(CCoinsStats& stats, const uint256& hash, const std::map<u
9499

95100
//! Calculate statistics about the unspent transaction output set
96101
template <typename T>
97-
static bool GetUTXOStats(CCoinsView* view, BlockManager& blockman, CCoinsStats& stats, T hash_obj, const std::function<void()>& interruption_point, const CBlockIndex* pindex, CoinStatsHashType& hash_type, bool index_requested)
102+
static bool ComputeUTXOStats(CCoinsView* view, CCoinsStats& stats, T hash_obj, const std::function<void()>& interruption_point)
98103
{
99104
std::unique_ptr<CCoinsViewCursor> pcursor(view->Cursor());
100105
assert(pcursor);
101106

102-
if (!pindex) {
103-
LOCK(cs_main);
104-
pindex = blockman.LookupBlockIndex(view->GetBestBlock());
105-
}
106-
stats.nHeight = Assert(pindex)->nHeight;
107-
stats.hashBlock = pindex->GetBlockHash();
108-
109-
// Use CoinStatsIndex if it is requested and available and a hash_type of Muhash or None was requested
110-
if ((hash_type == CoinStatsHashType::MUHASH || hash_type == CoinStatsHashType::NONE) && g_coin_stats_index && index_requested) {
111-
stats.index_used = true;
112-
return g_coin_stats_index->LookUpStats(pindex, stats);
113-
}
114-
115107
PrepareHash(hash_obj, stats);
116108

117109
uint256 prevkey;
@@ -142,25 +134,27 @@ static bool GetUTXOStats(CCoinsView* view, BlockManager& blockman, CCoinsStats&
142134
FinalizeHash(hash_obj, stats);
143135

144136
stats.nDiskSize = view->EstimateSize();
137+
145138
return true;
146139
}
147140

148-
std::optional<CCoinsStats> GetUTXOStats(CCoinsView* view, BlockManager& blockman, CoinStatsHashType hash_type, const std::function<void()>& interruption_point, const CBlockIndex* pindex, bool index_requested)
141+
std::optional<CCoinsStats> ComputeUTXOStats(CoinStatsHashType hash_type, CCoinsView* view, BlockManager& blockman, const std::function<void()>& interruption_point)
149142
{
150-
CCoinsStats stats{};
143+
CBlockIndex* pindex = WITH_LOCK(::cs_main, return blockman.LookupBlockIndex(view->GetBestBlock()));
144+
CCoinsStats stats{Assert(pindex)->nHeight, pindex->GetBlockHash()};
151145

152146
bool success = [&]() -> bool {
153147
switch (hash_type) {
154148
case(CoinStatsHashType::HASH_SERIALIZED): {
155149
CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION);
156-
return GetUTXOStats(view, blockman, stats, ss, interruption_point, pindex, hash_type, index_requested);
150+
return ComputeUTXOStats(view, stats, ss, interruption_point);
157151
}
158152
case(CoinStatsHashType::MUHASH): {
159153
MuHash3072 muhash;
160-
return GetUTXOStats(view, blockman, stats, muhash, interruption_point, pindex, hash_type, index_requested);
154+
return ComputeUTXOStats(view, stats, muhash, interruption_point);
161155
}
162156
case(CoinStatsHashType::NONE): {
163-
return GetUTXOStats(view, blockman, stats, nullptr, interruption_point, pindex, hash_type, index_requested);
157+
return ComputeUTXOStats(view, stats, nullptr, interruption_point);
164158
}
165159
} // no default case, so the compiler can warn about missing cases
166160
assert(false);
@@ -192,4 +186,43 @@ static void FinalizeHash(MuHash3072& muhash, CCoinsStats& stats)
192186
stats.hashSerialized = out;
193187
}
194188
static void FinalizeHash(std::nullptr_t, CCoinsStats& stats) {}
189+
190+
std::optional<CCoinsStats> LookupUTXOStatsWithIndex(CoinStatsIndex& coin_stats_index, const CBlockIndex* pindex)
191+
{
192+
CCoinsStats stats{Assert(pindex)->nHeight, pindex->GetBlockHash()};
193+
194+
stats.index_used = true;
195+
if (!coin_stats_index.LookUpStats(pindex, stats)) {
196+
return std::nullopt;
197+
}
198+
199+
return stats;
200+
}
201+
202+
std::optional<CCoinsStats> LookupUTXOStatsWithIndex(CoinStatsIndex& coin_stats_index, CCoinsView* view, BlockManager& blockman)
203+
{
204+
CBlockIndex* pindex = WITH_LOCK(::cs_main, return blockman.LookupBlockIndex(view->GetBestBlock()));
205+
206+
return LookupUTXOStatsWithIndex(coin_stats_index, pindex);
207+
}
208+
209+
std::optional<CCoinsStats> GetUTXOStats(CCoinsView* view, BlockManager& blockman, CoinStatsHashType hash_type, const std::function<void()>& interruption_point, const CBlockIndex* pindex, bool index_requested)
210+
{
211+
// Use CoinStatsIndex if it is requested and available and a hash_type of Muhash or None was requested
212+
if ((hash_type == CoinStatsHashType::MUHASH || hash_type == CoinStatsHashType::NONE) && g_coin_stats_index && index_requested) {
213+
if (pindex) {
214+
return LookupUTXOStatsWithIndex(*g_coin_stats_index, pindex);
215+
} else {
216+
return LookupUTXOStatsWithIndex(*g_coin_stats_index, view, blockman);
217+
}
218+
}
219+
220+
// If the coinstats index isn't requested or is otherwise not usable, the
221+
// pindex should either be null or equal to the view's best block. This is
222+
// because without the coinstats index we can only get coinstats about the
223+
// best block.
224+
assert(!pindex || pindex->GetBlockHash() == view->GetBestBlock());
225+
226+
return ComputeUTXOStats(hash_type, view, blockman, interruption_point);
227+
}
195228
} // namespace node

src/node/coinstats.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@ struct CCoinsStats {
6464
CAmount total_unspendables_scripts{0};
6565
//! Total cumulative amount of coins lost due to unclaimed miner rewards up to and including this block
6666
CAmount total_unspendables_unclaimed_rewards{0};
67+
68+
CCoinsStats() = default;
69+
CCoinsStats(int block_height, const uint256& block_hash);
6770
};
6871

6972
/**

0 commit comments

Comments
 (0)