mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-11-12 15:09:59 +01:00
Merge bitcoin/bitcoin#28554: bugfix: throw an error if an invalid parameter is passed to getnetworkhashps RPC
9ac114e5cdThrow error if invalid parameters passed to getnetworkhashps RPC endpoint (Jameson Lopp) Pull request description: When writing some scripts that iterated over many blocks to generate hashrate estimates I realized that my script was going out of range of the current chain tip height but was not encountering any errors. I believe that passing an invalid block height to this function but receiving the hashrate estimate for the chain tip instead should be considered unexpected behavior. ACKs for top commit: Sjors: re-utACK9ac114e5cdkevkevinpal: reACK [9ac114e](9ac114e5cd) achow101: ACK9ac114e5cdTree-SHA512: eefb465c2dd654fc48267f444e1809597ec5363cdd131ea9ec812458fed1e4bffbbbb0617d74687c9f7bb16274b598d8292f5eeb7953421e5d2a8dc2cc081f2b
This commit is contained in:
@@ -49,13 +49,22 @@ using node::UpdateTime;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 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 -1.
|
||||||
* If 'height' is nonnegative, compute the estimate at the time when a given block was found.
|
* If 'height' is -1, compute the estimate from current chain tip.
|
||||||
|
* If 'height' is a valid block height, 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) {
|
||||||
|
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) {
|
||||||
pb = active_chain[height];
|
pb = active_chain[height];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,7 +72,7 @@ static UniValue GetNetworkHashPS(int lookup, int height, const CChain& active_ch
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
// If lookup is -1, then use blocks since last difficulty change.
|
// If lookup is -1, then use blocks since last difficulty change.
|
||||||
if (lookup <= 0)
|
if (lookup == -1)
|
||||||
lookup = pb->nHeight % Params().GetConsensus().DifficultyAdjustmentInterval() + 1;
|
lookup = pb->nHeight % Params().GetConsensus().DifficultyAdjustmentInterval() + 1;
|
||||||
|
|
||||||
// If lookup is larger than chain, then set it to chain length.
|
// If lookup is larger than chain, then set it to chain length.
|
||||||
@@ -97,7 +106,7 @@ static RPCHelpMan getnetworkhashps()
|
|||||||
"Pass in [blocks] to override # of blocks, -1 specifies since last difficulty change.\n"
|
"Pass in [blocks] to override # of blocks, -1 specifies since last difficulty change.\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 previous blocks to calculate estimate from, or -1 for blocks since last difficulty change."},
|
||||||
{"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."},
|
||||||
},
|
},
|
||||||
RPCResult{
|
RPCResult{
|
||||||
|
|||||||
@@ -437,7 +437,6 @@ class BlockchainTest(BitcoinTestFramework):
|
|||||||
|
|
||||||
def _test_getnetworkhashps(self):
|
def _test_getnetworkhashps(self):
|
||||||
self.log.info("Test getnetworkhashps")
|
self.log.info("Test getnetworkhashps")
|
||||||
hashes_per_second = self.nodes[0].getnetworkhashps()
|
|
||||||
assert_raises_rpc_error(
|
assert_raises_rpc_error(
|
||||||
-3,
|
-3,
|
||||||
textwrap.dedent("""
|
textwrap.dedent("""
|
||||||
@@ -449,17 +448,46 @@ class BlockchainTest(BitcoinTestFramework):
|
|||||||
""").strip(),
|
""").strip(),
|
||||||
lambda: self.nodes[0].getnetworkhashps("a", []),
|
lambda: self.nodes[0].getnetworkhashps("a", []),
|
||||||
)
|
)
|
||||||
|
assert_raises_rpc_error(
|
||||||
|
-8,
|
||||||
|
"Block does not exist at specified height",
|
||||||
|
lambda: self.nodes[0].getnetworkhashps(100, self.nodes[0].getblockcount() + 1),
|
||||||
|
)
|
||||||
|
assert_raises_rpc_error(
|
||||||
|
-8,
|
||||||
|
"Block does not exist at specified height",
|
||||||
|
lambda: self.nodes[0].getnetworkhashps(100, -10),
|
||||||
|
)
|
||||||
|
assert_raises_rpc_error(
|
||||||
|
-8,
|
||||||
|
"Invalid nblocks. Must be a positive number or -1.",
|
||||||
|
lambda: self.nodes[0].getnetworkhashps(-100),
|
||||||
|
)
|
||||||
|
assert_raises_rpc_error(
|
||||||
|
-8,
|
||||||
|
"Invalid nblocks. Must be a positive number or -1.",
|
||||||
|
lambda: self.nodes[0].getnetworkhashps(0),
|
||||||
|
)
|
||||||
|
|
||||||
|
# Genesis block height estimate should return 0
|
||||||
|
hashes_per_second = self.nodes[0].getnetworkhashps(100, 0)
|
||||||
|
assert_equal(hashes_per_second, 0)
|
||||||
|
|
||||||
# This should be 2 hashes every 10 minutes or 1/300
|
# This should be 2 hashes every 10 minutes or 1/300
|
||||||
|
hashes_per_second = self.nodes[0].getnetworkhashps()
|
||||||
assert abs(hashes_per_second * 300 - 1) < 0.0001
|
assert abs(hashes_per_second * 300 - 1) < 0.0001
|
||||||
|
|
||||||
# Test setting the first param of getnetworkhashps to negative value returns the average network
|
# Test setting the first param of getnetworkhashps to -1 returns the average network
|
||||||
# hashes per second from the last difficulty change.
|
# hashes per second from the last difficulty change.
|
||||||
current_block_height = self.nodes[0].getmininginfo()['blocks']
|
current_block_height = self.nodes[0].getmininginfo()['blocks']
|
||||||
blocks_since_last_diff_change = current_block_height % DIFFICULTY_ADJUSTMENT_INTERVAL + 1
|
blocks_since_last_diff_change = current_block_height % DIFFICULTY_ADJUSTMENT_INTERVAL + 1
|
||||||
expected_hashes_per_second_since_diff_change = self.nodes[0].getnetworkhashps(blocks_since_last_diff_change)
|
expected_hashes_per_second_since_diff_change = self.nodes[0].getnetworkhashps(blocks_since_last_diff_change)
|
||||||
|
|
||||||
assert_equal(self.nodes[0].getnetworkhashps(-1), expected_hashes_per_second_since_diff_change)
|
assert_equal(self.nodes[0].getnetworkhashps(-1), expected_hashes_per_second_since_diff_change)
|
||||||
assert_equal(self.nodes[0].getnetworkhashps(-2), expected_hashes_per_second_since_diff_change)
|
|
||||||
|
# Ensure long lookups get truncated to chain length
|
||||||
|
hashes_per_second = self.nodes[0].getnetworkhashps(self.nodes[0].getblockcount() + 1000)
|
||||||
|
assert hashes_per_second > 0.003
|
||||||
|
|
||||||
def _test_stopatheight(self):
|
def _test_stopatheight(self):
|
||||||
self.log.info("Test stopping at height")
|
self.log.info("Test stopping at height")
|
||||||
|
|||||||
Reference in New Issue
Block a user