Skip to content

Commit cfcbc8d

Browse files
achow101vijaydasmp
authored andcommitted
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
1 parent e652406 commit cfcbc8d

2 files changed

Lines changed: 45 additions & 8 deletions

File tree

src/rpc/mining.cpp

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -54,21 +54,30 @@ using node::UpdateTime;
5454

5555
/**
5656
* Return average network hashes per second based on the last 'lookup' blocks,
57-
* or from the last difficulty change if 'lookup' is nonpositive.
58-
* If 'height' is nonnegative, compute the estimate at the time when a given block was found.
57+
* or from the last difficulty change if 'lookup' is -1.
58+
* If 'height' is -1, compute the estimate from current chain tip.
59+
* If 'height' is a valid block height, compute the estimate at the time when a given block was found.
5960
*/
6061
static UniValue GetNetworkHashPS(int lookup, int height, const CChain& active_chain) {
62+
if (lookup < -1 || lookup == 0) {
63+
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid nblocks. Must be a positive number or -1.");
64+
}
65+
66+
if (height < -1 || height > active_chain.Height()) {
67+
throw JSONRPCError(RPC_INVALID_PARAMETER, "Block does not exist at specified height");
68+
}
69+
6170
const CBlockIndex* pb = active_chain.Tip();
6271

63-
if (height >= 0 && height < active_chain.Height()) {
72+
if (height >= 0) {
6473
pb = active_chain[height];
6574
}
6675

6776
if (pb == nullptr || !pb->nHeight)
6877
return 0;
6978

7079
// If lookup is -1, then use blocks since last difficulty change.
71-
if (lookup <= 0)
80+
if (lookup == -1)
7281
lookup = pb->nHeight % Params().GetConsensus().DifficultyAdjustmentInterval() + 1;
7382

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

test/functional/rpc_blockchain.py

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -428,18 +428,46 @@ def _test_getdifficulty(self):
428428

429429
def _test_getnetworkhashps(self):
430430
self.log.info("Test getnetworkhashps")
431-
hashes_per_second = self.nodes[0].getnetworkhashps()
431+
assert_raises_rpc_error(
432+
-8,
433+
"Block does not exist at specified height",
434+
lambda: self.nodes[0].getnetworkhashps(100, self.nodes[0].getblockcount() + 1),
435+
)
436+
assert_raises_rpc_error(
437+
-8,
438+
"Block does not exist at specified height",
439+
lambda: self.nodes[0].getnetworkhashps(100, -10),
440+
)
441+
assert_raises_rpc_error(
442+
-8,
443+
"Invalid nblocks. Must be a positive number or -1.",
444+
lambda: self.nodes[0].getnetworkhashps(-100),
445+
)
446+
assert_raises_rpc_error(
447+
-8,
448+
"Invalid nblocks. Must be a positive number or -1.",
449+
lambda: self.nodes[0].getnetworkhashps(0),
450+
)
451+
452+
# Genesis block height estimate should return 0
453+
hashes_per_second = self.nodes[0].getnetworkhashps(100, 0)
454+
assert_equal(hashes_per_second, 0)
455+
432456
# This should be 2 hashes every 2.6 minutes (156 seconds) or 1/78
457+
hashes_per_second = self.nodes[0].getnetworkhashps()
433458
assert abs(hashes_per_second * 78 - 1) < 0.0001
434459

435-
# Test setting the first param of getnetworkhashps to negative value returns the average network
460+
# Test setting the first param of getnetworkhashps to -1 returns the average network
436461
# hashes per second from the last difficulty change.
437462
current_block_height = self.nodes[0].getmininginfo()['blocks']
438463
blocks_since_last_diff_change = current_block_height % DIFFICULTY_ADJUSTMENT_INTERVAL + 1
439464
expected_hashes_per_second_since_diff_change = self.nodes[0].getnetworkhashps(blocks_since_last_diff_change)
440465

441466
assert_equal(self.nodes[0].getnetworkhashps(-1), expected_hashes_per_second_since_diff_change)
442-
assert_equal(self.nodes[0].getnetworkhashps(-2), expected_hashes_per_second_since_diff_change)
467+
468+
# Ensure long lookups get truncated to chain length
469+
hashes_per_second = self.nodes[0].getnetworkhashps(self.nodes[0].getblockcount() + 1000)
470+
assert hashes_per_second > 0.003
443471

444472
def _test_stopatheight(self):
445473
assert_equal(self.nodes[0].getblockcount(), HEIGHT)

0 commit comments

Comments
 (0)