/ test / functional / p2p_getaddr_caching.py
p2p_getaddr_caching.py
  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()