LCOV - code coverage report
Current view: top level - src - addrman.cpp (source / functions) Hit Total Coverage
Test: test_dash_coverage.info Lines: 720 805 89.4 %
Date: 2026-06-25 07:23:51 Functions: 62 71 87.3 %

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

Generated by: LCOV version 1.16