rpc: gettxoutsetinfo can specify hash_type (only legacy option for now)

This commit is contained in:
Fabian Jahr 2020-06-02 23:52:34 +02:00
parent 605884ef21
commit a712cf6f68
No known key found for this signature in database
GPG key ID: F13D1E9D890798CD
6 changed files with 69 additions and 15 deletions

View file

@ -24,7 +24,7 @@ static uint64_t GetBogoSize(const CScript& scriptPubKey)
scriptPubKey.size() /* scriptPubKey */;
}
static void ApplyStats(CCoinsStats &stats, CHashWriter& ss, const uint256& hash, const std::map<uint32_t, Coin>& outputs)
static void ApplyStats(CCoinsStats& stats, CHashWriter& ss, const uint256& hash, const std::map<uint32_t, Coin>& outputs)
{
assert(!outputs.empty());
ss << hash;
@ -42,19 +42,21 @@ static void ApplyStats(CCoinsStats &stats, CHashWriter& ss, const uint256& hash,
}
//! Calculate statistics about the unspent transaction output set
bool GetUTXOStats(CCoinsView* view, CCoinsStats& stats, const std::function<void()>& interruption_point)
template <typename T>
static bool GetUTXOStats(CCoinsView* view, CCoinsStats& stats, T hash_obj, const std::function<void()>& interruption_point)
{
stats = CCoinsStats();
std::unique_ptr<CCoinsViewCursor> pcursor(view->Cursor());
assert(pcursor);
CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION);
stats.hashBlock = pcursor->GetBestBlock();
{
LOCK(cs_main);
stats.nHeight = LookupBlockIndex(stats.hashBlock)->nHeight;
}
ss << stats.hashBlock;
PrepareHash(hash_obj, stats);
uint256 prevkey;
std::map<uint32_t, Coin> outputs;
while (pcursor->Valid()) {
@ -63,7 +65,7 @@ bool GetUTXOStats(CCoinsView* view, CCoinsStats& stats, const std::function<void
Coin coin;
if (pcursor->GetKey(key) && pcursor->GetValue(coin)) {
if (!outputs.empty() && key.hash != prevkey) {
ApplyStats(stats, ss, prevkey, outputs);
ApplyStats(stats, hash_obj, prevkey, outputs);
outputs.clear();
}
prevkey = key.hash;
@ -75,9 +77,33 @@ bool GetUTXOStats(CCoinsView* view, CCoinsStats& stats, const std::function<void
pcursor->Next();
}
if (!outputs.empty()) {
ApplyStats(stats, ss, prevkey, outputs);
ApplyStats(stats, hash_obj, prevkey, outputs);
}
stats.hashSerialized = ss.GetHash();
FinalizeHash(hash_obj, stats);
stats.nDiskSize = view->EstimateSize();
return true;
}
bool GetUTXOStats(CCoinsView* view, CCoinsStats& stats, CoinStatsHashType hash_type, const std::function<void()>& interruption_point)
{
switch (hash_type) {
case(CoinStatsHashType::HASH_SERIALIZED): {
CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION);
return GetUTXOStats(view, stats, ss, interruption_point);
}
} // no default case, so the compiler can warn about missing cases
assert(false);
}
// The legacy hash serializes the hashBlock
static void PrepareHash(CHashWriter& ss, CCoinsStats& stats)
{
ss << stats.hashBlock;
}
static void FinalizeHash(CHashWriter& ss, CCoinsStats& stats)
{
stats.hashSerialized = ss.GetHash();
}

View file

@ -14,6 +14,10 @@
class CCoinsView;
enum class CoinStatsHashType {
HASH_SERIALIZED,
};
struct CCoinsStats
{
int nHeight{0};
@ -30,6 +34,6 @@ struct CCoinsStats
};
//! Calculate statistics about the unspent transaction output set
bool GetUTXOStats(CCoinsView* view, CCoinsStats& stats, const std::function<void()>& interruption_point = {});
bool GetUTXOStats(CCoinsView* view, CCoinsStats& stats, const CoinStatsHashType hash_type, const std::function<void()>& interruption_point = {});
#endif // BITCOIN_NODE_COINSTATS_H

View file

@ -972,7 +972,9 @@ static UniValue gettxoutsetinfo(const JSONRPCRequest& request)
RPCHelpMan{"gettxoutsetinfo",
"\nReturns statistics about the unspent transaction output set.\n"
"Note this call may take some time.\n",
{},
{
{"hash_type", RPCArg::Type::STR, /* default */ "hash_serialized_2", "Which UTXO set hash should be calculated. Options: 'hash_serialized_2' (the legacy algorithm)."},
},
RPCResult{
RPCResult::Type::OBJ, "", "",
{
@ -981,7 +983,7 @@ static UniValue gettxoutsetinfo(const JSONRPCRequest& request)
{RPCResult::Type::NUM, "transactions", "The number of transactions with unspent outputs"},
{RPCResult::Type::NUM, "txouts", "The number of unspent transaction outputs"},
{RPCResult::Type::NUM, "bogosize", "A meaningless metric for UTXO set size"},
{RPCResult::Type::STR_HEX, "hash_serialized_2", "The serialized hash"},
{RPCResult::Type::STR_HEX, "hash_serialized_2", "The serialized hash (only present if 'hash_serialized_2' hash_type is chosen)"},
{RPCResult::Type::NUM, "disk_size", "The estimated size of the chainstate on disk"},
{RPCResult::Type::STR_AMOUNT, "total_amount", "The total amount"},
}},
@ -996,14 +998,18 @@ static UniValue gettxoutsetinfo(const JSONRPCRequest& request)
CCoinsStats stats;
::ChainstateActive().ForceFlushStateToDisk();
const CoinStatsHashType hash_type = ParseHashType(request.params[0], CoinStatsHashType::HASH_SERIALIZED);
CCoinsView* coins_view = WITH_LOCK(cs_main, return &ChainstateActive().CoinsDB());
if (GetUTXOStats(coins_view, stats, RpcInterruptionPoint)) {
if (GetUTXOStats(coins_view, stats, hash_type, RpcInterruptionPoint)) {
ret.pushKV("height", (int64_t)stats.nHeight);
ret.pushKV("bestblock", stats.hashBlock.GetHex());
ret.pushKV("transactions", (int64_t)stats.nTransactions);
ret.pushKV("txouts", (int64_t)stats.nTransactionOutputs);
ret.pushKV("bogosize", (int64_t)stats.nBogoSize);
ret.pushKV("hash_serialized_2", stats.hashSerialized.GetHex());
if (hash_type == CoinStatsHashType::HASH_SERIALIZED) {
ret.pushKV("hash_serialized_2", stats.hashSerialized.GetHex());
}
ret.pushKV("disk_size", stats.nDiskSize);
ret.pushKV("total_amount", ValueFromAmount(stats.nTotalAmount));
} else {
@ -2316,7 +2322,7 @@ UniValue dumptxoutset(const JSONRPCRequest& request)
::ChainstateActive().ForceFlushStateToDisk();
if (!GetUTXOStats(&::ChainstateActive().CoinsDB(), stats, RpcInterruptionPoint)) {
if (!GetUTXOStats(&::ChainstateActive().CoinsDB(), stats, CoinStatsHashType::HASH_SERIALIZED, RpcInterruptionPoint)) {
throw JSONRPCError(RPC_INTERNAL_ERROR, "Unable to read UTXO set");
}
@ -2377,7 +2383,7 @@ static const CRPCCommand commands[] =
{ "blockchain", "getmempoolinfo", &getmempoolinfo, {} },
{ "blockchain", "getrawmempool", &getrawmempool, {"verbose"} },
{ "blockchain", "gettxout", &gettxout, {"txid","n","include_mempool"} },
{ "blockchain", "gettxoutsetinfo", &gettxoutsetinfo, {} },
{ "blockchain", "gettxoutsetinfo", &gettxoutsetinfo, {"hash_type"} },
{ "blockchain", "pruneblockchain", &pruneblockchain, {"height"} },
{ "blockchain", "savemempool", &savemempool, {} },
{ "blockchain", "verifychain", &verifychain, {"checklevel","nblocks"} },

View file

@ -113,6 +113,21 @@ std::vector<unsigned char> ParseHexO(const UniValue& o, std::string strKey)
return ParseHexV(find_value(o, strKey), strKey);
}
CoinStatsHashType ParseHashType(const UniValue& param, const CoinStatsHashType default_type)
{
if (param.isNull()) {
return default_type;
} else {
std::string hash_type_input = param.get_str();
if (hash_type_input == "hash_serialized_2") {
return CoinStatsHashType::HASH_SERIALIZED;
} else {
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("%d is not a valid hash_type", hash_type_input));
}
}
}
std::string HelpExampleCli(const std::string& methodname, const std::string& args)
{
return "> bitcoin-cli " + methodname + " " + args + "\n";

View file

@ -5,6 +5,7 @@
#ifndef BITCOIN_RPC_UTIL_H
#define BITCOIN_RPC_UTIL_H
#include <node/coinstats.h>
#include <node/transaction.h>
#include <outputtype.h>
#include <protocol.h>
@ -77,6 +78,8 @@ extern uint256 ParseHashO(const UniValue& o, std::string strKey);
extern std::vector<unsigned char> ParseHexV(const UniValue& v, std::string strName);
extern std::vector<unsigned char> ParseHexO(const UniValue& o, std::string strKey);
CoinStatsHashType ParseHashType(const UniValue& param, const CoinStatsHashType default_type);
extern CAmount AmountFromValue(const UniValue& value);
extern std::string HelpExampleCli(const std::string& methodname, const std::string& args);
extern std::string HelpExampleRpc(const std::string& methodname, const std::string& args);

View file

@ -278,7 +278,7 @@ void test_one_input(const std::vector<uint8_t>& buffer)
CCoinsStats stats;
bool expected_code_path = false;
try {
(void)GetUTXOStats(&coins_view_cache, stats);
(void)GetUTXOStats(&coins_view_cache, stats, CoinStatsHashType::HASH_SERIALIZED);
} catch (const std::logic_error&) {
expected_code_path = true;
}