Skip to content

Commit 75462b3

Browse files
committed
Merge bitcoin#28554: bugfix: throw an error if an invalid parameter is passed to getnetworkhashps RPC
9ac114e Throw 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-utACK 9ac114e kevkevinpal: reACK [9ac114e](bitcoin@9ac114e) achow101: ACK 9ac114e Tree-SHA512: eefb465c2dd654fc48267f444e1809597ec5363cdd131ea9ec812458fed1e4bffbbbb0617d74687c9f7bb16274b598d8292f5eeb7953421e5d2a8dc2cc081f2b
2 parents 535424a + 9ac114e commit 75462b3

File tree

2 files changed

+45
-8
lines changed

2 files changed

+45
-8
lines changed

src/rpc/mining.cpp

+14-5
Original file line numberDiff line numberDiff line change
@@ -49,21 +49,30 @@ using node::UpdateTime;
4949

5050
/**
5151
* Return average network hashes per second based on the last 'lookup' blocks,
52-
* or from the last difficulty change if 'lookup' is nonpositive.
53-
* If 'height' is nonnegative, compute the estimate at the time when a given block was found.
52+
* or from the last difficulty change if 'lookup' is -1.
53+
* If 'height' is -1, compute the estimate from current chain tip.
54+
* If 'height' is a valid block height, compute the estimate at the time when a given block was found.
5455
*/
5556
static UniValue GetNetworkHashPS(int lookup, int height, const CChain& active_chain) {
57+
if (lookup < -1 || lookup == 0) {
58+
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid nblocks. Must be a positive number or -1.");
59+
}
60+
61+
if (height < -1 || height > active_chain.Height()) {
62+
throw JSONRPCError(RPC_INVALID_PARAMETER, "Block does not exist at specified height");
63+
}
64+
5665
const CBlockIndex* pb = active_chain.Tip();
5766

58-
if (height >= 0 && height < active_chain.Height()) {
67+
if (height >= 0) {
5968
pb = active_chain[height];
6069
}
6170

6271
if (pb == nullptr || !pb->nHeight)
6372
return 0;
6473

6574
// If lookup is -1, then use blocks since last difficulty change.
66-
if (lookup <= 0)
75+
if (lookup == -1)
6776
lookup = pb->nHeight % Params().GetConsensus().DifficultyAdjustmentInterval() + 1;
6877

6978
// If lookup is larger than chain, then set it to chain length.
@@ -97,7 +106,7 @@ static RPCHelpMan getnetworkhashps()
97106
"Pass in [blocks] to override # of blocks, -1 specifies since last difficulty change.\n"
98107
"Pass in [height] to estimate the network speed at the time when a certain block was found.\n",
99108
{
100-
{"nblocks", RPCArg::Type::NUM, RPCArg::Default{120}, "The number of blocks, or -1 for blocks since last difficulty change."},
109+
{"nblocks", RPCArg::Type::NUM, RPCArg::Default{120}, "The number of previous blocks to calculate estimate from, or -1 for blocks since last difficulty change."},
101110
{"height", RPCArg::Type::NUM, RPCArg::Default{-1}, "To estimate at the time of the given height."},
102111
},
103112
RPCResult{

test/functional/rpc_blockchain.py

+31-3
Original file line numberDiff line numberDiff line change
@@ -437,7 +437,6 @@ def _test_getdifficulty(self):
437437

438438
def _test_getnetworkhashps(self):
439439
self.log.info("Test getnetworkhashps")
440-
hashes_per_second = self.nodes[0].getnetworkhashps()
441440
assert_raises_rpc_error(
442441
-3,
443442
textwrap.dedent("""
@@ -449,17 +448,46 @@ def _test_getnetworkhashps(self):
449448
""").strip(),
450449
lambda: self.nodes[0].getnetworkhashps("a", []),
451450
)
451+
assert_raises_rpc_error(
452+
-8,
453+
"Block does not exist at specified height",
454+
lambda: self.nodes[0].getnetworkhashps(100, self.nodes[0].getblockcount() + 1),
455+
)
456+
assert_raises_rpc_error(
457+
-8,
458+
"Block does not exist at specified height",
459+
lambda: self.nodes[0].getnetworkhashps(100, -10),
460+
)
461+
assert_raises_rpc_error(
462+
-8,
463+
"Invalid nblocks. Must be a positive number or -1.",
464+
lambda: self.nodes[0].getnetworkhashps(-100),
465+
)
466+
assert_raises_rpc_error(
467+
-8,
468+
"Invalid nblocks. Must be a positive number or -1.",
469+
lambda: self.nodes[0].getnetworkhashps(0),
470+
)
471+
472+
# Genesis block height estimate should return 0
473+
hashes_per_second = self.nodes[0].getnetworkhashps(100, 0)
474+
assert_equal(hashes_per_second, 0)
475+
452476
# This should be 2 hashes every 10 minutes or 1/300
477+
hashes_per_second = self.nodes[0].getnetworkhashps()
453478
assert abs(hashes_per_second * 300 - 1) < 0.0001
454479

455-
# Test setting the first param of getnetworkhashps to negative value returns the average network
480+
# Test setting the first param of getnetworkhashps to -1 returns the average network
456481
# hashes per second from the last difficulty change.
457482
current_block_height = self.nodes[0].getmininginfo()['blocks']
458483
blocks_since_last_diff_change = current_block_height % DIFFICULTY_ADJUSTMENT_INTERVAL + 1
459484
expected_hashes_per_second_since_diff_change = self.nodes[0].getnetworkhashps(blocks_since_last_diff_change)
460485

461486
assert_equal(self.nodes[0].getnetworkhashps(-1), expected_hashes_per_second_since_diff_change)
462-
assert_equal(self.nodes[0].getnetworkhashps(-2), expected_hashes_per_second_since_diff_change)
487+
488+
# Ensure long lookups get truncated to chain length
489+
hashes_per_second = self.nodes[0].getnetworkhashps(self.nodes[0].getblockcount() + 1000)
490+
assert hashes_per_second > 0.003
463491

464492
def _test_stopatheight(self):
465493
self.log.info("Test stopping at height")

0 commit comments

Comments
 (0)