Skip to content

Commit 524463d

Browse files
committed
coinstats: Return purely out-param CCoinsStats
In previous commits in this patchset, we removed all in-param members of CCoinsStats. Now that that's done, we can modify GetUTXOStats to return an optional CCoinsStats instead of a status bool. Callers are modified accordingly. In rpc/blockchain.cpp, we discover that GetUTXOStats' status bool when getting UTXO stats for pprev was not checked for error. We fix this as well.
1 parent 46eb9fc commit 524463d

File tree

4 files changed

+48
-33
lines changed

4 files changed

+48
-33
lines changed

src/node/coinstats.cpp

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include <crypto/muhash.h>
1010
#include <hash.h>
1111
#include <index/coinstatsindex.h>
12+
#include <optional>
1213
#include <serialize.h>
1314
#include <uint256.h>
1415
#include <util/overflow.h>
@@ -144,22 +145,31 @@ static bool GetUTXOStats(CCoinsView* view, BlockManager& blockman, CCoinsStats&
144145
return true;
145146
}
146147

147-
bool GetUTXOStats(CCoinsView* view, BlockManager& blockman, CCoinsStats& stats, CoinStatsHashType hash_type, const std::function<void()>& interruption_point, const CBlockIndex* pindex, bool index_requested)
148+
std::optional<CCoinsStats> GetUTXOStats(CCoinsView* view, BlockManager& blockman, CoinStatsHashType hash_type, const std::function<void()>& interruption_point, const CBlockIndex* pindex, bool index_requested)
148149
{
149-
switch (hash_type) {
150-
case(CoinStatsHashType::HASH_SERIALIZED): {
151-
CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION);
152-
return GetUTXOStats(view, blockman, stats, ss, interruption_point, pindex, hash_type, index_requested);
153-
}
154-
case(CoinStatsHashType::MUHASH): {
155-
MuHash3072 muhash;
156-
return GetUTXOStats(view, blockman, stats, muhash, interruption_point, pindex, hash_type, index_requested);
157-
}
158-
case(CoinStatsHashType::NONE): {
159-
return GetUTXOStats(view, blockman, stats, nullptr, interruption_point, pindex, hash_type, index_requested);
150+
CCoinsStats stats{};
151+
152+
bool success = [&]() -> bool {
153+
switch (hash_type) {
154+
case(CoinStatsHashType::HASH_SERIALIZED): {
155+
CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION);
156+
return GetUTXOStats(view, blockman, stats, ss, interruption_point, pindex, hash_type, index_requested);
157+
}
158+
case(CoinStatsHashType::MUHASH): {
159+
MuHash3072 muhash;
160+
return GetUTXOStats(view, blockman, stats, muhash, interruption_point, pindex, hash_type, index_requested);
161+
}
162+
case(CoinStatsHashType::NONE): {
163+
return GetUTXOStats(view, blockman, stats, nullptr, interruption_point, pindex, hash_type, index_requested);
164+
}
165+
} // no default case, so the compiler can warn about missing cases
166+
assert(false);
167+
}();
168+
169+
if (!success) {
170+
return std::nullopt;
160171
}
161-
} // no default case, so the compiler can warn about missing cases
162-
assert(false);
172+
return stats;
163173
}
164174

165175
// The legacy hash serializes the hashBlock

src/node/coinstats.h

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -71,11 +71,11 @@ struct CCoinsStats {
7171
*
7272
* @param[in] index_requested Signals if the coinstatsindex should be used (when available).
7373
*/
74-
bool GetUTXOStats(CCoinsView* view, node::BlockManager& blockman,
75-
CCoinsStats& stats, CoinStatsHashType hash_type,
76-
const std::function<void()>& interruption_point = {},
77-
const CBlockIndex* pindex = nullptr,
78-
bool index_requested = true);
74+
std::optional<CCoinsStats> GetUTXOStats(CCoinsView* view, node::BlockManager& blockman,
75+
CoinStatsHashType hash_type,
76+
const std::function<void()>& interruption_point = {},
77+
const CBlockIndex* pindex = nullptr,
78+
bool index_requested = true);
7979

8080
uint64_t GetBogoSize(const CScript& script_pub_key);
8181

src/rpc/blockchain.cpp

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -862,7 +862,6 @@ static RPCHelpMan gettxoutsetinfo()
862862

863863
const CBlockIndex* pindex{nullptr};
864864
const CoinStatsHashType hash_type{request.params[0].isNull() ? CoinStatsHashType::HASH_SERIALIZED : ParseHashType(request.params[0].get_str())};
865-
CCoinsStats stats{};
866865
bool index_requested = request.params[2].isNull() || request.params[2].get_bool();
867866

868867
NodeContext& node = EnsureAnyNodeContext(request.context);
@@ -903,7 +902,9 @@ static RPCHelpMan gettxoutsetinfo()
903902
}
904903
}
905904

