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.

maxuploadtarget.py 9.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  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 behavior of -maxuploadtarget.
  6. * Verify that getdata requests for old blocks (>1week) are dropped
  7. if uploadtarget has been reached.
  8. * Verify that getdata requests for recent blocks are respecteved even
  9. if uploadtarget has been reached.
  10. * Verify that the upload counters are reset after 24 hours.
  11. """
  12. from test_framework.mininode import *
  13. from test_framework.test_framework import BitcoinTestFramework
  14. from test_framework.util import *
  15. import time
  16. # TestNode: bare-bones "peer". Used mostly as a conduit for a test to sending
  17. # p2p messages to a node, generating the messages in the main testing logic.
  18. class TestNode(NodeConnCB):
  19. def __init__(self):
  20. NodeConnCB.__init__(self)
  21. self.connection = None
  22. self.ping_counter = 1
  23. self.last_pong = msg_pong()
  24. self.block_receive_map = {}
  25. def add_connection(self, conn):
  26. self.connection = conn
  27. self.peer_disconnected = False
  28. def on_inv(self, conn, message):
  29. pass
  30. # Track the last getdata message we receive (used in the test)
  31. def on_getdata(self, conn, message):
  32. self.last_getdata = message
  33. def on_block(self, conn, message):
  34. message.block.calc_sha256()
  35. try:
  36. self.block_receive_map[message.block.sha256] += 1
  37. except KeyError as e:
  38. self.block_receive_map[message.block.sha256] = 1
  39. # Spin until verack message is received from the node.
  40. # We use this to signal that our test can begin. This
  41. # is called from the testing thread, so it needs to acquire
  42. # the global lock.
  43. def wait_for_verack(self):
  44. def veracked():
  45. return self.verack_received
  46. return wait_until(veracked, timeout=10)
  47. def wait_for_disconnect(self):
  48. def disconnected():
  49. return self.peer_disconnected
  50. return wait_until(disconnected, timeout=10)
  51. # Wrapper for the NodeConn's send_message function
  52. def send_message(self, message):
  53. self.connection.send_message(message)
  54. def on_pong(self, conn, message):
  55. self.last_pong = message
  56. def on_close(self, conn):
  57. self.peer_disconnected = True
  58. # Sync up with the node after delivery of a block
  59. def sync_with_ping(self, timeout=30):
  60. def received_pong():
  61. return (self.last_pong.nonce == self.ping_counter)
  62. self.connection.send_message(msg_ping(nonce=self.ping_counter))
  63. success = wait_until(received_pong, timeout=timeout)
  64. self.ping_counter += 1
  65. return success
  66. class MaxUploadTest(BitcoinTestFramework):
  67. def __init__(self):
  68. super().__init__()
  69. self.setup_clean_chain = True
  70. self.num_nodes = 1
  71. # Cache for utxos, as the listunspent may take a long time later in the test
  72. self.utxo_cache = []
  73. def setup_network(self):
  74. # Start a node with maxuploadtarget of 200 MB (/24h)
  75. self.nodes = []
  76. self.nodes.append(start_node(0, self.options.tmpdir, ["-maxuploadtarget=800", "-blockmaxsize=999000"]))
  77. def run_test(self):
  78. # Before we connect anything, we first set the time on the node
  79. # to be in the past, otherwise things break because the CNode
  80. # time counters can't be reset backward after initialization
  81. old_time = int(time.time() - 2*60*60*24*7)
  82. self.nodes[0].setmocktime(old_time)
  83. # Generate some old blocks
  84. self.nodes[0].generate(130)
  85. # test_nodes[0] will only request old blocks
  86. # test_nodes[1] will only request new blocks
  87. # test_nodes[2] will test resetting the counters
  88. test_nodes = []
  89. connections = []
  90. for i in range(3):
  91. test_nodes.append(TestNode())
  92. connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], test_nodes[i]))
  93. test_nodes[i].add_connection(connections[i])
  94. NetworkThread().start() # Start up network handling in another thread
  95. [x.wait_for_verack() for x in test_nodes]
  96. # Test logic begins here
  97. # Now mine a big block
  98. mine_large_block(self.nodes[0], self.utxo_cache)
  99. # Store the hash; we'll request this later
  100. big_old_block = self.nodes[0].getbestblockhash()
  101. old_block_size = self.nodes[0].getblock(big_old_block, True)['size']
  102. big_old_block = int(big_old_block, 16)
  103. # Advance to two days ago
  104. self.nodes[0].setmocktime(int(time.time()) - 2*60*60*24)
  105. # Mine one more block, so that the prior block looks old
  106. mine_large_block(self.nodes[0], self.utxo_cache)
  107. # We'll be requesting this new block too
  108. big_new_block = self.nodes[0].getbestblockhash()
  109. big_new_block = int(big_new_block, 16)
  110. # test_nodes[0] will test what happens if we just keep requesting the
  111. # the same big old block too many times (expect: disconnect)
  112. getdata_request = msg_getdata()
  113. getdata_request.inv.append(CInv(2, big_old_block))
  114. max_bytes_per_day = 800*1024*1024
  115. daily_buffer = 144 * 4000000
  116. max_bytes_available = max_bytes_per_day - daily_buffer
  117. success_count = max_bytes_available // old_block_size
  118. # 576MB will be reserved for relaying new blocks, so expect this to
  119. # succeed for ~235 tries.
  120. for i in range(success_count):
  121. test_nodes[0].send_message(getdata_request)
  122. test_nodes[0].sync_with_ping()
  123. assert_equal(test_nodes[0].block_receive_map[big_old_block], i+1)
  124. assert_equal(len(self.nodes[0].getpeerinfo()), 3)
  125. # At most a couple more tries should succeed (depending on how long
  126. # the test has been running so far).
  127. for i in range(3):
  128. test_nodes[0].send_message(getdata_request)
  129. test_nodes[0].wait_for_disconnect()
  130. assert_equal(len(self.nodes[0].getpeerinfo()), 2)
  131. self.log.info("Peer 0 disconnected after downloading old block too many times")
  132. # Requesting the current block on test_nodes[1] should succeed indefinitely,
  133. # even when over the max upload target.
  134. # We'll try 800 times
  135. getdata_request.inv = [CInv(2, big_new_block)]
  136. for i in range(800):
  137. test_nodes[1].send_message(getdata_request)
  138. test_nodes[1].sync_with_ping()
  139. assert_equal(test_nodes[1].block_receive_map[big_new_block], i+1)
  140. self.log.info("Peer 1 able to repeatedly download new block")
  141. # But if test_nodes[1] tries for an old block, it gets disconnected too.
  142. getdata_request.inv = [CInv(2, big_old_block)]
  143. test_nodes[1].send_message(getdata_request)
  144. test_nodes[1].wait_for_disconnect()
  145. assert_equal(len(self.nodes[0].getpeerinfo()), 1)
  146. self.log.info("Peer 1 disconnected after trying to download old block")
  147. self.log.info("Advancing system time on node to clear counters...")
  148. # If we advance the time by 24 hours, then the counters should reset,
  149. # and test_nodes[2] should be able to retrieve the old block.
  150. self.nodes[0].setmocktime(int(time.time()))
  151. test_nodes[2].sync_with_ping()
  152. test_nodes[2].send_message(getdata_request)
  153. test_nodes[2].sync_with_ping()
  154. assert_equal(test_nodes[2].block_receive_map[big_old_block], 1)
  155. self.log.info("Peer 2 able to download old block")
  156. [c.disconnect_node() for c in connections]
  157. #stop and start node 0 with 1MB maxuploadtarget, whitelist 127.0.0.1
  158. self.log.info("Restarting nodes with -whitelist=127.0.0.1")
  159. stop_node(self.nodes[0], 0)
  160. self.nodes[0] = start_node(0, self.options.tmpdir, ["-whitelist=127.0.0.1", "-maxuploadtarget=1", "-blockmaxsize=999000"])
  161. #recreate/reconnect 3 test nodes
  162. test_nodes = []
  163. connections = []
  164. for i in range(3):
  165. test_nodes.append(TestNode())
  166. connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], test_nodes[i]))
  167. test_nodes[i].add_connection(connections[i])
  168. NetworkThread().start() # Start up network handling in another thread
  169. [x.wait_for_verack() for x in test_nodes]
  170. #retrieve 20 blocks which should be enough to break the 1MB limit
  171. getdata_request.inv = [CInv(2, big_new_block)]
  172. for i in range(20):
  173. test_nodes[1].send_message(getdata_request)
  174. test_nodes[1].sync_with_ping()
  175. assert_equal(test_nodes[1].block_receive_map[big_new_block], i+1)
  176. getdata_request.inv = [CInv(2, big_old_block)]
  177. test_nodes[1].send_message(getdata_request)
  178. test_nodes[1].wait_for_disconnect()
  179. assert_equal(len(self.nodes[0].getpeerinfo()), 3) #node is still connected because of the whitelist
  180. self.log.info("Peer 1 still connected after trying to download old block (whitelisted)")
  181. [c.disconnect_node() for c in connections]
  182. if __name__ == '__main__':
  183. MaxUploadTest().main()