|
21 | 21 | from test_framework.test_framework import BitcoinTestFramework
|
22 | 22 | from test_framework.util import (
|
23 | 23 | assert_equal,
|
| 24 | + assert_raises_rpc_error, |
| 25 | + try_rpc |
24 | 26 | )
|
25 | 27 |
|
| 28 | +# Minimum blocks required to signal NODE_NETWORK_LIMITED # |
| 29 | +NODE_NETWORK_LIMITED_MIN_BLOCKS = 288 |
26 | 30 |
|
27 | 31 | class P2PIgnoreInv(P2PInterface):
|
28 | 32 | firstAddrnServices = 0
|
@@ -54,6 +58,63 @@ def setup_network(self):
|
54 | 58 | self.add_nodes(self.num_nodes, self.extra_args)
|
55 | 59 | self.start_nodes()
|
56 | 60 |
|
| 61 | + def test_avoid_requesting_historical_blocks(self): |
| 62 | + self.log.info("Test full node does not request blocks beyond the limited peer threshold") |
| 63 | + pruned_node = self.nodes[0] |
| 64 | + miner = self.nodes[1] |
| 65 | + full_node = self.nodes[2] |
| 66 | + |
| 67 | + # Connect and generate block to ensure IBD=false |
| 68 | + self.connect_nodes(1, 0) |
| 69 | + self.connect_nodes(1, 2) |
| 70 | + self.generate(miner, 1) |
| 71 | + |
| 72 | + # Verify peers are out of IBD |
| 73 | + for node in self.nodes: |
| 74 | + assert not node.getblockchaininfo()['initialblockdownload'] |
| 75 | + |
| 76 | + # Isolate full_node (the node will remain out of IBD) |
| 77 | + full_node.setnetworkactive(False) |
| 78 | + self.wait_until(lambda: len(full_node.getpeerinfo()) == 0) |
| 79 | + |
| 80 | + # Mine blocks and sync the pruned node. Surpass the NETWORK_NODE_LIMITED threshold. |
| 81 | + # Blocks deeper than the threshold are considered "historical blocks" |
| 82 | + num_historial_blocks = 12 |
| 83 | + self.generate(miner, NODE_NETWORK_LIMITED_MIN_BLOCKS + num_historial_blocks, sync_fun=self.no_op) |
| 84 | + self.sync_blocks([miner, pruned_node]) |
| 85 | + |
| 86 | + # Connect full_node to prune_node and check peers don't disconnect right away. |
| 87 | + # (they will disconnect if full_node, which is chain-wise behind, request blocks |
| 88 | + # older than NODE_NETWORK_LIMITED_MIN_BLOCKS) |
| 89 | + start_height_full_node = full_node.getblockcount() |
| 90 | + full_node.setnetworkactive(True) |
| 91 | + self.connect_nodes(2, 0) |
| 92 | + assert_equal(len(full_node.getpeerinfo()), 1) |
| 93 | + |
| 94 | + # Wait until the full_node is headers-wise sync |
| 95 | + best_block_hash = pruned_node.getbestblockhash() |
| 96 | + self.wait_until(lambda: next(filter(lambda x: x['hash'] == best_block_hash, full_node.getchaintips()))['status'] == "headers-only") |
| 97 | + |
| 98 | + # Now, since the node aims to download a window of 1024 blocks, |
| 99 | + # ensure it requests the blocks below the threshold only (with a |
| 100 | + # 2-block buffer). And also, ensure it does not request any |
| 101 | + # historical block. |
| 102 | + tip_height = pruned_node.getblockcount() |
| 103 | + limit_buffer = 2 |
| 104 | + # Prevent races by waiting for the tip to arrive first |
| 105 | + self.wait_until(lambda: not try_rpc(-1, "Block not found", full_node.getblock, pruned_node.getbestblockhash())) |
| 106 | + for height in range(start_height_full_node + 1, tip_height + 1): |
| 107 | + if height <= tip_height - (NODE_NETWORK_LIMITED_MIN_BLOCKS - limit_buffer): |
| 108 | + assert_raises_rpc_error(-1, "Block not found on disk", full_node.getblock, pruned_node.getblockhash(height)) |
| 109 | + else: |
| 110 | + full_node.getblock(pruned_node.getblockhash(height)) # just assert it does not throw an exception |
| 111 | + |
| 112 | + # Lastly, ensure the full_node is not sync and verify it can get synced by |
| 113 | + # establishing a connection with another full node capable of providing them. |
| 114 | + assert_equal(full_node.getblockcount(), start_height_full_node) |
| 115 | + self.connect_nodes(2, 1) |
| 116 | + self.sync_blocks([miner, full_node]) |
| 117 | + |
57 | 118 | def run_test(self):
|
58 | 119 | node = self.nodes[0].add_p2p_connection(P2PIgnoreInv())
|
59 | 120 |
|
@@ -118,5 +179,7 @@ def run_test(self):
|
118 | 179 | # sync must be possible, node 1 is no longer in IBD and should therefore connect to node 0 (NODE_NETWORK_LIMITED)
|
119 | 180 | self.sync_blocks([self.nodes[0], self.nodes[1]])
|
120 | 181 |
|
| 182 | + self.test_avoid_requesting_historical_blocks() |
| 183 | + |
121 | 184 | if __name__ == '__main__':
|
122 | 185 | NodeNetworkLimitedTest().main()
|
0 commit comments