You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

p2p-acceptblock.py 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  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.
  6. Setup: two nodes, node0+node1, not connected to each other. Node1 will have
  7. nMinimumChainWork set to 0x10, so it won't process low-work unrequested blocks.
  8. We have one NodeConn connection to node0 called test_node, and one to node1
  9. called min_work_node.
  10. The test:
  11. 1. Generate one block on each node, to leave IBD.
  12. 2. Mine a new block on each tip, and deliver to each node from node's peer.
  13. The tip should advance for node0, but node1 should skip processing due to
  14. nMinimumChainWork.
  15. Node1 is unused in tests 3-7:
  16. 3. Mine a block that forks from the genesis block, and deliver to test_node.
  17. Node0 should not process this block (just accept the header), because it
  18. is unrequested and doesn't have more or equal work to the tip.
  19. 4a,b. Send another two blocks that build on the forking block.
  20. Node0 should process the second block but be stuck on the shorter chain,
  21. because it's missing an intermediate block.
  22. 4c.Send 288 more blocks on the longer chain (the number of blocks ahead
  23. we currently store).
  24. Node0 should process all but the last block (too far ahead in height).
  25. 5. Send a duplicate of the block in #3 to Node0.
  26. Node0 should not process the block because it is unrequested, and stay on
  27. the shorter chain.
  28. 6. Send Node0 an inv for the height 3 block produced in #4 above.
  29. Node0 should figure out that Node0 has the missing height 2 block and send a
  30. getdata.
  31. 7. Send Node0 the missing block again.
  32. Node0 should process and the tip should advance.
  33. 8. Test Node1 is able to sync when connected to node0 (which should have sufficient
  34. work on its chain).
  35. """
  36. from test_framework.mininode import *
  37. from test_framework.test_framework import BitcoinTestFramework
  38. from test_framework.util import *
  39. import time
  40. from test_framework.blocktools import create_block, create_coinbase
  41. class AcceptBlockTest(BitcoinTestFramework):
  42. def add_options(self, parser):
  43. parser.add_option("--testbinary", dest="testbinary",
  44. default=os.getenv("BITCOIND", "bitcoind"),
  45. help="bitcoind binary to test")
  46. def set_test_params(self):
  47. self.setup_clean_chain = True
  48. self.num_nodes = 2
  49. self.extra_args = [[], ["-minimumchainwork=0x10"]]
  50. def setup_network(self):
  51. # Node0 will be used to test behavior of processing unrequested blocks
  52. # from peers which are not whitelisted, while Node1 will be used for
  53. # the whitelisted case.
  54. # Node2 will be used for non-whitelisted peers to test the interaction
  55. # with nMinimumChainWork.
  56. self.setup_nodes()
  57. def run_test(self):
  58. # Setup the p2p connections and start up the network thread.
  59. test_node = NodeConnCB() # connects to node0
  60. min_work_node = NodeConnCB() # connects to node1
  61. connections = []
  62. connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], test_node))
  63. connections.append(NodeConn('127.0.0.1', p2p_port(1), self.nodes[1], min_work_node))
  64. test_node.add_connection(connections[0])
  65. min_work_node.add_connection(connections[1])
  66. NetworkThread().start() # Start up network handling in another thread
  67. # Test logic begins here
  68. test_node.wait_for_verack()
  69. min_work_node.wait_for_verack()
  70. # 1. Have nodes mine a block (leave IBD)
  71. [ n.generate(1) for n in self.nodes ]
  72. tips = [ int("0x" + n.getbestblockhash(), 0) for n in self.nodes ]
  73. # 2. Send one block that builds on each tip.
  74. # This should be accepted by node0
  75. blocks_h2 = [] # the height 2 blocks on each node's chain
  76. block_time = int(time.time()) + 1
  77. for i in range(2):
  78. blocks_h2.append(create_block(tips[i], create_coinbase(2), block_time))
  79. blocks_h2[i].solve()
  80. block_time += 1
  81. test_node.send_message(msg_block(blocks_h2[0]))
  82. min_work_node.send_message(msg_block(blocks_h2[1]))
  83. for x in [test_node, min_work_node]:
  84. x.sync_with_ping()
  85. assert_equal(self.nodes[0].getblockcount(), 2)
  86. assert_equal(self.nodes[1].getblockcount(), 1)
  87. self.log.info("First height 2 block accepted by node0; correctly rejected by node1")
  88. # 3. Send another block that builds on genesis.
  89. block_h1f = create_block(int("0x" + self.nodes[0].getblockhash(0), 0), create_coinbase(1), block_time)
  90. block_time += 1
  91. block_h1f.solve()
  92. test_node.send_message(msg_block(block_h1f))
  93. test_node.sync_with_ping()
  94. tip_entry_found = False
  95. for x in self.nodes[0].getchaintips():
  96. if x['hash'] == block_h1f.hash:
  97. assert_equal(x['status'], "headers-only")
  98. tip_entry_found = True
  99. assert(tip_entry_found)
  100. assert_raises_rpc_error(-1, "Block not found on disk", self.nodes[0].getblock, block_h1f.hash)
  101. # 4. Send another two block that build on the fork.
  102. block_h2f = create_block(block_h1f.sha256, create_coinbase(2), block_time)
  103. block_time += 1
  104. block_h2f.solve()
  105. test_node.send_message(msg_block(block_h2f))
  106. test_node.sync_with_ping()
  107. # Since the earlier block was not processed by node, the new block
  108. # can't be fully validated.
  109. tip_entry_found = False
  110. for x in self.nodes[0].getchaintips():
  111. if x['hash'] == block_h2f.hash:
  112. assert_equal(x['status'], "headers-only")
  113. tip_entry_found = True
  114. assert(tip_entry_found)
  115. # But this block should be accepted by node since it has equal work.
  116. # TODO: We currently drop this block but likely shouldn't
  117. #self.nodes[0].getblock(block_h2f.hash)
  118. self.log.info("Second height 2 block accepted, but not reorg'ed to")
  119. # 4b. Now send another block that builds on the forking chain.
  120. block_h3 = create_block(block_h2f.sha256, create_coinbase(3), block_h2f.nTime+1)
  121. block_h3.solve()
  122. test_node.send_message(msg_block(block_h3))
  123. test_node.sync_with_ping()
  124. # Since the earlier block was not processed by node, the new block
  125. # can't be fully validated.
  126. tip_entry_found = False
  127. for x in self.nodes[0].getchaintips():
  128. if x['hash'] == block_h3.hash:
  129. assert_equal(x['status'], "headers-only")
  130. tip_entry_found = True
  131. assert(tip_entry_found)
  132. self.nodes[0].getblock(block_h3.hash)
  133. # But this block should be accepted by node since it has more work.
  134. self.nodes[0].getblock(block_h3.hash)
  135. self.log.info("Unrequested more-work block accepted")
  136. # 4c. Now mine 288 more blocks and deliver; all should be processed but
  137. # the last (height-too-high) on node (as long as its not missing any headers)
  138. tip = block_h3
  139. all_blocks = []
  140. for i in range(288):
  141. next_block = create_block(tip.sha256, create_coinbase(i + 4), tip.nTime+1)
  142. next_block.solve()
  143. all_blocks.append(next_block)
  144. tip = next_block
  145. # Now send the block at height 5 and check that it wasn't accepted (missing header)
  146. test_node.send_message(msg_block(all_blocks[1]))
  147. test_node.sync_with_ping()
  148. assert_raises_rpc_error(-5, "Block not found", self.nodes[0].getblock, all_blocks[1].hash)
  149. assert_raises_rpc_error(-5, "Block not found", self.nodes[0].getblockheader, all_blocks[1].hash)
  150. # The block at height 5 should be accepted if we provide the missing header, though
  151. headers_message = msg_headers()
  152. headers_message.headers.append(CBlockHeader(all_blocks[0]))
  153. test_node.send_message(headers_message)
  154. test_node.send_message(msg_block(all_blocks[1]))
  155. test_node.sync_with_ping()
  156. self.nodes[0].getblock(all_blocks[1].hash)
  157. # Now send the blocks in all_blocks
  158. for i in range(288):
  159. test_node.send_message(msg_block(all_blocks[i]))
  160. test_node.sync_with_ping()
  161. # Blocks 1-287 should be accepted, block 288 should be ignored because it's too far ahead
  162. for x in all_blocks[:-1]:
  163. self.nodes[0].getblock(x.hash)
  164. assert_raises_rpc_error(-1, "Block not found on disk", self.nodes[0].getblock, all_blocks[-1].hash)
  165. # 5. Test handling of unrequested block on the node that didn't process
  166. # Should still not be processed (even though it has a child that has more
  167. # work).
  168. # The node should have requested the blocks at some point, so
  169. # disconnect/reconnect first
  170. connections[0].disconnect_node()
  171. test_node.wait_for_disconnect()
  172. test_node = NodeConnCB() # connects to node (not whitelisted)
  173. connections[0] = NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], test_node)
  174. test_node.add_connection(connections[0])
  175. test_node.wait_for_verack()
  176. test_node.send_message(msg_block(block_h1f))
  177. test_node.send_message(msg_block(block_h2f)) # This should not be required
  178. test_node.sync_with_ping()
  179. assert_equal(self.nodes[0].getblockcount(), 2)
  180. self.log.info("Unrequested block that would complete more-work chain was ignored")
  181. # 6. Try to get node to request the missing block.
  182. # Poke the node with an inv for block at height 3 and see if that
  183. # triggers a getdata on block 2 (it should if block 2 is missing).
  184. with mininode_lock:
  185. # Clear state so we can check the getdata request
  186. test_node.last_message.pop("getdata", None)
  187. test_node.send_message(msg_inv([CInv(2, block_h3.sha256)]))
  188. test_node.sync_with_ping()
  189. with mininode_lock:
  190. getdata = test_node.last_message["getdata"]
  191. # Check that the getdata includes the right block
  192. assert_equal(getdata.inv[0].hash, block_h1f.sha256)
  193. self.log.info("Inv at tip triggered getdata for unprocessed block")
  194. # 7. Send the missing block for the third time (now it is requested)
  195. test_node.send_message(msg_block(block_h1f))
  196. test_node.send_message(msg_block(block_h2f)) # This should not be required
  197. test_node.sync_with_ping()
  198. assert_equal(self.nodes[0].getblockcount(), 290)
  199. self.log.info("Successfully reorged to longer chain from non-whitelisted peer")
  200. # 8. Connect node1 to node0 and ensure it is able to sync
  201. connect_nodes(self.nodes[0], 1)
  202. sync_blocks([self.nodes[0], self.nodes[1]])
  203. self.log.info("Successfully synced nodes 1 and 0")
  204. [ c.disconnect_node() for c in connections ]
  205. if __name__ == '__main__':
  206. AcceptBlockTest().main()