p2p_getaddr_caching.py raw

   1  #!/usr/bin/env python3
   2  # Copyright (c) 2020-present 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 addr response caching"""
   6  
   7  import time
   8  
   9  from test_framework.p2p import (
  10      P2PInterface,
  11      p2p_lock
  12  )
  13  from test_framework.test_framework import BitcoinTestFramework
  14  from test_framework.util import (
  15      assert_not_equal,
  16      assert_equal,
  17      p2p_port,
  18  )
  19  
  20  # As defined in net_processing.
  21  MAX_ADDR_TO_SEND = 1000
  22  MAX_PCT_ADDR_TO_SEND = 23
  23  
  24  
  25  class AddrReceiver(P2PInterface):
  26  
  27      def __init__(self):
  28          super().__init__()
  29          self.received_addrs = None
  30  
  31      def get_received_addrs(self):
  32          with p2p_lock:
  33              return self.received_addrs
  34  
  35      def on_addr(self, message):
  36          self.received_addrs = []
  37          for addr in message.addrs:
  38              self.received_addrs.append(addr.ip)
  39  
  40      def addr_received(self):
  41          return self.received_addrs is not None
  42  
  43  
  44  class AddrTest(BitcoinTestFramework):
  45      def set_test_params(self):
  46          self.num_nodes = 1
  47          # Use some of the remaining p2p ports for the onion binds.
  48          self.onion_port1 = p2p_port(self.num_nodes)
  49          self.onion_port2 = p2p_port(self.num_nodes + 1)
  50          self.extra_args = [
  51              [f"-bind=127.0.0.1:{self.onion_port1}=onion", f"-bind=127.0.0.1:{self.onion_port2}=onion"],
  52          ]
  53  
  54      def run_test(self):
  55          self.log.info('Fill peer AddrMan with a lot of records')
  56          for i in range(10000):
  57              first_octet = i >> 8
  58              second_octet = i % 256
  59              a = "{}.{}.1.1".format(first_octet, second_octet)
  60              self.nodes[0].addpeeraddress(a, 8333)
  61  
  62          # Need to make sure we hit MAX_ADDR_TO_SEND records in the addr response later because
  63          # only a fraction of all known addresses can be cached and returned.
  64          assert len(self.nodes[0].getnodeaddresses(0)) > int(MAX_ADDR_TO_SEND / (MAX_PCT_ADDR_TO_SEND / 100))
  65  
  66          last_response_on_local_bind = None
  67          last_response_on_onion_bind1 = None
  68          last_response_on_onion_bind2 = None
  69          self.log.info('Send many addr requests within short time to receive same response')
  70          N = 5
  71          cur_mock_time = int(time.time())
  72          for i in range(N):
  73              addr_receiver_local = self.nodes[0].add_p2p_connection(AddrReceiver())
  74              addr_receiver_onion1 = self.nodes[0].add_p2p_connection(AddrReceiver(), dstport=self.onion_port1)
  75              addr_receiver_onion2 = self.nodes[0].add_p2p_connection(AddrReceiver(), dstport=self.onion_port2)
  76  
  77              # Trigger response
  78              cur_mock_time += 5 * 60
  79              self.nodes[0].setmocktime(cur_mock_time)
  80              addr_receiver_local.wait_until(addr_receiver_local.addr_received)
  81              addr_receiver_onion1.wait_until(addr_receiver_onion1.addr_received)
  82              addr_receiver_onion2.wait_until(addr_receiver_onion2.addr_received)
  83  
  84              if i > 0:
  85                  # Responses from different binds should be unique
  86                  assert_not_equal(last_response_on_local_bind, addr_receiver_onion1.get_received_addrs())
  87                  assert_not_equal(last_response_on_local_bind, addr_receiver_onion2.get_received_addrs())
  88                  assert_not_equal(last_response_on_onion_bind1, addr_receiver_onion2.get_received_addrs())
  89                  # Responses on from the same bind should be the same
  90                  assert_equal(last_response_on_local_bind, addr_receiver_local.get_received_addrs())
  91                  assert_equal(last_response_on_onion_bind1, addr_receiver_onion1.get_received_addrs())
  92                  assert_equal(last_response_on_onion_bind2, addr_receiver_onion2.get_received_addrs())
  93  
  94              last_response_on_local_bind = addr_receiver_local.get_received_addrs()
  95              last_response_on_onion_bind1 = addr_receiver_onion1.get_received_addrs()
  96              last_response_on_onion_bind2 = addr_receiver_onion2.get_received_addrs()
  97  
  98              for response in [last_response_on_local_bind, last_response_on_onion_bind1, last_response_on_onion_bind2]:
  99                  assert_equal(len(response), MAX_ADDR_TO_SEND)
 100  
 101          cur_mock_time += 3 * 24 * 60 * 60
 102          self.nodes[0].setmocktime(cur_mock_time)
 103  
 104          self.log.info('After time passed, see a new response to addr request')
 105          addr_receiver_local = self.nodes[0].add_p2p_connection(AddrReceiver())
 106          addr_receiver_onion1 = self.nodes[0].add_p2p_connection(AddrReceiver(), dstport=self.onion_port1)
 107          addr_receiver_onion2 = self.nodes[0].add_p2p_connection(AddrReceiver(), dstport=self.onion_port2)
 108  
 109          # Trigger response
 110          cur_mock_time += 5 * 60
 111          self.nodes[0].setmocktime(cur_mock_time)
 112          addr_receiver_local.wait_until(addr_receiver_local.addr_received)
 113          addr_receiver_onion1.wait_until(addr_receiver_onion1.addr_received)
 114          addr_receiver_onion2.wait_until(addr_receiver_onion2.addr_received)
 115  
 116          # new response is different
 117          assert_not_equal(set(last_response_on_local_bind), set(addr_receiver_local.get_received_addrs()))
 118          assert_not_equal(set(last_response_on_onion_bind1), set(addr_receiver_onion1.get_received_addrs()))
 119          assert_not_equal(set(last_response_on_onion_bind2), set(addr_receiver_onion2.get_received_addrs()))
 120  
 121  
 122  if __name__ == '__main__':
 123      AddrTest(__file__).main()
 124