906-
if (GetUTXOStats(coins_view, *blockman, stats, hash_type, node.rpc_interruption_point, pindex, index_requested)) {
905+
const std::optional<CCoinsStats> maybe_stats = GetUTXOStats(coins_view, *blockman, hash_type, node.rpc_interruption_point, pindex, index_requested);
906+
if (maybe_stats.has_value()) {
907+
const CCoinsStats& stats = maybe_stats.value();
907908
ret.pushKV("height", (int64_t)stats.nHeight);
908909
ret.pushKV("bestblock", stats.hashBlock.GetHex());
909910
ret.pushKV("txouts", (int64_t)stats.nTransactionOutputs);
@@ -923,9 +924,12 @@ static RPCHelpMan gettxoutsetinfo()
923924
ret.pushKV("total_unspendable_amount", ValueFromAmount(stats.total_unspendable_amount));
924925

925926
CCoinsStats prev_stats{};
926-
927927
if (pindex->nHeight > 0) {
928-
GetUTXOStats(coins_view, *blockman, prev_stats, hash_type, node.rpc_interruption_point, pindex->pprev, index_requested);
928+
const std::optional<CCoinsStats> maybe_prev_stats = GetUTXOStats(coins_view, *blockman, hash_type, node.rpc_interruption_point, pindex->pprev, index_requested);
929+
if (!maybe_prev_stats) {
930+
throw JSONRPCError(RPC_INTERNAL_ERROR, "Unable to read UTXO set");
931+
}
932+
prev_stats = maybe_prev_stats.value();
929933
}
930934

931935
UniValue block_info(UniValue::VOBJ);
@@ -2285,7 +2289,7 @@ UniValue CreateUTXOSnapshot(
22852289
const fs::path& temppath)
22862290
{
22872291
std::unique_ptr<CCoinsViewCursor> pcursor;
2288-
CCoinsStats stats{};
2292+
std::optional<CCoinsStats> maybe_stats;
22892293
const CBlockIndex* tip;
22902294

22912295
{
@@ -2305,19 +2309,20 @@ UniValue CreateUTXOSnapshot(
23052309

23062310
chainstate.ForceFlushStateToDisk();
23072311

2308-
if (!GetUTXOStats(&chainstate.CoinsDB(), chainstate.m_blockman, stats, CoinStatsHashType::HASH_SERIALIZED, node.rpc_interruption_point)) {
2312+
maybe_stats = GetUTXOStats(&chainstate.CoinsDB(), chainstate.m_blockman, CoinStatsHashType::HASH_SERIALIZED, node.rpc_interruption_point);
2313+
if (!maybe_stats) {
23092314
throw JSONRPCError(RPC_INTERNAL_ERROR, "Unable to read UTXO set");
23102315
}
23112316

23122317
pcursor = chainstate.CoinsDB().Cursor();
2313-
tip = CHECK_NONFATAL(chainstate.m_blockman.LookupBlockIndex(stats.hashBlock));
2318+
tip = CHECK_NONFATAL(chainstate.m_blockman.LookupBlockIndex(maybe_stats->hashBlock));
23142319
}
23152320

23162321
LOG_TIME_SECONDS(strprintf("writing UTXO snapshot at height %s (%s) to file %s (via %s)",
23172322
tip->nHeight, tip->GetBlockHash().ToString(),
23182323
fs::PathToString(path), fs::PathToString(temppath)));
23192324

2320-
SnapshotMetadata metadata{tip->GetBlockHash(), stats.coins_count, tip->nChainTx};
2325+
SnapshotMetadata metadata{tip->GetBlockHash(), maybe_stats->coins_count, tip->nChainTx};
23212326

23222327
afile << metadata;
23232328

@@ -2339,11 +2344,11 @@ UniValue CreateUTXOSnapshot(
23392344
afile.fclose();
23402345

23412346
UniValue result(UniValue::VOBJ);
2342-
result.pushKV("coins_written", stats.coins_count);
2347+
result.pushKV("coins_written", maybe_stats->coins_count);
23432348
result.pushKV("base_hash", tip->GetBlockHash().ToString());
23442349
result.pushKV("base_height", tip->nHeight);
23452350
result.pushKV("path", path.u8string());
2346-
result.pushKV("txoutset_hash", stats.hashSerialized.ToString());
2351+
result.pushKV("txoutset_hash", maybe_stats->hashSerialized.ToString());
23472352
// Cast required because univalue doesn't have serialization specified for
23482353
// `unsigned int`, nChainTx's type.
23492354
result.pushKV("nchaintx", uint64_t{tip->nChainTx});

src/validation.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5095,22 +5095,22 @@ bool ChainstateManager::PopulateAndValidateSnapshot(
50955095

50965096
assert(coins_cache.GetBestBlock() == base_blockhash);
50975097

5098-
CCoinsStats stats{};
50995098
auto breakpoint_fnc = [] { /* TODO insert breakpoint here? */ };
51005099

51015100
// As above, okay to immediately release cs_main here since no other context knows
51025101
// about the snapshot_chainstate.
51035102
CCoinsViewDB* snapshot_coinsdb = WITH_LOCK(::cs_main, return &snapshot_chainstate.CoinsDB());
51045103

5105-
if (!GetUTXOStats(snapshot_coinsdb, m_blockman, stats, CoinStatsHashType::HASH_SERIALIZED, breakpoint_fnc)) {
5104+
const std::optional<CCoinsStats> maybe_stats = GetUTXOStats(snapshot_coinsdb, m_blockman, CoinStatsHashType::HASH_SERIALIZED, breakpoint_fnc);
5105+
if (!maybe_stats.has_value()) {
51065106
LogPrintf("[snapshot] failed to generate coins stats\n");
51075107
return false;
51085108
}
51095109

51105110
// Assert that the deserialized chainstate contents match the expected assumeutxo value.
5111-
if (AssumeutxoHash{stats.hashSerialized} != au_data.hash_serialized) {
5111+
if (AssumeutxoHash{maybe_stats->hashSerialized} != au_data.hash_serialized) {
51125112
LogPrintf("[snapshot] bad snapshot content hash: expected %s, got %s\n",
5113-
au_data.hash_serialized.ToString(), stats.hashSerialized.ToString());
5113+
au_data.hash_serialized.ToString(), maybe_stats->hashSerialized.ToString());
51145114
return false;
51155115
}
51165116

0 commit comments

Comments
 (0)