Browse Source

Return correct error codes in blockchain.cpp.

RPCs in blockchain.cpp were returning misleading or incorrect error
codes (for example getblock() returning RPC_INTERNAL_ERROR when the
block had been pruned). This commit fixes those error codes:

- RPC_INTERNAL_ERROR should not be returned for application-level
  errors, only for genuine internal errors such as corrupted data.
- RPC_METHOD_NOT_FOUND should not be returned in response to a
  JSON request for an existing method.

Those error codes have been replaced with RPC_MISC_ERROR or
RPC_INVALID_PARAMETER as appropriate.
0.15
John Newbery 8 years ago
parent
commit
c1190963b3
  1. 18
      qa/rpc-tests/p2p-acceptblock.py
  2. 17
      qa/rpc-tests/pruning.py
  3. 17
      src/rpc/blockchain.cpp

18
qa/rpc-tests/p2p-acceptblock.py

@ -197,11 +197,8 @@ class AcceptBlockTest(BitcoinTestFramework):
assert_equal(x['status'], "headers-only") assert_equal(x['status'], "headers-only")
# But this block should be accepted by node0 since it has more work. # But this block should be accepted by node0 since it has more work.
try:
self.nodes[0].getblock(blocks_h3[0].hash) self.nodes[0].getblock(blocks_h3[0].hash)
print("Unrequested more-work block accepted from non-whitelisted peer") print("Unrequested more-work block accepted from non-whitelisted peer")
except:
raise AssertionError("Unrequested more work block was not processed")
# Node1 should have accepted and reorged. # Node1 should have accepted and reorged.
assert_equal(self.nodes[1].getblockcount(), 3) assert_equal(self.nodes[1].getblockcount(), 3)
@ -225,26 +222,17 @@ class AcceptBlockTest(BitcoinTestFramework):
tips[j] = next_block tips[j] = next_block
time.sleep(2) time.sleep(2)
for x in all_blocks: # Blocks 1-287 should be accepted, block 288 should be ignored because it's too far ahead
try: for x in all_blocks[:-1]:
self.nodes[0].getblock(x.hash) self.nodes[0].getblock(x.hash)
if x == all_blocks[287]: assert_raises_jsonrpc(-1, "Block not found on disk", self.nodes[0].getblock, all_blocks[-1].hash)
raise AssertionError("Unrequested block too far-ahead should have been ignored")
except:
if x == all_blocks[287]:
print("Unrequested block too far-ahead not processed")
else:
raise AssertionError("Unrequested block with more work should have been accepted")
headers_message.headers.pop() # Ensure the last block is unrequested headers_message.headers.pop() # Ensure the last block is unrequested
white_node.send_message(headers_message) # Send headers leading to tip white_node.send_message(headers_message) # Send headers leading to tip
white_node.send_message(msg_block(tips[1])) # Now deliver the tip white_node.send_message(msg_block(tips[1])) # Now deliver the tip
try:
white_node.sync_with_ping() white_node.sync_with_ping()
self.nodes[1].getblock(tips[1].hash) self.nodes[1].getblock(tips[1].hash)
print("Unrequested block far ahead of tip accepted from whitelisted peer") print("Unrequested block far ahead of tip accepted from whitelisted peer")
except:
raise AssertionError("Unrequested block from whitelisted peer not accepted")
# 5. Test handling of unrequested block on the node that didn't process # 5. Test handling of unrequested block on the node that didn't process
# Should still not be processed (even though it has a child that has more # Should still not be processed (even though it has a child that has more

17
qa/rpc-tests/pruning.py

