Add per-algorithm mining RPC statistics
This commit is contained in:
parent
00158bc780
commit
8434841e7a
2 changed files with 180 additions and 37 deletions
|
|
@ -42,47 +42,98 @@
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
static const char* GetMiningAlgoName(int algo)
|
||||||
|
{
|
||||||
|
switch (algo) {
|
||||||
|
case ALGO_SHA256D:
|
||||||
|
return "sha256d";
|
||||||
|
case ALGO_RANDOMX:
|
||||||
|
return "randomx";
|
||||||
|
default:
|
||||||
|
return "unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ParseMiningAlgo(const std::string& name)
|
||||||
|
{
|
||||||
|
if (name == "sha256d") {
|
||||||
|
return ALGO_SHA256D;
|
||||||
|
}
|
||||||
|
if (name == "randomx") {
|
||||||
|
return ALGO_RANDOMX;
|
||||||
|
}
|
||||||
|
return ALGO_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return average network hashes per second based on the last 'lookup' blocks,
|
* Return average network hashes per second based on the last 'lookup' blocks,
|
||||||
* or from the last difficulty change if 'lookup' is nonpositive.
|
* or from the last difficulty change if 'lookup' is nonpositive.
|
||||||
* If 'height' is nonnegative, compute the estimate at the time when a given block was found.
|
* If 'height' is nonnegative, compute the estimate at the time when a given block was found.
|
||||||
*/
|
*/
|
||||||
static UniValue GetNetworkHashPS(int lookup, int height, const CChain& active_chain) {
|
static UniValue GetNetworkHashPS(int lookup, int height, const CChain& active_chain, int algo)
|
||||||
|
{
|
||||||
|
if (algo != ALGO_SHA256D && algo != ALGO_RANDOMX) {
|
||||||
|
throw JSONRPCError(RPC_INVALID_PARAMETER, "Unknown mining algorithm");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lookup < -1 || lookup == 0) {
|
||||||
|
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid nblocks. Must be a positive number or -1.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (height < -1 || height > active_chain.Height()) {
|
||||||
|
throw JSONRPCError(RPC_INVALID_PARAMETER, "Block does not exist at specified height");
|
||||||
|
}
|
||||||
|
|
||||||
const CBlockIndex* pb = active_chain.Tip();
|
const CBlockIndex* pb = active_chain.Tip();
|
||||||
|
|
||||||
if (height >= 0 && height < active_chain.Height()) {
|
if (height >= 0 && height < active_chain.Height()) {
|
||||||
pb = active_chain[height];
|
pb = active_chain[height];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pb == nullptr || !pb->nHeight)
|
if (pb == nullptr || pb->nHeight == 0) {
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
// If lookup is -1, then use blocks since last difficulty change.
|
|
||||||
if (lookup <= 0)
|
|
||||||
lookup = pb->nHeight % Params().GetConsensus().nMinerConfirmationWindow + 1;
|
|
||||||
|
|
||||||
// If lookup is larger than chain, then set it to chain length.
|
|
||||||
if (lookup > pb->nHeight)
|
|
||||||
lookup = pb->nHeight;
|
|
||||||
|
|
||||||
const CBlockIndex *pb0 = pb;
|
|
||||||
int64_t minTime = pb0->GetBlockTime();
|
|
||||||
int64_t maxTime = minTime;
|
|
||||||
arith_uint256 workDiff = GetBlockProof(*pb0);
|
|
||||||
|
|
||||||
for (int i = 0; i < lookup; i++) {
|
|
||||||
pb0 = pb0->pprev;
|
|
||||||
int64_t time = pb0->GetBlockTime();
|
|
||||||
minTime = std::min(time, minTime);
|
|
||||||
maxTime = std::max(time, maxTime);
|
|
||||||
workDiff += GetBlockProof(*pb0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// In case there's a situation where minTime == maxTime, we don't want a divide by zero exception.
|
pb = GetLastBlockIndexForAlgoFast(pb, algo);
|
||||||
if (minTime == maxTime)
|
if (pb == nullptr) {
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int64_t timeDiff = maxTime - minTime;
|
if (lookup == -1) {
|
||||||
|
lookup = Params().GetConsensus().difficultyAveragingWindow;
|
||||||
|
}
|
||||||
|
|
||||||
|
const CBlockIndex* first = pb;
|
||||||
|
const CBlockIndex* last = pb;
|
||||||
|
arith_uint256 workDiff{0};
|
||||||
|
int intervalsCounted = 0;
|
||||||
|
|
||||||
|
while (intervalsCounted < lookup) {
|
||||||
|
if (last->pprev == nullptr) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const CBlockIndex* previous =
|
||||||
|
GetLastBlockIndexForAlgoFast(last->pprev, algo);
|
||||||
|
|
||||||
|
if (previous == nullptr) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
workDiff += GetBlockProof(*last);
|
||||||
|
first = previous;
|
||||||
|
last = previous;
|
||||||
|
++intervalsCounted;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (intervalsCounted == 0 || first == pb) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int64_t timeDiff = pb->GetBlockTime() - first->GetBlockTime();
|
||||||
|
if (timeDiff <= 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
return workDiff.getdouble() / timeDiff;
|
return workDiff.getdouble() / timeDiff;
|
||||||
}
|
}
|
||||||
|
|
@ -91,12 +142,12 @@ static RPCHelpMan getnetworkhashps()
|
||||||
{
|
{
|
||||||
return RPCHelpMan{"getnetworkhashps",
|
return RPCHelpMan{"getnetworkhashps",
|
||||||
"\nReturns the estimated network hashes per second based on the last n blocks.\n"
|
"\nReturns the estimated network hashes per second based on the last n blocks.\n"
|
||||||
"Pass in [blocks] to override # of blocks, -1 specifies since last difficulty change.\n"
|
"Pass in [blocks] to override # of intervals, -1 uses the difficulty averaging window.\n"
|
||||||
"Pass in [height] to estimate the network speed at the time when a certain block was found.\n",
|
"Pass in [height] to estimate the network speed at the time when a certain block was found.\n",
|
||||||
{
|
{
|
||||||
{"nblocks", RPCArg::Type::NUM, RPCArg::Default{120}, "The number of blocks, or -1 for blocks since last difficulty change."},
|
{"nblocks", RPCArg::Type::NUM, RPCArg::Default{120}, "The number of same-algorithm block intervals, or -1 for the difficulty averaging window."},
|
||||||
{"height", RPCArg::Type::NUM, RPCArg::Default{-1}, "To estimate at the time of the given height."},
|
{"height", RPCArg::Type::NUM, RPCArg::Default{-1}, "To estimate at the time of the given height."},
|
||||||
{"algo", RPCArg::Type::STR, RPCArg::Default{"sha256d"}, "Only SHA256D is supported."},
|
{"algo", RPCArg::Type::STR, RPCArg::Default{"sha256d"}, "Mining algorithm: sha256d or randomx."},
|
||||||
},
|
},
|
||||||
RPCResult{
|
RPCResult{
|
||||||
RPCResult::Type::NUM, "", "Hashes per second estimated"},
|
RPCResult::Type::NUM, "", "Hashes per second estimated"},
|
||||||
|
|
@ -108,7 +159,22 @@ static RPCHelpMan getnetworkhashps()
|
||||||
{
|
{
|
||||||
ChainstateManager& chainman = EnsureAnyChainman(request.context);
|
ChainstateManager& chainman = EnsureAnyChainman(request.context);
|
||||||
LOCK(cs_main);
|
LOCK(cs_main);
|
||||||
return GetNetworkHashPS(!request.params[0].isNull() ? request.params[0].get_int() : 120, !request.params[1].isNull() ? request.params[1].get_int() : -1, chainman.ActiveChain());
|
|
||||||
|
const std::string algo_name =
|
||||||
|
request.params[2].isNull() ? "sha256d" : request.params[2].get_str();
|
||||||
|
const int algo = ParseMiningAlgo(algo_name);
|
||||||
|
|
||||||
|
if (algo == ALGO_UNKNOWN) {
|
||||||
|
throw JSONRPCError(
|
||||||
|
RPC_INVALID_PARAMETER,
|
||||||
|
strprintf("Unknown mining algorithm: %s", algo_name));
|
||||||
|
}
|
||||||
|
|
||||||
|
return GetNetworkHashPS(
|
||||||
|
!request.params[0].isNull() ? request.params[0].get_int() : 120,
|
||||||
|
!request.params[1].isNull() ? request.params[1].get_int() : -1,
|
||||||
|
chainman.ActiveChain(),
|
||||||
|
algo);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -420,10 +486,12 @@ static RPCHelpMan getmininginfo()
|
||||||
{RPCResult::Type::NUM, "blocks", "The current block"},
|
{RPCResult::Type::NUM, "blocks", "The current block"},
|
||||||
{RPCResult::Type::NUM, "currentblockweight", /* optional */ true, "The block weight of the last assembled block (only present if a block was ever assembled)"},
|
{RPCResult::Type::NUM, "currentblockweight", /* optional */ true, "The block weight of the last assembled block (only present if a block was ever assembled)"},
|
||||||
{RPCResult::Type::NUM, "currentblocktx", /* optional */ true, "The number of block transactions of the last assembled block (only present if a block was ever assembled)"},
|
{RPCResult::Type::NUM, "currentblocktx", /* optional */ true, "The number of block transactions of the last assembled block (only present if a block was ever assembled)"},
|
||||||
{RPCResult::Type::NUM, "difficulty", "The current difficulty."},
|
{RPCResult::Type::NUM, "pow_algo_id", "The legacy/default mining algorithm ID (SHA256D)."},
|
||||||
{RPCResult::Type::NUM, "difficulties", "The current difficulty for all 5 VTX algos."},
|
{RPCResult::Type::STR, "pow_algo", "The legacy/default mining algorithm name (sha256d)."},
|
||||||
{RPCResult::Type::NUM, "networkhashps", "The network hashes per second."},
|
{RPCResult::Type::NUM, "difficulty", "The next-block SHA256D difficulty."},
|
||||||
{RPCResult::Type::NUM, "networkhashesps", "The network hashes per second for all 5 VTX algos."},
|
{RPCResult::Type::OBJ, "difficulties", "The next-block difficulty for each active mining algorithm."},
|
||||||
|
{RPCResult::Type::NUM, "networkhashps", "The estimated SHA256D network hashes per second."},
|
||||||
|
{RPCResult::Type::OBJ, "networkhashesps", "The estimated network hashes per second for each active mining algorithm."},
|
||||||
{RPCResult::Type::NUM, "pooledtx", "The size of the mempool"},
|
{RPCResult::Type::NUM, "pooledtx", "The size of the mempool"},
|
||||||
{RPCResult::Type::STR, "chain", "current network name (main, test, signet, regtest)"},
|
{RPCResult::Type::STR, "chain", "current network name (main, test, signet, regtest)"},
|
||||||
{RPCResult::Type::STR, "warnings", "any network and blockchain warnings"},
|
{RPCResult::Type::STR, "warnings", "any network and blockchain warnings"},
|
||||||
|
|
@ -449,18 +517,54 @@ static RPCHelpMan getmininginfo()
|
||||||
obj.pushKV("blocks", active_chain.Height());
|
obj.pushKV("blocks", active_chain.Height());
|
||||||
if (BlockAssembler::m_last_block_weight) obj.pushKV("currentblockweight", *BlockAssembler::m_last_block_weight);
|
if (BlockAssembler::m_last_block_weight) obj.pushKV("currentblockweight", *BlockAssembler::m_last_block_weight);
|
||||||
if (BlockAssembler::m_last_block_num_txs) obj.pushKV("currentblocktx", *BlockAssembler::m_last_block_num_txs);
|
if (BlockAssembler::m_last_block_num_txs) obj.pushKV("currentblocktx", *BlockAssembler::m_last_block_num_txs);
|
||||||
obj.pushKV("pow_algo_id", 0);
|
obj.pushKV("pow_algo_id", ALGO_SHA256D);
|
||||||
obj.pushKV("pow_algo", "sha256d");
|
obj.pushKV("pow_algo", GetMiningAlgoName(ALGO_SHA256D));
|
||||||
|
|
||||||
const Consensus::Params& consensusParams = Params().GetConsensus();
|
const Consensus::Params& consensusParams = Params().GetConsensus();
|
||||||
|
|
||||||
UniValue difficulties(UniValue::VOBJ);
|
UniValue difficulties(UniValue::VOBJ);
|
||||||
difficulties.pushKV("sha256d", (double)GetDifficulty(tip, NULL));
|
|
||||||
obj.pushKV("difficulty", (double)GetDifficulty(tip, NULL));
|
CBlockIndex sha_next;
|
||||||
|
sha_next.nBits =
|
||||||
|
GetNextWorkRequired(tip, nullptr, consensusParams, ALGO_SHA256D);
|
||||||
|
|
||||||
|
CBlockIndex randomx_next;
|
||||||
|
randomx_next.nBits =
|
||||||
|
GetNextWorkRequired(tip, nullptr, consensusParams, ALGO_RANDOMX);
|
||||||
|
|
||||||
|
const double sha_difficulty =
|
||||||
|
GetDifficulty(nullptr, &sha_next);
|
||||||
|
const double randomx_difficulty =
|
||||||
|
GetDifficulty(nullptr, &randomx_next);
|
||||||
|
|
||||||
|
difficulties.pushKV(
|
||||||
|
GetMiningAlgoName(ALGO_SHA256D),
|
||||||
|
sha_difficulty);
|
||||||
|
|
||||||
|
if (tip->nHeight + 1 >= consensusParams.randomXActivationHeight) {
|
||||||
|
difficulties.pushKV(
|
||||||
|
GetMiningAlgoName(ALGO_RANDOMX),
|
||||||
|
randomx_difficulty);
|
||||||
|
}
|
||||||
|
|
||||||
|
obj.pushKV("difficulty", sha_difficulty);
|
||||||
obj.pushKV("difficulties", difficulties);
|
obj.pushKV("difficulties", difficulties);
|
||||||
|
|
||||||
UniValue networkhashesps(UniValue::VOBJ);
|
UniValue networkhashesps(UniValue::VOBJ);
|
||||||
networkhashesps.pushKV("sha256d", (UniValue)GetNetworkHashPS(120, -1, active_chain));
|
networkhashesps.pushKV(
|
||||||
obj.pushKV("networkhashps", getnetworkhashps().HandleRequest(request));
|
GetMiningAlgoName(ALGO_SHA256D),
|
||||||
obj.pushKV("networkhashesps", networkhashesps);
|
GetNetworkHashPS(120, -1, active_chain, ALGO_SHA256D));
|
||||||
|
|
||||||
|
if (tip->nHeight + 1 >= consensusParams.randomXActivationHeight) {
|
||||||
|
networkhashesps.pushKV(
|
||||||
|
GetMiningAlgoName(ALGO_RANDOMX),
|
||||||
|
GetNetworkHashPS(120, -1, active_chain, ALGO_RANDOMX));
|
||||||
|
}
|
||||||
|
|
||||||
|
obj.pushKV(
|
||||||
|
"networkhashps",
|
||||||
|
GetNetworkHashPS(120, -1, active_chain, ALGO_SHA256D));
|
||||||
|
obj.pushKV("networkhashesps", networkhashesps);
|
||||||
obj.pushKV("pooledtx", (uint64_t)mempool.size());
|
obj.pushKV("pooledtx", (uint64_t)mempool.size());
|
||||||
obj.pushKV("chain", Params().NetworkIDString());
|
obj.pushKV("chain", Params().NetworkIDString());
|
||||||
obj.pushKV("warnings", GetWarnings(false).original);
|
obj.pushKV("warnings", GetWarnings(false).original);
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
# Distributed under the MIT software license, see the accompanying
|
# Distributed under the MIT software license, see the accompanying
|
||||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
"""Test activation-aware RandomX getblocktemplate handling."""
|
"""Test activation-aware RandomX mining RPC handling."""
|
||||||
|
|
||||||
from test_framework.blocktools import NORMAL_GBT_REQUEST_PARAMS
|
from test_framework.blocktools import NORMAL_GBT_REQUEST_PARAMS
|
||||||
from test_framework.test_framework import DigiByteTestFramework
|
from test_framework.test_framework import DigiByteTestFramework
|
||||||
|
|
@ -41,6 +41,33 @@ class RandomXGetBlockTemplateTest(DigiByteTestFramework):
|
||||||
BLOCK_VERSION_SHA256D,
|
BLOCK_VERSION_SHA256D,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
self.log.info("Mining RPC reports only SHA256D before activation")
|
||||||
|
mining_info = node.getmininginfo()
|
||||||
|
assert_equal(mining_info["pow_algo_id"], 0)
|
||||||
|
assert_equal(mining_info["pow_algo"], "sha256d")
|
||||||
|
assert_equal(set(mining_info["difficulties"]), {"sha256d"})
|
||||||
|
assert_equal(set(mining_info["networkhashesps"]), {"sha256d"})
|
||||||
|
assert_equal(
|
||||||
|
mining_info["difficulty"],
|
||||||
|
mining_info["difficulties"]["sha256d"],
|
||||||
|
)
|
||||||
|
assert_equal(
|
||||||
|
mining_info["networkhashps"],
|
||||||
|
mining_info["networkhashesps"]["sha256d"],
|
||||||
|
)
|
||||||
|
|
||||||
|
self.log.info("getnetworkhashps accepts both known algorithms")
|
||||||
|
assert_equal(node.getnetworkhashps(120, -1, "sha256d"), 0)
|
||||||
|
assert_equal(node.getnetworkhashps(120, -1, "randomx"), 0)
|
||||||
|
assert_raises_rpc_error(
|
||||||
|
-8,
|
||||||
|
"Unknown mining algorithm",
|
||||||
|
node.getnetworkhashps,
|
||||||
|
120,
|
||||||
|
-1,
|
||||||
|
"invalid",
|
||||||
|
)
|
||||||
|
|
||||||
self.log.info("Activate RandomX at block 1 on regtest only")
|
self.log.info("Activate RandomX at block 1 on regtest only")
|
||||||
self.restart_node(0, extra_args=["-testactivationheight=randomx@1"])
|
self.restart_node(0, extra_args=["-testactivationheight=randomx@1"])
|
||||||
node = self.nodes[0]
|
node = self.nodes[0]
|
||||||
|
|
@ -64,6 +91,18 @@ class RandomXGetBlockTemplateTest(DigiByteTestFramework):
|
||||||
BLOCK_VERSION_SHA256D,
|
BLOCK_VERSION_SHA256D,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
self.log.info("Mining RPC exposes SHA256D and RandomX at activation")
|
||||||
|
mining_info = node.getmininginfo()
|
||||||
|
assert_equal(set(mining_info["difficulties"]), {"sha256d", "randomx"})
|
||||||
|
assert_equal(
|
||||||
|
set(mining_info["networkhashesps"]),
|
||||||
|
{"sha256d", "randomx"},
|
||||||
|
)
|
||||||
|
assert mining_info["difficulties"]["sha256d"] > 0
|
||||||
|
assert mining_info["difficulties"]["randomx"] > 0
|
||||||
|
assert_equal(mining_info["networkhashesps"]["randomx"], 0)
|
||||||
|
assert_equal(node.getnetworkhashps(120, -1, "randomx"), 0)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
RandomXGetBlockTemplateTest().main()
|
RandomXGetBlockTemplateTest().main()
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue