addrman_tests.cpp raw

   1  // Copyright (c) 2012-2022 The Limenka developers
   2  // Distributed under the MIT software license, see the accompanying
   3  // file COPYING or http://www.opensource.org/licenses/mit-license.php.
   4  
   5  #include <addrdb.h>
   6  #include <addrman.h>
   7  #include <addrman_impl.h>
   8  #include <chainparams.h>
   9  #include <clientversion.h>
  10  #include <hash.h>
  11  #include <netbase.h>
  12  #include <random.h>
  13  #include <test/data/asmap.raw.h>
  14  #include <test/util/setup_common.h>
  15  #include <util/asmap.h>
  16  #include <util/string.h>
  17  
  18  #include <boost/test/unit_test.hpp>
  19  
  20  #include <optional>
  21  #include <string>
  22  
  23  using namespace std::literals;
  24  using node::NodeContext;
  25  using util::ToString;
  26  
  27  static NetGroupManager EMPTY_NETGROUPMAN{std::vector<bool>()};
  28  static const bool DETERMINISTIC{true};
  29  
  30  static int32_t GetCheckRatio(const NodeContext& node_ctx)
  31  {
  32      return std::clamp<int32_t>(node_ctx.args->GetIntArg("-checkaddrman", 100), 0, 1000000);
  33  }
  34  
  35  static CNetAddr ResolveIP(const std::string& ip)
  36  {
  37      const std::optional<CNetAddr> addr{LookupHost(ip, false)};
  38      BOOST_CHECK_MESSAGE(addr.has_value(), strprintf("failed to resolve: %s", ip));
  39      return addr.value_or(CNetAddr{});
  40  }
  41  
  42  static CService ResolveService(const std::string& ip, uint16_t port = 0)
  43  {
  44      const std::optional<CService> serv{Lookup(ip, port, false)};
  45      BOOST_CHECK_MESSAGE(serv.has_value(), strprintf("failed to resolve: %s:%i", ip, port));
  46      return serv.value_or(CService{});
  47  }
  48  
  49  
  50  static std::vector<bool> FromBytes(std::span<const std::byte> source)
  51  {
  52      int vector_size(source.size() * 8);
  53      std::vector<bool> result(vector_size);
  54      for (int byte_i = 0; byte_i < vector_size / 8; ++byte_i) {
  55          uint8_t cur_byte{std::to_integer<uint8_t>(source[byte_i])};
  56          for (int bit_i = 0; bit_i < 8; ++bit_i) {
  57              result[byte_i * 8 + bit_i] = (cur_byte >> bit_i) & 1;
  58          }
  59      }
  60      return result;
  61  }
  62  
  63  BOOST_FIXTURE_TEST_SUITE(addrman_tests, BasicTestingSetup)
  64  
  65  BOOST_AUTO_TEST_CASE(addrman_simple)
  66  {
  67      auto addrman = std::make_unique<AddrMan>(EMPTY_NETGROUPMAN, DETERMINISTIC, GetCheckRatio(m_node));
  68  
  69      CNetAddr source = ResolveIP("252.2.2.2");
  70  
  71      // Test: Does Addrman respond correctly when empty.
  72      BOOST_CHECK_EQUAL(addrman->Size(), 0U);
  73      auto addr_null = addrman->Select().first;
  74      BOOST_CHECK_EQUAL(addr_null.ToStringAddrPort(), "[::]:0");
  75  
  76      // Test: Does Addrman::Add work as expected.
  77      CService addr1 = ResolveService("250.1.1.1", 8333);
  78      BOOST_CHECK(addrman->Add({CAddress(addr1, NODE_NONE)}, source));
  79      BOOST_CHECK_EQUAL(addrman->Size(), 1U);
  80      auto addr_ret1 = addrman->Select().first;
  81      BOOST_CHECK_EQUAL(addr_ret1.ToStringAddrPort(), "250.1.1.1:8333");
  82  
  83      // Test: Does IP address deduplication work correctly.
  84      //  Expected dup IP should not be added.
  85      CService addr1_dup = ResolveService("250.1.1.1", 8333);
  86      BOOST_CHECK(!addrman->Add({CAddress(addr1_dup, NODE_NONE)}, source));
  87      BOOST_CHECK_EQUAL(addrman->Size(), 1U);
  88  
  89  
  90      // Test: New table has one addr and we add a diff addr we should
  91      //  have at least one addr.
  92      // Note that addrman's size cannot be tested reliably after insertion, as
  93      // hash collisions may occur. But we can always be sure of at least one
  94      // success.
  95  
  96      CService addr2 = ResolveService("250.1.1.2", 8333);
  97      BOOST_CHECK(addrman->Add({CAddress(addr2, NODE_NONE)}, source));
  98      BOOST_CHECK(addrman->Size() >= 1);
  99  
 100      // Test: reset addrman and test AddrMan::Add multiple addresses works as expected
 101      addrman = std::make_unique<AddrMan>(EMPTY_NETGROUPMAN, DETERMINISTIC, GetCheckRatio(m_node));
 102      std::vector<CAddress> vAddr;
 103      vAddr.emplace_back(ResolveService("250.1.1.3", 8333), NODE_NONE);
 104      vAddr.emplace_back(ResolveService("250.1.1.4", 8333), NODE_NONE);
 105      BOOST_CHECK(addrman->Add(vAddr, source));
 106      BOOST_CHECK(addrman->Size() >= 1);
 107  }
 108  
 109  BOOST_AUTO_TEST_CASE(addrman_ports)
 110  {
 111      auto addrman = std::make_unique<AddrMan>(EMPTY_NETGROUPMAN, DETERMINISTIC, GetCheckRatio(m_node));
 112  
 113      CNetAddr source = ResolveIP("252.2.2.2");
 114  
 115      BOOST_CHECK_EQUAL(addrman->Size(), 0U);
 116  
 117      // Test 7; Addr with same IP but diff port does not replace existing addr.
 118      CService addr1 = ResolveService("250.1.1.1", 8333);
 119      BOOST_CHECK(addrman->Add({CAddress(addr1, NODE_NONE)}, source));
 120      BOOST_CHECK_EQUAL(addrman->Size(), 1U);
 121  
 122      CService addr1_port = ResolveService("250.1.1.1", 8334);
 123      BOOST_CHECK(addrman->Add({CAddress(addr1_port, NODE_NONE)}, source));
 124      BOOST_CHECK_EQUAL(addrman->Size(), 2U);
 125      auto addr_ret2 = addrman->Select().first;
 126      BOOST_CHECK(addr_ret2.ToStringAddrPort() == "250.1.1.1:8333" || addr_ret2.ToStringAddrPort() == "250.1.1.1:8334");
 127  
 128      // Test: Add same IP but diff port to tried table; this converts the entry with
 129      // the specified port to tried, but not the other.
 130      addrman->Good(CAddress(addr1_port, NODE_NONE));
 131      BOOST_CHECK_EQUAL(addrman->Size(), 2U);
 132      bool new_only = true;
 133      auto addr_ret3 = addrman->Select(new_only).first;
 134      BOOST_CHECK_EQUAL(addr_ret3.ToStringAddrPort(), "250.1.1.1:8333");
 135  }
 136  
 137  BOOST_AUTO_TEST_CASE(addrman_select)
 138  {
 139      auto addrman = std::make_unique<AddrMan>(EMPTY_NETGROUPMAN, DETERMINISTIC, GetCheckRatio(m_node));
 140      BOOST_CHECK(!addrman->Select(false).first.IsValid());
 141      BOOST_CHECK(!addrman->Select(true).first.IsValid());
 142  
 143      CNetAddr source = ResolveIP("252.2.2.2");
 144  
 145      // Add 1 address to the new table
 146      CService addr1 = ResolveService("250.1.1.1", 8333);
 147      BOOST_CHECK(addrman->Add({CAddress(addr1, NODE_NONE)}, source));
 148      BOOST_CHECK_EQUAL(addrman->Size(), 1U);
 149  
 150      BOOST_CHECK(addrman->Select(/*new_only=*/true).first == addr1);
 151      BOOST_CHECK(addrman->Select(/*new_only=*/false).first == addr1);
 152  
 153      // Move address to the tried table
 154      BOOST_CHECK(addrman->Good(CAddress(addr1, NODE_NONE)));
 155  
 156      BOOST_CHECK_EQUAL(addrman->Size(), 1U);
 157      BOOST_CHECK(!addrman->Select(/*new_only=*/true).first.IsValid());
 158      BOOST_CHECK(addrman->Select().first == addr1);
 159      BOOST_CHECK_EQUAL(addrman->Size(), 1U);
 160  
 161      // Add one address to the new table
 162      CService addr2 = ResolveService("250.3.1.1", 8333);
 163      BOOST_CHECK(addrman->Add({CAddress(addr2, NODE_NONE)}, addr2));
 164      BOOST_CHECK(addrman->Select(/*new_only=*/true).first == addr2);
 165  
 166      // Add two more addresses to the new table
 167      CService addr3 = ResolveService("250.3.2.2", 9999);
 168      CService addr4 = ResolveService("250.3.3.3", 9999);
 169  
 170      BOOST_CHECK(addrman->Add({CAddress(addr3, NODE_NONE)}, addr2));
 171      BOOST_CHECK(addrman->Add({CAddress(addr4, NODE_NONE)}, ResolveService("250.4.1.1", 8333)));
 172  
 173      // Add three addresses to tried table.
 174      CService addr5 = ResolveService("250.4.4.4", 8333);
 175      CService addr6 = ResolveService("250.4.5.5", 7777);
 176      CService addr7 = ResolveService("250.4.6.6", 8333);
 177  
 178      BOOST_CHECK(addrman->Add({CAddress(addr5, NODE_NONE)}, addr3));
 179      BOOST_CHECK(addrman->Good(CAddress(addr5, NODE_NONE)));
 180      BOOST_CHECK(addrman->Add({CAddress(addr6, NODE_NONE)}, addr3));
 181      BOOST_CHECK(addrman->Good(CAddress(addr6, NODE_NONE)));
 182      BOOST_CHECK(addrman->Add({CAddress(addr7, NODE_NONE)}, ResolveService("250.1.1.3", 8333)));
 183      BOOST_CHECK(addrman->Good(CAddress(addr7, NODE_NONE)));
 184  
 185      // 6 addrs + 1 addr from last test = 7.
 186      BOOST_CHECK_EQUAL(addrman->Size(), 7U);
 187  
 188      // Select pulls from new and tried regardless of port number.
 189      std::set<uint16_t> ports;
 190      for (int i = 0; i < 20; ++i) {
 191          ports.insert(addrman->Select().first.GetPort());
 192      }
 193      BOOST_CHECK_EQUAL(ports.size(), 3U);
 194  }
 195  
 196  BOOST_AUTO_TEST_CASE(addrman_select_by_network)
 197  {
 198      auto addrman = std::make_unique<AddrMan>(EMPTY_NETGROUPMAN, DETERMINISTIC, GetCheckRatio(m_node));
 199      BOOST_CHECK(!addrman->Select(/*new_only=*/true, {NET_IPV4}).first.IsValid());
 200      BOOST_CHECK(!addrman->Select(/*new_only=*/false, {NET_IPV4}).first.IsValid());
 201  
 202      // add ipv4 address to the new table
 203      CNetAddr source = ResolveIP("252.2.2.2");
 204      CService addr1 = ResolveService("250.1.1.1", 8333);
 205      BOOST_CHECK(addrman->Add({CAddress(addr1, NODE_NONE)}, source));
 206  
 207      BOOST_CHECK(addrman->Select(/*new_only=*/true, {NET_IPV4}).first == addr1);
 208      BOOST_CHECK(addrman->Select(/*new_only=*/false, {NET_IPV4}).first == addr1);
 209      BOOST_CHECK(!addrman->Select(/*new_only=*/false, {NET_IPV6}).first.IsValid());
 210      BOOST_CHECK(!addrman->Select(/*new_only=*/false, {NET_ONION}).first.IsValid());
 211      BOOST_CHECK(!addrman->Select(/*new_only=*/false, {NET_I2P}).first.IsValid());
 212      BOOST_CHECK(!addrman->Select(/*new_only=*/false, {NET_CJDNS}).first.IsValid());
 213      BOOST_CHECK(!addrman->Select(/*new_only=*/true, {NET_CJDNS}).first.IsValid());
 214      BOOST_CHECK(addrman->Select(/*new_only=*/false).first == addr1);
 215  
 216      // add I2P address to the new table
 217      CAddress i2p_addr;
 218      i2p_addr.SetSpecial("udhdrtrcetjm5sxzskjyr5ztpeszydbh4dpl3pl4utgqqw2v4jna.b32.i2p");
 219      BOOST_CHECK(addrman->Add({i2p_addr}, source));
 220  
 221      BOOST_CHECK(addrman->Select(/*new_only=*/true, {NET_I2P}).first == i2p_addr);
 222      BOOST_CHECK(addrman->Select(/*new_only=*/false, {NET_I2P}).first == i2p_addr);
 223      BOOST_CHECK(addrman->Select(/*new_only=*/false, {NET_IPV4}).first == addr1);
 224      std::unordered_set<Network> nets_with_entries = {NET_IPV4, NET_I2P};
 225      BOOST_CHECK(addrman->Select(/*new_only=*/false, nets_with_entries).first.IsValid());
 226      BOOST_CHECK(!addrman->Select(/*new_only=*/false, {NET_IPV6}).first.IsValid());
 227      BOOST_CHECK(!addrman->Select(/*new_only=*/false, {NET_ONION}).first.IsValid());
 228      BOOST_CHECK(!addrman->Select(/*new_only=*/false, {NET_CJDNS}).first.IsValid());
 229      std::unordered_set<Network> nets_without_entries = {NET_IPV6, NET_ONION, NET_CJDNS};
 230      BOOST_CHECK(!addrman->Select(/*new_only=*/false, nets_without_entries).first.IsValid());
 231  
 232      // bump I2P address to tried table
 233      BOOST_CHECK(addrman->Good(i2p_addr));
 234  
 235      BOOST_CHECK(!addrman->Select(/*new_only=*/true, {NET_I2P}).first.IsValid());
 236      BOOST_CHECK(addrman->Select(/*new_only=*/false, {NET_I2P}).first == i2p_addr);
 237  
 238      // add another I2P address to the new table
 239      CAddress i2p_addr2;
 240      i2p_addr2.SetSpecial("c4gfnttsuwqomiygupdqqqyy5y5emnk5c73hrfvatri67prd7vyq.b32.i2p");
 241      BOOST_CHECK(addrman->Add({i2p_addr2}, source));
 242  
 243      BOOST_CHECK(addrman->Select(/*new_only=*/true, {NET_I2P}).first == i2p_addr2);
 244  
 245      // ensure that both new and tried table are selected from
 246      bool new_selected{false};
 247      bool tried_selected{false};
 248      int counter = 256;
 249  
 250      while (--counter > 0 && (!new_selected || !tried_selected)) {
 251          const CAddress selected{addrman->Select(/*new_only=*/false, {NET_I2P}).first};
 252          BOOST_REQUIRE(selected == i2p_addr || selected == i2p_addr2);
 253          if (selected == i2p_addr) {
 254              tried_selected = true;
 255          } else {
 256              new_selected = true;
 257          }
 258      }
 259  
 260      BOOST_CHECK(new_selected);
 261      BOOST_CHECK(tried_selected);
 262  }
 263  
 264  BOOST_AUTO_TEST_CASE(addrman_select_special)
 265  {
 266      // use a non-deterministic addrman to ensure a passing test isn't due to setup
 267      auto addrman = std::make_unique<AddrMan>(EMPTY_NETGROUPMAN, /*deterministic=*/false, GetCheckRatio(m_node));
 268  
 269      CNetAddr source = ResolveIP("252.2.2.2");
 270  
 271      // add I2P address to the tried table
 272      CAddress i2p_addr;
 273      i2p_addr.SetSpecial("udhdrtrcetjm5sxzskjyr5ztpeszydbh4dpl3pl4utgqqw2v4jna.b32.i2p");
 274      BOOST_CHECK(addrman->Add({i2p_addr}, source));
 275      BOOST_CHECK(addrman->Good(i2p_addr));
 276  
 277      // add ipv4 address to the new table
 278      CService addr1 = ResolveService("250.1.1.3", 8333);
 279      BOOST_CHECK(addrman->Add({CAddress(addr1, NODE_NONE)}, source));
 280  
 281      // since the only ipv4 address is on the new table, ensure that the new
 282      // table gets selected even if new_only is false. if the table was being
 283      // selected at random, this test will sporadically fail
 284      BOOST_CHECK(addrman->Select(/*new_only=*/false, {NET_IPV4}).first == addr1);
 285  }
 286  
 287  BOOST_AUTO_TEST_CASE(addrman_new_collisions)
 288  {
 289      auto addrman = std::make_unique<AddrMan>(EMPTY_NETGROUPMAN, DETERMINISTIC, GetCheckRatio(m_node));
 290  
 291      CNetAddr source = ResolveIP("252.2.2.2");
 292  
 293      uint32_t num_addrs{0};
 294  
 295      BOOST_CHECK_EQUAL(addrman->Size(), num_addrs);
 296  
 297      while (num_addrs < 22) { // Magic number! 250.1.1.1 - 250.1.1.22 do not collide with deterministic key = 1
 298          CService addr = ResolveService("250.1.1." + ToString(++num_addrs));
 299          BOOST_CHECK(addrman->Add({CAddress(addr, NODE_NONE)}, source));
 300  
 301          // Test: No collision in new table yet.
 302          BOOST_CHECK_EQUAL(addrman->Size(), num_addrs);
 303      }
 304  
 305      // Test: new table collision!
 306      CService addr1 = ResolveService("250.1.1." + ToString(++num_addrs));
 307      uint32_t collisions{1};
 308      BOOST_CHECK(addrman->Add({CAddress(addr1, NODE_NONE)}, source));
 309      BOOST_CHECK_EQUAL(addrman->Size(), num_addrs - collisions);
 310  
 311      CService addr2 = ResolveService("250.1.1." + ToString(++num_addrs));
 312      BOOST_CHECK(addrman->Add({CAddress(addr2, NODE_NONE)}, source));
 313      BOOST_CHECK_EQUAL(addrman->Size(), num_addrs - collisions);
 314  }
 315  
 316  BOOST_AUTO_TEST_CASE(addrman_new_multiplicity)
 317  {
 318      auto addrman = std::make_unique<AddrMan>(EMPTY_NETGROUPMAN, DETERMINISTIC, GetCheckRatio(m_node));
 319      CAddress addr{CAddress(ResolveService("253.3.3.3", 8333), NODE_NONE)};
 320      const auto start_time{Now<NodeSeconds>()};
 321      addr.nTime = start_time;
 322  
 323      // test that multiplicity stays at 1 if nTime doesn't increase
 324      for (unsigned int i = 1; i < 20; ++i) {
 325          std::string addr_ip{ToString(i % 256) + "." + ToString(i >> 8 % 256) + ".1.1"};
 326          CNetAddr source{ResolveIP(addr_ip)};
 327          addrman->Add({addr}, source);
 328      }
 329      AddressPosition addr_pos = addrman->FindAddressEntry(addr).value();
 330      BOOST_CHECK_EQUAL(addr_pos.multiplicity, 1U);
 331      BOOST_CHECK_EQUAL(addrman->Size(), 1U);
 332  
 333      // if nTime increases, an addr can occur in up to 8 buckets
 334      // The acceptance probability decreases exponentially with existing multiplicity -
 335      // choose number of iterations such that it gets to 8 with deterministic addrman.
 336      for (unsigned int i = 1; i < 400; ++i) {
 337          std::string addr_ip{ToString(i % 256) + "." + ToString(i >> 8 % 256) + ".1.1"};
 338          CNetAddr source{ResolveIP(addr_ip)};
 339          addr.nTime = start_time + std::chrono::seconds{i};
 340          addrman->Add({addr}, source);
 341      }
 342      AddressPosition addr_pos_multi = addrman->FindAddressEntry(addr).value();
 343      BOOST_CHECK_EQUAL(addr_pos_multi.multiplicity, 8U);
 344      // multiplicity doesn't affect size
 345      BOOST_CHECK_EQUAL(addrman->Size(), 1U);
 346  }
 347  
 348  BOOST_AUTO_TEST_CASE(addrman_tried_collisions)
 349  {
 350      auto addrman = std::make_unique<AddrMan>(EMPTY_NETGROUPMAN, DETERMINISTIC, GetCheckRatio(m_node));
 351  
 352      CNetAddr source = ResolveIP("252.2.2.2");
 353  
 354      uint32_t num_addrs{0};
 355  
 356      BOOST_CHECK_EQUAL(addrman->Size(), num_addrs);
 357  
 358      while (num_addrs < 35) { // Magic number! 250.1.1.1 - 250.1.1.35 do not collide in tried with deterministic key = 1
 359          CService addr = ResolveService("250.1.1." + ToString(++num_addrs));
 360          BOOST_CHECK(addrman->Add({CAddress(addr, NODE_NONE)}, source));
 361  
 362          // Test: Add to tried without collision
 363          BOOST_CHECK(addrman->Good(CAddress(addr, NODE_NONE)));
 364  
 365      }
 366  
 367      // Test: Unable to add to tried table due to collision!
 368      CService addr1 = ResolveService("250.1.1." + ToString(++num_addrs));
 369      BOOST_CHECK(addrman->Add({CAddress(addr1, NODE_NONE)}, source));
 370      BOOST_CHECK(!addrman->Good(CAddress(addr1, NODE_NONE)));
 371  
 372      // Test: Add the next address to tried without collision
 373      CService addr2 = ResolveService("250.1.1." + ToString(++num_addrs));
 374      BOOST_CHECK(addrman->Add({CAddress(addr2, NODE_NONE)}, source));
 375      BOOST_CHECK(addrman->Good(CAddress(addr2, NODE_NONE)));
 376  }
 377  
 378  
 379  BOOST_AUTO_TEST_CASE(addrman_getaddr)
 380  {
 381      auto addrman = std::make_unique<AddrMan>(EMPTY_NETGROUPMAN, DETERMINISTIC, GetCheckRatio(m_node));
 382  
 383      // Test: Sanity check, GetAddr should never return anything if addrman
 384      //  is empty.
 385      BOOST_CHECK_EQUAL(addrman->Size(), 0U);
 386      std::vector<CAddress> vAddr1 = addrman->GetAddr(/*max_addresses=*/0, /*max_pct=*/0, /*network=*/std::nullopt);
 387      BOOST_CHECK_EQUAL(vAddr1.size(), 0U);
 388  
 389      CAddress addr1 = CAddress(ResolveService("250.250.2.1", 8333), NODE_NONE);
 390      addr1.nTime = Now<NodeSeconds>(); // Set time so isTerrible = false
 391      CAddress addr2 = CAddress(ResolveService("250.251.2.2", 9999), NODE_NONE);
 392      addr2.nTime = Now<NodeSeconds>();
 393      CAddress addr3 = CAddress(ResolveService("251.252.2.3", 8333), NODE_NONE);
 394      addr3.nTime = Now<NodeSeconds>();
 395      CAddress addr4 = CAddress(ResolveService("252.253.3.4", 8333), NODE_NONE);
 396      addr4.nTime = Now<NodeSeconds>();
 397      CAddress addr5 = CAddress(ResolveService("252.254.4.5", 8333), NODE_NONE);
 398      addr5.nTime = Now<NodeSeconds>();
 399      CNetAddr source1 = ResolveIP("250.1.2.1");
 400      CNetAddr source2 = ResolveIP("250.2.3.3");
 401  
 402      // Test: Ensure GetAddr works with new addresses.
 403      BOOST_CHECK(addrman->Add({addr1, addr3, addr5}, source1));
 404      BOOST_CHECK(addrman->Add({addr2, addr4}, source2));
 405  
 406      BOOST_CHECK_EQUAL(addrman->GetAddr(/*max_addresses=*/0, /*max_pct=*/0, /*network=*/std::nullopt).size(), 5U);
 407      // Net processing asks for 23% of addresses. 23% of 5 is 1 rounded down.
 408      BOOST_CHECK_EQUAL(addrman->GetAddr(/*max_addresses=*/2500, /*max_pct=*/23, /*network=*/std::nullopt).size(), 1U);
 409  
 410      // Test: Ensure GetAddr works with new and tried addresses.
 411      BOOST_CHECK(addrman->Good(CAddress(addr1, NODE_NONE)));
 412      BOOST_CHECK(addrman->Good(CAddress(addr2, NODE_NONE)));
 413      BOOST_CHECK_EQUAL(addrman->GetAddr(/*max_addresses=*/0, /*max_pct=*/0, /*network=*/std::nullopt).size(), 5U);
 414      BOOST_CHECK_EQUAL(addrman->GetAddr(/*max_addresses=*/2500, /*max_pct=*/23, /*network=*/std::nullopt).size(), 1U);
 415  
 416      // Test: Ensure GetAddr still returns 23% when addrman has many addrs.
 417      for (unsigned int i = 1; i < (8 * 256); i++) {
 418          int octet1 = i % 256;
 419          int octet2 = i >> 8 % 256;
 420          std::string strAddr = ToString(octet1) + "." + ToString(octet2) + ".1.23";
 421          CAddress addr = CAddress(ResolveService(strAddr), NODE_NONE);
 422  
 423          // Ensure that for all addrs in addrman, isTerrible == false.
 424          addr.nTime = Now<NodeSeconds>();
 425          addrman->Add({addr}, ResolveIP(strAddr));
 426          if (i % 8 == 0)
 427              addrman->Good(addr);
 428      }
 429      std::vector<CAddress> vAddr = addrman->GetAddr(/*max_addresses=*/2500, /*max_pct=*/23, /*network=*/std::nullopt);
 430  
 431      size_t percent23 = (addrman->Size() * 23) / 100;
 432      BOOST_CHECK_EQUAL(vAddr.size(), percent23);
 433      BOOST_CHECK_EQUAL(vAddr.size(), 461U);
 434      // (addrman.Size() < number of addresses added) due to address collisions.
 435      BOOST_CHECK_EQUAL(addrman->Size(), 2006U);
 436  }
 437  
 438  BOOST_AUTO_TEST_CASE(getaddr_unfiltered)
 439  {
 440      auto addrman = std::make_unique<AddrMan>(EMPTY_NETGROUPMAN, DETERMINISTIC, GetCheckRatio(m_node));
 441  
 442      // Set time on this addr so isTerrible = false
 443      CAddress addr1 = CAddress(ResolveService("250.250.2.1", 8333), NODE_NONE);
 444      addr1.nTime = Now<NodeSeconds>();
 445      // Not setting time so this addr should be isTerrible = true
 446      CAddress addr2 = CAddress(ResolveService("250.251.2.2", 9999), NODE_NONE);
 447  
 448      CNetAddr source = ResolveIP("250.1.2.1");
 449      BOOST_CHECK(addrman->Add({addr1, addr2}, source));
 450  
 451      // Set time on this addr so isTerrible = false
 452      CAddress addr3 = CAddress(ResolveService("250.251.2.3", 9998), NODE_NONE);
 453      addr3.nTime = Now<NodeSeconds>();
 454      addrman->Good(addr3, /*time=*/Now<NodeSeconds>());
 455      BOOST_CHECK(addrman->Add({addr3}, source));
 456      // The time is set, but after ADDRMAN_RETRIES unsuccessful attempts not
 457      // retried in the last minute, this addr should be isTerrible = true
 458      for (size_t i = 0; i < 3; ++i) {
 459          addrman->Attempt(addr3, /*fCountFailure=*/true, /*time=*/Now<NodeSeconds>() - 61s);
 460      }
 461  
 462      // GetAddr filtered by quality (i.e. not IsTerrible) should only return addr1
 463      BOOST_CHECK_EQUAL(addrman->GetAddr(/*max_addresses=*/0, /*max_pct=*/0, /*network=*/std::nullopt).size(), 1U);
 464      // Unfiltered GetAddr should return all addrs
 465      BOOST_CHECK_EQUAL(addrman->GetAddr(/*max_addresses=*/0, /*max_pct=*/0, /*network=*/std::nullopt, /*filtered=*/false).size(), 3U);
 466  }
 467  
 468  BOOST_AUTO_TEST_CASE(caddrinfo_get_tried_bucket_legacy)
 469  {
 470      CAddress addr1 = CAddress(ResolveService("250.1.1.1", 8333), NODE_NONE);
 471      CAddress addr2 = CAddress(ResolveService("250.1.1.1", 9999), NODE_NONE);
 472  
 473      CNetAddr source1 = ResolveIP("250.1.1.1");
 474  
 475  
 476      AddrInfo info1 = AddrInfo(addr1, source1);
 477  
 478      uint256 nKey1 = (HashWriter{} << 1).GetHash();
 479      uint256 nKey2 = (HashWriter{} << 2).GetHash();
 480  
 481      BOOST_CHECK_EQUAL(info1.GetTriedBucket(nKey1, EMPTY_NETGROUPMAN), 40);
 482  
 483      // Test: Make sure key actually randomizes bucket placement. A fail on
 484      //  this test could be a security issue.
 485      BOOST_CHECK(info1.GetTriedBucket(nKey1, EMPTY_NETGROUPMAN) != info1.GetTriedBucket(nKey2, EMPTY_NETGROUPMAN));
 486  
 487      // Test: Two addresses with same IP but different ports can map to
 488      //  different buckets because they have different keys.
 489      AddrInfo info2 = AddrInfo(addr2, source1);
 490  
 491      BOOST_CHECK(info1.GetKey() != info2.GetKey());
 492      BOOST_CHECK(info1.GetTriedBucket(nKey1, EMPTY_NETGROUPMAN) != info2.GetTriedBucket(nKey1, EMPTY_NETGROUPMAN));
 493  
 494      std::set<int> buckets;
 495      for (int i = 0; i < 255; i++) {
 496          AddrInfo infoi = AddrInfo(
 497              CAddress(ResolveService("250.1.1." + ToString(i)), NODE_NONE),
 498              ResolveIP("250.1.1." + ToString(i)));
 499          int bucket = infoi.GetTriedBucket(nKey1, EMPTY_NETGROUPMAN);
 500          buckets.insert(bucket);
 501      }
 502      // Test: IP addresses in the same /16 prefix should
 503      // never get more than 8 buckets with legacy grouping
 504      BOOST_CHECK_EQUAL(buckets.size(), 8U);
 505  
 506      buckets.clear();
 507      for (int j = 0; j < 255; j++) {
 508          AddrInfo infoj = AddrInfo(
 509              CAddress(ResolveService("250." + ToString(j) + ".1.1"), NODE_NONE),
 510              ResolveIP("250." + ToString(j) + ".1.1"));
 511          int bucket = infoj.GetTriedBucket(nKey1, EMPTY_NETGROUPMAN);
 512          buckets.insert(bucket);
 513      }
 514      // Test: IP addresses in the different /16 prefix should map to more than
 515      // 8 buckets with legacy grouping
 516      BOOST_CHECK_EQUAL(buckets.size(), 160U);
 517  }
 518  
 519  BOOST_AUTO_TEST_CASE(caddrinfo_get_new_bucket_legacy)
 520  {
 521      CAddress addr1 = CAddress(ResolveService("250.1.2.1", 8333), NODE_NONE);
 522      CAddress addr2 = CAddress(ResolveService("250.1.2.1", 9999), NODE_NONE);
 523  
 524      CNetAddr source1 = ResolveIP("250.1.2.1");
 525  
 526      AddrInfo info1 = AddrInfo(addr1, source1);
 527  
 528      uint256 nKey1 = (HashWriter{} << 1).GetHash();
 529      uint256 nKey2 = (HashWriter{} << 2).GetHash();
 530  
 531      // Test: Make sure the buckets are what we expect
 532      BOOST_CHECK_EQUAL(info1.GetNewBucket(nKey1, EMPTY_NETGROUPMAN), 786);
 533      BOOST_CHECK_EQUAL(info1.GetNewBucket(nKey1, source1, EMPTY_NETGROUPMAN), 786);
 534  
 535      // Test: Make sure key actually randomizes bucket placement. A fail on
 536      //  this test could be a security issue.
 537      BOOST_CHECK(info1.GetNewBucket(nKey1, EMPTY_NETGROUPMAN) != info1.GetNewBucket(nKey2, EMPTY_NETGROUPMAN));
 538  
 539      // Test: Ports should not affect bucket placement in the addr
 540      AddrInfo info2 = AddrInfo(addr2, source1);
 541      BOOST_CHECK(info1.GetKey() != info2.GetKey());
 542      BOOST_CHECK_EQUAL(info1.GetNewBucket(nKey1, EMPTY_NETGROUPMAN), info2.GetNewBucket(nKey1, EMPTY_NETGROUPMAN));
 543  
 544      std::set<int> buckets;
 545      for (int i = 0; i < 255; i++) {
 546          AddrInfo infoi = AddrInfo(
 547              CAddress(ResolveService("250.1.1." + ToString(i)), NODE_NONE),
 548              ResolveIP("250.1.1." + ToString(i)));
 549          int bucket = infoi.GetNewBucket(nKey1, EMPTY_NETGROUPMAN);
 550          buckets.insert(bucket);
 551      }
 552      // Test: IP addresses in the same group (\16 prefix for IPv4) should
 553      //  always map to the same bucket.
 554      BOOST_CHECK_EQUAL(buckets.size(), 1U);
 555  
 556      buckets.clear();
 557      for (int j = 0; j < 4 * 255; j++) {
 558          AddrInfo infoj = AddrInfo(CAddress(
 559                                          ResolveService(
 560                                              ToString(250 + (j / 255)) + "." + ToString(j % 256) + ".1.1"), NODE_NONE),
 561              ResolveIP("251.4.1.1"));
 562          int bucket = infoj.GetNewBucket(nKey1, EMPTY_NETGROUPMAN);
 563          buckets.insert(bucket);
 564      }
 565      // Test: IP addresses in the same source groups should map to NO MORE
 566      //  than 64 buckets.
 567      BOOST_CHECK(buckets.size() <= 64);
 568  
 569      buckets.clear();
 570      for (int p = 0; p < 255; p++) {
 571          AddrInfo infoj = AddrInfo(
 572              CAddress(ResolveService("250.1.1.1"), NODE_NONE),
 573              ResolveIP("250." + ToString(p) + ".1.1"));
 574          int bucket = infoj.GetNewBucket(nKey1, EMPTY_NETGROUPMAN);
 575          buckets.insert(bucket);
 576      }
 577      // Test: IP addresses in the different source groups should map to MORE
 578      //  than 64 buckets.
 579      BOOST_CHECK(buckets.size() > 64);
 580  }
 581  
 582  // The following three test cases use asmap.raw
 583  // We use an artificial minimal mock mapping
 584  // 250.0.0.0/8 AS1000
 585  // 101.1.0.0/16 AS1
 586  // 101.2.0.0/16 AS2
 587  // 101.3.0.0/16 AS3
 588  // 101.4.0.0/16 AS4
 589  // 101.5.0.0/16 AS5
 590  // 101.6.0.0/16 AS6
 591  // 101.7.0.0/16 AS7
 592  // 101.8.0.0/16 AS8
 593  BOOST_AUTO_TEST_CASE(caddrinfo_get_tried_bucket)
 594  {
 595      std::vector<bool> asmap = FromBytes(test::data::asmap);
 596      NetGroupManager ngm_asmap{asmap};
 597  
 598      CAddress addr1 = CAddress(ResolveService("250.1.1.1", 8333), NODE_NONE);
 599      CAddress addr2 = CAddress(ResolveService("250.1.1.1", 9999), NODE_NONE);
 600  
 601      CNetAddr source1 = ResolveIP("250.1.1.1");
 602  
 603  
 604      AddrInfo info1 = AddrInfo(addr1, source1);
 605  
 606      uint256 nKey1 = (HashWriter{} << 1).GetHash();
 607      uint256 nKey2 = (HashWriter{} << 2).GetHash();
 608  
 609      BOOST_CHECK_EQUAL(info1.GetTriedBucket(nKey1, ngm_asmap), 236);
 610  
 611      // Test: Make sure key actually randomizes bucket placement. A fail on
 612      //  this test could be a security issue.
 613      BOOST_CHECK(info1.GetTriedBucket(nKey1, ngm_asmap) != info1.GetTriedBucket(nKey2, ngm_asmap));
 614  
 615      // Test: Two addresses with same IP but different ports can map to
 616      //  different buckets because they have different keys.
 617      AddrInfo info2 = AddrInfo(addr2, source1);
 618  
 619      BOOST_CHECK(info1.GetKey() != info2.GetKey());
 620      BOOST_CHECK(info1.GetTriedBucket(nKey1, ngm_asmap) != info2.GetTriedBucket(nKey1, ngm_asmap));
 621  
 622      std::set<int> buckets;
 623      for (int j = 0; j < 255; j++) {
 624          AddrInfo infoj = AddrInfo(
 625              CAddress(ResolveService("101." + ToString(j) + ".1.1"), NODE_NONE),
 626              ResolveIP("101." + ToString(j) + ".1.1"));
 627          int bucket = infoj.GetTriedBucket(nKey1, ngm_asmap);
 628          buckets.insert(bucket);
 629      }
 630      // Test: IP addresses in the different /16 prefix MAY map to more than
 631      // 8 buckets.
 632      BOOST_CHECK(buckets.size() > 8);
 633  
 634      buckets.clear();
 635      for (int j = 0; j < 255; j++) {
 636          AddrInfo infoj = AddrInfo(
 637              CAddress(ResolveService("250." + ToString(j) + ".1.1"), NODE_NONE),
 638              ResolveIP("250." + ToString(j) + ".1.1"));
 639          int bucket = infoj.GetTriedBucket(nKey1, ngm_asmap);
 640          buckets.insert(bucket);
 641      }
 642      // Test: IP addresses in the different /16 prefix MAY NOT map to more than
 643      // 8 buckets.
 644      BOOST_CHECK(buckets.size() == 8);
 645  }
 646  
 647  BOOST_AUTO_TEST_CASE(caddrinfo_get_new_bucket)
 648  {
 649      std::vector<bool> asmap = FromBytes(test::data::asmap);
 650      NetGroupManager ngm_asmap{asmap};
 651  
 652      CAddress addr1 = CAddress(ResolveService("250.1.2.1", 8333), NODE_NONE);
 653      CAddress addr2 = CAddress(ResolveService("250.1.2.1", 9999), NODE_NONE);
 654  
 655      CNetAddr source1 = ResolveIP("250.1.2.1");
 656  
 657      AddrInfo info1 = AddrInfo(addr1, source1);
 658  
 659      uint256 nKey1 = (HashWriter{} << 1).GetHash();
 660      uint256 nKey2 = (HashWriter{} << 2).GetHash();
 661  
 662      // Test: Make sure the buckets are what we expect
 663      BOOST_CHECK_EQUAL(info1.GetNewBucket(nKey1, ngm_asmap), 795);
 664      BOOST_CHECK_EQUAL(info1.GetNewBucket(nKey1, source1, ngm_asmap), 795);
 665  
 666      // Test: Make sure key actually randomizes bucket placement. A fail on
 667      //  this test could be a security issue.
 668      BOOST_CHECK(info1.GetNewBucket(nKey1, ngm_asmap) != info1.GetNewBucket(nKey2, ngm_asmap));
 669  
 670      // Test: Ports should not affect bucket placement in the addr
 671      AddrInfo info2 = AddrInfo(addr2, source1);
 672      BOOST_CHECK(info1.GetKey() != info2.GetKey());
 673      BOOST_CHECK_EQUAL(info1.GetNewBucket(nKey1, ngm_asmap), info2.GetNewBucket(nKey1, ngm_asmap));
 674  
 675      std::set<int> buckets;
 676      for (int i = 0; i < 255; i++) {
 677          AddrInfo infoi = AddrInfo(
 678              CAddress(ResolveService("250.1.1." + ToString(i)), NODE_NONE),
 679              ResolveIP("250.1.1." + ToString(i)));
 680          int bucket = infoi.GetNewBucket(nKey1, ngm_asmap);
 681          buckets.insert(bucket);
 682      }
 683      // Test: IP addresses in the same /16 prefix
 684      // usually map to the same bucket.
 685      BOOST_CHECK_EQUAL(buckets.size(), 1U);
 686  
 687      buckets.clear();
 688      for (int j = 0; j < 4 * 255; j++) {
 689          AddrInfo infoj = AddrInfo(CAddress(
 690                                          ResolveService(
 691                                              ToString(250 + (j / 255)) + "." + ToString(j % 256) + ".1.1"), NODE_NONE),
 692              ResolveIP("251.4.1.1"));
 693          int bucket = infoj.GetNewBucket(nKey1, ngm_asmap);
 694          buckets.insert(bucket);
 695      }
 696      // Test: IP addresses in the same source /16 prefix should not map to more
 697      // than 64 buckets.
 698      BOOST_CHECK(buckets.size() <= 64);
 699  
 700      buckets.clear();
 701      for (int p = 0; p < 255; p++) {
 702          AddrInfo infoj = AddrInfo(
 703              CAddress(ResolveService("250.1.1.1"), NODE_NONE),
 704              ResolveIP("101." + ToString(p) + ".1.1"));
 705          int bucket = infoj.GetNewBucket(nKey1, ngm_asmap);
 706          buckets.insert(bucket);
 707      }
 708      // Test: IP addresses in the different source /16 prefixes usually map to MORE
 709      // than 1 bucket.
 710      BOOST_CHECK(buckets.size() > 1);
 711  
 712      buckets.clear();
 713      for (int p = 0; p < 255; p++) {
 714          AddrInfo infoj = AddrInfo(
 715              CAddress(ResolveService("250.1.1.1"), NODE_NONE),
 716              ResolveIP("250." + ToString(p) + ".1.1"));
 717          int bucket = infoj.GetNewBucket(nKey1, ngm_asmap);
 718          buckets.insert(bucket);
 719      }
 720      // Test: IP addresses in the different source /16 prefixes sometimes map to NO MORE
 721      // than 1 bucket.
 722      BOOST_CHECK(buckets.size() == 1);
 723  }
 724  
 725  BOOST_AUTO_TEST_CASE(addrman_serialization)
 726  {
 727      std::vector<bool> asmap1 = FromBytes(test::data::asmap);
 728      NetGroupManager netgroupman{asmap1};
 729  
 730      const auto ratio = GetCheckRatio(m_node);
 731      auto addrman_asmap1 = std::make_unique<AddrMan>(netgroupman, DETERMINISTIC, ratio);
 732      auto addrman_asmap1_dup = std::make_unique<AddrMan>(netgroupman, DETERMINISTIC, ratio);
 733      auto addrman_noasmap = std::make_unique<AddrMan>(EMPTY_NETGROUPMAN, DETERMINISTIC, ratio);
 734  
 735      DataStream stream{};
 736  
 737      CAddress addr = CAddress(ResolveService("250.1.1.1"), NODE_NONE);
 738      CNetAddr default_source;
 739  
 740      addrman_asmap1->Add({addr}, default_source);
 741  
 742      stream << *addrman_asmap1;
 743      // serizalizing/deserializing addrman with the same asmap
 744      stream >> *addrman_asmap1_dup;
 745  
 746      AddressPosition addr_pos1 = addrman_asmap1->FindAddressEntry(addr).value();
 747      AddressPosition addr_pos2 = addrman_asmap1_dup->FindAddressEntry(addr).value();
 748      BOOST_CHECK(addr_pos1.multiplicity != 0);
 749      BOOST_CHECK(addr_pos2.multiplicity != 0);
 750  
 751      BOOST_CHECK(addr_pos1 == addr_pos2);
 752  
 753      // deserializing asmaped peers.dat to non-asmaped addrman
 754      stream << *addrman_asmap1;
 755      stream >> *addrman_noasmap;
 756      AddressPosition addr_pos3 = addrman_noasmap->FindAddressEntry(addr).value();
 757      BOOST_CHECK(addr_pos3.multiplicity != 0);
 758      BOOST_CHECK(addr_pos1.bucket != addr_pos3.bucket);
 759      BOOST_CHECK(addr_pos1.position != addr_pos3.position);
 760  
 761      // deserializing non-asmaped peers.dat to asmaped addrman
 762      addrman_asmap1 = std::make_unique<AddrMan>(netgroupman, DETERMINISTIC, ratio);
 763      addrman_noasmap = std::make_unique<AddrMan>(EMPTY_NETGROUPMAN, DETERMINISTIC, ratio);
 764      addrman_noasmap->Add({addr}, default_source);
 765      stream << *addrman_noasmap;
 766      stream >> *addrman_asmap1;
 767  
 768      AddressPosition addr_pos4 = addrman_asmap1->FindAddressEntry(addr).value();
 769      BOOST_CHECK(addr_pos4.multiplicity != 0);
 770      BOOST_CHECK(addr_pos4.bucket != addr_pos3.bucket);
 771      BOOST_CHECK(addr_pos4 == addr_pos2);
 772  
 773      // used to map to different buckets, now maps to the same bucket.
 774      addrman_asmap1 = std::make_unique<AddrMan>(netgroupman, DETERMINISTIC, ratio);
 775      addrman_noasmap = std::make_unique<AddrMan>(EMPTY_NETGROUPMAN, DETERMINISTIC, ratio);
 776      CAddress addr1 = CAddress(ResolveService("250.1.1.1"), NODE_NONE);
 777      CAddress addr2 = CAddress(ResolveService("250.2.1.1"), NODE_NONE);
 778      addrman_noasmap->Add({addr, addr2}, default_source);
 779      AddressPosition addr_pos5 = addrman_noasmap->FindAddressEntry(addr1).value();
 780      AddressPosition addr_pos6 = addrman_noasmap->FindAddressEntry(addr2).value();
 781      BOOST_CHECK(addr_pos5.bucket != addr_pos6.bucket);
 782      stream << *addrman_noasmap;
 783      stream >> *addrman_asmap1;
 784      AddressPosition addr_pos7 = addrman_asmap1->FindAddressEntry(addr1).value();
 785      AddressPosition addr_pos8 = addrman_asmap1->FindAddressEntry(addr2).value();
 786      BOOST_CHECK(addr_pos7.bucket == addr_pos8.bucket);
 787      BOOST_CHECK(addr_pos7.position != addr_pos8.position);
 788  }
 789  
 790  BOOST_AUTO_TEST_CASE(remove_invalid)
 791  {
 792      // Confirm that invalid addresses are ignored in unserialization.
 793  
 794      auto addrman = std::make_unique<AddrMan>(EMPTY_NETGROUPMAN, DETERMINISTIC, GetCheckRatio(m_node));
 795      DataStream stream{};
 796  
 797      const CAddress new1{ResolveService("5.5.5.5"), NODE_NONE};
 798      const CAddress new2{ResolveService("6.6.6.6"), NODE_NONE};
 799      const CAddress tried1{ResolveService("7.7.7.7"), NODE_NONE};
 800      const CAddress tried2{ResolveService("8.8.8.8"), NODE_NONE};
 801  
 802      addrman->Add({new1, tried1, new2, tried2}, CNetAddr{});
 803      addrman->Good(tried1);
 804      addrman->Good(tried2);
 805      BOOST_REQUIRE_EQUAL(addrman->Size(), 4);
 806  
 807      stream << *addrman;
 808  
 809      const std::string str{stream.str()};
 810      size_t pos;
 811  
 812      const char new2_raw[]{6, 6, 6, 6};
 813      const uint8_t new2_raw_replacement[]{0, 0, 0, 0}; // 0.0.0.0 is !IsValid()
 814      pos = str.find(new2_raw, 0, sizeof(new2_raw));
 815      BOOST_REQUIRE(pos != std::string::npos);
 816      BOOST_REQUIRE(pos + sizeof(new2_raw_replacement) <= stream.size());
 817      memcpy(stream.data() + pos, new2_raw_replacement, sizeof(new2_raw_replacement));
 818  
 819      const char tried2_raw[]{8, 8, 8, 8};
 820      const uint8_t tried2_raw_replacement[]{255, 255, 255, 255}; // 255.255.255.255 is !IsValid()
 821      pos = str.find(tried2_raw, 0, sizeof(tried2_raw));
 822      BOOST_REQUIRE(pos != std::string::npos);
 823      BOOST_REQUIRE(pos + sizeof(tried2_raw_replacement) <= stream.size());
 824      memcpy(stream.data() + pos, tried2_raw_replacement, sizeof(tried2_raw_replacement));
 825  
 826      addrman = std::make_unique<AddrMan>(EMPTY_NETGROUPMAN, DETERMINISTIC, GetCheckRatio(m_node));
 827      stream >> *addrman;
 828      BOOST_CHECK_EQUAL(addrman->Size(), 2);
 829  }
 830  
 831  BOOST_AUTO_TEST_CASE(addrman_selecttriedcollision)
 832  {
 833      auto addrman = std::make_unique<AddrMan>(EMPTY_NETGROUPMAN, DETERMINISTIC, GetCheckRatio(m_node));
 834  
 835      BOOST_CHECK(addrman->Size() == 0);
 836  
 837      // Empty addrman should return blank addrman info.
 838      BOOST_CHECK(addrman->SelectTriedCollision().first.ToStringAddrPort() == "[::]:0");
 839  
 840      // Add twenty two addresses.
 841      CNetAddr source = ResolveIP("252.2.2.2");
 842      for (unsigned int i = 1; i < 23; i++) {
 843          CService addr = ResolveService("250.1.1." + ToString(i));
 844          BOOST_CHECK(addrman->Add({CAddress(addr, NODE_NONE)}, source));
 845  
 846          // No collisions in tried.
 847          BOOST_CHECK(addrman->Good(addr));
 848          BOOST_CHECK(addrman->SelectTriedCollision().first.ToStringAddrPort() == "[::]:0");
 849      }
 850  
 851      // Ensure Good handles duplicates well.
 852      // If an address is a duplicate, Good will return false but will not count it as a collision.
 853      for (unsigned int i = 1; i < 23; i++) {
 854          CService addr = ResolveService("250.1.1." + ToString(i));
 855  
 856          // Unable to add duplicate address to tried table.
 857          BOOST_CHECK(!addrman->Good(addr));
 858  
 859          // Verify duplicate address not marked as a collision.
 860          BOOST_CHECK(addrman->SelectTriedCollision().first.ToStringAddrPort() == "[::]:0");
 861      }
 862  }
 863  
 864  BOOST_AUTO_TEST_CASE(addrman_noevict)
 865  {
 866      auto addrman = std::make_unique<AddrMan>(EMPTY_NETGROUPMAN, DETERMINISTIC, GetCheckRatio(m_node));
 867  
 868      // Add 35 addresses.
 869      CNetAddr source = ResolveIP("252.2.2.2");
 870      for (unsigned int i = 1; i < 36; i++) {
 871          CService addr = ResolveService("250.1.1." + ToString(i));
 872          BOOST_CHECK(addrman->Add({CAddress(addr, NODE_NONE)}, source));
 873  
 874          // No collision yet.
 875          BOOST_CHECK(addrman->Good(addr));
 876      }
 877  
 878      // Collision in tried table between 36 and 19.
 879      CService addr36 = ResolveService("250.1.1.36");
 880      BOOST_CHECK(addrman->Add({CAddress(addr36, NODE_NONE)}, source));
 881      BOOST_CHECK(!addrman->Good(addr36));
 882      BOOST_CHECK_EQUAL(addrman->SelectTriedCollision().first.ToStringAddrPort(), "250.1.1.19:0");
 883  
 884      // 36 should be discarded and 19 not evicted.
 885      // This means we keep 19 in the tried table and
 886      // 36 stays in the new table.
 887      addrman->ResolveCollisions();
 888      BOOST_CHECK(addrman->SelectTriedCollision().first.ToStringAddrPort() == "[::]:0");
 889  
 890      // Lets create two collisions.
 891      for (unsigned int i = 37; i < 59; i++) {
 892          CService addr = ResolveService("250.1.1." + ToString(i));
 893          BOOST_CHECK(addrman->Add({CAddress(addr, NODE_NONE)}, source));
 894          BOOST_CHECK(addrman->Good(addr));
 895      }
 896  
 897      // Cause a collision in the tried table.
 898      CService addr59 = ResolveService("250.1.1.59");
 899      BOOST_CHECK(addrman->Add({CAddress(addr59, NODE_NONE)}, source));
 900      BOOST_CHECK(!addrman->Good(addr59));
 901  
 902      BOOST_CHECK_EQUAL(addrman->SelectTriedCollision().first.ToStringAddrPort(), "250.1.1.10:0");
 903  
 904      // Cause a second collision in the new table.
 905      BOOST_CHECK(!addrman->Add({CAddress(addr36, NODE_NONE)}, source));
 906  
 907      // 36 still cannot be moved from new to tried due to colliding with 19
 908      BOOST_CHECK(!addrman->Good(addr36));
 909      BOOST_CHECK(addrman->SelectTriedCollision().first.ToStringAddrPort() != "[::]:0");
 910  
 911      // Resolve all collisions.
 912      addrman->ResolveCollisions();
 913      BOOST_CHECK(addrman->SelectTriedCollision().first.ToStringAddrPort() == "[::]:0");
 914  }
 915  
 916  BOOST_AUTO_TEST_CASE(addrman_evictionworks)
 917  {
 918      auto addrman = std::make_unique<AddrMan>(EMPTY_NETGROUPMAN, DETERMINISTIC, GetCheckRatio(m_node));
 919  
 920      BOOST_CHECK(addrman->Size() == 0);
 921  
 922      // Empty addrman should return blank addrman info.
 923      BOOST_CHECK(addrman->SelectTriedCollision().first.ToStringAddrPort() == "[::]:0");
 924  
 925      // Add 35 addresses
 926      CNetAddr source = ResolveIP("252.2.2.2");
 927      for (unsigned int i = 1; i < 36; i++) {
 928          CService addr = ResolveService("250.1.1." + ToString(i));
 929          BOOST_CHECK(addrman->Add({CAddress(addr, NODE_NONE)}, source));
 930  
 931          // No collision yet.
 932          BOOST_CHECK(addrman->Good(addr));
 933      }
 934  
 935      // Collision between 36 and 19.
 936      CService addr = ResolveService("250.1.1.36");
 937      BOOST_CHECK(addrman->Add({CAddress(addr, NODE_NONE)}, source));
 938      BOOST_CHECK(!addrman->Good(addr));
 939  
 940      auto info = addrman->SelectTriedCollision().first;
 941      BOOST_CHECK_EQUAL(info.ToStringAddrPort(), "250.1.1.19:0");
 942  
 943      // Ensure test of address fails, so that it is evicted.
 944      // Update entry in tried by setting last good connection in the deep past.
 945      BOOST_CHECK(!addrman->Good(info, NodeSeconds{1s}));
 946      addrman->Attempt(info, /*fCountFailure=*/false, Now<NodeSeconds>() - 61s);
 947  
 948      // Should swap 36 for 19.
 949      addrman->ResolveCollisions();
 950      BOOST_CHECK(addrman->SelectTriedCollision().first.ToStringAddrPort() == "[::]:0");
 951      AddressPosition addr_pos{addrman->FindAddressEntry(CAddress(addr, NODE_NONE)).value()};
 952      BOOST_CHECK(addr_pos.tried);
 953  
 954      // If 36 was swapped for 19, then adding 36 to tried should fail because we
 955      // are attempting to add a duplicate.
 956      // We check this by verifying Good() returns false and also verifying that
 957      // we have no collisions.
 958      BOOST_CHECK(!addrman->Good(addr));
 959      BOOST_CHECK(addrman->SelectTriedCollision().first.ToStringAddrPort() == "[::]:0");
 960  
 961      // 19 should fail as a collision (not a duplicate) if we now attempt to move
 962      // it to the tried table.
 963      CService addr19 = ResolveService("250.1.1.19");
 964      BOOST_CHECK(!addrman->Good(addr19));
 965      BOOST_CHECK_EQUAL(addrman->SelectTriedCollision().first.ToStringAddrPort(), "250.1.1.36:0");
 966  
 967      // Eviction is also successful if too much time has passed since last try
 968      SetMockTime(GetTime() + 4 * 60 *60);
 969      addrman->ResolveCollisions();
 970      BOOST_CHECK(addrman->SelectTriedCollision().first.ToStringAddrPort() == "[::]:0");
 971      //Now 19 is in tried again, and 36 back to new
 972      AddressPosition addr_pos19{addrman->FindAddressEntry(CAddress(addr19, NODE_NONE)).value()};
 973      BOOST_CHECK(addr_pos19.tried);
 974      AddressPosition addr_pos36{addrman->FindAddressEntry(CAddress(addr, NODE_NONE)).value()};
 975      BOOST_CHECK(!addr_pos36.tried);
 976  }
 977  
 978  static auto AddrmanToStream(const AddrMan& addrman)
 979  {
 980      DataStream ssPeersIn{};
 981      ssPeersIn << Params().MessageStart();
 982      ssPeersIn << addrman;
 983      return ssPeersIn;
 984  }
 985  
 986  BOOST_AUTO_TEST_CASE(load_addrman)
 987  {
 988      AddrMan addrman{EMPTY_NETGROUPMAN, DETERMINISTIC, GetCheckRatio(m_node)};
 989  
 990      std::optional<CService> addr1, addr2, addr3, addr4;
 991      addr1 = Lookup("250.7.1.1", 8333, false);
 992      BOOST_CHECK(addr1.has_value());
 993      addr2 = Lookup("250.7.2.2", 9999, false);
 994      BOOST_CHECK(addr2.has_value());
 995      addr3 = Lookup("250.7.3.3", 9999, false);
 996      BOOST_CHECK(addr3.has_value());
 997      addr3 = Lookup("250.7.3.3"s, 9999, false);
 998      BOOST_CHECK(addr3.has_value());
 999      addr4 = Lookup("250.7.3.3\0example.com"s, 9999, false);
1000      BOOST_CHECK(!addr4.has_value());
1001  
1002      // Add three addresses to new table.
1003      const std::optional<CService> source{Lookup("252.5.1.1", 8333, false)};
1004      BOOST_CHECK(source.has_value());
1005      std::vector<CAddress> addresses{CAddress(addr1.value(), NODE_NONE), CAddress(addr2.value(), NODE_NONE), CAddress(addr3.value(), NODE_NONE)};
1006      BOOST_CHECK(addrman.Add(addresses, source.value()));
1007      BOOST_CHECK(addrman.Size() == 3);
1008  
1009      // Test that the de-serialization does not throw an exception.
1010      auto ssPeers1{AddrmanToStream(addrman)};
1011      bool exceptionThrown = false;
1012      AddrMan addrman1{EMPTY_NETGROUPMAN, !DETERMINISTIC, GetCheckRatio(m_node)};
1013  
1014      BOOST_CHECK(addrman1.Size() == 0);
1015      try {
1016          unsigned char pchMsgTmp[4];
1017          ssPeers1 >> pchMsgTmp;
1018          ssPeers1 >> addrman1;
1019      } catch (const std::exception&) {
1020          exceptionThrown = true;
1021      }
1022  
1023      BOOST_CHECK(addrman1.Size() == 3);
1024      BOOST_CHECK(exceptionThrown == false);
1025  
1026      // Test that ReadFromStream creates an addrman with the correct number of addrs.
1027      DataStream ssPeers2 = AddrmanToStream(addrman);
1028  
1029      AddrMan addrman2{EMPTY_NETGROUPMAN, !DETERMINISTIC, GetCheckRatio(m_node)};
1030      BOOST_CHECK(addrman2.Size() == 0);
1031      ReadFromStream(addrman2, ssPeers2);
1032      BOOST_CHECK(addrman2.Size() == 3);
1033  }
1034  
1035  // Produce a corrupt peers.dat that claims 20 addrs when it only has one addr.
1036  static auto MakeCorruptPeersDat()
1037  {
1038      DataStream s{};
1039      s << ::Params().MessageStart();
1040  
1041      unsigned char nVersion = 1;
1042      s << nVersion;
1043      s << ((unsigned char)32);
1044      s << uint256::ONE;
1045      s << 10; // nNew
1046      s << 10; // nTried
1047  
1048      int nUBuckets = ADDRMAN_NEW_BUCKET_COUNT ^ (1 << 30);
1049      s << nUBuckets;
1050  
1051      const std::optional<CService> serv{Lookup("252.1.1.1", 7777, false)};
1052      BOOST_REQUIRE(serv.has_value());
1053      CAddress addr = CAddress(serv.value(), NODE_NONE);
1054      std::optional<CNetAddr> resolved{LookupHost("252.2.2.2", false)};
1055      BOOST_REQUIRE(resolved.has_value());
1056      AddrInfo info = AddrInfo(addr, resolved.value());
1057      s << CAddress::V1_DISK(info);
1058  
1059      return s;
1060  }
1061  
1062  BOOST_AUTO_TEST_CASE(load_addrman_corrupted)
1063  {
1064      // Test that the de-serialization of corrupted peers.dat throws an exception.
1065      auto ssPeers1{MakeCorruptPeersDat()};
1066      bool exceptionThrown = false;
1067      AddrMan addrman1{EMPTY_NETGROUPMAN, !DETERMINISTIC, GetCheckRatio(m_node)};
1068      BOOST_CHECK(addrman1.Size() == 0);
1069      try {
1070          unsigned char pchMsgTmp[4];
1071          ssPeers1 >> pchMsgTmp;
1072          ssPeers1 >> addrman1;
1073      } catch (const std::exception&) {
1074          exceptionThrown = true;
1075      }
1076      BOOST_CHECK(exceptionThrown);
1077  
1078      // Test that ReadFromStream fails if peers.dat is corrupt
1079      auto ssPeers2{MakeCorruptPeersDat()};
1080  
1081      AddrMan addrman2{EMPTY_NETGROUPMAN, !DETERMINISTIC, GetCheckRatio(m_node)};
1082      BOOST_CHECK(addrman2.Size() == 0);
1083      BOOST_CHECK_THROW(ReadFromStream(addrman2, ssPeers2), std::ios_base::failure);
1084  }
1085  
1086  BOOST_AUTO_TEST_CASE(addrman_update_address)
1087  {
1088      // Tests updating nTime via Connected() and nServices via SetServices() and Add()
1089      auto addrman = std::make_unique<AddrMan>(EMPTY_NETGROUPMAN, DETERMINISTIC, GetCheckRatio(m_node));
1090      CNetAddr source{ResolveIP("252.2.2.2")};
1091      CAddress addr{CAddress(ResolveService("250.1.1.1", 8333), NODE_NONE)};
1092  
1093      const auto start_time{Now<NodeSeconds>() - 10000s};
1094      addr.nTime = start_time;
1095      BOOST_CHECK(addrman->Add({addr}, source));
1096      BOOST_CHECK_EQUAL(addrman->Size(), 1U);
1097  
1098      // Updating an addrman entry with a different port doesn't change it
1099      CAddress addr_diff_port{CAddress(ResolveService("250.1.1.1", 8334), NODE_NONE)};
1100      addr_diff_port.nTime = start_time;
1101      addrman->Connected(addr_diff_port);
1102      addrman->SetServices(addr_diff_port, NODE_NETWORK_LIMITED);
1103      std::vector<CAddress> vAddr1{addrman->GetAddr(/*max_addresses=*/0, /*max_pct=*/0, /*network=*/std::nullopt)};
1104      BOOST_CHECK_EQUAL(vAddr1.size(), 1U);
1105      BOOST_CHECK(vAddr1.at(0).nTime == start_time);
1106      BOOST_CHECK_EQUAL(vAddr1.at(0).nServices, NODE_NONE);
1107  
1108      // Updating an addrman entry with the correct port is successful
1109      addrman->Connected(addr);
1110      addrman->SetServices(addr, NODE_NETWORK_LIMITED);
1111      std::vector<CAddress> vAddr2 = addrman->GetAddr(/*max_addresses=*/0, /*max_pct=*/0, /*network=*/std::nullopt);
1112      BOOST_CHECK_EQUAL(vAddr2.size(), 1U);
1113      BOOST_CHECK(vAddr2.at(0).nTime >= start_time + 10000s);
1114      BOOST_CHECK_EQUAL(vAddr2.at(0).nServices, NODE_NETWORK_LIMITED);
1115  
1116      // Updating an existing addr through Add() (used in gossip relay) can add additional services but can't remove existing ones.
1117      CAddress addr_v2{CAddress(ResolveService("250.1.1.1", 8333), NODE_P2P_V2)};
1118      addr_v2.nTime = start_time;
1119      BOOST_CHECK(!addrman->Add({addr_v2}, source));
1120      std::vector<CAddress> vAddr3{addrman->GetAddr(/*max_addresses=*/0, /*max_pct=*/0, /*network=*/std::nullopt)};
1121      BOOST_CHECK_EQUAL(vAddr3.size(), 1U);
1122      BOOST_CHECK_EQUAL(vAddr3.at(0).nServices, NODE_P2P_V2 | NODE_NETWORK_LIMITED);
1123  
1124      // SetServices() (used when we connected to them) overwrites existing service flags
1125      addrman->SetServices(addr, NODE_NETWORK);
1126      std::vector<CAddress> vAddr4{addrman->GetAddr(/*max_addresses=*/0, /*max_pct=*/0, /*network=*/std::nullopt)};
1127      BOOST_CHECK_EQUAL(vAddr4.size(), 1U);
1128      BOOST_CHECK_EQUAL(vAddr4.at(0).nServices, NODE_NETWORK);
1129  
1130      // Promoting to Tried does not affect the service flags
1131      BOOST_CHECK(addrman->Good(addr)); // addr has NODE_NONE
1132      std::vector<CAddress> vAddr5{addrman->GetAddr(/*max_addresses=*/0, /*max_pct=*/0, /*network=*/std::nullopt)};
1133      BOOST_CHECK_EQUAL(vAddr5.size(), 1U);
1134      BOOST_CHECK_EQUAL(vAddr5.at(0).nServices, NODE_NETWORK);
1135  
1136      // Adding service flags even works when the addr is in Tried
1137      BOOST_CHECK(!addrman->Add({addr_v2}, source));
1138      std::vector<CAddress> vAddr6{addrman->GetAddr(/*max_addresses=*/0, /*max_pct=*/0, /*network=*/std::nullopt)};
1139      BOOST_CHECK_EQUAL(vAddr6.size(), 1U);
1140      BOOST_CHECK_EQUAL(vAddr6.at(0).nServices, NODE_NETWORK | NODE_P2P_V2);
1141  }
1142  
1143  BOOST_AUTO_TEST_CASE(addrman_size)
1144  {
1145      auto addrman = std::make_unique<AddrMan>(EMPTY_NETGROUPMAN, DETERMINISTIC, GetCheckRatio(m_node));
1146      const CNetAddr source = ResolveIP("252.2.2.2");
1147  
1148      // empty addrman
1149      BOOST_CHECK_EQUAL(addrman->Size(/*net=*/std::nullopt, /*in_new=*/std::nullopt), 0U);
1150      BOOST_CHECK_EQUAL(addrman->Size(/*net=*/NET_IPV4, /*in_new=*/std::nullopt), 0U);
1151      BOOST_CHECK_EQUAL(addrman->Size(/*net=*/std::nullopt, /*in_new=*/true), 0U);
1152      BOOST_CHECK_EQUAL(addrman->Size(/*net=*/NET_IPV4, /*in_new=*/false), 0U);
1153  
1154      // add two ipv4 addresses, one to tried and new
1155      const CAddress addr1{ResolveService("250.1.1.1", 8333), NODE_NONE};
1156      BOOST_CHECK(addrman->Add({addr1}, source));
1157      BOOST_CHECK(addrman->Good(addr1));
1158      const CAddress addr2{ResolveService("250.1.1.2", 8333), NODE_NONE};
1159      BOOST_CHECK(addrman->Add({addr2}, source));
1160  
1161      BOOST_CHECK_EQUAL(addrman->Size(/*net=*/std::nullopt, /*in_new=*/std::nullopt), 2U);
1162      BOOST_CHECK_EQUAL(addrman->Size(/*net=*/NET_IPV4, /*in_new=*/std::nullopt), 2U);
1163      BOOST_CHECK_EQUAL(addrman->Size(/*net=*/std::nullopt, /*in_new=*/true), 1U);
1164      BOOST_CHECK_EQUAL(addrman->Size(/*net=*/std::nullopt, /*in_new=*/false), 1U);
1165      BOOST_CHECK_EQUAL(addrman->Size(/*net=*/NET_IPV4, /*in_new=*/true), 1U);
1166      BOOST_CHECK_EQUAL(addrman->Size(/*net=*/NET_IPV4, /*in_new=*/false), 1U);
1167  
1168      // add one i2p address to new
1169      CService i2p_addr;
1170      i2p_addr.SetSpecial("UDHDrtrcetjm5sxzskjyr5ztpeszydbh4dpl3pl4utgqqw2v4jna.b32.I2P");
1171      const CAddress addr3{i2p_addr, NODE_NONE};
1172      BOOST_CHECK(addrman->Add({addr3}, source));
1173      BOOST_CHECK_EQUAL(addrman->Size(/*net=*/std::nullopt, /*in_new=*/std::nullopt), 3U);
1174      BOOST_CHECK_EQUAL(addrman->Size(/*net=*/NET_IPV4, /*in_new=*/std::nullopt), 2U);
1175      BOOST_CHECK_EQUAL(addrman->Size(/*net=*/NET_I2P, /*in_new=*/std::nullopt), 1U);
1176      BOOST_CHECK_EQUAL(addrman->Size(/*net=*/NET_I2P, /*in_new=*/true), 1U);
1177      BOOST_CHECK_EQUAL(addrman->Size(/*net=*/std::nullopt, /*in_new=*/true), 2U);
1178      BOOST_CHECK_EQUAL(addrman->Size(/*net=*/std::nullopt, /*in_new=*/false), 1U);
1179  }
1180  
1181  BOOST_AUTO_TEST_SUITE_END()
1182