Remove unused Python imports
[bitcoinplatinum.git] / test / functional / p2p-acceptblock.py
blobd9d7c24416555c2119939138b86afd850865be21
1 #!/usr/bin/env python3
2 # Copyright (c) 2015-2016 The Bitcoin Core developers
3 # Distributed under the MIT software license, see the accompanying
4 # file COPYING or http://www.opensource.org/licenses/mit-license.php.
5 """Test processing of unrequested blocks.
7 Setup: two nodes, node0+node1, not connected to each other. Node1 will have
8 nMinimumChainWork set to 0x10, so it won't process low-work unrequested blocks.
10 We have one P2PInterface connection to node0 called test_node, and one to node1
11 called min_work_node.
13 The test:
14 1. Generate one block on each node, to leave IBD.
16 2. Mine a new block on each tip, and deliver to each node from node's peer.
17 The tip should advance for node0, but node1 should skip processing due to
18 nMinimumChainWork.
20 Node1 is unused in tests 3-7:
22 3. Mine a block that forks from the genesis block, and deliver to test_node.
23 Node0 should not process this block (just accept the header), because it
24 is unrequested and doesn't have more or equal work to the tip.
26 4a,b. Send another two blocks that build on the forking block.
27 Node0 should process the second block but be stuck on the shorter chain,
28 because it's missing an intermediate block.
30 4c.Send 288 more blocks on the longer chain (the number of blocks ahead
31 we currently store).
32 Node0 should process all but the last block (too far ahead in height).
34 5. Send a duplicate of the block in #3 to Node0.
35 Node0 should not process the block because it is unrequested, and stay on
36 the shorter chain.
38 6. Send Node0 an inv for the height 3 block produced in #4 above.
39 Node0 should figure out that Node0 has the missing height 2 block and send a
40 getdata.
42 7. Send Node0 the missing block again.
43 Node0 should process and the tip should advance.
45 8. Create a fork which is invalid at a height longer than the current chain
46 (ie to which the node will try to reorg) but which has headers built on top
47 of the invalid block. Check that we get disconnected if we send more headers
48 on the chain the node now knows to be invalid.
50 9. Test Node1 is able to sync when connected to node0 (which should have sufficient
51 work on its chain).
52 """
54 from test_framework.mininode import *
55 from test_framework.test_framework import BitcoinTestFramework
56 from test_framework.util import *
57 import time
58 from test_framework.blocktools import create_block, create_coinbase, create_transaction
60 class AcceptBlockTest(BitcoinTestFramework):
61 def add_options(self, parser):
62 parser.add_option("--testbinary", dest="testbinary",
63 default=os.getenv("BITCOIND", "bitcoind"),
64 help="bitcoind binary to test")
66 def set_test_params(self):
67 self.setup_clean_chain = True
68 self.num_nodes = 2
69 self.extra_args = [[], ["-minimumchainwork=0x10"]]
71 def setup_network(self):
72 # Node0 will be used to test behavior of processing unrequested blocks
73 # from peers which are not whitelisted, while Node1 will be used for
74 # the whitelisted case.
75 # Node2 will be used for non-whitelisted peers to test the interaction
76 # with nMinimumChainWork.
77 self.setup_nodes()
79 def run_test(self):
80 # Setup the p2p connections and start up the network thread.
81 # test_node connects to node0 (not whitelisted)
82 test_node = self.nodes[0].add_p2p_connection(P2PInterface())
83 # min_work_node connects to node1 (whitelisted)
84 min_work_node = self.nodes[1].add_p2p_connection(P2PInterface())
86 NetworkThread().start() # Start up network handling in another thread
88 # Test logic begins here
89 test_node.wait_for_verack()
90 min_work_node.wait_for_verack()
92 # 1. Have nodes mine a block (leave IBD)
93 [ n.generate(1) for n in self.nodes ]
94 tips = [ int("0x" + n.getbestblockhash(), 0) for n in self.nodes ]
96 # 2. Send one block that builds on each tip.
97 # This should be accepted by node0
98 blocks_h2 = [] # the height 2 blocks on each node's chain
99 block_time = int(time.time()) + 1
100 for i in range(2):
101 blocks_h2.append(create_block(tips[i], create_coinbase(2), block_time))
102 blocks_h2[i].solve()
103 block_time += 1
104 test_node.send_message(msg_block(blocks_h2[0]))
105 min_work_node.send_message(msg_block(blocks_h2[1]))
107 for x in [test_node, min_work_node]:
108 x.sync_with_ping()
109 assert_equal(self.nodes[0].getblockcount(), 2)
110 assert_equal(self.nodes[1].getblockcount(), 1)
111 self.log.info("First height 2 block accepted by node0; correctly rejected by node1")
113 # 3. Send another block that builds on genesis.
114 block_h1f = create_block(int("0x" + self.nodes[0].getblockhash(0), 0), create_coinbase(1), block_time)
115 block_time += 1
116 block_h1f.solve()
117 test_node.send_message(msg_block(block_h1f))
119 test_node.sync_with_ping()
120 tip_entry_found = False
121 for x in self.nodes[0].getchaintips():
122 if x['hash'] == block_h1f.hash:
123 assert_equal(x['status'], "headers-only")
124 tip_entry_found = True
125 assert(tip_entry_found)
126 assert_raises_rpc_error(-1, "Block not found on disk", self.nodes[0].getblock, block_h1f.hash)
128 # 4. Send another two block that build on the fork.
129 block_h2f = create_block(block_h1f.sha256, create_coinbase(2), block_time)
130 block_time += 1
131 block_h2f.solve()
132 test_node.send_message(msg_block(block_h2f))
134 test_node.sync_with_ping()
135 # Since the earlier block was not processed by node, the new block
136 # can't be fully validated.
137 tip_entry_found = False
138 for x in self.nodes[0].getchaintips():
139 if x['hash'] == block_h2f.hash:
140 assert_equal(x['status'], "headers-only")
141 tip_entry_found = True
142 assert(tip_entry_found)
144 # But this block should be accepted by node since it has equal work.
145 self.nodes[0].getblock(block_h2f.hash)
146 self.log.info("Second height 2 block accepted, but not reorg'ed to")
148 # 4b. Now send another block that builds on the forking chain.
149 block_h3 = create_block(block_h2f.sha256, create_coinbase(3), block_h2f.nTime+1)
150 block_h3.solve()
151 test_node.send_message(msg_block(block_h3))
153 test_node.sync_with_ping()
154 # Since the earlier block was not processed by node, the new block
155 # can't be fully validated.
156 tip_entry_found = False
157 for x in self.nodes[0].getchaintips():
158 if x['hash'] == block_h3.hash:
159 assert_equal(x['status'], "headers-only")
160 tip_entry_found = True
161 assert(tip_entry_found)
162 self.nodes[0].getblock(block_h3.hash)
164 # But this block should be accepted by node since it has more work.
165 self.nodes[0].getblock(block_h3.hash)
166 self.log.info("Unrequested more-work block accepted")
168 # 4c. Now mine 288 more blocks and deliver; all should be processed but
169 # the last (height-too-high) on node (as long as its not missing any headers)
170 tip = block_h3
171 all_blocks = []
172 for i in range(288):
173 next_block = create_block(tip.sha256, create_coinbase(i + 4), tip.nTime+1)
174 next_block.solve()
175 all_blocks.append(next_block)
176 tip = next_block
178 # Now send the block at height 5 and check that it wasn't accepted (missing header)
179 test_node.send_message(msg_block(all_blocks[1]))
180 test_node.sync_with_ping()
181 assert_raises_rpc_error(-5, "Block not found", self.nodes[0].getblock, all_blocks[1].hash)
182 assert_raises_rpc_error(-5, "Block not found", self.nodes[0].getblockheader, all_blocks[1].hash)
184 # The block at height 5 should be accepted if we provide the missing header, though
185 headers_message = msg_headers()
186 headers_message.headers.append(CBlockHeader(all_blocks[0]))
187 test_node.send_message(headers_message)
188 test_node.send_message(msg_block(all_blocks[1]))
189 test_node.sync_with_ping()
190 self.nodes[0].getblock(all_blocks[1].hash)
192 # Now send the blocks in all_blocks
193 for i in range(288):
194 test_node.send_message(msg_block(all_blocks[i]))
195 test_node.sync_with_ping()
197 # Blocks 1-287 should be accepted, block 288 should be ignored because it's too far ahead
198 for x in all_blocks[:-1]:
199 self.nodes[0].getblock(x.hash)
200 assert_raises_rpc_error(-1, "Block not found on disk", self.nodes[0].getblock, all_blocks[-1].hash)
202 # 5. Test handling of unrequested block on the node that didn't process
203 # Should still not be processed (even though it has a child that has more
204 # work).
206 # The node should have requested the blocks at some point, so
207 # disconnect/reconnect first
209 self.nodes[0].disconnect_p2ps()
210 test_node = self.nodes[0].add_p2p_connection(P2PInterface())
212 test_node.wait_for_verack()
213 test_node.send_message(msg_block(block_h1f))
215 test_node.sync_with_ping()
216 assert_equal(self.nodes[0].getblockcount(), 2)
217 self.log.info("Unrequested block that would complete more-work chain was ignored")
219 # 6. Try to get node to request the missing block.
220 # Poke the node with an inv for block at height 3 and see if that
221 # triggers a getdata on block 2 (it should if block 2 is missing).
222 with mininode_lock:
223 # Clear state so we can check the getdata request
224 test_node.last_message.pop("getdata", None)
225 test_node.send_message(msg_inv([CInv(2, block_h3.sha256)]))
227 test_node.sync_with_ping()
228 with mininode_lock:
229 getdata = test_node.last_message["getdata"]
231 # Check that the getdata includes the right block
232 assert_equal(getdata.inv[0].hash, block_h1f.sha256)
233 self.log.info("Inv at tip triggered getdata for unprocessed block")
235 # 7. Send the missing block for the third time (now it is requested)
236 test_node.send_message(msg_block(block_h1f))
238 test_node.sync_with_ping()
239 assert_equal(self.nodes[0].getblockcount(), 290)
240 self.nodes[0].getblock(all_blocks[286].hash)
241 assert_equal(self.nodes[0].getbestblockhash(), all_blocks[286].hash)
242 assert_raises_rpc_error(-1, "Block not found on disk", self.nodes[0].getblock, all_blocks[287].hash)
243 self.log.info("Successfully reorged to longer chain from non-whitelisted peer")
245 # 8. Create a chain which is invalid at a height longer than the
246 # current chain, but which has more blocks on top of that
247 block_289f = create_block(all_blocks[284].sha256, create_coinbase(289), all_blocks[284].nTime+1)
248 block_289f.solve()
249 block_290f = create_block(block_289f.sha256, create_coinbase(290), block_289f.nTime+1)
250 block_290f.solve()
251 block_291 = create_block(block_290f.sha256, create_coinbase(291), block_290f.nTime+1)
252 # block_291 spends a coinbase below maturity!
253 block_291.vtx.append(create_transaction(block_290f.vtx[0], 0, b"42", 1))
254 block_291.hashMerkleRoot = block_291.calc_merkle_root()
255 block_291.solve()
256 block_292 = create_block(block_291.sha256, create_coinbase(292), block_291.nTime+1)
257 block_292.solve()
259 # Now send all the headers on the chain and enough blocks to trigger reorg
260 headers_message = msg_headers()
261 headers_message.headers.append(CBlockHeader(block_289f))
262 headers_message.headers.append(CBlockHeader(block_290f))
263 headers_message.headers.append(CBlockHeader(block_291))
264 headers_message.headers.append(CBlockHeader(block_292))
265 test_node.send_message(headers_message)
267 test_node.sync_with_ping()
268 tip_entry_found = False
269 for x in self.nodes[0].getchaintips():
270 if x['hash'] == block_292.hash:
271 assert_equal(x['status'], "headers-only")
272 tip_entry_found = True
273 assert(tip_entry_found)
274 assert_raises_rpc_error(-1, "Block not found on disk", self.nodes[0].getblock, block_292.hash)
276 test_node.send_message(msg_block(block_289f))
277 test_node.send_message(msg_block(block_290f))
279 test_node.sync_with_ping()
280 self.nodes[0].getblock(block_289f.hash)
281 self.nodes[0].getblock(block_290f.hash)
283 test_node.send_message(msg_block(block_291))
285 # At this point we've sent an obviously-bogus block, wait for full processing
286 # without assuming whether we will be disconnected or not
287 try:
288 # Only wait a short while so the test doesn't take forever if we do get
289 # disconnected
290 test_node.sync_with_ping(timeout=1)
291 except AssertionError:
292 test_node.wait_for_disconnect()
294 self.nodes[0].disconnect_p2ps()
295 test_node = self.nodes[0].add_p2p_connection(P2PInterface())
297 NetworkThread().start() # Start up network handling in another thread
298 test_node.wait_for_verack()
300 # We should have failed reorg and switched back to 290 (but have block 291)
301 assert_equal(self.nodes[0].getblockcount(), 290)
302 assert_equal(self.nodes[0].getbestblockhash(), all_blocks[286].hash)
303 assert_equal(self.nodes[0].getblock(block_291.hash)["confirmations"], -1)
305 # Now send a new header on the invalid chain, indicating we're forked off, and expect to get disconnected
306 block_293 = create_block(block_292.sha256, create_coinbase(293), block_292.nTime+1)
307 block_293.solve()
308 headers_message = msg_headers()
309 headers_message.headers.append(CBlockHeader(block_293))
310 test_node.send_message(headers_message)
311 test_node.wait_for_disconnect()
313 # 9. Connect node1 to node0 and ensure it is able to sync
314 connect_nodes(self.nodes[0], 1)
315 sync_blocks([self.nodes[0], self.nodes[1]])
316 self.log.info("Successfully synced nodes 1 and 0")
318 if __name__ == '__main__':
319 AcceptBlockTest().main()