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 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. #!/usr/bin/env python2
  2. #
  3. # Distributed under the MIT/X11 software license, see the accompanying
  4. # file COPYING or http://www.opensource.org/licenses/mit-license.php.
  5. #
  6. from test_framework.mininode import *
  7. from test_framework.test_framework import BitcoinTestFramework
  8. from test_framework.util import *
  9. import time
  10. '''
  11. Test behavior of -maxuploadtarget.
  12. * Verify that getdata requests for old blocks (>1week) are dropped
  13. if uploadtarget has been reached.
  14. * Verify that getdata requests for recent blocks are respecteved even
  15. if uploadtarget has been reached.
  16. * Verify that the upload counters are reset after 24 hours.
  17. '''
  18. # TestNode: bare-bones "peer". Used mostly as a conduit for a test to sending
  19. # p2p messages to a node, generating the messages in the main testing logic.
  20. class TestNode(NodeConnCB):
  21. def __init__(self):
  22. NodeConnCB.__init__(self)
  23. self.connection = None
  24. self.ping_counter = 1
  25. self.last_pong = msg_pong()
  26. self.block_receive_map = {}
  27. def add_connection(self, conn):
  28. self.connection = conn
  29. self.peer_disconnected = False
  30. def on_inv(self, conn, message):
  31. pass
  32. # Track the last getdata message we receive (used in the test)
  33. def on_getdata(self, conn, message):
  34. self.last_getdata = message
  35. def on_block(self, conn, message):
  36. message.block.calc_sha256()
  37. try:
  38. self.block_receive_map[message.block.sha256] += 1
  39. except KeyError as e:
  40. self.block_receive_map[message.block.sha256] = 1
  41. # Spin until verack message is received from the node.
  42. # We use this to signal that our test can begin. This
  43. # is called from the testing thread, so it needs to acquire
  44. # the global lock.
  45. def wait_for_verack(self):
  46. def veracked():
  47. return self.verack_received
  48. return wait_until(veracked, timeout=10)
  49. def wait_for_disconnect(self):
  50. def disconnected():
  51. return self.peer_disconnected
  52. return wait_until(disconnected, timeout=10)
  53. # Wrapper for the NodeConn's send_message function
  54. def send_message(self, message):
  55. self.connection.send_message(message)
  56. def on_pong(self, conn, message):
  57. self.last_pong = message
  58. def on_close(self, conn):
  59. self.peer_disconnected = True
  60. # Sync up with the node after delivery of a block
  61. def sync_with_ping(self, timeout=30):
  62. def received_pong():
  63. return (self.last_pong.nonce == self.ping_counter)
  64. self.connection.send_message(msg_ping(nonce=self.ping_counter))
  65. success = wait_until(received_pong, timeout)
  66. self.ping_counter += 1
  67. return success
  68. class MaxUploadTest(BitcoinTestFramework):
  69. def __init__(self):
  70. self.utxo = []
  71. self.txouts = gen_return_txouts()
  72. def add_options(self, parser):
  73. parser.add_option("--testbinary", dest="testbinary",
  74. default=os.getenv("BITCOIND", "bitcoind"),
  75. help="bitcoind binary to test")
  76. def setup_chain(self):
  77. initialize_chain_clean(self.options.tmpdir, 2)
  78. def setup_network(self):
  79. # Start a node with maxuploadtarget of 200 MB (/24h)
  80. self.nodes = []
  81. self.nodes.append(start_node(0, self.options.tmpdir, ["-debug", "-maxuploadtarget=200", "-blockmaxsize=999000"]))
  82. def mine_full_block(self, node, address):
  83. # Want to create a full block
  84. # We'll generate a 66k transaction below, and 14 of them is close to the 1MB block limit
  85. for j in xrange(14):
  86. if len(self.utxo) < 14:
  87. self.utxo = node.listunspent()
  88. inputs=[]
  89. outputs = {}
  90. t = self.utxo.pop()
  91. inputs.append({ "txid" : t["txid"], "vout" : t["vout"]})
  92. remchange = t["amount"] - Decimal("0.001000")
  93. outputs[address]=remchange
  94. # Create a basic transaction that will send change back to ourself after account for a fee
  95. # And then insert the 128 generated transaction outs in the middle rawtx[92] is where the #
  96. # of txouts is stored and is the only thing we overwrite from the original transaction
  97. rawtx = node.createrawtransaction(inputs, outputs)
  98. newtx = rawtx[0:92]
  99. newtx = newtx + self.txouts
  100. newtx = newtx + rawtx[94:]
  101. # Appears to be ever so slightly faster to sign with SIGHASH_NONE
  102. signresult = node.signrawtransaction(newtx,None,None,"NONE")
  103. txid = node.sendrawtransaction(signresult["hex"], True)
  104. # Mine a full sized block which will be these transactions we just created
  105. node.generate(1)
  106. def run_test(self):
  107. # Before we connect anything, we first set the time on the node
  108. # to be in the past, otherwise things break because the CNode
  109. # time counters can't be reset backward after initialization
  110. old_time = int(time.time() - 2*60*60*24*7)
  111. self.nodes[0].setmocktime(old_time)
  112. # Generate some old blocks
  113. self.nodes[0].generate(130)
  114. # test_nodes[0] will only request old blocks
  115. # test_nodes[1] will only request new blocks
  116. # test_nodes[2] will test resetting the counters
  117. test_nodes = []
  118. connections = []
  119. for i in xrange(3):
  120. test_nodes.append(TestNode())
  121. connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], test_nodes[i]))
  122. test_nodes[i].add_connection(connections[i])
  123. NetworkThread().start() # Start up network handling in another thread
  124. [x.wait_for_verack() for x in test_nodes]
  125. # Test logic begins here
  126. # Now mine a big block
  127. self.mine_full_block(self.nodes[0], self.nodes[0].getnewaddress())
  128. # Store the hash; we'll request this later
  129. big_old_block = self.nodes[0].getbestblockhash()
  130. old_block_size = self.nodes[0].getblock(big_old_block, True)['size']
  131. big_old_block = int(big_old_block, 16)
  132. # Advance to two days ago
  133. self.nodes[0].setmocktime(int(time.time()) - 2*60*60*24)
  134. # Mine one more block, so that the prior block looks old
  135. self.mine_full_block(self.nodes[0], self.nodes[0].getnewaddress())
  136. # We'll be requesting this new block too
  137. big_new_block = self.nodes[0].getbestblockhash()
  138. new_block_size = self.nodes[0].getblock(big_new_block)['size']
  139. big_new_block = int(big_new_block, 16)
  140. # test_nodes[0] will test what happens if we just keep requesting the
  141. # the same big old block too many times (expect: disconnect)
  142. getdata_request = msg_getdata()
  143. getdata_request.inv.append(CInv(2, big_old_block))
  144. max_bytes_per_day = 200*1024*1024
  145. daily_buffer = 144 * MAX_BLOCK_SIZE
  146. max_bytes_available = max_bytes_per_day - daily_buffer
  147. success_count = max_bytes_available // old_block_size
  148. # 144MB will be reserved for relaying new blocks, so expect this to
  149. # succeed for ~70 tries.
  150. for i in xrange(success_count):
  151. test_nodes[0].send_message(getdata_request)
  152. test_nodes[0].sync_with_ping()
  153. assert_equal(test_nodes[0].block_receive_map[big_old_block], i+1)
  154. assert_equal(len(self.nodes[0].getpeerinfo()), 3)
  155. # At most a couple more tries should succeed (depending on how long
  156. # the test has been running so far).
  157. for i in xrange(3):
  158. test_nodes[0].send_message(getdata_request)
  159. test_nodes[0].wait_for_disconnect()
  160. assert_equal(len(self.nodes[0].getpeerinfo()), 2)
  161. print "Peer 0 disconnected after downloading old block too many times"
  162. # Requesting the current block on test_nodes[1] should succeed indefinitely,
  163. # even when over the max upload target.
  164. # We'll try 200 times
  165. getdata_request.inv = [CInv(2, big_new_block)]
  166. for i in xrange(200):
  167. test_nodes[1].send_message(getdata_request)
  168. test_nodes[1].sync_with_ping()
  169. assert_equal(test_nodes[1].block_receive_map[big_new_block], i+1)
  170. print "Peer 1 able to repeatedly download new block"
  171. # But if test_nodes[1] tries for an old block, it gets disconnected too.
  172. getdata_request.inv = [CInv(2, big_old_block)]
  173. test_nodes[1].send_message(getdata_request)
  174. test_nodes[1].wait_for_disconnect()
  175. assert_equal(len(self.nodes[0].getpeerinfo()), 1)
  176. print "Peer 1 disconnected after trying to download old block"
  177. print "Advancing system time on node to clear counters..."
  178. # If we advance the time by 24 hours, then the counters should reset,
  179. # and test_nodes[2] should be able to retrieve the old block.
  180. self.nodes[0].setmocktime(int(time.time()))
  181. test_nodes[2].sync_with_ping()
  182. test_nodes[2].send_message(getdata_request)
  183. test_nodes[2].sync_with_ping()
  184. assert_equal(test_nodes[2].block_receive_map[big_old_block], 1)
  185. print "Peer 2 able to download old block"
  186. [c.disconnect_node() for c in connections]
  187. #stop and start node 0 with 1MB maxuploadtarget, whitelist 127.0.0.1
  188. print "Restarting nodes with -whitelist=127.0.0.1"
  189. stop_node(self.nodes[0], 0)
  190. self.nodes[0] = start_node(0, self.options.tmpdir, ["-debug", "-whitelist=127.0.0.1", "-maxuploadtarget=1", "-blockmaxsize=999000"])
  191. #recreate/reconnect 3 test nodes
  192. test_nodes = []
  193. connections = []
  194. for i in xrange(3):
  195. test_nodes.append(TestNode())
  196. connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], test_nodes[i]))
  197. test_nodes[i].add_connection(connections[i])
  198. NetworkThread().start() # Start up network handling in another thread
  199. [x.wait_for_verack() for x in test_nodes]
  200. #retrieve 20 blocks which should be enough to break the 1MB limit
  201. getdata_request.inv = [CInv(2, big_new_block)]
  202. for i in xrange(20):
  203. test_nodes[1].send_message(getdata_request)
  204. test_nodes[1].sync_with_ping()
  205. assert_equal(test_nodes[1].block_receive_map[big_new_block], i+1)
  206. getdata_request.inv = [CInv(2, big_old_block)]
  207. test_nodes[1].send_message(getdata_request)
  208. test_nodes[1].wait_for_disconnect()
  209. assert_equal(len(self.nodes[0].getpeerinfo()), 3) #node is still connected because of the whitelist
  210. print "Peer 1 still connected after trying to download old block (whitelisted)"
  211. [c.disconnect_node() for c in connections]
  212. if __name__ == '__main__':
  213. MaxUploadTest().main()