p2p_getaddr_caching.py raw

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