@ -184,10 +184,7 @@ class PruneTest(BitcoinTestFramework):
def reorg_back(self): def reorg_back(self):
# Verify that a block on the old main chain fork has been pruned away # Verify that a block on the old main chain fork has been pruned away
try: assert_raises_jsonrpc(-1, "Block not available (pruned data)", self.nodes[2].getblock, self.forkhash)
self.nodes[2].getblock(self.forkhash)
raise AssertionError("Old block wasn't pruned so can't test redownload")
except JSONRPCException as e:
print("Will need to redownload block",self.forkheight) print("Will need to redownload block",self.forkheight)
# Verify that we have enough history to reorg back to the fork point # Verify that we have enough history to reorg back to the fork point
@ -233,7 +230,7 @@ class PruneTest(BitcoinTestFramework):
# at this point, node has 995 blocks and has not yet run in prune mode # at this point, node has 995 blocks and has not yet run in prune mode
node = self.nodes[node_number] = start_node(node_number, self.options.tmpdir, ["-debug=0"], timewait=900) node = self.nodes[node_number] = start_node(node_number, self.options.tmpdir, ["-debug=0"], timewait=900)
assert_equal(node.getblockcount(), 995) assert_equal(node.getblockcount(), 995)
assert_raises_message(JSONRPCException, "not in prune mode", node.pruneblockchain, 500) assert_raises_jsonrpc(-1, "not in prune mode", node.pruneblockchain, 500)
self.stop_node(node_number) self.stop_node(node_number)
# now re-start in manual pruning mode # now re-start in manual pruning mode
@ -265,14 +262,14 @@ class PruneTest(BitcoinTestFramework):
return os.path.isfile(self.options.tmpdir + "/node{}/regtest/blocks/blk{:05}.dat".format(node_number, index)) return os.path.isfile(self.options.tmpdir + "/node{}/regtest/blocks/blk{:05}.dat".format(node_number, index))
# should not prune because chain tip of node 3 (995) < PruneAfterHeight (1000) # should not prune because chain tip of node 3 (995) < PruneAfterHeight (1000)
assert_raises_message(JSONRPCException, "Blockchain is too short for pruning", node.pruneblockchain, height(500)) assert_raises_jsonrpc(-1, "Blockchain is too short for pruning", node.pruneblockchain, height(500))
# mine 6 blocks so we are at height 1001 (i.e., above PruneAfterHeight) # mine 6 blocks so we are at height 1001 (i.e., above PruneAfterHeight)
node.generate(6) node.generate(6)
assert_equal(node.getblockchaininfo()["blocks"], 1001) assert_equal(node.getblockchaininfo()["blocks"], 1001)
# negative heights should raise an exception # negative heights should raise an exception
assert_raises_message(JSONRPCException, "Negative", node.pruneblockchain, -10) assert_raises_jsonrpc(-8, "Negative", node.pruneblockchain, -10)
# height=100 too low to prune first block file so this is a no-op # height=100 too low to prune first block file so this is a no-op
prune(100) prune(100)
@ -318,12 +315,9 @@ class PruneTest(BitcoinTestFramework):
def wallet_test(self): def wallet_test(self):
# check that the pruning node's wallet is still in good shape # check that the pruning node's wallet is still in good shape
print("Stop and start pruning node to trigger wallet rescan") print("Stop and start pruning node to trigger wallet rescan")
try:
self.stop_node(2) self.stop_node(2)
start_node(2, self.options.tmpdir, ["-debug=1","-prune=550"]) start_node(2, self.options.tmpdir, ["-debug=1","-prune=550"])
print("Success") print("Success")
except Exception as detail:
raise AssertionError("Wallet test: unable to re-start the pruning node")
# check that wallet loads loads successfully when restarting a pruned node after IBD. # check that wallet loads loads successfully when restarting a pruned node after IBD.
# this was reported to fail in #7494. # this was reported to fail in #7494.
@ -331,12 +325,9 @@ class PruneTest(BitcoinTestFramework):
connect_nodes(self.nodes[0], 5) connect_nodes(self.nodes[0], 5)
nds = [self.nodes[0], self.nodes[5]] nds = [self.nodes[0], self.nodes[5]]
sync_blocks(nds, wait=5, timeout=300) sync_blocks(nds, wait=5, timeout=300)
try:
self.stop_node(5) #stop and start to trigger rescan self.stop_node(5) #stop and start to trigger rescan
start_node(5, self.options.tmpdir, ["-debug=1","-prune=550"]) start_node(5, self.options.tmpdir, ["-debug=1","-prune=550"])
print ("Success") print ("Success")
except Exception as detail:
raise AssertionError("Wallet test: unable to re-start node5")
def run_test(self): def run_test(self):
print("Warning! This test requires 4GB of disk space and takes over 30 mins (up to 2 hours)") print("Warning! This test requires 4GB of disk space and takes over 30 mins (up to 2 hours)")

