mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-03-05 14:06:27 -05:00
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.
This commit is contained in:
parent
46eb9fc56a
commit
524463daf6
4 changed files with 48 additions and 33 deletions
|
@ -9,6 +9,7 @@
|
||||||
#include <crypto/muhash.h>
|
#include <crypto/muhash.h>
|
||||||
#include <hash.h>
|
#include <hash.h>
|
||||||
#include <index/coinstatsindex.h>
|
#include <index/coinstatsindex.h>
|
||||||
|
#include <optional>
|
||||||
#include <serialize.h>
|
#include <serialize.h>
|
||||||
#include <uint256.h>
|
#include <uint256.h>
|
||||||
#include <util/overflow.h>
|
#include <util/overflow.h>
|
||||||
|
@ -144,8 +145,11 @@ static bool GetUTXOStats(CCoinsView* view, BlockManager& blockman, CCoinsStats&
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GetUTXOStats(CCoinsView* view, BlockManager& blockman, CCoinsStats& stats, CoinStatsHashType hash_type, const std::function<void()>& interruption_point, const CBlockIndex* pindex, bool index_requested)
|
std::optional<CCoinsStats> GetUTXOStats(CCoinsView* view, BlockManager& blockman, CoinStatsHashType hash_type, const std::function<void()>& interruption_point, const CBlockIndex* pindex, bool index_requested)
|
||||||
{
|
{
|
||||||
|
CCoinsStats stats{};
|
||||||
|
|
||||||
|
bool success = [&]() -> bool {
|
||||||
switch (hash_type) {
|
switch (hash_type) {
|
||||||
case(CoinStatsHashType::HASH_SERIALIZED): {
|
case(CoinStatsHashType::HASH_SERIALIZED): {
|
||||||
CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION);
|
CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION);
|
||||||
|
@ -160,6 +164,12 @@ bool GetUTXOStats(CCoinsView* view, BlockManager& blockman, CCoinsStats& stats,
|
||||||
}
|
}
|
||||||
} // no default case, so the compiler can warn about missing cases
|
} // no default case, so the compiler can warn about missing cases
|
||||||
assert(false);
|
assert(false);
|
||||||
|
}();
|
||||||
|
|
||||||
|
if (!success) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
return stats;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The legacy hash serializes the hashBlock
|
// The legacy hash serializes the hashBlock
|
||||||
|
|
|
@ -71,8 +71,8 @@ struct CCoinsStats {
|
||||||
*
|
*
|
||||||
* @param[in] index_requested Signals if the coinstatsindex should be used (when available).
|
* @param[in] index_requested Signals if the coinstatsindex should be used (when available).
|
||||||
*/
|
*/
|
||||||
bool GetUTXOStats(CCoinsView* view, node::BlockManager& blockman,
|
std::optional<CCoinsStats> GetUTXOStats(CCoinsView* view, node::BlockManager& blockman,
|
||||||
CCoinsStats& stats, CoinStatsHashType hash_type,
|
CoinStatsHashType hash_type,
|
||||||
const std::function<void()>& interruption_point = {},
|
const std::function<void()>& interruption_point = {},
|
||||||
const CBlockIndex* pindex = nullptr,
|
const CBlockIndex* pindex = nullptr,
|
||||||
bool index_requested = true);
|
bool index_requested = true);
|
||||||
|
|
|
@ -862,7 +862,6 @@ static RPCHelpMan gettxoutsetinfo()
|
||||||
|
|
||||||
const CBlockIndex* pindex{nullptr};
|
const CBlockIndex* pindex{nullptr};
|
||||||
const CoinStatsHashType hash_type{request.params[0].isNull() ? CoinStatsHashType::HASH_SERIALIZED : ParseHashType(request.params[0].get_str())};
|
const CoinStatsHashType hash_type{request.params[0].isNull() ? CoinStatsHashType::HASH_SERIALIZED : ParseHashType(request.params[0].get_str())};
|
||||||
CCoinsStats stats{};
|
|
||||||
bool index_requested = request.params[2].isNull() || request.params[2].get_bool();
|
bool index_requested = request.params[2].isNull() || request.params[2].get_bool();
|
||||||
|
|
||||||
NodeContext& node = EnsureAnyNodeContext(request.context);
|
NodeContext& node = EnsureAnyNodeContext(request.context);
|
||||||
|
@ -903,7 +902,9 @@ static RPCHelpMan gettxoutsetinfo()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (GetUTXOStats(coins_view, *blockman, stats, hash_type, node.rpc_interruption_point, pindex, index_requested)) {
|
const std::optional<CCoinsStats> maybe_stats = GetUTXOStats(coins_view, *blockman, hash_type, node.rpc_interruption_point, pindex, index_requested);
|
||||||
|
if (maybe_stats.has_value()) {
|
||||||
|
const CCoinsStats& stats = maybe_stats.value();
|
||||||
ret.pushKV("height", (int64_t)stats.nHeight);
|
ret.pushKV("height", (int64_t)stats.nHeight);
|
||||||
ret.pushKV("bestblock", stats.hashBlock.GetHex());
|
ret.pushKV("bestblock", stats.hashBlock.GetHex());
|
||||||
ret.pushKV("txouts", (int64_t)stats.nTransactionOutputs);
|
ret.pushKV("txouts", (int64_t)stats.nTransactionOutputs);
|
||||||
|
@ -923,9 +924,12 @@ static RPCHelpMan gettxoutsetinfo()
|
||||||
ret.pushKV("total_unspendable_amount", ValueFromAmount(stats.total_unspendable_amount));
|
ret.pushKV("total_unspendable_amount", ValueFromAmount(stats.total_unspendable_amount));
|
||||||
|
|
||||||
CCoinsStats prev_stats{};
|
CCoinsStats prev_stats{};
|
||||||
|
|
||||||
if (pindex->nHeight > 0) {
|
if (pindex->nHeight > 0) {
|
||||||
GetUTXOStats(coins_view, *blockman, prev_stats, hash_type, node.rpc_interruption_point, pindex->pprev, index_requested);
|
const std::optional<CCoinsStats> maybe_prev_stats = GetUTXOStats(coins_view, *blockman, hash_type, node.rpc_interruption_point, pindex->pprev, index_requested);
|
||||||
|
if (!maybe_prev_stats) {
|
||||||
|
throw JSONRPCError(RPC_INTERNAL_ERROR, "Unable to read UTXO set");
|
||||||
|
}
|
||||||
|
prev_stats = maybe_prev_stats.value();
|
||||||
}
|
}
|
||||||
|
|
||||||
UniValue block_info(UniValue::VOBJ);
|
UniValue block_info(UniValue::VOBJ);
|
||||||
|
@ -2285,7 +2289,7 @@ UniValue CreateUTXOSnapshot(
|
||||||
const fs::path& temppath)
|
const fs::path& temppath)
|
||||||
{
|
{
|
||||||
std::unique_ptr<CCoinsViewCursor> pcursor;
|
std::unique_ptr<CCoinsViewCursor> pcursor;
|
||||||
CCoinsStats stats{};
|
std::optional<CCoinsStats> maybe_stats;
|
||||||
const CBlockIndex* tip;
|
const CBlockIndex* tip;
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -2305,19 +2309,20 @@ UniValue CreateUTXOSnapshot(
|
||||||
|
|
||||||
chainstate.ForceFlushStateToDisk();
|
chainstate.ForceFlushStateToDisk();
|
||||||
|
|
||||||
if (!GetUTXOStats(&chainstate.CoinsDB(), chainstate.m_blockman, stats, CoinStatsHashType::HASH_SERIALIZED, node.rpc_interruption_point)) {
|
maybe_stats = GetUTXOStats(&chainstate.CoinsDB(), chainstate.m_blockman, CoinStatsHashType::HASH_SERIALIZED, node.rpc_interruption_point);
|
||||||
|
if (!maybe_stats) {
|
||||||
throw JSONRPCError(RPC_INTERNAL_ERROR, "Unable to read UTXO set");
|
throw JSONRPCError(RPC_INTERNAL_ERROR, "Unable to read UTXO set");
|
||||||
}
|
}
|
||||||
|
|
||||||
pcursor = chainstate.CoinsDB().Cursor();
|
pcursor = chainstate.CoinsDB().Cursor();
|
||||||
tip = CHECK_NONFATAL(chainstate.m_blockman.LookupBlockIndex(stats.hashBlock));
|
tip = CHECK_NONFATAL(chainstate.m_blockman.LookupBlockIndex(maybe_stats->hashBlock));
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_TIME_SECONDS(strprintf("writing UTXO snapshot at height %s (%s) to file %s (via %s)",
|
LOG_TIME_SECONDS(strprintf("writing UTXO snapshot at height %s (%s) to file %s (via %s)",
|
||||||
tip->nHeight, tip->GetBlockHash().ToString(),
|
tip->nHeight, tip->GetBlockHash().ToString(),
|
||||||
fs::PathToString(path), fs::PathToString(temppath)));
|
fs::PathToString(path), fs::PathToString(temppath)));
|
||||||
|
|
||||||
SnapshotMetadata metadata{tip->GetBlockHash(), stats.coins_count, tip->nChainTx};
|
SnapshotMetadata metadata{tip->GetBlockHash(), maybe_stats->coins_count, tip->nChainTx};
|
||||||
|
|
||||||
afile << metadata;
|
afile << metadata;
|
||||||
|
|
||||||
|
@ -2339,11 +2344,11 @@ UniValue CreateUTXOSnapshot(
|
||||||
afile.fclose();
|
afile.fclose();
|
||||||
|
|
||||||
UniValue result(UniValue::VOBJ);
|
UniValue result(UniValue::VOBJ);
|
||||||
result.pushKV("coins_written", stats.coins_count);
|
result.pushKV("coins_written", maybe_stats->coins_count);
|
||||||
result.pushKV("base_hash", tip->GetBlockHash().ToString());
|
result.pushKV("base_hash", tip->GetBlockHash().ToString());
|
||||||
result.pushKV("base_height", tip->nHeight);
|
result.pushKV("base_height", tip->nHeight);
|
||||||
result.pushKV("path", path.u8string());
|
result.pushKV("path", path.u8string());
|
||||||
result.pushKV("txoutset_hash", stats.hashSerialized.ToString());
|
result.pushKV("txoutset_hash", maybe_stats->hashSerialized.ToString());
|
||||||
// Cast required because univalue doesn't have serialization specified for
|
// Cast required because univalue doesn't have serialization specified for
|
||||||
// `unsigned int`, nChainTx's type.
|
// `unsigned int`, nChainTx's type.
|
||||||
result.pushKV("nchaintx", uint64_t{tip->nChainTx});
|
result.pushKV("nchaintx", uint64_t{tip->nChainTx});
|
||||||
|
|
|
@ -5095,22 +5095,22 @@ bool ChainstateManager::PopulateAndValidateSnapshot(
|
||||||
|
|
||||||
assert(coins_cache.GetBestBlock() == base_blockhash);
|
assert(coins_cache.GetBestBlock() == base_blockhash);
|
||||||
|
|
||||||
CCoinsStats stats{};
|
|
||||||
auto breakpoint_fnc = [] { /* TODO insert breakpoint here? */ };
|
auto breakpoint_fnc = [] { /* TODO insert breakpoint here? */ };
|
||||||
|
|
||||||
// As above, okay to immediately release cs_main here since no other context knows
|
// As above, okay to immediately release cs_main here since no other context knows
|
||||||
// about the snapshot_chainstate.
|
// about the snapshot_chainstate.
|
||||||
CCoinsViewDB* snapshot_coinsdb = WITH_LOCK(::cs_main, return &snapshot_chainstate.CoinsDB());
|
CCoinsViewDB* snapshot_coinsdb = WITH_LOCK(::cs_main, return &snapshot_chainstate.CoinsDB());
|
||||||
|
|
||||||
if (!GetUTXOStats(snapshot_coinsdb, m_blockman, stats, CoinStatsHashType::HASH_SERIALIZED, breakpoint_fnc)) {
|
const std::optional<CCoinsStats> maybe_stats = GetUTXOStats(snapshot_coinsdb, m_blockman, CoinStatsHashType::HASH_SERIALIZED, breakpoint_fnc);
|
||||||
|
if (!maybe_stats.has_value()) {
|
||||||
LogPrintf("[snapshot] failed to generate coins stats\n");
|
LogPrintf("[snapshot] failed to generate coins stats\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assert that the deserialized chainstate contents match the expected assumeutxo value.
|
// Assert that the deserialized chainstate contents match the expected assumeutxo value.
|
||||||
if (AssumeutxoHash{stats.hashSerialized} != au_data.hash_serialized) {
|
if (AssumeutxoHash{maybe_stats->hashSerialized} != au_data.hash_serialized) {
|
||||||
LogPrintf("[snapshot] bad snapshot content hash: expected %s, got %s\n",
|
LogPrintf("[snapshot] bad snapshot content hash: expected %s, got %s\n",
|
||||||
au_data.hash_serialized.ToString(), stats.hashSerialized.ToString());
|
au_data.hash_serialized.ToString(), maybe_stats->hashSerialized.ToString());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue