/ test / functional / p2p_getaddr_caching.py
p2p_getaddr_caching.py
  1  #!/usr/bin/env python3
  2  # Copyright (c) 2020-2022 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_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(BitcoinTestFramework):
 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().main()