17
src/rpc/blockchain.cpp

@ -745,10 +745,15 @@ UniValue getblock(const JSONRPCRequest& request)
CBlockIndex* pblockindex = mapBlockIndex[hash]; CBlockIndex* pblockindex = mapBlockIndex[hash];
if (fHavePruned && !(pblockindex->nStatus & BLOCK_HAVE_DATA) && pblockindex->nTx > 0) if (fHavePruned && !(pblockindex->nStatus & BLOCK_HAVE_DATA) && pblockindex->nTx > 0)
throw JSONRPCError(RPC_INTERNAL_ERROR, "Block not available (pruned data)"); throw JSONRPCError(RPC_MISC_ERROR, "Block not available (pruned data)");
if(!ReadBlockFromDisk(block, pblockindex, Params().GetConsensus())) if (!ReadBlockFromDisk(block, pblockindex, Params().GetConsensus()))
throw JSONRPCError(RPC_INTERNAL_ERROR, "Can't read block from disk"); // Block not found on disk. This could be because we have the block
// header in our index but don't have the block (for example if a
// non-whitelisted node sends us an unrequested long chain of valid
// blocks, we add the headers to our index, but don't accept the
// block).
throw JSONRPCError(RPC_MISC_ERROR, "Block not found on disk");
if (!fVerbose) if (!fVerbose)
{ {
@ -830,7 +835,7 @@ UniValue pruneblockchain(const JSONRPCRequest& request)
+ HelpExampleRpc("pruneblockchain", "1000")); + HelpExampleRpc("pruneblockchain", "1000"));
if (!fPruneMode) if (!fPruneMode)
throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Cannot prune blocks because node is not in prune mode."); throw JSONRPCError(RPC_MISC_ERROR, "Cannot prune blocks because node is not in prune mode.");
LOCK(cs_main); LOCK(cs_main);
@ -844,7 +849,7 @@ UniValue pruneblockchain(const JSONRPCRequest& request)
// Add a 2 hour buffer to include blocks which might have had old timestamps // Add a 2 hour buffer to include blocks which might have had old timestamps
CBlockIndex* pindex = chainActive.FindEarliestAtLeast(heightParam - TIMESTAMP_WINDOW); CBlockIndex* pindex = chainActive.FindEarliestAtLeast(heightParam - TIMESTAMP_WINDOW);
if (!pindex) { if (!pindex) {
throw JSONRPCError(RPC_INTERNAL_ERROR, "Could not find block with at least the specified timestamp."); throw JSONRPCError(RPC_INVALID_PARAMETER, "Could not find block with at least the specified timestamp.");
} }
heightParam = pindex->nHeight; heightParam = pindex->nHeight;
} }
@ -852,7 +857,7 @@ UniValue pruneblockchain(const JSONRPCRequest& request)
unsigned int height = (unsigned int) heightParam; unsigned int height = (unsigned int) heightParam;
unsigned int chainHeight = (unsigned int) chainActive.Height(); unsigned int chainHeight = (unsigned int) chainActive.Height();
if (chainHeight < Params().PruneAfterHeight()) if (chainHeight < Params().PruneAfterHeight())
throw JSONRPCError(RPC_INTERNAL_ERROR, "Blockchain is too short for pruning."); throw JSONRPCError(RPC_MISC_ERROR, "Blockchain is too short for pruning.");
else if (height > chainHeight) else if (height > chainHeight)
throw JSONRPCError(RPC_INVALID_PARAMETER, "Blockchain is shorter than the attempted prune height."); throw JSONRPCError(RPC_INVALID_PARAMETER, "Blockchain is shorter than the attempted prune height.");
else if (height > chainHeight - MIN_BLOCKS_TO_KEEP) { else if (height > chainHeight - MIN_BLOCKS_TO_KEEP) {

Loading…
Cancel
Save