1#!/usr/bin/env python3 2# Copyright (c) 2015-2018 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 7* Verify that getdata requests for old blocks (>1week) are dropped 8if uploadtarget has been reached. 9* Verify that getdata requests for recent blocks are respected even 10if uploadtarget has been reached. 11* Verify that the upload counters are reset after 24 hours. 12""" 13from collections import defaultdict 14import time 15 16from test_framework.messages import CInv, msg_getdata 17from test_framework.mininode import P2PInterface 18from test_framework.test_framework import BitcoinTestFramework 19from test_framework.util import assert_equal, mine_large_block 20 21class TestP2PConn(P2PInterface): 22 def __init__(self): 23 super().__init__() 24 self.block_receive_map = defaultdict(int) 25 26 def on_inv(self, message): 27 pass 28 29 def on_block(self, message): 30 message.block.calc_sha256() 31 self.block_receive_map[message.block.sha256] += 1 32 33class MaxUploadTest(BitcoinTestFramework): 34 35 def set_test_params(self): 36 self.setup_clean_chain = True 37 self.num_nodes = 1 38 self.extra_args = [["-maxuploadtarget=800"]] 39 40 # Cache for utxos, as the listunspent may take a long time later in the test 41 self.utxo_cache = [] 42 43 def skip_test_if_missing_module(self): 44 self.skip_if_no_wallet() 45 46 def run_test(self): 47 # Before we connect anything, we first set the time on the node 48 # to be in the past, otherwise things break because the CNode 49 # time counters can't be reset backward after initialization 50 old_time = int(time.time() - 2*60*60*24*7) 51 self.nodes[0].setmocktime(old_time) 52 53 # Generate some old blocks 54 self.nodes[0].generate(130) 55 56 # p2p_conns[0] will only request old blocks 57 # p2p_conns[1] will only request new blocks 58 # p2p_conns[2] will test resetting the counters 59 p2p_conns = [] 60 61 for _ in range(3): 62 p2p_conns.append(self.nodes[0].add_p2p_connection(TestP2PConn())) 63 64 # Now mine a big block 65 mine_large_block(self.nodes[0], self.utxo_cache) 66 67 # Store the hash; we'll request this later 68 big_old_block = self.nodes[0].getbestblockhash() 69 old_block_size = self.nodes[0].getblock(big_old_block, True)['size'] 70 big_old_block = int(big_old_block, 16) 71 72 # Advance to two days ago 73 self.nodes[0].setmocktime(int(time.time()) - 2*60*60*24) 74 75 # Mine one more block, so that the prior block looks old 76 mine_large_block(self.nodes[0], self.utxo_cache) 77 78 # We'll be requesting this new block too 79 big_new_block = self.nodes[0].getbestblockhash() 80 big_new_block = int(big_new_block, 16) 81 82 # p2p_conns[0] will test what happens if we just keep requesting the 83 # the same big old block too many times (expect: disconnect) 84 85 getdata_request = msg_getdata() 86 getdata_request.inv.append(CInv(2, big_old_block)) 87 88 max_bytes_per_day = 800*1024*1024 89 daily_buffer = 144 * 4000000 90 max_bytes_available = max_bytes_per_day - daily_buffer 91 success_count = max_bytes_available // old_block_size 92 93 # 576MB will be reserved for relaying new blocks, so expect this to 94 # succeed for ~235 tries. 95 for i in range(success_count): 96 p2p_conns[0].send_message(getdata_request) 97 p2p_conns[0].sync_with_ping() 98 assert_equal(p2p_conns[0].block_receive_map[big_old_block], i+1) 99 100 assert_equal(len(self.nodes[0].getpeerinfo()), 3) 101 # At most a couple more tries should succeed (depending on how long 102 # the test has been running so far). 103 for i in range(3): 104 p2p_conns[0].send_message(getdata_request) 105 p2p_conns[0].wait_for_disconnect() 106 assert_equal(len(self.nodes[0].getpeerinfo()), 2) 107 self.log.info("Peer 0 disconnected after downloading old block too many times") 108 109 # Requesting the current block on p2p_conns[1] should succeed indefinitely, 110 # even when over the max upload target. 111 # We'll try 800 times 112 getdata_request.inv = [CInv(2, big_new_block)] 113 for i in range(800): 114 p2p_conns[1].send_message(getdata_request) 115 p2p_conns[1].sync_with_ping() 116 assert_equal(p2p_conns[1].block_receive_map[big_new_block], i+1) 117 118 self.log.info("Peer 1 able to repeatedly download new block") 119 120 # But if p2p_conns[1] tries for an old block, it gets disconnected too. 121 getdata_request.inv = [CInv(2, big_old_block)] 122 p2p_conns[1].send_message(getdata_request) 123 p2p_conns[1].wait_for_disconnect() 124 assert_equal(len(self.nodes[0].getpeerinfo()), 1) 125 126 self.log.info("Peer 1 disconnected after trying to download old block") 127 128 self.log.info("Advancing system time on node to clear counters...") 129 130 # If we advance the time by 24 hours, then the counters should reset, 131 # and p2p_conns[2] should be able to retrieve the old block. 132 self.nodes[0].setmocktime(int(time.time())) 133 p2p_conns[2].sync_with_ping() 134 p2p_conns[2].send_message(getdata_request) 135 p2p_conns[2].sync_with_ping() 136 assert_equal(p2p_conns[2].block_receive_map[big_old_block], 1) 137 138 self.log.info("Peer 2 able to download old block") 139 140 self.nodes[0].disconnect_p2ps() 141 142 #stop and start node 0 with 1MB maxuploadtarget, whitelist 127.0.0.1 143 self.log.info("Restarting nodes with -whitelist=127.0.0.1") 144 self.stop_node(0) 145 self.start_node(0, ["-whitelist=127.0.0.1", "-maxuploadtarget=1"]) 146 147 # Reconnect to self.nodes[0] 148 self.nodes[0].add_p2p_connection(TestP2PConn()) 149 150 #retrieve 20 blocks which should be enough to break the 1MB limit 151 getdata_request.inv = [CInv(2, big_new_block)] 152 for i in range(20): 153 self.nodes[0].p2p.send_message(getdata_request) 154 self.nodes[0].p2p.sync_with_ping() 155 assert_equal(self.nodes[0].p2p.block_receive_map[big_new_block], i+1) 156 157 getdata_request.inv = [CInv(2, big_old_block)] 158 self.nodes[0].p2p.send_and_ping(getdata_request) 159 assert_equal(len(self.nodes[0].getpeerinfo()), 1) #node is still connected because of the whitelist 160 161 self.log.info("Peer still connected after trying to download old block (whitelisted)") 162 163if __name__ == '__main__': 164 MaxUploadTest().main() 165