/ src / test / addrman_tests.cpp
addrman_tests.cpp
   1  // Copyright (c) 2012-2022 The Bitcoin Core 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      // Set time more than 10 minutes in the future (flying DeLorean), so this
 463      // addr should be isTerrible = true
 464      CAddress addr4 = CAddress(ResolveService("250.252.2.4", 9997), NODE_NONE);
 465      addr4.nTime = Now<NodeSeconds>() + 11min;
 466      BOOST_CHECK(addrman->Add({addr4}, source));
 467  
 468      // GetAddr filtered by quality (i.e. not IsTerrible) should only return addr1
 469      BOOST_CHECK_EQUAL(addrman->GetAddr(/*max_addresses=*/0, /*max_pct=*/0, /*network=*/std::nullopt).size(), 1U);
 470      // Unfiltered GetAddr should return all addrs
 471      BOOST_CHECK_EQUAL(addrman->GetAddr(/*max_addresses=*/0, /*max_pct=*/0, /*network=*/std::nullopt, /*filtered=*/false).size(), 4U);
 472  }
 473  
 474  BOOST_AUTO_TEST_CASE(caddrinfo_get_tried_bucket_legacy)
 475  {
 476      CAddress addr1 = CAddress(ResolveService("250.1.1.1", 8333), NODE_NONE);
 477      CAddress addr2 = CAddress(ResolveService("250.1.1.1", 9999), NODE_NONE);
 478  
 479      CNetAddr source1 = ResolveIP("250.1.1.1");
 480  
 481  
 482      AddrInfo info1 = AddrInfo(addr1, source1);
 483  
 484      uint256 nKey1 = (HashWriter{} << 1).GetHash();
 485      uint256 nKey2 = (HashWriter{} << 2).GetHash();
 486  
 487      BOOST_CHECK_EQUAL(info1.GetTriedBucket(nKey1, EMPTY_NETGROUPMAN), 40);
 488  
 489      // Test: Make sure key actually randomizes bucket placement. A fail on
 490      //  this test could be a security issue.
 491      BOOST_CHECK(info1.GetTriedBucket(nKey1, EMPTY_NETGROUPMAN) != info1.GetTriedBucket(nKey2, EMPTY_NETGROUPMAN));
 492  
 493      // Test: Two addresses with same IP but different ports can map to
 494      //  different buckets because they have different keys.
 495      AddrInfo info2 = AddrInfo(addr2, source1);
 496  
 497      BOOST_CHECK(info1.GetKey() != info2.GetKey());
 498      BOOST_CHECK(info1.GetTriedBucket(nKey1, EMPTY_NETGROUPMAN) != info2.GetTriedBucket(nKey1, EMPTY_NETGROUPMAN));
 499  
 500      std::set<int> buckets;
 501      for (int i = 0; i < 255; i++) {
 502          AddrInfo infoi = AddrInfo(
 503              CAddress(ResolveService("250.1.1." + ToString(i)), NODE_NONE),
 504              ResolveIP("250.1.1." + ToString(i)));
 505          int bucket = infoi.GetTriedBucket(nKey1, EMPTY_NETGROUPMAN);
 506          buckets.insert(bucket);
 507      }
 508      // Test: IP addresses in the same /16 prefix should
 509      // never get more than 8 buckets with legacy grouping
 510      BOOST_CHECK_EQUAL(buckets.size(), 8U);
 511  
 512      buckets.clear();
 513      for (int j = 0; j < 255; j++) {
 514          AddrInfo infoj = AddrInfo(
 515              CAddress(ResolveService("250." + ToString(j) + ".1.1"), NODE_NONE),
 516              ResolveIP("250." + ToString(j) + ".1.1"));
 517          int bucket = infoj.GetTriedBucket(nKey1, EMPTY_NETGROUPMAN);
 518          buckets.insert(bucket);
 519      }
 520      // Test: IP addresses in the different /16 prefix should map to more than
 521      // 8 buckets with legacy grouping
 522      BOOST_CHECK_EQUAL(buckets.size(), 160U);
 523  }
 524  
 525  BOOST_AUTO_TEST_CASE(caddrinfo_get_new_bucket_legacy)
 526  {
 527      CAddress addr1 = CAddress(ResolveService("250.1.2.1", 8333), NODE_NONE);
 528      CAddress addr2 = CAddress(ResolveService("250.1.2.1", 9999), NODE_NONE);
 529  
 530      CNetAddr source1 = ResolveIP("250.1.2.1");
 531  
 532      AddrInfo info1 = AddrInfo(addr1, source1);
 533  
 534      uint256 nKey1 = (HashWriter{} << 1).GetHash();
 535      uint256 nKey2 = (HashWriter{} << 2).GetHash();
 536  
 537      // Test: Make sure the buckets are what we expect
 538      BOOST_CHECK_EQUAL(info1.GetNewBucket(nKey1, EMPTY_NETGROUPMAN), 786);
 539      BOOST_CHECK_EQUAL(info1.GetNewBucket(nKey1, source1, EMPTY_NETGROUPMAN), 786);
 540  
 541      // Test: Make sure key actually randomizes bucket placement. A fail on
 542      //  this test could be a security issue.
 543      BOOST_CHECK(info1.GetNewBucket(nKey1, EMPTY_NETGROUPMAN) != info1.GetNewBucket(nKey2, EMPTY_NETGROUPMAN));
 544  
 545      // Test: Ports should not affect bucket placement in the addr
 546      AddrInfo info2 = AddrInfo(addr2, source1);
 547      BOOST_CHECK(info1.GetKey() != info2.GetKey());
 548      BOOST_CHECK_EQUAL(info1.GetNewBucket(nKey1, EMPTY_NETGROUPMAN), info2.GetNewBucket(nKey1, EMPTY_NETGROUPMAN));
 549  
 550      std::set<int> buckets;
 551      for (int i = 0; i < 255; i++) {
 552          AddrInfo infoi = AddrInfo(
 553              CAddress(ResolveService("250.1.1." + ToString(i)), NODE_NONE),
 554              ResolveIP("250.1.1." + ToString(i)));
 555          int bucket = infoi.GetNewBucket(nKey1, EMPTY_NETGROUPMAN);
 556          buckets.insert(bucket);
 557      }
 558      // Test: IP addresses in the same group (\16 prefix for IPv4) should
 559      //  always map to the same bucket.
 560      BOOST_CHECK_EQUAL(buckets.size(), 1U);
 561  
 562      buckets.clear();
 563      for (int j = 0; j < 4 * 255; j++) {
 564          AddrInfo infoj = AddrInfo(CAddress(
 565                                          ResolveService(
 566                                              ToString(250 + (j / 255)) + "." + ToString(j % 256) + ".1.1"), NODE_NONE),
 567              ResolveIP("251.4.1.1"));
 568          int bucket = infoj.GetNewBucket(nKey1, EMPTY_NETGROUPMAN);
 569          buckets.insert(bucket);
 570      }
 571      // Test: IP addresses in the same source groups should map to NO MORE
 572      //  than 64 buckets.
 573      BOOST_CHECK(buckets.size() <= 64);
 574  
 575      buckets.clear();
 576      for (int p = 0; p < 255; p++) {
 577          AddrInfo infoj = AddrInfo(
 578              CAddress(ResolveService("250.1.1.1"), NODE_NONE),
 579              ResolveIP("250." + ToString(p) + ".1.1"));
 580          int bucket = infoj.GetNewBucket(nKey1, EMPTY_NETGROUPMAN);
 581          buckets.insert(bucket);
 582      }
 583      // Test: IP addresses in the different source groups should map to MORE
 584      //  than 64 buckets.
 585      BOOST_CHECK(buckets.size() > 64);
 586  }
 587  
 588  // The following three test cases use asmap.raw
 589  // We use an artificial minimal mock mapping
 590  // 250.0.0.0/8 AS1000
 591  // 101.1.0.0/16 AS1
 592  // 101.2.0.0/16 AS2
 593  // 101.3.0.0/16 AS3
 594  // 101.4.0.0/16 AS4
 595  // 101.5.0.0/16 AS5
 596  // 101.6.0.0/16 AS6
 597  // 101.7.0.0/16 AS7
 598  // 101.8.0.0/16 AS8
 599  BOOST_AUTO_TEST_CASE(caddrinfo_get_tried_bucket)
 600  {
 601      std::vector<bool> asmap = FromBytes(test::data::asmap);
 602      NetGroupManager ngm_asmap{asmap};
 603  
 604      CAddress addr1 = CAddress(ResolveService("250.1.1.1", 8333), NODE_NONE);
 605      CAddress addr2 = CAddress(ResolveService("250.1.1.1", 9999), NODE_NONE);
 606  
 607      CNetAddr source1 = ResolveIP("250.1.1.1");
 608  
 609  
 610      AddrInfo info1 = AddrInfo(addr1, source1);
 611  
 612      uint256 nKey1 = (HashWriter{} << 1).GetHash();
 613      uint256 nKey2 = (HashWriter{} << 2).GetHash();
 614  
 615      BOOST_CHECK_EQUAL(info1.GetTriedBucket(nKey1, ngm_asmap), 236);
 616  
 617      // Test: Make sure key actually randomizes bucket placement. A fail on
 618      //  this test could be a security issue.
 619      BOOST_CHECK(info1.GetTriedBucket(nKey1, ngm_asmap) != info1.GetTriedBucket(nKey2, ngm_asmap));
 620  
 621      // Test: Two addresses with same IP but different ports can map to
 622      //  different buckets because they have different keys.
 623      AddrInfo info2 = AddrInfo(addr2, source1);
 624  
 625      BOOST_CHECK(info1.GetKey() != info2.GetKey());
 626      BOOST_CHECK(info1.GetTriedBucket(nKey1, ngm_asmap) != info2.GetTriedBucket(nKey1, ngm_asmap));
 627  
 628      std::set<int> buckets;
 629      for (int j = 0; j < 255; j++) {
 630          AddrInfo infoj = AddrInfo(
 631              CAddress(ResolveService("101." + ToString(j) + ".1.1"), NODE_NONE),
 632              ResolveIP("101." + ToString(j) + ".1.1"));
 633          int bucket = infoj.GetTriedBucket(nKey1, ngm_asmap);
 634          buckets.insert(bucket);
 635      }
 636      // Test: IP addresses in the different /16 prefix MAY map to more than
 637      // 8 buckets.
 638      BOOST_CHECK(buckets.size() > 8);
 639  
 640      buckets.clear();
 641      for (int j = 0; j < 255; j++) {
 642          AddrInfo infoj = AddrInfo(
 643              CAddress(ResolveService("250." + ToString(j) + ".1.1"), NODE_NONE),
 644              ResolveIP("250." + ToString(j) + ".1.1"));
 645          int bucket = infoj.GetTriedBucket(nKey1, ngm_asmap);
 646          buckets.insert(bucket);
 647      }
 648      // Test: IP addresses in the different /16 prefix MAY NOT map to more than
 649      // 8 buckets.
 650      BOOST_CHECK(buckets.size() == 8);
 651  }
 652  
 653  BOOST_AUTO_TEST_CASE(caddrinfo_get_new_bucket)
 654  {
 655      std::vector<bool> asmap = FromBytes(test::data::asmap);
 656      NetGroupManager ngm_asmap{asmap};
 657  
 658      CAddress addr1 = CAddress(ResolveService("250.1.2.1", 8333), NODE_NONE);
 659      CAddress addr2 = CAddress(ResolveService("250.1.2.1", 9999), NODE_NONE);
 660  
 661      CNetAddr source1 = ResolveIP("250.1.2.1");
 662  
 663      AddrInfo info1 = AddrInfo(addr1, source1);
 664  
 665      uint256 nKey1 = (HashWriter{} << 1).GetHash();
 666      uint256 nKey2 = (HashWriter{} << 2).GetHash();
 667  
 668      // Test: Make sure the buckets are what we expect
 669      BOOST_CHECK_EQUAL(info1.GetNewBucket(nKey1, ngm_asmap), 795);
 670      BOOST_CHECK_EQUAL(info1.GetNewBucket(nKey1, source1, ngm_asmap), 795);
 671  
 672      // Test: Make sure key actually randomizes bucket placement. A fail on
 673      //  this test could be a security issue.
 674      BOOST_CHECK(info1.GetNewBucket(nKey1, ngm_asmap) != info1.GetNewBucket(nKey2, ngm_asmap));
 675  
 676      // Test: Ports should not affect bucket placement in the addr
 677      AddrInfo info2 = AddrInfo(addr2, source1);
 678      BOOST_CHECK(info1.GetKey() != info2.GetKey());
 679      BOOST_CHECK_EQUAL(info1.GetNewBucket(nKey1, ngm_asmap), info2.GetNewBucket(nKey1, ngm_asmap));
 680  
 681      std::set<int> buckets;
 682      for (int i = 0; i < 255; i++) {
 683          AddrInfo infoi = AddrInfo(
 684              CAddress(ResolveService("250.1.1." + ToString(i)), NODE_NONE),
 685              ResolveIP("250.1.1." + ToString(i)));
 686          int bucket = infoi.GetNewBucket(nKey1, ngm_asmap);
 687          buckets.insert(bucket);
 688      }
 689      // Test: IP addresses in the same /16 prefix
 690      // usually map to the same bucket.
 691      BOOST_CHECK_EQUAL(buckets.size(), 1U);
 692  
 693      buckets.clear();
 694      for (int j = 0; j < 4 * 255; j++) {
 695          AddrInfo infoj = AddrInfo(CAddress(
 696                                          ResolveService(
 697                                              ToString(250 + (j / 255)) + "." + ToString(j % 256) + ".1.1"), NODE_NONE),
 698              ResolveIP("251.4.1.1"));
 699          int bucket = infoj.GetNewBucket(nKey1, ngm_asmap);
 700          buckets.insert(bucket);
 701      }
 702      // Test: IP addresses in the same source /16 prefix should not map to more
 703      // than 64 buckets.
 704      BOOST_CHECK(buckets.size() <= 64);
 705  
 706      buckets.clear();
 707      for (int p = 0; p < 255; p++) {
 708          AddrInfo infoj = AddrInfo(
 709              CAddress(ResolveService("250.1.1.1"), NODE_NONE),
 710              ResolveIP("101." + ToString(p) + ".1.1"));
 711          int bucket = infoj.GetNewBucket(nKey1, ngm_asmap);
 712          buckets.insert(bucket);
 713      }
 714      // Test: IP addresses in the different source /16 prefixes usually map to MORE
 715      // than 1 bucket.
 716      BOOST_CHECK(buckets.size() > 1);
 717  
 718      buckets.clear();
 719      for (int p = 0; p < 255; p++) {
 720          AddrInfo infoj = AddrInfo(
 721              CAddress(ResolveService("250.1.1.1"), NODE_NONE),
 722              ResolveIP("250." + ToString(p) + ".1.1"));
 723          int bucket = infoj.GetNewBucket(nKey1, ngm_asmap);
 724          buckets.insert(bucket);
 725      }
 726      // Test: IP addresses in the different source /16 prefixes sometimes map to NO MORE
 727      // than 1 bucket.
 728      BOOST_CHECK(buckets.size() == 1);
 729  }
 730  
 731  BOOST_AUTO_TEST_CASE(addrman_serialization)
 732  {
 733      std::vector<bool> asmap1 = FromBytes(test::data::asmap);
 734      NetGroupManager netgroupman{asmap1};
 735  
 736      const auto ratio = GetCheckRatio(m_node);
 737      auto addrman_asmap1 = std::make_unique<AddrMan>(netgroupman, DETERMINISTIC, ratio);
 738      auto addrman_asmap1_dup = std::make_unique<AddrMan>(netgroupman, DETERMINISTIC, ratio);
 739      auto addrman_noasmap = std::make_unique<AddrMan>(EMPTY_NETGROUPMAN, DETERMINISTIC, ratio);
 740  
 741      DataStream stream{};
 742  
 743      CAddress addr = CAddress(ResolveService("250.1.1.1"), NODE_NONE);
 744      CNetAddr default_source;
 745  
 746      addrman_asmap1->Add({addr}, default_source);
 747  
 748      stream << *addrman_asmap1;
 749      // serizalizing/deserializing addrman with the same asmap
 750      stream >> *addrman_asmap1_dup;
 751  
 752      AddressPosition addr_pos1 = addrman_asmap1->FindAddressEntry(addr).value();
 753      AddressPosition addr_pos2 = addrman_asmap1_dup->FindAddressEntry(addr).value();
 754      BOOST_CHECK(addr_pos1.multiplicity != 0);
 755      BOOST_CHECK(addr_pos2.multiplicity != 0);
 756  
 757      BOOST_CHECK(addr_pos1 == addr_pos2);
 758  
 759      // deserializing asmaped peers.dat to non-asmaped addrman
 760      stream << *addrman_asmap1;
 761      stream >> *addrman_noasmap;
 762      AddressPosition addr_pos3 = addrman_noasmap->FindAddressEntry(addr).value();
 763      BOOST_CHECK(addr_pos3.multiplicity != 0);
 764      BOOST_CHECK(addr_pos1.bucket != addr_pos3.bucket);
 765      BOOST_CHECK(addr_pos1.position != addr_pos3.position);
 766  
 767      // deserializing non-asmaped peers.dat to asmaped addrman
 768      addrman_asmap1 = std::make_unique<AddrMan>(netgroupman, DETERMINISTIC, ratio);
 769      addrman_noasmap = std::make_unique<AddrMan>(EMPTY_NETGROUPMAN, DETERMINISTIC, ratio);
 770      addrman_noasmap->Add({addr}, default_source);
 771      stream << *addrman_noasmap;
 772      stream >> *addrman_asmap1;
 773  
 774      AddressPosition addr_pos4 = addrman_asmap1->FindAddressEntry(addr).value();
 775      BOOST_CHECK(addr_pos4.multiplicity != 0);
 776      BOOST_CHECK(addr_pos4.bucket != addr_pos3.bucket);
 777      BOOST_CHECK(addr_pos4 == addr_pos2);
 778  
 779      // used to map to different buckets, now maps to the same bucket.
 780      addrman_asmap1 = std::make_unique<AddrMan>(netgroupman, DETERMINISTIC, ratio);
 781      addrman_noasmap = std::make_unique<AddrMan>(EMPTY_NETGROUPMAN, DETERMINISTIC, ratio);
 782      CAddress addr1 = CAddress(ResolveService("250.1.1.1"), NODE_NONE);
 783      CAddress addr2 = CAddress(ResolveService("250.2.1.1"), NODE_NONE);
 784      addrman_noasmap->Add({addr, addr2}, default_source);
 785      AddressPosition addr_pos5 = addrman_noasmap->FindAddressEntry(addr1).value();
 786      AddressPosition addr_pos6 = addrman_noasmap->FindAddressEntry(addr2).value();
 787      BOOST_CHECK(addr_pos5.bucket != addr_pos6.bucket);
 788      stream << *addrman_noasmap;
 789      stream >> *addrman_asmap1;
 790      AddressPosition addr_pos7 = addrman_asmap1->FindAddressEntry(addr1).value();
 791      AddressPosition addr_pos8 = addrman_asmap1->FindAddressEntry(addr2).value();
 792      BOOST_CHECK(addr_pos7.bucket == addr_pos8.bucket);
 793      BOOST_CHECK(addr_pos7.position != addr_pos8.position);
 794  }
 795  
 796  BOOST_AUTO_TEST_CASE(remove_invalid)
 797  {
 798      // Confirm that invalid addresses are ignored in unserialization.
 799  
 800      auto addrman = std::make_unique<AddrMan>(EMPTY_NETGROUPMAN, DETERMINISTIC, GetCheckRatio(m_node));
 801      DataStream stream{};
 802  
 803      const CAddress new1{ResolveService("5.5.5.5"), NODE_NONE};
 804      const CAddress new2{ResolveService("6.6.6.6"), NODE_NONE};
 805      const CAddress tried1{ResolveService("7.7.7.7"), NODE_NONE};
 806      const CAddress tried2{ResolveService("8.8.8.8"), NODE_NONE};
 807  
 808      addrman->Add({new1, tried1, new2, tried2}, CNetAddr{});
 809      addrman->Good(tried1);
 810      addrman->Good(tried2);
 811      BOOST_REQUIRE_EQUAL(addrman->Size(), 4);
 812  
 813      stream << *addrman;
 814  
 815      const std::string str{stream.str()};
 816      size_t pos;
 817  
 818      const char new2_raw[]{6, 6, 6, 6};
 819      const uint8_t new2_raw_replacement[]{0, 0, 0, 0}; // 0.0.0.0 is !IsValid()
 820      pos = str.find(new2_raw, 0, sizeof(new2_raw));
 821      BOOST_REQUIRE(pos != std::string::npos);
 822      BOOST_REQUIRE(pos + sizeof(new2_raw_replacement) <= stream.size());
 823      memcpy(stream.data() + pos, new2_raw_replacement, sizeof(new2_raw_replacement));
 824  
 825      const char tried2_raw[]{8, 8, 8, 8};
 826      const uint8_t tried2_raw_replacement[]{255, 255, 255, 255}; // 255.255.255.255 is !IsValid()
 827      pos = str.find(tried2_raw, 0, sizeof(tried2_raw));
 828      BOOST_REQUIRE(pos != std::string::npos);
 829      BOOST_REQUIRE(pos + sizeof(tried2_raw_replacement) <= stream.size());
 830      memcpy(stream.data() + pos, tried2_raw_replacement, sizeof(tried2_raw_replacement));
 831  
 832      addrman = std::make_unique<AddrMan>(EMPTY_NETGROUPMAN, DETERMINISTIC, GetCheckRatio(m_node));
 833      stream >> *addrman;
 834      BOOST_CHECK_EQUAL(addrman->Size(), 2);
 835  }
 836  
 837  BOOST_AUTO_TEST_CASE(addrman_selecttriedcollision)
 838  {
 839      auto addrman = std::make_unique<AddrMan>(EMPTY_NETGROUPMAN, DETERMINISTIC, GetCheckRatio(m_node));
 840  
 841      BOOST_CHECK(addrman->Size() == 0);
 842  
 843      // Empty addrman should return blank addrman info.
 844      BOOST_CHECK(addrman->SelectTriedCollision().first.ToStringAddrPort() == "[::]:0");
 845  
 846      // Add twenty two addresses.
 847      CNetAddr source = ResolveIP("252.2.2.2");
 848      for (unsigned int i = 1; i < 23; i++) {
 849          CService addr = ResolveService("250.1.1." + ToString(i));
 850          BOOST_CHECK(addrman->Add({CAddress(addr, NODE_NONE)}, source));
 851  
 852          // No collisions in tried.
 853          BOOST_CHECK(addrman->Good(addr));
 854          BOOST_CHECK(addrman->SelectTriedCollision().first.ToStringAddrPort() == "[::]:0");
 855      }
 856  
 857      // Ensure Good handles duplicates well.
 858      // If an address is a duplicate, Good will return false but will not count it as a collision.
 859      for (unsigned int i = 1; i < 23; i++) {
 860          CService addr = ResolveService("250.1.1." + ToString(i));
 861  
 862          // Unable to add duplicate address to tried table.
 863          BOOST_CHECK(!addrman->Good(addr));
 864  
 865          // Verify duplicate address not marked as a collision.
 866          BOOST_CHECK(addrman->SelectTriedCollision().first.ToStringAddrPort() == "[::]:0");
 867      }
 868  }
 869  
 870  BOOST_AUTO_TEST_CASE(addrman_noevict)
 871  {
 872      auto addrman = std::make_unique<AddrMan>(EMPTY_NETGROUPMAN, DETERMINISTIC, GetCheckRatio(m_node));
 873  
 874      // Add 35 addresses.
 875      CNetAddr source = ResolveIP("252.2.2.2");
 876      for (unsigned int i = 1; i < 36; i++) {
 877          CService addr = ResolveService("250.1.1." + ToString(i));
 878          BOOST_CHECK(addrman->Add({CAddress(addr, NODE_NONE)}, source));
 879  
 880          // No collision yet.
 881          BOOST_CHECK(addrman->Good(addr));
 882      }
 883  
 884      // Collision in tried table between 36 and 19.
 885      CService addr36 = ResolveService("250.1.1.36");
 886      BOOST_CHECK(addrman->Add({CAddress(addr36, NODE_NONE)}, source));
 887      BOOST_CHECK(!addrman->Good(addr36));
 888      BOOST_CHECK_EQUAL(addrman->SelectTriedCollision().first.ToStringAddrPort(), "250.1.1.19:0");
 889  
 890      // 36 should be discarded and 19 not evicted.
 891      // This means we keep 19 in the tried table and
 892      // 36 stays in the new table.
 893      addrman->ResolveCollisions();
 894      BOOST_CHECK(addrman->SelectTriedCollision().first.ToStringAddrPort() == "[::]:0");
 895  
 896      // Lets create two collisions.
 897      for (unsigned int i = 37; i < 59; i++) {
 898          CService addr = ResolveService("250.1.1." + ToString(i));
 899          BOOST_CHECK(addrman->Add({CAddress(addr, NODE_NONE)}, source));
 900          BOOST_CHECK(addrman->Good(addr));
 901      }
 902  
 903      // Cause a collision in the tried table.
 904      CService addr59 = ResolveService("250.1.1.59");
 905      BOOST_CHECK(addrman->Add({CAddress(addr59, NODE_NONE)}, source));
 906      BOOST_CHECK(!addrman->Good(addr59));
 907  
 908      BOOST_CHECK_EQUAL(addrman->SelectTriedCollision().first.ToStringAddrPort(), "250.1.1.10:0");
 909  
 910      // Cause a second collision in the new table.
 911      BOOST_CHECK(!addrman->Add({CAddress(addr36, NODE_NONE)}, source));
 912  
 913      // 36 still cannot be moved from new to tried due to colliding with 19
 914      BOOST_CHECK(!addrman->Good(addr36));
 915      BOOST_CHECK(addrman->SelectTriedCollision().first.ToStringAddrPort() != "[::]:0");
 916  
 917      // Resolve all collisions.
 918      addrman->ResolveCollisions();
 919      BOOST_CHECK(addrman->SelectTriedCollision().first.ToStringAddrPort() == "[::]:0");
 920  }
 921  
 922  BOOST_AUTO_TEST_CASE(addrman_evictionworks)
 923  {
 924      auto addrman = std::make_unique<AddrMan>(EMPTY_NETGROUPMAN, DETERMINISTIC, GetCheckRatio(m_node));
 925  
 926      BOOST_CHECK(addrman->Size() == 0);
 927  
 928      // Empty addrman should return blank addrman info.
 929      BOOST_CHECK(addrman->SelectTriedCollision().first.ToStringAddrPort() == "[::]:0");
 930  
 931      // Add 35 addresses
 932      CNetAddr source = ResolveIP("252.2.2.2");
 933      for (unsigned int i = 1; i < 36; i++) {
 934          CService addr = ResolveService("250.1.1." + ToString(i));
 935          BOOST_CHECK(addrman->Add({CAddress(addr, NODE_NONE)}, source));
 936  
 937          // No collision yet.
 938          BOOST_CHECK(addrman->Good(addr));
 939      }
 940  
 941      // Collision between 36 and 19.
 942      CService addr = ResolveService("250.1.1.36");
 943      BOOST_CHECK(addrman->Add({CAddress(addr, NODE_NONE)}, source));
 944      BOOST_CHECK(!addrman->Good(addr));
 945  
 946      auto info = addrman->SelectTriedCollision().first;
 947      BOOST_CHECK_EQUAL(info.ToStringAddrPort(), "250.1.1.19:0");
 948  
 949      // Ensure test of address fails, so that it is evicted.
 950      // Update entry in tried by setting last good connection in the deep past.
 951      BOOST_CHECK(!addrman->Good(info, NodeSeconds{1s}));
 952      addrman->Attempt(info, /*fCountFailure=*/false, Now<NodeSeconds>() - 61s);
 953  
 954      // Should swap 36 for 19.
 955      addrman->ResolveCollisions();
 956      BOOST_CHECK(addrman->SelectTriedCollision().first.ToStringAddrPort() == "[::]:0");
 957      AddressPosition addr_pos{addrman->FindAddressEntry(CAddress(addr, NODE_NONE)).value()};
 958      BOOST_CHECK(addr_pos.tried);
 959  
 960      // If 36 was swapped for 19, then adding 36 to tried should fail because we
 961      // are attempting to add a duplicate.
 962      // We check this by verifying Good() returns false and also verifying that
 963      // we have no collisions.
 964      BOOST_CHECK(!addrman->Good(addr));
 965      BOOST_CHECK(addrman->SelectTriedCollision().first.ToStringAddrPort() == "[::]:0");
 966  
 967      // 19 should fail as a collision (not a duplicate) if we now attempt to move
 968      // it to the tried table.
 969      CService addr19 = ResolveService("250.1.1.19");
 970      BOOST_CHECK(!addrman->Good(addr19));
 971      BOOST_CHECK_EQUAL(addrman->SelectTriedCollision().first.ToStringAddrPort(), "250.1.1.36:0");
 972  
 973      // Eviction is also successful if too much time has passed since last try
 974      SetMockTime(GetTime() + 4 * 60 *60);
 975      addrman->ResolveCollisions();
 976      BOOST_CHECK(addrman->SelectTriedCollision().first.ToStringAddrPort() == "[::]:0");
 977      //Now 19 is in tried again, and 36 back to new
 978      AddressPosition addr_pos19{addrman->FindAddressEntry(CAddress(addr19, NODE_NONE)).value()};
 979      BOOST_CHECK(addr_pos19.tried);
 980      AddressPosition addr_pos36{addrman->FindAddressEntry(CAddress(addr, NODE_NONE)).value()};
 981      BOOST_CHECK(!addr_pos36.tried);
 982  }
 983  
 984  static auto AddrmanToStream(const AddrMan& addrman)
 985  {
 986      DataStream ssPeersIn{};
 987      ssPeersIn << Params().MessageStart();
 988      ssPeersIn << addrman;
 989      return ssPeersIn;
 990  }
 991  
 992  BOOST_AUTO_TEST_CASE(load_addrman)
 993  {
 994      AddrMan addrman{EMPTY_NETGROUPMAN, DETERMINISTIC, GetCheckRatio(m_node)};
 995  
 996      std::optional<CService> addr1, addr2, addr3, addr4;
 997      addr1 = Lookup("250.7.1.1", 8333, false);
 998      BOOST_CHECK(addr1.has_value());
 999      addr2 = Lookup("250.7.2.2", 9999, false);
1000      BOOST_CHECK(addr2.has_value());
1001      addr3 = Lookup("250.7.3.3", 9999, false);
1002      BOOST_CHECK(addr3.has_value());
1003      addr3 = Lookup("250.7.3.3"s, 9999, false);
1004      BOOST_CHECK(addr3.has_value());
1005      addr4 = Lookup("250.7.3.3\0example.com"s, 9999, false);
1006      BOOST_CHECK(!addr4.has_value());
1007  
1008      // Add three addresses to new table.
1009      const std::optional<CService> source{Lookup("252.5.1.1", 8333, false)};
1010      BOOST_CHECK(source.has_value());
1011      std::vector<CAddress> addresses{CAddress(addr1.value(), NODE_NONE), CAddress(addr2.value(), NODE_NONE), CAddress(addr3.value(), NODE_NONE)};
1012      BOOST_CHECK(addrman.Add(addresses, source.value()));
1013      BOOST_CHECK(addrman.Size() == 3);
1014  
1015      // Test that the de-serialization does not throw an exception.
1016      auto ssPeers1{AddrmanToStream(addrman)};
1017      bool exceptionThrown = false;
1018      AddrMan addrman1{EMPTY_NETGROUPMAN, !DETERMINISTIC, GetCheckRatio(m_node)};
1019  
1020      BOOST_CHECK(addrman1.Size() == 0);
1021      try {
1022          unsigned char pchMsgTmp[4];
1023          ssPeers1 >> pchMsgTmp;
1024          ssPeers1 >> addrman1;
1025      } catch (const std::exception&) {
1026          exceptionThrown = true;
1027      }
1028  
1029      BOOST_CHECK(addrman1.Size() == 3);
1030      BOOST_CHECK(exceptionThrown == false);
1031  
1032      // Test that ReadFromStream creates an addrman with the correct number of addrs.
1033      DataStream ssPeers2 = AddrmanToStream(addrman);
1034  
1035      AddrMan addrman2{EMPTY_NETGROUPMAN, !DETERMINISTIC, GetCheckRatio(m_node)};
1036      BOOST_CHECK(addrman2.Size() == 0);
1037      ReadFromStream(addrman2, ssPeers2);
1038      BOOST_CHECK(addrman2.Size() == 3);
1039  }
1040  
1041  // Produce a corrupt peers.dat that claims 20 addrs when it only has one addr.
1042  static auto MakeCorruptPeersDat()
1043  {
1044      DataStream s{};
1045      s << ::Params().MessageStart();
1046  
1047      unsigned char nVersion = 1;
1048      s << nVersion;
1049      s << ((unsigned char)32);
1050      s << uint256::ONE;
1051      s << 10; // nNew
1052      s << 10; // nTried
1053  
1054      int nUBuckets = ADDRMAN_NEW_BUCKET_COUNT ^ (1 << 30);
1055      s << nUBuckets;
1056  
1057      const std::optional<CService> serv{Lookup("252.1.1.1", 7777, false)};
1058      BOOST_REQUIRE(serv.has_value());
1059      CAddress addr = CAddress(serv.value(), NODE_NONE);
1060      std::optional<CNetAddr> resolved{LookupHost("252.2.2.2", false)};
1061      BOOST_REQUIRE(resolved.has_value());
1062      AddrInfo info = AddrInfo(addr, resolved.value());
1063      s << CAddress::V1_DISK(info);
1064  
1065      return s;
1066  }
1067  
1068  BOOST_AUTO_TEST_CASE(load_addrman_corrupted)
1069  {
1070      // Test that the de-serialization of corrupted peers.dat throws an exception.
1071      auto ssPeers1{MakeCorruptPeersDat()};
1072      bool exceptionThrown = false;
1073      AddrMan addrman1{EMPTY_NETGROUPMAN, !DETERMINISTIC, GetCheckRatio(m_node)};
1074      BOOST_CHECK(addrman1.Size() == 0);
1075      try {
1076          unsigned char pchMsgTmp[4];
1077          ssPeers1 >> pchMsgTmp;
1078          ssPeers1 >> addrman1;
1079      } catch (const std::exception&) {
1080          exceptionThrown = true;
1081      }
1082      BOOST_CHECK(exceptionThrown);
1083  
1084      // Test that ReadFromStream fails if peers.dat is corrupt
1085      auto ssPeers2{MakeCorruptPeersDat()};
1086  
1087      AddrMan addrman2{EMPTY_NETGROUPMAN, !DETERMINISTIC, GetCheckRatio(m_node)};
1088      BOOST_CHECK(addrman2.Size() == 0);
1089      BOOST_CHECK_THROW(ReadFromStream(addrman2, ssPeers2), std::ios_base::failure);
1090  }
1091  
1092  BOOST_AUTO_TEST_CASE(addrman_update_address)
1093  {
1094      // Tests updating nTime via Connected() and nServices via SetServices() and Add()
1095      auto addrman = std::make_unique<AddrMan>(EMPTY_NETGROUPMAN, DETERMINISTIC, GetCheckRatio(m_node));
1096      CNetAddr source{ResolveIP("252.2.2.2")};
1097      CAddress addr{CAddress(ResolveService("250.1.1.1", 8333), NODE_NONE)};
1098  
1099      const auto start_time{Now<NodeSeconds>() - 10000s};
1100      addr.nTime = start_time;
1101      BOOST_CHECK(addrman->Add({addr}, source));
1102      BOOST_CHECK_EQUAL(addrman->Size(), 1U);
1103  
1104      // Updating an addrman entry with a different port doesn't change it
1105      CAddress addr_diff_port{CAddress(ResolveService("250.1.1.1", 8334), NODE_NONE)};
1106      addr_diff_port.nTime = start_time;
1107      addrman->Connected(addr_diff_port);
1108      addrman->SetServices(addr_diff_port, NODE_NETWORK_LIMITED);
1109      std::vector<CAddress> vAddr1{addrman->GetAddr(/*max_addresses=*/0, /*max_pct=*/0, /*network=*/std::nullopt)};
1110      BOOST_CHECK_EQUAL(vAddr1.size(), 1U);
1111      BOOST_CHECK(vAddr1.at(0).nTime == start_time);
1112      BOOST_CHECK_EQUAL(vAddr1.at(0).nServices, NODE_NONE);
1113  
1114      // Updating an addrman entry with the correct port is successful
1115      addrman->Connected(addr);
1116      addrman->SetServices(addr, NODE_NETWORK_LIMITED);
1117      std::vector<CAddress> vAddr2 = addrman->GetAddr(/*max_addresses=*/0, /*max_pct=*/0, /*network=*/std::nullopt);
1118      BOOST_CHECK_EQUAL(vAddr2.size(), 1U);
1119      BOOST_CHECK(vAddr2.at(0).nTime >= start_time + 10000s);
1120      BOOST_CHECK_EQUAL(vAddr2.at(0).nServices, NODE_NETWORK_LIMITED);
1121  
1122      // Updating an existing addr through Add() (used in gossip relay) can add additional services but can't remove existing ones.
1123      CAddress addr_v2{CAddress(ResolveService("250.1.1.1", 8333), NODE_P2P_V2)};
1124      addr_v2.nTime = start_time;
1125      BOOST_CHECK(!addrman->Add({addr_v2}, source));
1126      std::vector<CAddress> vAddr3{addrman->GetAddr(/*max_addresses=*/0, /*max_pct=*/0, /*network=*/std::nullopt)};
1127      BOOST_CHECK_EQUAL(vAddr3.size(), 1U);
1128      BOOST_CHECK_EQUAL(vAddr3.at(0).nServices, NODE_P2P_V2 | NODE_NETWORK_LIMITED);
1129  
1130      // SetServices() (used when we connected to them) overwrites existing service flags
1131      addrman->SetServices(addr, NODE_NETWORK);
1132      std::vector<CAddress> vAddr4{addrman->GetAddr(/*max_addresses=*/0, /*max_pct=*/0, /*network=*/std::nullopt)};
1133      BOOST_CHECK_EQUAL(vAddr4.size(), 1U);
1134      BOOST_CHECK_EQUAL(vAddr4.at(0).nServices, NODE_NETWORK);
1135  
1136      // Promoting to Tried does not affect the service flags
1137      BOOST_CHECK(addrman->Good(addr)); // addr has NODE_NONE
1138      std::vector<CAddress> vAddr5{addrman->GetAddr(/*max_addresses=*/0, /*max_pct=*/0, /*network=*/std::nullopt)};
1139      BOOST_CHECK_EQUAL(vAddr5.size(), 1U);
1140      BOOST_CHECK_EQUAL(vAddr5.at(0).nServices, NODE_NETWORK);
1141  
1142      // Adding service flags even works when the addr is in Tried
1143      BOOST_CHECK(!addrman->Add({addr_v2}, source));
1144      std::vector<CAddress> vAddr6{addrman->GetAddr(/*max_addresses=*/0, /*max_pct=*/0, /*network=*/std::nullopt)};
1145      BOOST_CHECK_EQUAL(vAddr6.size(), 1U);
1146      BOOST_CHECK_EQUAL(vAddr6.at(0).nServices, NODE_NETWORK | NODE_P2P_V2);
1147  }
1148  
1149  BOOST_AUTO_TEST_CASE(addrman_size)
1150  {
1151      auto addrman = std::make_unique<AddrMan>(EMPTY_NETGROUPMAN, DETERMINISTIC, GetCheckRatio(m_node));
1152      const CNetAddr source = ResolveIP("252.2.2.2");
1153  
1154      // empty addrman
1155      BOOST_CHECK_EQUAL(addrman->Size(/*net=*/std::nullopt, /*in_new=*/std::nullopt), 0U);
1156      BOOST_CHECK_EQUAL(addrman->Size(/*net=*/NET_IPV4, /*in_new=*/std::nullopt), 0U);
1157      BOOST_CHECK_EQUAL(addrman->Size(/*net=*/std::nullopt, /*in_new=*/true), 0U);
1158      BOOST_CHECK_EQUAL(addrman->Size(/*net=*/NET_IPV4, /*in_new=*/false), 0U);
1159  
1160      // add two ipv4 addresses, one to tried and new
1161      const CAddress addr1{ResolveService("250.1.1.1", 8333), NODE_NONE};
1162      BOOST_CHECK(addrman->Add({addr1}, source));
1163      BOOST_CHECK(addrman->Good(addr1));
1164      const CAddress addr2{ResolveService("250.1.1.2", 8333), NODE_NONE};
1165      BOOST_CHECK(addrman->Add({addr2}, source));
1166  
1167      BOOST_CHECK_EQUAL(addrman->Size(/*net=*/std::nullopt, /*in_new=*/std::nullopt), 2U);
1168      BOOST_CHECK_EQUAL(addrman->Size(/*net=*/NET_IPV4, /*in_new=*/std::nullopt), 2U);
1169      BOOST_CHECK_EQUAL(addrman->Size(/*net=*/std::nullopt, /*in_new=*/true), 1U);
1170      BOOST_CHECK_EQUAL(addrman->Size(/*net=*/std::nullopt, /*in_new=*/false), 1U);
1171      BOOST_CHECK_EQUAL(addrman->Size(/*net=*/NET_IPV4, /*in_new=*/true), 1U);
1172      BOOST_CHECK_EQUAL(addrman->Size(/*net=*/NET_IPV4, /*in_new=*/false), 1U);
1173  
1174      // add one i2p address to new
1175      CService i2p_addr;
1176      i2p_addr.SetSpecial("UDHDrtrcetjm5sxzskjyr5ztpeszydbh4dpl3pl4utgqqw2v4jna.b32.I2P");
1177      const CAddress addr3{i2p_addr, NODE_NONE};
1178      BOOST_CHECK(addrman->Add({addr3}, source));
1179      BOOST_CHECK_EQUAL(addrman->Size(/*net=*/std::nullopt, /*in_new=*/std::nullopt), 3U);
1180      BOOST_CHECK_EQUAL(addrman->Size(/*net=*/NET_IPV4, /*in_new=*/std::nullopt), 2U);
1181      BOOST_CHECK_EQUAL(addrman->Size(/*net=*/NET_I2P, /*in_new=*/std::nullopt), 1U);
1182      BOOST_CHECK_EQUAL(addrman->Size(/*net=*/NET_I2P, /*in_new=*/true), 1U);
1183      BOOST_CHECK_EQUAL(addrman->Size(/*net=*/std::nullopt, /*in_new=*/true), 2U);
1184      BOOST_CHECK_EQUAL(addrman->Size(/*net=*/std::nullopt, /*in_new=*/false), 1U);
1185  }
1186  
1187  BOOST_AUTO_TEST_SUITE_END()