Skip to content

Commit 6e662a8

Browse files
committed
Merge bitcoin/bitcoin#23813: Add test and docs for getblockfrompeer with pruning
fe329dc test: Add test for getblockfrompeer on pruned nodes (Fabian Jahr) cd761e6 rpc: Add note on guarantees to getblockfrompeer (Fabian Jahr) Pull request description: These are additions to `getblockfrompeer` that I already [suggested on the original PR](bitcoin/bitcoin#20295 (review)). The two commits do the following: 1. Add a test for `getblockfrompeer` usage on pruned nodes. This is important because many use-cases for `getblockfrompeer` are in a context of a pruned node. 2. Add some information on how long the users of pruned nodes can expect the block to be available after they have used the RPC. I think the behavior is not very intuitive for users and I would not be surprised if users expect the block to be available indefinitely. ACKs for top commit: Sjors: re-utACK fe329dc MarcoFalke: review ACK fe329dc 🍉 stratospher: ACK fe329dc. brunoerg: re-ACK fe329dc Tree-SHA512: a686bd8955d9c3baf365db384e497d6ee1aa9ce2fdb0733fe6150f7e3d94bae19d55bc1b347f1c9f619e749e18b41a52b9f8c0aa2042dd311a968a4b5d251fac
2 parents f7bdcfc + fe329dc commit 6e662a8

File tree

2 files changed

+46
-6
lines changed

2 files changed

+46
-6
lines changed

src/rpc/blockchain.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -432,7 +432,8 @@ static RPCHelpMan getblockfrompeer()
432432
"We must have the header for this block, e.g. using submitheader.\n"
433433
"Subsequent calls for the same block and a new peer will cause the response from the previous peer to be ignored.\n"
434434
"Peers generally ignore requests for a stale block that they never fully verified, or one that is more than a month old.\n"
435-
"When a peer does not respond with a block, we will disconnect.\n\n"
435+
"When a peer does not respond with a block, we will disconnect.\n"
436+
"Note: The block could be re-pruned as soon as it is received.\n\n"
436437
"Returns an empty JSON object if the request was successfully scheduled.",
437438
{
438439
{"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The block hash to try to fetch"},

test/functional/rpc_getblockfrompeer.py

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,19 @@
2424

2525
class GetBlockFromPeerTest(BitcoinTestFramework):
2626
def set_test_params(self):
27-
self.num_nodes = 2
27+
self.num_nodes = 3
28+
self.extra_args = [
29+
[],
30+
[],
31+
["-fastprune", "-prune=1"]
32+
]
2833

2934
def setup_network(self):
3035
self.setup_nodes()
3136

32-
def check_for_block(self, hash):
37+
def check_for_block(self, node, hash):
3338
try:
34-
self.nodes[0].getblock(hash)
39+
self.nodes[node].getblock(hash)
3540
return True
3641
except JSONRPCException:
3742
return False
@@ -48,7 +53,7 @@ def run_test(self):
4853

4954
self.log.info("Connect nodes to sync headers")
5055
self.connect_nodes(0, 1)
51-
self.sync_blocks()
56+
self.sync_blocks(self.nodes[0:2])
5257

5358
self.log.info("Node 0 should only have the header for node 1's block 3")
5459
x = next(filter(lambda x: x['hash'] == short_tip, self.nodes[0].getchaintips()))
@@ -81,7 +86,7 @@ def run_test(self):
8186

8287
self.log.info("Successful fetch")
8388
result = self.nodes[0].getblockfrompeer(short_tip, peer_0_peer_1_id)
84-
self.wait_until(lambda: self.check_for_block(short_tip), timeout=1)
89+
self.wait_until(lambda: self.check_for_block(node=0, hash=short_tip), timeout=1)
8590
assert_equal(result, {})
8691

8792
self.log.info("Don't fetch blocks we already have")
@@ -111,6 +116,40 @@ def run_test(self):
111116
error_msg = "In prune mode, only blocks that the node has already synced previously can be fetched from a peer"
112117
assert_raises_rpc_error(-1, error_msg, self.nodes[1].getblockfrompeer, blockhash, node1_interface_id)
113118

119+
self.log.info("Connect pruned node")
120+
# We need to generate more blocks to be able to prune
121+
self.connect_nodes(0, 2)
122+
pruned_node = self.nodes[2]
123+
self.generate(self.nodes[0], 400, sync_fun=self.no_op)
124+
self.sync_blocks([self.nodes[0], pruned_node])
125+
pruneheight = pruned_node.pruneblockchain(300)
126+
assert_equal(pruneheight, 248)
127+
# Ensure the block is actually pruned
128+
pruned_block = self.nodes[0].getblockhash(2)
129+
assert_raises_rpc_error(-1, "Block not available (pruned data)", pruned_node.getblock, pruned_block)
130+
131+
self.log.info("Fetch pruned block")
132+
peers = pruned_node.getpeerinfo()
133+
assert_equal(len(peers), 1)
134+
pruned_node_peer_0_id = peers[0]["id"]
135+
result = pruned_node.getblockfrompeer(pruned_block, pruned_node_peer_0_id)
136+
self.wait_until(lambda: self.check_for_block(node=2, hash=pruned_block), timeout=1)
137+
assert_equal(result, {})
138+
139+
self.log.info("Fetched block persists after next pruning event")
140+
self.generate(self.nodes[0], 250, sync_fun=self.no_op)
141+
self.sync_blocks([self.nodes[0], pruned_node])
142+
pruneheight += 251
143+
assert_equal(pruned_node.pruneblockchain(700), pruneheight)
144+
assert_equal(pruned_node.getblock(pruned_block)["hash"], "36c56c5b5ebbaf90d76b0d1a074dcb32d42abab75b7ec6fa0ffd9b4fbce8f0f7")
145+
146+
self.log.info("Fetched block can be pruned again when prune height exceeds the height of the tip at the time when the block was fetched")
147+
self.generate(self.nodes[0], 250, sync_fun=self.no_op)
148+
self.sync_blocks([self.nodes[0], pruned_node])
149+
pruneheight += 250
150+
assert_equal(pruned_node.pruneblockchain(1000), pruneheight)
151+
assert_raises_rpc_error(-1, "Block not available (pruned data)", pruned_node.getblock, pruned_block)
152+
114153

115154
if __name__ == '__main__':
116155
GetBlockFromPeerTest().main()

0 commit comments

Comments
 (0)