LCOV - code coverage report
Current view: top level - src - addrman.cpp (source / functions) Hit Total Coverage
Test: fuzz_coverage.info Lines: 23 790 2.9 %
Date: 2023-09-26 12:08:55 Functions: 5 65 7.7 %

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

Generated by: LCOV version 1.14