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