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