/ src / addrman.cpp
addrman.cpp
   1  // Copyright (c) 2012 Pieter Wuille
   2  // Copyright (c) 2012-2022 The Bitcoin Core developers
   3  // Distributed under the MIT software license, see the accompanying
   4  // file COPYING or http://www.opensource.org/licenses/mit-license.php.
   5  
   6  #if defined(HAVE_CONFIG_H)
   7  #include <config/bitcoin-config.h>
   8  #endif
   9  
  10  #include <addrman.h>
  11  #include <addrman_impl.h>
  12  
  13  #include <hash.h>
  14  #include <logging.h>
  15  #include <logging/timer.h>
  16  #include <netaddress.h>
  17  #include <protocol.h>
  18  #include <random.h>
  19  #include <serialize.h>
  20  #include <streams.h>
  21  #include <tinyformat.h>
  22  #include <uint256.h>
  23  #include <util/check.h>
  24  #include <util/time.h>
  25  
  26  #include <cmath>
  27  #include <optional>
  28  
  29  /** Over how many buckets entries with tried addresses from a single group (/16 for IPv4) are spread */
  30  static constexpr uint32_t ADDRMAN_TRIED_BUCKETS_PER_GROUP{8};
  31  /** Over how many buckets entries with new addresses originating from a single group are spread */
  32  static constexpr uint32_t ADDRMAN_NEW_BUCKETS_PER_SOURCE_GROUP{64};
  33  /** Maximum number of times an address can occur in the new table */
  34  static constexpr int32_t ADDRMAN_NEW_BUCKETS_PER_ADDRESS{8};
  35  /** How old addresses can maximally be */
  36  static constexpr auto ADDRMAN_HORIZON{30 * 24h};
  37  /** After how many failed attempts we give up on a new node */
  38  static constexpr int32_t ADDRMAN_RETRIES{3};
  39  /** How many successive failures are allowed ... */
  40  static constexpr int32_t ADDRMAN_MAX_FAILURES{10};
  41  /** ... in at least this duration */
  42  static constexpr auto ADDRMAN_MIN_FAIL{7 * 24h};
  43  /** How recent a successful connection should be before we allow an address to be evicted from tried */
  44  static constexpr auto ADDRMAN_REPLACEMENT{4h};
  45  /** The maximum number of tried addr collisions to store */
  46  static constexpr size_t ADDRMAN_SET_TRIED_COLLISION_SIZE{10};
  47  /** The maximum time we'll spend trying to resolve a tried table collision */
  48  static constexpr auto ADDRMAN_TEST_WINDOW{40min};
  49  
  50  int AddrInfo::GetTriedBucket(const uint256& nKey, const NetGroupManager& netgroupman) const
  51  {
  52      uint64_t hash1 = (HashWriter{} << nKey << GetKey()).GetCheapHash();
  53      uint64_t hash2 = (HashWriter{} << nKey << netgroupman.GetGroup(*this) << (hash1 % ADDRMAN_TRIED_BUCKETS_PER_GROUP)).GetCheapHash();
  54      return hash2 % ADDRMAN_TRIED_BUCKET_COUNT;
  55  }
  56  
  57  int AddrInfo::GetNewBucket(const uint256& nKey, const CNetAddr& src, const NetGroupManager& netgroupman) const
  58  {
  59      std::vector<unsigned char> vchSourceGroupKey = netgroupman.GetGroup(src);
  60      uint64_t hash1 = (HashWriter{} << nKey << netgroupman.GetGroup(*this) << vchSourceGroupKey).GetCheapHash();
  61      uint64_t hash2 = (HashWriter{} << nKey << vchSourceGroupKey << (hash1 % ADDRMAN_NEW_BUCKETS_PER_SOURCE_GROUP)).GetCheapHash();
  62      return hash2 % ADDRMAN_NEW_BUCKET_COUNT;
  63  }
  64  
  65  int AddrInfo::GetBucketPosition(const uint256& nKey, bool fNew, int bucket) const
  66  {
  67      uint64_t hash1 = (HashWriter{} << nKey << (fNew ? uint8_t{'N'} : uint8_t{'K'}) << bucket << GetKey()).GetCheapHash();
  68      return hash1 % ADDRMAN_BUCKET_SIZE;
  69  }
  70  
  71  bool AddrInfo::IsTerrible(NodeSeconds now) const
  72  {
  73      if (now - m_last_try <= 1min) { // never remove things tried in the last minute
  74          return false;
  75      }
  76  
  77      if (nTime > now + 10min) { // came in a flying DeLorean
  78          return true;
  79      }
  80  
  81      if (now - nTime > ADDRMAN_HORIZON) { // not seen in recent history
  82          return true;
  83      }
  84  
  85      if (TicksSinceEpoch<std::chrono::seconds>(m_last_success) == 0 && nAttempts >= ADDRMAN_RETRIES) { // tried N times and never a success
  86          return true;
  87      }
  88  
  89      if (now - m_last_success > ADDRMAN_MIN_FAIL && nAttempts >= ADDRMAN_MAX_FAILURES) { // N successive failures in the last week
  90          return true;
  91      }
  92  
  93      return false;
  94  }
  95  
  96  double AddrInfo::GetChance(NodeSeconds now) const
  97  {
  98      double fChance = 1.0;
  99  
 100      // deprioritize very recent attempts away
 101      if (now - m_last_try < 10min) {
 102          fChance *= 0.01;
 103      }
 104  
 105      // deprioritize 66% after each failed attempt, but at most 1/28th to avoid the search taking forever or overly penalizing outages.
 106      fChance *= pow(0.66, std::min(nAttempts, 8));
 107  
 108      return fChance;
 109  }
 110  
 111  AddrManImpl::AddrManImpl(const NetGroupManager& netgroupman, bool deterministic, int32_t consistency_check_ratio)
 112      : insecure_rand{deterministic}
 113      , nKey{deterministic ? uint256{1} : insecure_rand.rand256()}
 114      , m_consistency_check_ratio{consistency_check_ratio}
 115      , m_netgroupman{netgroupman}
 116  {
 117      for (auto& bucket : vvNew) {
 118          for (auto& entry : bucket) {
 119              entry = -1;
 120          }
 121      }
 122      for (auto& bucket : vvTried) {
 123          for (auto& entry : bucket) {
 124              entry = -1;
 125          }
 126      }
 127  }
 128  
 129  AddrManImpl::~AddrManImpl()
 130  {
 131      nKey.SetNull();
 132  }
 133  
 134  template <typename Stream>
 135  void AddrManImpl::Serialize(Stream& s_) const
 136  {
 137      LOCK(cs);
 138  
 139      /**
 140       * Serialized format.
 141       * * format version byte (@see `Format`)
 142       * * lowest compatible format version byte. This is used to help old software decide
 143       *   whether to parse the file. For example:
 144       *   * Bitcoin Core version N knows how to parse up to format=3. If a new format=4 is
 145       *     introduced in version N+1 that is compatible with format=3 and it is known that
 146       *     version N will be able to parse it, then version N+1 will write
 147       *     (format=4, lowest_compatible=3) in the first two bytes of the file, and so
 148       *     version N will still try to parse it.
 149       *   * Bitcoin Core version N+2 introduces a new incompatible format=5. It will write
 150       *     (format=5, lowest_compatible=5) and so any versions that do not know how to parse
 151       *     format=5 will not try to read the file.
 152       * * nKey
 153       * * nNew
 154       * * nTried
 155       * * number of "new" buckets XOR 2**30
 156       * * all new addresses (total count: nNew)
 157       * * all tried addresses (total count: nTried)
 158       * * for each new bucket:
 159       *   * number of elements
 160       *   * for each element: index in the serialized "all new addresses"
 161       * * asmap checksum
 162       *
 163       * 2**30 is xorred with the number of buckets to make addrman deserializer v0 detect it
 164       * as incompatible. This is necessary because it did not check the version number on
 165       * deserialization.
 166       *
 167       * vvNew, vvTried, mapInfo, mapAddr and vRandom are never encoded explicitly;
 168       * they are instead reconstructed from the other information.
 169       *
 170       * This format is more complex, but significantly smaller (at most 1.5 MiB), and supports
 171       * changes to the ADDRMAN_ parameters without breaking the on-disk structure.
 172       *
 173       * We don't use SERIALIZE_METHODS since the serialization and deserialization code has
 174       * very little in common.
 175       */
 176  
 177      // Always serialize in the latest version (FILE_FORMAT).
 178      ParamsStream s{CAddress::V2_DISK, s_};
 179  
 180      s << static_cast<uint8_t>(FILE_FORMAT);
 181  
 182      // Increment `lowest_compatible` iff a newly introduced format is incompatible with
 183      // the previous one.
 184      static constexpr uint8_t lowest_compatible = Format::V4_MULTIPORT;
 185      s << static_cast<uint8_t>(INCOMPATIBILITY_BASE + lowest_compatible);
 186  
 187      s << nKey;
 188      s << nNew;
 189      s << nTried;
 190  
 191      int nUBuckets = ADDRMAN_NEW_BUCKET_COUNT ^ (1 << 30);
 192      s << nUBuckets;
 193      std::unordered_map<int, int> mapUnkIds;
 194      int nIds = 0;
 195      for (const auto& entry : mapInfo) {
 196          mapUnkIds[entry.first] = nIds;
 197          const AddrInfo& info = entry.second;
 198          if (info.nRefCount) {
 199              assert(nIds != nNew); // this means nNew was wrong, oh ow
 200              s << info;
 201              nIds++;
 202          }
 203      }
 204      nIds = 0;
 205      for (const auto& entry : mapInfo) {
 206          const AddrInfo& info = entry.second;
 207          if (info.fInTried) {
 208              assert(nIds != nTried); // this means nTried was wrong, oh ow
 209              s << info;
 210              nIds++;
 211          }
 212      }
 213      for (int bucket = 0; bucket < ADDRMAN_NEW_BUCKET_COUNT; bucket++) {
 214          int nSize = 0;
 215          for (int i = 0; i < ADDRMAN_BUCKET_SIZE; i++) {
 216              if (vvNew[bucket][i] != -1)
 217                  nSize++;
 218          }
 219          s << nSize;
 220          for (int i = 0; i < ADDRMAN_BUCKET_SIZE; i++) {
 221              if (vvNew[bucket][i] != -1) {
 222                  int nIndex = mapUnkIds[vvNew[bucket][i]];
 223                  s << nIndex;
 224              }
 225          }
 226      }
 227      // Store asmap checksum after bucket entries so that it
 228      // can be ignored by older clients for backward compatibility.
 229      s << m_netgroupman.GetAsmapChecksum();
 230  }
 231  
 232  template <typename Stream>
 233  void AddrManImpl::Unserialize(Stream& s_)
 234  {
 235      LOCK(cs);
 236  
 237      assert(vRandom.empty());
 238  
 239      Format format;
 240      s_ >> Using<CustomUintFormatter<1>>(format);
 241  
 242      const auto ser_params = (format >= Format::V3_BIP155 ? CAddress::V2_DISK : CAddress::V1_DISK);
 243      ParamsStream s{ser_params, s_};
 244  
 245      uint8_t compat;
 246      s >> compat;
 247      if (compat < INCOMPATIBILITY_BASE) {
 248          throw std::ios_base::failure(strprintf(
 249              "Corrupted addrman database: The compat value (%u) "
 250              "is lower than the expected minimum value %u.",
 251              compat, INCOMPATIBILITY_BASE));
 252      }
 253      const uint8_t lowest_compatible = compat - INCOMPATIBILITY_BASE;
 254      if (lowest_compatible > FILE_FORMAT) {
 255          throw InvalidAddrManVersionError(strprintf(
 256              "Unsupported format of addrman database: %u. It is compatible with formats >=%u, "
 257              "but the maximum supported by this version of %s is %u.",
 258              uint8_t{format}, lowest_compatible, PACKAGE_NAME, uint8_t{FILE_FORMAT}));
 259      }
 260  
 261      s >> nKey;
 262      s >> nNew;
 263      s >> nTried;
 264      int nUBuckets = 0;
 265      s >> nUBuckets;
 266      if (format >= Format::V1_DETERMINISTIC) {
 267          nUBuckets ^= (1 << 30);
 268      }
 269  
 270      if (nNew > ADDRMAN_NEW_BUCKET_COUNT * ADDRMAN_BUCKET_SIZE || nNew < 0) {
 271          throw std::ios_base::failure(
 272                  strprintf("Corrupt AddrMan serialization: nNew=%d, should be in [0, %d]",
 273                      nNew,
 274                      ADDRMAN_NEW_BUCKET_COUNT * ADDRMAN_BUCKET_SIZE));
 275      }
 276  
 277      if (nTried > ADDRMAN_TRIED_BUCKET_COUNT * ADDRMAN_BUCKET_SIZE || nTried < 0) {
 278          throw std::ios_base::failure(
 279                  strprintf("Corrupt AddrMan serialization: nTried=%d, should be in [0, %d]",
 280                      nTried,
 281                      ADDRMAN_TRIED_BUCKET_COUNT * ADDRMAN_BUCKET_SIZE));
 282      }
 283  
 284      // Deserialize entries from the new table.
 285      for (int n = 0; n < nNew; n++) {
 286          AddrInfo& info = mapInfo[n];
 287          s >> info;
 288          mapAddr[info] = n;
 289          info.nRandomPos = vRandom.size();
 290          vRandom.push_back(n);
 291          m_network_counts[info.GetNetwork()].n_new++;
 292      }
 293      nIdCount = nNew;
 294  
 295      // Deserialize entries from the tried table.
 296      int nLost = 0;
 297      for (int n = 0; n < nTried; n++) {
 298          AddrInfo info;
 299          s >> info;
 300          int nKBucket = info.GetTriedBucket(nKey, m_netgroupman);
 301          int nKBucketPos = info.GetBucketPosition(nKey, false, nKBucket);
 302          if (info.IsValid()
 303                  && vvTried[nKBucket][nKBucketPos] == -1) {
 304              info.nRandomPos = vRandom.size();
 305              info.fInTried = true;
 306              vRandom.push_back(nIdCount);
 307              mapInfo[nIdCount] = info;
 308              mapAddr[info] = nIdCount;
 309              vvTried[nKBucket][nKBucketPos] = nIdCount;
 310              nIdCount++;
 311              m_network_counts[info.GetNetwork()].n_tried++;
 312          } else {
 313              nLost++;
 314          }
 315      }
 316      nTried -= nLost;
 317  
 318      // Store positions in the new table buckets to apply later (if possible).
 319      // An entry may appear in up to ADDRMAN_NEW_BUCKETS_PER_ADDRESS buckets,
 320      // so we store all bucket-entry_index pairs to iterate through later.
 321      std::vector<std::pair<int, int>> bucket_entries;
 322  
 323      for (int bucket = 0; bucket < nUBuckets; ++bucket) {
 324          int num_entries{0};
 325          s >> num_entries;
 326          for (int n = 0; n < num_entries; ++n) {
 327              int entry_index{0};
 328              s >> entry_index;
 329              if (entry_index >= 0 && entry_index < nNew) {
 330                  bucket_entries.emplace_back(bucket, entry_index);
 331              }
 332          }
 333      }
 334  
 335      // If the bucket count and asmap checksum haven't changed, then attempt
 336      // to restore the entries to the buckets/positions they were in before
 337      // serialization.
 338      uint256 supplied_asmap_checksum{m_netgroupman.GetAsmapChecksum()};
 339      uint256 serialized_asmap_checksum;
 340      if (format >= Format::V2_ASMAP) {
 341          s >> serialized_asmap_checksum;
 342      }
 343      const bool restore_bucketing{nUBuckets == ADDRMAN_NEW_BUCKET_COUNT &&
 344          serialized_asmap_checksum == supplied_asmap_checksum};
 345  
 346      if (!restore_bucketing) {
 347          LogPrint(BCLog::ADDRMAN, "Bucketing method was updated, re-bucketing addrman entries from disk\n");
 348      }
 349  
 350      for (auto bucket_entry : bucket_entries) {
 351          int bucket{bucket_entry.first};
 352          const int entry_index{bucket_entry.second};
 353          AddrInfo& info = mapInfo[entry_index];
 354  
 355          // Don't store the entry in the new bucket if it's not a valid address for our addrman
 356          if (!info.IsValid()) continue;
 357  
 358          // The entry shouldn't appear in more than
 359          // ADDRMAN_NEW_BUCKETS_PER_ADDRESS. If it has already, just skip
 360          // this bucket_entry.
 361          if (info.nRefCount >= ADDRMAN_NEW_BUCKETS_PER_ADDRESS) continue;
 362  
 363          int bucket_position = info.GetBucketPosition(nKey, true, bucket);
 364          if (restore_bucketing && vvNew[bucket][bucket_position] == -1) {
 365              // Bucketing has not changed, using existing bucket positions for the new table
 366              vvNew[bucket][bucket_position] = entry_index;
 367              ++info.nRefCount;
 368          } else {
 369              // In case the new table data cannot be used (bucket count wrong or new asmap),
 370              // try to give them a reference based on their primary source address.
 371              bucket = info.GetNewBucket(nKey, m_netgroupman);
 372              bucket_position = info.GetBucketPosition(nKey, true, bucket);
 373              if (vvNew[bucket][bucket_position] == -1) {
 374                  vvNew[bucket][bucket_position] = entry_index;
 375                  ++info.nRefCount;
 376              }
 377          }
 378      }
 379  
 380      // Prune new entries with refcount 0 (as a result of collisions or invalid address).
 381      int nLostUnk = 0;
 382      for (auto it = mapInfo.cbegin(); it != mapInfo.cend(); ) {
 383          if (it->second.fInTried == false && it->second.nRefCount == 0) {
 384              const auto itCopy = it++;
 385              Delete(itCopy->first);
 386              ++nLostUnk;
 387          } else {
 388              ++it;
 389          }
 390      }
 391      if (nLost + nLostUnk > 0) {
 392          LogPrint(BCLog::ADDRMAN, "addrman lost %i new and %i tried addresses due to collisions or invalid addresses\n", nLostUnk, nLost);
 393      }
 394  
 395      const int check_code{CheckAddrman()};
 396      if (check_code != 0) {
 397          throw std::ios_base::failure(strprintf(
 398              "Corrupt data. Consistency check failed with code %s",
 399              check_code));
 400      }
 401  }
 402  
 403  AddrInfo* AddrManImpl::Find(const CService& addr, int* pnId)
 404  {
 405      AssertLockHeld(cs);
 406  
 407      const auto it = mapAddr.find(addr);
 408      if (it == mapAddr.end())
 409          return nullptr;
 410      if (pnId)
 411          *pnId = (*it).second;
 412      const auto it2 = mapInfo.find((*it).second);
 413      if (it2 != mapInfo.end())
 414          return &(*it2).second;
 415      return nullptr;
 416  }
 417  
 418  AddrInfo* AddrManImpl::Create(const CAddress& addr, const CNetAddr& addrSource, int* pnId)
 419  {
 420      AssertLockHeld(cs);
 421  
 422      int nId = nIdCount++;
 423      mapInfo[nId] = AddrInfo(addr, addrSource);
 424      mapAddr[addr] = nId;
 425      mapInfo[nId].nRandomPos = vRandom.size();
 426      vRandom.push_back(nId);
 427      nNew++;
 428      m_network_counts[addr.GetNetwork()].n_new++;
 429      if (pnId)
 430          *pnId = nId;
 431      return &mapInfo[nId];
 432  }
 433  
 434  void AddrManImpl::SwapRandom(unsigned int nRndPos1, unsigned int nRndPos2) const
 435  {
 436      AssertLockHeld(cs);
 437  
 438      if (nRndPos1 == nRndPos2)
 439          return;
 440  
 441      assert(nRndPos1 < vRandom.size() && nRndPos2 < vRandom.size());
 442  
 443      int nId1 = vRandom[nRndPos1];
 444      int nId2 = vRandom[nRndPos2];
 445  
 446      const auto it_1{mapInfo.find(nId1)};
 447      const auto it_2{mapInfo.find(nId2)};
 448      assert(it_1 != mapInfo.end());
 449      assert(it_2 != mapInfo.end());
 450  
 451      it_1->second.nRandomPos = nRndPos2;
 452      it_2->second.nRandomPos = nRndPos1;
 453  
 454      vRandom[nRndPos1] = nId2;
 455      vRandom[nRndPos2] = nId1;
 456  }
 457  
 458  void AddrManImpl::Delete(int nId)
 459  {
 460      AssertLockHeld(cs);
 461  
 462      assert(mapInfo.count(nId) != 0);
 463      AddrInfo& info = mapInfo[nId];
 464      assert(!info.fInTried);
 465      assert(info.nRefCount == 0);
 466  
 467      SwapRandom(info.nRandomPos, vRandom.size() - 1);
 468      m_network_counts[info.GetNetwork()].n_new--;
 469      vRandom.pop_back();
 470      mapAddr.erase(info);
 471      mapInfo.erase(nId);
 472      nNew--;
 473  }
 474  
 475  void AddrManImpl::ClearNew(int nUBucket, int nUBucketPos)
 476  {
 477      AssertLockHeld(cs);
 478  
 479      // if there is an entry in the specified bucket, delete it.
 480      if (vvNew[nUBucket][nUBucketPos] != -1) {
 481          int nIdDelete = vvNew[nUBucket][nUBucketPos];
 482          AddrInfo& infoDelete = mapInfo[nIdDelete];
 483          assert(infoDelete.nRefCount > 0);
 484          infoDelete.nRefCount--;
 485          vvNew[nUBucket][nUBucketPos] = -1;
 486          LogPrint(BCLog::ADDRMAN, "Removed %s from new[%i][%i]\n", infoDelete.ToStringAddrPort(), nUBucket, nUBucketPos);
 487          if (infoDelete.nRefCount == 0) {
 488              Delete(nIdDelete);
 489          }
 490      }
 491  }
 492  
 493  void AddrManImpl::MakeTried(AddrInfo& info, int nId)
 494  {
 495      AssertLockHeld(cs);
 496  
 497      // remove the entry from all new buckets
 498      const int start_bucket{info.GetNewBucket(nKey, m_netgroupman)};
 499      for (int n = 0; n < ADDRMAN_NEW_BUCKET_COUNT; ++n) {
 500          const int bucket{(start_bucket + n) % ADDRMAN_NEW_BUCKET_COUNT};
 501          const int pos{info.GetBucketPosition(nKey, true, bucket)};
 502          if (vvNew[bucket][pos] == nId) {
 503              vvNew[bucket][pos] = -1;
 504              info.nRefCount--;
 505              if (info.nRefCount == 0) break;
 506          }
 507      }
 508      nNew--;
 509      m_network_counts[info.GetNetwork()].n_new--;
 510  
 511      assert(info.nRefCount == 0);
 512  
 513      // which tried bucket to move the entry to
 514      int nKBucket = info.GetTriedBucket(nKey, m_netgroupman);
 515      int nKBucketPos = info.GetBucketPosition(nKey, false, nKBucket);
 516  
 517      // first make space to add it (the existing tried entry there is moved to new, deleting whatever is there).
 518      if (vvTried[nKBucket][nKBucketPos] != -1) {
 519          // find an item to evict
 520          int nIdEvict = vvTried[nKBucket][nKBucketPos];
 521          assert(mapInfo.count(nIdEvict) == 1);
 522          AddrInfo& infoOld = mapInfo[nIdEvict];
 523  
 524          // Remove the to-be-evicted item from the tried set.
 525          infoOld.fInTried = false;
 526          vvTried[nKBucket][nKBucketPos] = -1;
 527          nTried--;
 528          m_network_counts[infoOld.GetNetwork()].n_tried--;
 529  
 530          // find which new bucket it belongs to
 531          int nUBucket = infoOld.GetNewBucket(nKey, m_netgroupman);
 532          int nUBucketPos = infoOld.GetBucketPosition(nKey, true, nUBucket);
 533          ClearNew(nUBucket, nUBucketPos);
 534          assert(vvNew[nUBucket][nUBucketPos] == -1);
 535  
 536          // Enter it into the new set again.
 537          infoOld.nRefCount = 1;
 538          vvNew[nUBucket][nUBucketPos] = nIdEvict;
 539          nNew++;
 540          m_network_counts[infoOld.GetNetwork()].n_new++;
 541          LogPrint(BCLog::ADDRMAN, "Moved %s from tried[%i][%i] to new[%i][%i] to make space\n",
 542                   infoOld.ToStringAddrPort(), nKBucket, nKBucketPos, nUBucket, nUBucketPos);
 543      }
 544      assert(vvTried[nKBucket][nKBucketPos] == -1);
 545  
 546      vvTried[nKBucket][nKBucketPos] = nId;
 547      nTried++;
 548      info.fInTried = true;
 549      m_network_counts[info.GetNetwork()].n_tried++;
 550  }
 551  
 552  bool AddrManImpl::AddSingle(const CAddress& addr, const CNetAddr& source, std::chrono::seconds time_penalty)
 553  {
 554      AssertLockHeld(cs);
 555  
 556      if (!addr.IsRoutable())
 557          return false;
 558  
 559      int nId;
 560      AddrInfo* pinfo = Find(addr, &nId);
 561  
 562      // Do not set a penalty for a source's self-announcement
 563      if (addr == source) {
 564          time_penalty = 0s;
 565      }
 566  
 567      if (pinfo) {
 568          // periodically update nTime
 569          const bool currently_online{NodeClock::now() - addr.nTime < 24h};
 570          const auto update_interval{currently_online ? 1h : 24h};
 571          if (pinfo->nTime < addr.nTime - update_interval - time_penalty) {
 572              pinfo->nTime = std::max(NodeSeconds{0s}, addr.nTime - time_penalty);
 573          }
 574  
 575          // add services
 576          pinfo->nServices = ServiceFlags(pinfo->nServices | addr.nServices);
 577  
 578          // do not update if no new information is present
 579          if (addr.nTime <= pinfo->nTime) {
 580              return false;
 581          }
 582  
 583          // do not update if the entry was already in the "tried" table
 584          if (pinfo->fInTried)
 585              return false;
 586  
 587          // do not update if the max reference count is reached
 588          if (pinfo->nRefCount == ADDRMAN_NEW_BUCKETS_PER_ADDRESS)
 589              return false;
 590  
 591          // stochastic test: previous nRefCount == N: 2^N times harder to increase it
 592          if (pinfo->nRefCount > 0) {
 593              const int nFactor{1 << pinfo->nRefCount};
 594              if (insecure_rand.randrange(nFactor) != 0) return false;
 595          }
 596      } else {
 597          pinfo = Create(addr, source, &nId);
 598          pinfo->nTime = std::max(NodeSeconds{0s}, pinfo->nTime - time_penalty);
 599      }
 600  
 601      int nUBucket = pinfo->GetNewBucket(nKey, source, m_netgroupman);
 602      int nUBucketPos = pinfo->GetBucketPosition(nKey, true, nUBucket);
 603      bool fInsert = vvNew[nUBucket][nUBucketPos] == -1;
 604      if (vvNew[nUBucket][nUBucketPos] != nId) {
 605          if (!fInsert) {
 606              AddrInfo& infoExisting = mapInfo[vvNew[nUBucket][nUBucketPos]];
 607              if (infoExisting.IsTerrible() || (infoExisting.nRefCount > 1 && pinfo->nRefCount == 0)) {
 608                  // Overwrite the existing new table entry.
 609                  fInsert = true;
 610              }
 611          }
 612          if (fInsert) {
 613              ClearNew(nUBucket, nUBucketPos);
 614              pinfo->nRefCount++;
 615              vvNew[nUBucket][nUBucketPos] = nId;
 616              const auto mapped_as{m_netgroupman.GetMappedAS(addr)};
 617              LogPrint(BCLog::ADDRMAN, "Added %s%s to new[%i][%i]\n",
 618                       addr.ToStringAddrPort(), (mapped_as ? strprintf(" mapped to AS%i", mapped_as) : ""), nUBucket, nUBucketPos);
 619          } else {
 620              if (pinfo->nRefCount == 0) {
 621                  Delete(nId);
 622              }
 623          }
 624      }
 625      return fInsert;
 626  }
 627  
 628  bool AddrManImpl::Good_(const CService& addr, bool test_before_evict, NodeSeconds time)
 629  {
 630      AssertLockHeld(cs);
 631  
 632      int nId;
 633  
 634      m_last_good = time;
 635  
 636      AddrInfo* pinfo = Find(addr, &nId);
 637  
 638      // if not found, bail out
 639      if (!pinfo) return false;
 640  
 641      AddrInfo& info = *pinfo;
 642  
 643      // update info
 644      info.m_last_success = time;
 645      info.m_last_try = time;
 646      info.nAttempts = 0;
 647      // nTime is not updated here, to avoid leaking information about
 648      // currently-connected peers.
 649  
 650      // if it is already in the tried set, don't do anything else
 651      if (info.fInTried) return false;
 652  
 653      // if it is not in new, something bad happened
 654      if (!Assume(info.nRefCount > 0)) return false;
 655  
 656  
 657      // which tried bucket to move the entry to
 658      int tried_bucket = info.GetTriedBucket(nKey, m_netgroupman);
 659      int tried_bucket_pos = info.GetBucketPosition(nKey, false, tried_bucket);
 660  
 661      // Will moving this address into tried evict another entry?
 662      if (test_before_evict && (vvTried[tried_bucket][tried_bucket_pos] != -1)) {
 663          if (m_tried_collisions.size() < ADDRMAN_SET_TRIED_COLLISION_SIZE) {
 664              m_tried_collisions.insert(nId);
 665          }
 666          // Output the entry we'd be colliding with, for debugging purposes
 667          auto colliding_entry = mapInfo.find(vvTried[tried_bucket][tried_bucket_pos]);
 668          LogPrint(BCLog::ADDRMAN, "Collision with %s while attempting to move %s to tried table. Collisions=%d\n",
 669                   colliding_entry != mapInfo.end() ? colliding_entry->second.ToStringAddrPort() : "",
 670                   addr.ToStringAddrPort(),
 671                   m_tried_collisions.size());
 672          return false;
 673      } else {
 674          // move nId to the tried tables
 675          MakeTried(info, nId);
 676          const auto mapped_as{m_netgroupman.GetMappedAS(addr)};
 677          LogPrint(BCLog::ADDRMAN, "Moved %s%s to tried[%i][%i]\n",
 678                   addr.ToStringAddrPort(), (mapped_as ? strprintf(" mapped to AS%i", mapped_as) : ""), tried_bucket, tried_bucket_pos);
 679          return true;
 680      }
 681  }
 682  
 683  bool AddrManImpl::Add_(const std::vector<CAddress>& vAddr, const CNetAddr& source, std::chrono::seconds time_penalty)
 684  {
 685      int added{0};
 686      for (std::vector<CAddress>::const_iterator it = vAddr.begin(); it != vAddr.end(); it++) {
 687          added += AddSingle(*it, source, time_penalty) ? 1 : 0;
 688      }
 689      if (added > 0) {
 690          LogPrint(BCLog::ADDRMAN, "Added %i addresses (of %i) from %s: %i tried, %i new\n", added, vAddr.size(), source.ToStringAddr(), nTried, nNew);
 691      }
 692      return added > 0;
 693  }
 694  
 695  void AddrManImpl::Attempt_(const CService& addr, bool fCountFailure, NodeSeconds time)
 696  {
 697      AssertLockHeld(cs);
 698  
 699      AddrInfo* pinfo = Find(addr);
 700  
 701      // if not found, bail out
 702      if (!pinfo)
 703          return;
 704  
 705      AddrInfo& info = *pinfo;
 706  
 707      // update info
 708      info.m_last_try = time;
 709      if (fCountFailure && info.m_last_count_attempt < m_last_good) {
 710          info.m_last_count_attempt = time;
 711          info.nAttempts++;
 712      }
 713  }
 714  
 715  std::pair<CAddress, NodeSeconds> AddrManImpl::Select_(bool new_only, std::optional<Network> network) const
 716  {
 717      AssertLockHeld(cs);
 718  
 719      if (vRandom.empty()) return {};
 720  
 721      size_t new_count = nNew;
 722      size_t tried_count = nTried;
 723  
 724      if (network.has_value()) {
 725          auto it = m_network_counts.find(*network);
 726          if (it == m_network_counts.end()) return {};
 727  
 728          auto counts = it->second;
 729          new_count = counts.n_new;
 730          tried_count = counts.n_tried;
 731      }
 732  
 733      if (new_only && new_count == 0) return {};
 734      if (new_count + tried_count == 0) return {};
 735  
 736      // Decide if we are going to search the new or tried table
 737      // If either option is viable, use a 50% chance to choose
 738      bool search_tried;
 739      if (new_only || tried_count == 0) {
 740          search_tried = false;
 741      } else if (new_count == 0) {
 742          search_tried = true;
 743      } else {
 744          search_tried = insecure_rand.randbool();
 745      }
 746  
 747      const int bucket_count{search_tried ? ADDRMAN_TRIED_BUCKET_COUNT : ADDRMAN_NEW_BUCKET_COUNT};
 748  
 749      // Loop through the addrman table until we find an appropriate entry
 750      double chance_factor = 1.0;
 751      while (1) {
 752          // Pick a bucket, and an initial position in that bucket.
 753          int bucket = insecure_rand.randrange(bucket_count);
 754          int initial_position = insecure_rand.randrange(ADDRMAN_BUCKET_SIZE);
 755  
 756          // Iterate over the positions of that bucket, starting at the initial one,
 757          // and looping around.
 758          int i, position, node_id;
 759          for (i = 0; i < ADDRMAN_BUCKET_SIZE; ++i) {
 760              position = (initial_position + i) % ADDRMAN_BUCKET_SIZE;
 761              node_id = GetEntry(search_tried, bucket, position);
 762              if (node_id != -1) {
 763                  if (network.has_value()) {
 764                      const auto it{mapInfo.find(node_id)};
 765                      if (Assume(it != mapInfo.end()) && it->second.GetNetwork() == *network) break;
 766                  } else {
 767                      break;
 768                  }
 769              }
 770          }
 771  
 772          // If the bucket is entirely empty, start over with a (likely) different one.
 773          if (i == ADDRMAN_BUCKET_SIZE) continue;
 774  
 775          // Find the entry to return.
 776          const auto it_found{mapInfo.find(node_id)};
 777          assert(it_found != mapInfo.end());
 778          const AddrInfo& info{it_found->second};
 779  
 780          // With probability GetChance() * chance_factor, return the entry.
 781          if (insecure_rand.randbits(30) < chance_factor * info.GetChance() * (1 << 30)) {
 782              LogPrint(BCLog::ADDRMAN, "Selected %s from %s\n", info.ToStringAddrPort(), search_tried ? "tried" : "new");
 783              return {info, info.m_last_try};
 784          }
 785  
 786          // Otherwise start over with a (likely) different bucket, and increased chance factor.
 787          chance_factor *= 1.2;
 788      }
 789  }
 790  
 791  int AddrManImpl::GetEntry(bool use_tried, size_t bucket, size_t position) const
 792  {
 793      AssertLockHeld(cs);
 794  
 795      if (use_tried) {
 796          if (Assume(position < ADDRMAN_BUCKET_SIZE) && Assume(bucket < ADDRMAN_TRIED_BUCKET_COUNT)) {
 797              return vvTried[bucket][position];
 798          }
 799      } else {
 800          if (Assume(position < ADDRMAN_BUCKET_SIZE) && Assume(bucket < ADDRMAN_NEW_BUCKET_COUNT)) {
 801              return vvNew[bucket][position];
 802          }
 803      }
 804  
 805      return -1;
 806  }
 807  
 808  std::vector<CAddress> AddrManImpl::GetAddr_(size_t max_addresses, size_t max_pct, std::optional<Network> network, const bool filtered) const
 809  {
 810      AssertLockHeld(cs);
 811  
 812      size_t nNodes = vRandom.size();
 813      if (max_pct != 0) {
 814          nNodes = max_pct * nNodes / 100;
 815      }
 816      if (max_addresses != 0) {
 817          nNodes = std::min(nNodes, max_addresses);
 818      }
 819  
 820      // gather a list of random nodes, skipping those of low quality
 821      const auto now{Now<NodeSeconds>()};
 822      std::vector<CAddress> addresses;
 823      for (unsigned int n = 0; n < vRandom.size(); n++) {
 824          if (addresses.size() >= nNodes)
 825              break;
 826  
 827          int nRndPos = insecure_rand.randrange(vRandom.size() - n) + n;
 828          SwapRandom(n, nRndPos);
 829          const auto it{mapInfo.find(vRandom[n])};
 830          assert(it != mapInfo.end());
 831  
 832          const AddrInfo& ai{it->second};
 833  
 834          // Filter by network (optional)
 835          if (network != std::nullopt && ai.GetNetClass() != network) continue;
 836  
 837          // Filter for quality
 838          if (ai.IsTerrible(now) && filtered) continue;
 839  
 840          addresses.push_back(ai);
 841      }
 842      LogPrint(BCLog::ADDRMAN, "GetAddr returned %d random addresses\n", addresses.size());
 843      return addresses;
 844  }
 845  
 846  std::vector<std::pair<AddrInfo, AddressPosition>> AddrManImpl::GetEntries_(bool from_tried) const
 847  {
 848      AssertLockHeld(cs);
 849  
 850      const int bucket_count = from_tried ? ADDRMAN_TRIED_BUCKET_COUNT : ADDRMAN_NEW_BUCKET_COUNT;
 851      std::vector<std::pair<AddrInfo, AddressPosition>> infos;
 852      for (int bucket = 0; bucket < bucket_count; ++bucket) {
 853          for (int position = 0; position < ADDRMAN_BUCKET_SIZE; ++position) {
 854              int id = GetEntry(from_tried, bucket, position);
 855              if (id >= 0) {
 856                  AddrInfo info = mapInfo.at(id);
 857                  AddressPosition location = AddressPosition(
 858                      from_tried,
 859                      /*multiplicity_in=*/from_tried ? 1 : info.nRefCount,
 860                      bucket,
 861                      position);
 862                  infos.emplace_back(info, location);
 863              }
 864          }
 865      }
 866  
 867      return infos;
 868  }
 869  
 870  void AddrManImpl::Connected_(const CService& addr, NodeSeconds time)
 871  {
 872      AssertLockHeld(cs);
 873  
 874      AddrInfo* pinfo = Find(addr);
 875  
 876      // if not found, bail out
 877      if (!pinfo)
 878          return;
 879  
 880      AddrInfo& info = *pinfo;
 881  
 882      // update info
 883      const auto update_interval{20min};
 884      if (time - info.nTime > update_interval) {
 885          info.nTime = time;
 886      }
 887  }
 888  
 889  void AddrManImpl::SetServices_(const CService& addr, ServiceFlags nServices)
 890  {
 891      AssertLockHeld(cs);
 892  
 893      AddrInfo* pinfo = Find(addr);
 894  
 895      // if not found, bail out
 896      if (!pinfo)
 897          return;
 898  
 899      AddrInfo& info = *pinfo;
 900  
 901      // update info
 902      info.nServices = nServices;
 903  }
 904  
 905  void AddrManImpl::ResolveCollisions_()
 906  {
 907      AssertLockHeld(cs);
 908  
 909      for (std::set<int>::iterator it = m_tried_collisions.begin(); it != m_tried_collisions.end();) {
 910          int id_new = *it;
 911  
 912          bool erase_collision = false;
 913  
 914          // If id_new not found in mapInfo remove it from m_tried_collisions
 915          if (mapInfo.count(id_new) != 1) {
 916              erase_collision = true;
 917          } else {
 918              AddrInfo& info_new = mapInfo[id_new];
 919  
 920              // Which tried bucket to move the entry to.
 921              int tried_bucket = info_new.GetTriedBucket(nKey, m_netgroupman);
 922              int tried_bucket_pos = info_new.GetBucketPosition(nKey, false, tried_bucket);
 923              if (!info_new.IsValid()) { // id_new may no longer map to a valid address
 924                  erase_collision = true;
 925              } else if (vvTried[tried_bucket][tried_bucket_pos] != -1) { // The position in the tried bucket is not empty
 926  
 927                  // Get the to-be-evicted address that is being tested
 928                  int id_old = vvTried[tried_bucket][tried_bucket_pos];
 929                  AddrInfo& info_old = mapInfo[id_old];
 930  
 931                  const auto current_time{Now<NodeSeconds>()};
 932  
 933                  // Has successfully connected in last X hours
 934                  if (current_time - info_old.m_last_success < ADDRMAN_REPLACEMENT) {
 935                      erase_collision = true;
 936                  } else if (current_time - info_old.m_last_try < ADDRMAN_REPLACEMENT) { // attempted to connect and failed in last X hours
 937  
 938                      // Give address at least 60 seconds to successfully connect
 939                      if (current_time - info_old.m_last_try > 60s) {
 940                          LogPrint(BCLog::ADDRMAN, "Replacing %s with %s in tried table\n", info_old.ToStringAddrPort(), info_new.ToStringAddrPort());
 941  
 942                          // Replaces an existing address already in the tried table with the new address
 943                          Good_(info_new, false, current_time);
 944                          erase_collision = true;
 945                      }
 946                  } else if (current_time - info_new.m_last_success > ADDRMAN_TEST_WINDOW) {
 947                      // If the collision hasn't resolved in some reasonable amount of time,
 948                      // just evict the old entry -- we must not be able to
 949                      // connect to it for some reason.
 950                      LogPrint(BCLog::ADDRMAN, "Unable to test; replacing %s with %s in tried table anyway\n", info_old.ToStringAddrPort(), info_new.ToStringAddrPort());
 951                      Good_(info_new, false, current_time);
 952                      erase_collision = true;
 953                  }
 954              } else { // Collision is not actually a collision anymore
 955                  Good_(info_new, false, Now<NodeSeconds>());
 956                  erase_collision = true;
 957              }
 958          }
 959  
 960          if (erase_collision) {
 961              m_tried_collisions.erase(it++);
 962          } else {
 963              it++;
 964          }
 965      }
 966  }
 967  
 968  std::pair<CAddress, NodeSeconds> AddrManImpl::SelectTriedCollision_()
 969  {
 970      AssertLockHeld(cs);
 971  
 972      if (m_tried_collisions.size() == 0) return {};
 973  
 974      std::set<int>::iterator it = m_tried_collisions.begin();
 975  
 976      // Selects a random element from m_tried_collisions
 977      std::advance(it, insecure_rand.randrange(m_tried_collisions.size()));
 978      int id_new = *it;
 979  
 980      // If id_new not found in mapInfo remove it from m_tried_collisions
 981      if (mapInfo.count(id_new) != 1) {
 982          m_tried_collisions.erase(it);
 983          return {};
 984      }
 985  
 986      const AddrInfo& newInfo = mapInfo[id_new];
 987  
 988      // which tried bucket to move the entry to
 989      int tried_bucket = newInfo.GetTriedBucket(nKey, m_netgroupman);
 990      int tried_bucket_pos = newInfo.GetBucketPosition(nKey, false, tried_bucket);
 991  
 992      const AddrInfo& info_old = mapInfo[vvTried[tried_bucket][tried_bucket_pos]];
 993      return {info_old, info_old.m_last_try};
 994  }
 995  
 996  std::optional<AddressPosition> AddrManImpl::FindAddressEntry_(const CAddress& addr)
 997  {
 998      AssertLockHeld(cs);
 999  
1000      AddrInfo* addr_info = Find(addr);
1001  
1002      if (!addr_info) return std::nullopt;
1003  
1004      if(addr_info->fInTried) {
1005          int bucket{addr_info->GetTriedBucket(nKey, m_netgroupman)};
1006          return AddressPosition(/*tried_in=*/true,
1007                                 /*multiplicity_in=*/1,
1008                                 /*bucket_in=*/bucket,
1009                                 /*position_in=*/addr_info->GetBucketPosition(nKey, false, bucket));
1010      } else {
1011          int bucket{addr_info->GetNewBucket(nKey, m_netgroupman)};
1012          return AddressPosition(/*tried_in=*/false,
1013                                 /*multiplicity_in=*/addr_info->nRefCount,
1014                                 /*bucket_in=*/bucket,
1015                                 /*position_in=*/addr_info->GetBucketPosition(nKey, true, bucket));
1016      }
1017  }
1018  
1019  size_t AddrManImpl::Size_(std::optional<Network> net, std::optional<bool> in_new) const
1020  {
1021      AssertLockHeld(cs);
1022  
1023      if (!net.has_value()) {
1024          if (in_new.has_value()) {
1025              return *in_new ? nNew : nTried;
1026          } else {
1027              return vRandom.size();
1028          }
1029      }
1030      if (auto it = m_network_counts.find(*net); it != m_network_counts.end()) {
1031          auto net_count = it->second;
1032          if (in_new.has_value()) {
1033              return *in_new ? net_count.n_new : net_count.n_tried;
1034          } else {
1035              return net_count.n_new + net_count.n_tried;
1036          }
1037      }
1038      return 0;
1039  }
1040  
1041  void AddrManImpl::Check() const
1042  {
1043      AssertLockHeld(cs);
1044  
1045      // Run consistency checks 1 in m_consistency_check_ratio times if enabled
1046      if (m_consistency_check_ratio == 0) return;
1047      if (insecure_rand.randrange(m_consistency_check_ratio) >= 1) return;
1048  
1049      const int err{CheckAddrman()};
1050      if (err) {
1051          LogPrintf("ADDRMAN CONSISTENCY CHECK FAILED!!! err=%i\n", err);
1052          assert(false);
1053      }
1054  }
1055  
1056  int AddrManImpl::CheckAddrman() const
1057  {
1058      AssertLockHeld(cs);
1059  
1060      LOG_TIME_MILLIS_WITH_CATEGORY_MSG_ONCE(
1061          strprintf("new %i, tried %i, total %u", nNew, nTried, vRandom.size()), BCLog::ADDRMAN);
1062  
1063      std::unordered_set<int> setTried;
1064      std::unordered_map<int, int> mapNew;
1065      std::unordered_map<Network, NewTriedCount> local_counts;
1066  
1067      if (vRandom.size() != (size_t)(nTried + nNew))
1068          return -7;
1069  
1070      for (const auto& entry : mapInfo) {
1071          int n = entry.first;
1072          const AddrInfo& info = entry.second;
1073          if (info.fInTried) {
1074              if (!TicksSinceEpoch<std::chrono::seconds>(info.m_last_success)) {
1075                  return -1;
1076              }
1077              if (info.nRefCount)
1078                  return -2;
1079              setTried.insert(n);
1080              local_counts[info.GetNetwork()].n_tried++;
1081          } else {
1082              if (info.nRefCount < 0 || info.nRefCount > ADDRMAN_NEW_BUCKETS_PER_ADDRESS)
1083                  return -3;
1084              if (!info.nRefCount)
1085                  return -4;
1086              mapNew[n] = info.nRefCount;
1087              local_counts[info.GetNetwork()].n_new++;
1088          }
1089          const auto it{mapAddr.find(info)};
1090          if (it == mapAddr.end() || it->second != n) {
1091              return -5;
1092          }
1093          if (info.nRandomPos < 0 || (size_t)info.nRandomPos >= vRandom.size() || vRandom[info.nRandomPos] != n)
1094              return -14;
1095          if (info.m_last_try < NodeSeconds{0s}) {
1096              return -6;
1097          }
1098          if (info.m_last_success < NodeSeconds{0s}) {
1099              return -8;
1100          }
1101      }
1102  
1103      if (setTried.size() != (size_t)nTried)
1104          return -9;
1105      if (mapNew.size() != (size_t)nNew)
1106          return -10;
1107  
1108      for (int n = 0; n < ADDRMAN_TRIED_BUCKET_COUNT; n++) {
1109          for (int i = 0; i < ADDRMAN_BUCKET_SIZE; i++) {
1110              if (vvTried[n][i] != -1) {
1111                  if (!setTried.count(vvTried[n][i]))
1112                      return -11;
1113                  const auto it{mapInfo.find(vvTried[n][i])};
1114                  if (it == mapInfo.end() || it->second.GetTriedBucket(nKey, m_netgroupman) != n) {
1115                      return -17;
1116                  }
1117                  if (it->second.GetBucketPosition(nKey, false, n) != i) {
1118                      return -18;
1119                  }
1120                  setTried.erase(vvTried[n][i]);
1121              }
1122          }
1123      }
1124  
1125      for (int n = 0; n < ADDRMAN_NEW_BUCKET_COUNT; n++) {
1126          for (int i = 0; i < ADDRMAN_BUCKET_SIZE; i++) {
1127              if (vvNew[n][i] != -1) {
1128                  if (!mapNew.count(vvNew[n][i]))
1129                      return -12;
1130                  const auto it{mapInfo.find(vvNew[n][i])};
1131                  if (it == mapInfo.end() || it->second.GetBucketPosition(nKey, true, n) != i) {
1132                      return -19;
1133                  }
1134                  if (--mapNew[vvNew[n][i]] == 0)
1135                      mapNew.erase(vvNew[n][i]);
1136              }
1137          }
1138      }
1139  
1140      if (setTried.size())
1141          return -13;
1142      if (mapNew.size())
1143          return -15;
1144      if (nKey.IsNull())
1145          return -16;
1146  
1147      // It's possible that m_network_counts may have all-zero entries that local_counts
1148      // doesn't have if addrs from a network were being added and then removed again in the past.
1149      if (m_network_counts.size() < local_counts.size()) {
1150          return -20;
1151      }
1152      for (const auto& [net, count] : m_network_counts) {
1153          if (local_counts[net].n_new != count.n_new || local_counts[net].n_tried != count.n_tried) {
1154              return -21;
1155          }
1156      }
1157  
1158      return 0;
1159  }
1160  
1161  size_t AddrManImpl::Size(std::optional<Network> net, std::optional<bool> in_new) const
1162  {
1163      LOCK(cs);
1164      Check();
1165      auto ret = Size_(net, in_new);
1166      Check();
1167      return ret;
1168  }
1169  
1170  bool AddrManImpl::Add(const std::vector<CAddress>& vAddr, const CNetAddr& source, std::chrono::seconds time_penalty)
1171  {
1172      LOCK(cs);
1173      Check();
1174      auto ret = Add_(vAddr, source, time_penalty);
1175      Check();
1176      return ret;
1177  }
1178  
1179  bool AddrManImpl::Good(const CService& addr, NodeSeconds time)
1180  {
1181      LOCK(cs);
1182      Check();
1183      auto ret = Good_(addr, /*test_before_evict=*/true, time);
1184      Check();
1185      return ret;
1186  }
1187  
1188  void AddrManImpl::Attempt(const CService& addr, bool fCountFailure, NodeSeconds time)
1189  {
1190      LOCK(cs);
1191      Check();
1192      Attempt_(addr, fCountFailure, time);
1193      Check();
1194  }
1195  
1196  void AddrManImpl::ResolveCollisions()
1197  {
1198      LOCK(cs);
1199      Check();
1200      ResolveCollisions_();
1201      Check();
1202  }
1203  
1204  std::pair<CAddress, NodeSeconds> AddrManImpl::SelectTriedCollision()
1205  {
1206      LOCK(cs);
1207      Check();
1208      auto ret = SelectTriedCollision_();
1209      Check();
1210      return ret;
1211  }
1212  
1213  std::pair<CAddress, NodeSeconds> AddrManImpl::Select(bool new_only, std::optional<Network> network) const
1214  {
1215      LOCK(cs);
1216      Check();
1217      auto addrRet = Select_(new_only, network);
1218      Check();
1219      return addrRet;
1220  }
1221  
1222  std::vector<CAddress> AddrManImpl::GetAddr(size_t max_addresses, size_t max_pct, std::optional<Network> network, const bool filtered) const
1223  {
1224      LOCK(cs);
1225      Check();
1226      auto addresses = GetAddr_(max_addresses, max_pct, network, filtered);
1227      Check();
1228      return addresses;
1229  }
1230  
1231  std::vector<std::pair<AddrInfo, AddressPosition>> AddrManImpl::GetEntries(bool from_tried) const
1232  {
1233      LOCK(cs);
1234      Check();
1235      auto addrInfos = GetEntries_(from_tried);
1236      Check();
1237      return addrInfos;
1238  }
1239  
1240  void AddrManImpl::Connected(const CService& addr, NodeSeconds time)
1241  {
1242      LOCK(cs);
1243      Check();
1244      Connected_(addr, time);
1245      Check();
1246  }
1247  
1248  void AddrManImpl::SetServices(const CService& addr, ServiceFlags nServices)
1249  {
1250      LOCK(cs);
1251      Check();
1252      SetServices_(addr, nServices);
1253      Check();
1254  }
1255  
1256  std::optional<AddressPosition> AddrManImpl::FindAddressEntry(const CAddress& addr)
1257  {
1258      LOCK(cs);
1259      Check();
1260      auto entry = FindAddressEntry_(addr);
1261      Check();
1262      return entry;
1263  }
1264  
1265  AddrMan::AddrMan(const NetGroupManager& netgroupman, bool deterministic, int32_t consistency_check_ratio)
1266      : m_impl(std::make_unique<AddrManImpl>(netgroupman, deterministic, consistency_check_ratio)) {}
1267  
1268  AddrMan::~AddrMan() = default;
1269  
1270  template <typename Stream>
1271  void AddrMan::Serialize(Stream& s_) const
1272  {
1273      m_impl->Serialize<Stream>(s_);
1274  }
1275  
1276  template <typename Stream>
1277  void AddrMan::Unserialize(Stream& s_)
1278  {
1279      m_impl->Unserialize<Stream>(s_);
1280  }
1281  
1282  // explicit instantiation
1283  template void AddrMan::Serialize(HashedSourceWriter<AutoFile>&) const;
1284  template void AddrMan::Serialize(DataStream&) const;
1285  template void AddrMan::Unserialize(AutoFile&);
1286  template void AddrMan::Unserialize(HashVerifier<AutoFile>&);
1287  template void AddrMan::Unserialize(DataStream&);
1288  template void AddrMan::Unserialize(HashVerifier<DataStream>&);
1289  
1290  size_t AddrMan::Size(std::optional<Network> net, std::optional<bool> in_new) const
1291  {
1292      return m_impl->Size(net, in_new);
1293  }
1294  
1295  bool AddrMan::Add(const std::vector<CAddress>& vAddr, const CNetAddr& source, std::chrono::seconds time_penalty)
1296  {
1297      return m_impl->Add(vAddr, source, time_penalty);
1298  }
1299  
1300  bool AddrMan::Good(const CService& addr, NodeSeconds time)
1301  {
1302      return m_impl->Good(addr, time);
1303  }
1304  
1305  void AddrMan::Attempt(const CService& addr, bool fCountFailure, NodeSeconds time)
1306  {
1307      m_impl->Attempt(addr, fCountFailure, time);
1308  }
1309  
1310  void AddrMan::ResolveCollisions()
1311  {
1312      m_impl->ResolveCollisions();
1313  }
1314  
1315  std::pair<CAddress, NodeSeconds> AddrMan::SelectTriedCollision()
1316  {
1317      return m_impl->SelectTriedCollision();
1318  }
1319  
1320  std::pair<CAddress, NodeSeconds> AddrMan::Select(bool new_only, std::optional<Network> network) const
1321  {
1322      return m_impl->Select(new_only, network);
1323  }
1324  
1325  std::vector<CAddress> AddrMan::GetAddr(size_t max_addresses, size_t max_pct, std::optional<Network> network, const bool filtered) const
1326  {
1327      return m_impl->GetAddr(max_addresses, max_pct, network, filtered);
1328  }
1329  
1330  std::vector<std::pair<AddrInfo, AddressPosition>> AddrMan::GetEntries(bool use_tried) const
1331  {
1332      return m_impl->GetEntries(use_tried);
1333  }
1334  
1335  void AddrMan::Connected(const CService& addr, NodeSeconds time)
1336  {
1337      m_impl->Connected(addr, time);
1338  }
1339  
1340  void AddrMan::SetServices(const CService& addr, ServiceFlags nServices)
1341  {
1342      m_impl->SetServices(addr, nServices);
1343  }
1344  
1345  std::optional<AddressPosition> AddrMan::FindAddressEntry(const CAddress& addr)
1346  {
1347      return m_impl->FindAddressEntry(addr);
1348  }