mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-02-22 06:52:36 +01:00
rpc: Add hash_type MUHASH to gettxoutsetinfo
Also small style fix in rpc/util.cpp
This commit is contained in:
parent
2474645f3b
commit
0d3b2f643d
6 changed files with 59 additions and 23 deletions
|
@ -6,6 +6,7 @@
|
||||||
#include <node/coinstats.h>
|
#include <node/coinstats.h>
|
||||||
|
|
||||||
#include <coins.h>
|
#include <coins.h>
|
||||||
|
#include <crypto/muhash.h>
|
||||||
#include <hash.h>
|
#include <hash.h>
|
||||||
#include <serialize.h>
|
#include <serialize.h>
|
||||||
#include <uint256.h>
|
#include <uint256.h>
|
||||||
|
@ -42,8 +43,20 @@ static void ApplyHash(CCoinsStats& stats, CHashWriter& ss, const uint256& hash,
|
||||||
|
|
||||||
static void ApplyHash(CCoinsStats& stats, std::nullptr_t, const uint256& hash, const std::map<uint32_t, Coin>& outputs, std::map<uint32_t, Coin>::const_iterator it) {}
|
static void ApplyHash(CCoinsStats& stats, std::nullptr_t, const uint256& hash, const std::map<uint32_t, Coin>& outputs, std::map<uint32_t, Coin>::const_iterator it) {}
|
||||||
|
|
||||||
|
static void ApplyHash(CCoinsStats& stats, MuHash3072& muhash, const uint256& hash, const std::map<uint32_t, Coin>& outputs, std::map<uint32_t, Coin>::const_iterator it)
|
||||||
|
{
|
||||||
|
COutPoint outpoint = COutPoint(hash, it->first);
|
||||||
|
Coin coin = it->second;
|
||||||
|
|
||||||
|
CDataStream ss(SER_DISK, PROTOCOL_VERSION);
|
||||||
|
ss << outpoint;
|
||||||
|
ss << static_cast<uint32_t>(coin.nHeight * 2 + coin.fCoinBase);
|
||||||
|
ss << coin.out;
|
||||||
|
muhash.Insert(MakeUCharSpan(ss));
|
||||||
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
static void ApplyStats(CCoinsStats &stats, T& hash_obj, const uint256& hash, const std::map<uint32_t, Coin>& outputs)
|
static void ApplyStats(CCoinsStats& stats, T& hash_obj, const uint256& hash, const std::map<uint32_t, Coin>& outputs)
|
||||||
{
|
{
|
||||||
assert(!outputs.empty());
|
assert(!outputs.empty());
|
||||||
stats.nTransactions++;
|
stats.nTransactions++;
|
||||||
|
@ -108,6 +121,10 @@ bool GetUTXOStats(CCoinsView* view, CCoinsStats& stats, CoinStatsHashType hash_t
|
||||||
CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION);
|
CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION);
|
||||||
return GetUTXOStats(view, stats, ss, interruption_point);
|
return GetUTXOStats(view, stats, ss, interruption_point);
|
||||||
}
|
}
|
||||||
|
case(CoinStatsHashType::MUHASH): {
|
||||||
|
MuHash3072 muhash;
|
||||||
|
return GetUTXOStats(view, stats, muhash, interruption_point);
|
||||||
|
}
|
||||||
case(CoinStatsHashType::NONE): {
|
case(CoinStatsHashType::NONE): {
|
||||||
return GetUTXOStats(view, stats, nullptr, interruption_point);
|
return GetUTXOStats(view, stats, nullptr, interruption_point);
|
||||||
}
|
}
|
||||||
|
@ -120,10 +137,18 @@ static void PrepareHash(CHashWriter& ss, const CCoinsStats& stats)
|
||||||
{
|
{
|
||||||
ss << stats.hashBlock;
|
ss << stats.hashBlock;
|
||||||
}
|
}
|
||||||
|
// MuHash does not need the prepare step
|
||||||
|
static void PrepareHash(MuHash3072& muhash, CCoinsStats& stats) {}
|
||||||
static void PrepareHash(std::nullptr_t, CCoinsStats& stats) {}
|
static void PrepareHash(std::nullptr_t, CCoinsStats& stats) {}
|
||||||
|
|
||||||
static void FinalizeHash(CHashWriter& ss, CCoinsStats& stats)
|
static void FinalizeHash(CHashWriter& ss, CCoinsStats& stats)
|
||||||
{
|
{
|
||||||
stats.hashSerialized = ss.GetHash();
|
stats.hashSerialized = ss.GetHash();
|
||||||
}
|
}
|
||||||
|
static void FinalizeHash(MuHash3072& muhash, CCoinsStats& stats)
|
||||||
|
{
|
||||||
|
uint256 out;
|
||||||
|
muhash.Finalize(out);
|
||||||
|
stats.hashSerialized = out;
|
||||||
|
}
|
||||||
static void FinalizeHash(std::nullptr_t, CCoinsStats& stats) {}
|
static void FinalizeHash(std::nullptr_t, CCoinsStats& stats) {}
|
||||||
|
|
|
@ -16,6 +16,7 @@ class CCoinsView;
|
||||||
|
|
||||||
enum class CoinStatsHashType {
|
enum class CoinStatsHashType {
|
||||||
HASH_SERIALIZED,
|
HASH_SERIALIZED,
|
||||||
|
MUHASH,
|
||||||
NONE,
|
NONE,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1047,13 +1047,26 @@ static RPCHelpMan pruneblockchain()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CoinStatsHashType ParseHashType(const std::string& hash_type_input)
|
||||||
|
{
|
||||||
|
if (hash_type_input == "hash_serialized_2") {
|
||||||
|
return CoinStatsHashType::HASH_SERIALIZED;
|
||||||
|
} else if (hash_type_input == "muhash") {
|
||||||
|
return CoinStatsHashType::MUHASH;
|
||||||
|
} else if (hash_type_input == "none") {
|
||||||
|
return CoinStatsHashType::NONE;
|
||||||
|
} else {
|
||||||
|
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("%s is not a valid hash_type", hash_type_input));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static RPCHelpMan gettxoutsetinfo()
|
static RPCHelpMan gettxoutsetinfo()
|
||||||
{
|
{
|
||||||
return RPCHelpMan{"gettxoutsetinfo",
|
return RPCHelpMan{"gettxoutsetinfo",
|
||||||
"\nReturns statistics about the unspent transaction output set.\n"
|
"\nReturns statistics about the unspent transaction output set.\n"
|
||||||
"Note this call may take some time.\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), 'none'."},
|
{"hash_type", RPCArg::Type::STR, /* default */ "hash_serialized_2", "Which UTXO set hash should be calculated. Options: 'hash_serialized_2' (the legacy algorithm), 'muhash', 'none'."},
|
||||||
},
|
},
|
||||||
RPCResult{
|
RPCResult{
|
||||||
RPCResult::Type::OBJ, "", "",
|
RPCResult::Type::OBJ, "", "",
|
||||||
|
@ -1063,7 +1076,8 @@ static RPCHelpMan gettxoutsetinfo()
|
||||||
{RPCResult::Type::NUM, "transactions", "The number of transactions with unspent outputs"},
|
{RPCResult::Type::NUM, "transactions", "The number of transactions with unspent outputs"},
|
||||||
{RPCResult::Type::NUM, "txouts", "The number of unspent transaction outputs"},
|
{RPCResult::Type::NUM, "txouts", "The number of unspent transaction outputs"},
|
||||||
{RPCResult::Type::NUM, "bogosize", "A meaningless metric for UTXO set size"},
|
{RPCResult::Type::NUM, "bogosize", "A meaningless metric for UTXO set size"},
|
||||||
{RPCResult::Type::STR_HEX, "hash_serialized_2", "The serialized hash (only present if 'hash_serialized_2' hash_type is chosen)"},
|
{RPCResult::Type::STR_HEX, "hash_serialized_2", /* optional */ true, "The serialized hash (only present if 'hash_serialized_2' hash_type is chosen)"},
|
||||||
|
{RPCResult::Type::STR_HEX, "muhash", /* optional */ true, "The serialized hash (only present if 'muhash' hash_type is chosen)"},
|
||||||
{RPCResult::Type::NUM, "disk_size", "The estimated size of the chainstate on disk"},
|
{RPCResult::Type::NUM, "disk_size", "The estimated size of the chainstate on disk"},
|
||||||
{RPCResult::Type::STR_AMOUNT, "total_amount", "The total amount"},
|
{RPCResult::Type::STR_AMOUNT, "total_amount", "The total amount"},
|
||||||
}},
|
}},
|
||||||
|
@ -1078,7 +1092,7 @@ static RPCHelpMan gettxoutsetinfo()
|
||||||
CCoinsStats stats;
|
CCoinsStats stats;
|
||||||
::ChainstateActive().ForceFlushStateToDisk();
|
::ChainstateActive().ForceFlushStateToDisk();
|
||||||
|
|
||||||
const CoinStatsHashType hash_type = ParseHashType(request.params[0], CoinStatsHashType::HASH_SERIALIZED);
|
const CoinStatsHashType hash_type{request.params[0].isNull() ? CoinStatsHashType::HASH_SERIALIZED : ParseHashType(request.params[0].get_str())};
|
||||||
|
|
||||||
CCoinsView* coins_view = WITH_LOCK(cs_main, return &ChainstateActive().CoinsDB());
|
CCoinsView* coins_view = WITH_LOCK(cs_main, return &ChainstateActive().CoinsDB());
|
||||||
NodeContext& node = EnsureNodeContext(request.context);
|
NodeContext& node = EnsureNodeContext(request.context);
|
||||||
|
@ -1091,6 +1105,9 @@ static RPCHelpMan gettxoutsetinfo()
|
||||||
if (hash_type == CoinStatsHashType::HASH_SERIALIZED) {
|
if (hash_type == CoinStatsHashType::HASH_SERIALIZED) {
|
||||||
ret.pushKV("hash_serialized_2", stats.hashSerialized.GetHex());
|
ret.pushKV("hash_serialized_2", stats.hashSerialized.GetHex());
|
||||||
}
|
}
|
||||||
|
if (hash_type == CoinStatsHashType::MUHASH) {
|
||||||
|
ret.pushKV("muhash", stats.hashSerialized.GetHex());
|
||||||
|
}
|
||||||
ret.pushKV("disk_size", stats.nDiskSize);
|
ret.pushKV("disk_size", stats.nDiskSize);
|
||||||
ret.pushKV("total_amount", ValueFromAmount(stats.nTotalAmount));
|
ret.pushKV("total_amount", ValueFromAmount(stats.nTotalAmount));
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -113,23 +113,6 @@ std::vector<unsigned char> ParseHexO(const UniValue& o, std::string strKey)
|
||||||
return ParseHexV(find_value(o, strKey), 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 if (hash_type_input == "none") {
|
|
||||||
return CoinStatsHashType::NONE;
|
|
||||||
} 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)
|
std::string HelpExampleCli(const std::string& methodname, const std::string& args)
|
||||||
{
|
{
|
||||||
return "> bitcoin-cli " + methodname + " " + args + "\n";
|
return "> bitcoin-cli " + methodname + " " + args + "\n";
|
||||||
|
|
|
@ -77,8 +77,6 @@ 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> ParseHexV(const UniValue& v, std::string strName);
|
||||||
extern std::vector<unsigned char> ParseHexO(const UniValue& o, std::string strKey);
|
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 CAmount AmountFromValue(const UniValue& value);
|
||||||
extern std::string HelpExampleCli(const std::string& methodname, const std::string& args);
|
extern std::string HelpExampleCli(const std::string& methodname, const std::string& args);
|
||||||
extern std::string HelpExampleRpc(const std::string& methodname, const std::string& args);
|
extern std::string HelpExampleRpc(const std::string& methodname, const std::string& args);
|
||||||
|
|
|
@ -268,6 +268,18 @@ class BlockchainTest(BitcoinTestFramework):
|
||||||
res5 = node.gettxoutsetinfo(hash_type='none')
|
res5 = node.gettxoutsetinfo(hash_type='none')
|
||||||
assert 'hash_serialized_2' not in res5
|
assert 'hash_serialized_2' not in res5
|
||||||
|
|
||||||
|
# hash_type muhash should return a different UTXO set hash.
|
||||||
|
res6 = node.gettxoutsetinfo(hash_type='muhash')
|
||||||
|
assert 'muhash' in res6
|
||||||
|
assert(res['hash_serialized_2'] != res6['muhash'])
|
||||||
|
|
||||||
|
# muhash should not be included in gettxoutset unless requested.
|
||||||
|
for r in [res, res2, res3, res4, res5]:
|
||||||
|
assert 'muhash' not in r
|
||||||
|
|
||||||
|
# Unknown hash_type raises an error
|
||||||
|
assert_raises_rpc_error(-8, "foohash is not a valid hash_type", node.gettxoutsetinfo, "foohash")
|
||||||
|
|
||||||
def _test_getblockheader(self):
|
def _test_getblockheader(self):
|
||||||
node = self.nodes[0]
|
node = self.nodes[0]
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue