Branch data Line data Source code
# 1 : : // Copyright (c) 2012 Pieter Wuille
# 2 : : // Copyright (c) 2012-2020 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 : :
# 8 : : #include <hash.h>
# 9 : : #include <i2p.h>
# 10 : : #include <logging.h>
# 11 : : #include <netaddress.h>
# 12 : : #include <serialize.h>
# 13 : :
# 14 : : #include <cmath>
# 15 : : #include <optional>
# 16 : :
# 17 : : int CAddrInfo::GetTriedBucket(const uint256& nKey, const std::vector<bool> &asmap) const
# 18 : 1181214 : {
# 19 : 1181214 : uint64_t hash1 = (CHashWriter(SER_GETHASH, 0) << nKey << GetKey()).GetCheapHash();
# 20 : 1181214 : uint64_t hash2 = (CHashWriter(SER_GETHASH, 0) << nKey << GetGroup(asmap) << (hash1 % ADDRMAN_TRIED_BUCKETS_PER_GROUP)).GetCheapHash();
# 21 : 1181214 : int tried_bucket = hash2 % ADDRMAN_TRIED_BUCKET_COUNT;
# 22 : 1181214 : uint32_t mapped_as = GetMappedAS(asmap);
# 23 [ + - ]: 1181214 : LogPrint(BCLog::NET, "IP %s mapped to AS%i belongs to tried bucket %i\n", ToStringIP(), mapped_as, tried_bucket);
# 24 : 1181214 : return tried_bucket;
# 25 : 1181214 : }
# 26 : :
# 27 : : int CAddrInfo::GetNewBucket(const uint256& nKey, const CNetAddr& src, const std::vector<bool> &asmap) const
# 28 : 30891 : {
# 29 : 30891 : std::vector<unsigned char> vchSourceGroupKey = src.GetGroup(asmap);
# 30 : 30891 : uint64_t hash1 = (CHashWriter(SER_GETHASH, 0) << nKey << GetGroup(asmap) << vchSourceGroupKey).GetCheapHash();
# 31 : 30891 : uint64_t hash2 = (CHashWriter(SER_GETHASH, 0) << nKey << vchSourceGroupKey << (hash1 % ADDRMAN_NEW_BUCKETS_PER_SOURCE_GROUP)).GetCheapHash();
# 32 : 30891 : int new_bucket = hash2 % ADDRMAN_NEW_BUCKET_COUNT;
# 33 : 30891 : uint32_t mapped_as = GetMappedAS(asmap);
# 34 [ + - ]: 30891 : LogPrint(BCLog::NET, "IP %s mapped to AS%i belongs to new bucket %i\n", ToStringIP(), mapped_as, new_bucket);
# 35 : 30891 : return new_bucket;
# 36 : 30891 : }
# 37 : :
# 38 : : int CAddrInfo::GetBucketPosition(const uint256 &nKey, bool fNew, int nBucket) const
# 39 : 286835690 : {
# 40 [ + + ]: 286835690 : uint64_t hash1 = (CHashWriter(SER_GETHASH, 0) << nKey << (fNew ? 'N' : 'K') << nBucket << GetKey()).GetCheapHash();
# 41 : 286835690 : return hash1 % ADDRMAN_BUCKET_SIZE;
# 42 : 286835690 : }
# 43 : :
# 44 : : bool CAddrInfo::IsTerrible(int64_t nNow) const
# 45 : 22180 : {
# 46 [ + + ][ + + ]: 22180 : if (nLastTry && nLastTry >= nNow - 60) // never remove things tried in the last minute
# 47 : 99 : return false;
# 48 : :
# 49 [ - + ]: 22081 : if (nTime > nNow + 10 * 60) // came in a flying DeLorean
# 50 : 0 : return true;
# 51 : :
# 52 [ - + ][ + + ]: 22081 : if (nTime == 0 || nNow - nTime > ADDRMAN_HORIZON_DAYS * 24 * 60 * 60) // not seen in recent history
# 53 : 2 : return true;
# 54 : :
# 55 [ + + ][ - + ]: 22079 : if (nLastSuccess == 0 && nAttempts >= ADDRMAN_RETRIES) // tried N times and never a success
# 56 : 22079 : return true;
# 57 : :
# 58 [ + + ][ - + ]: 22079 : if (nNow - nLastSuccess > ADDRMAN_MIN_FAIL_DAYS * 24 * 60 * 60 && nAttempts >= ADDRMAN_MAX_FAILURES) // N successive failures in the last week
# 59 : 22079 : return true;
# 60 : :
# 61 : 22079 : return false;
# 62 : 22079 : }
# 63 : :
# 64 : : double CAddrInfo::GetChance(int64_t nNow) const
# 65 : 13114 : {
# 66 : 13114 : double fChance = 1.0;
# 67 : 13114 : int64_t nSinceLastTry = std::max<int64_t>(nNow - nLastTry, 0);
# 68 : :
# 69 : : // deprioritize very recent attempts away
# 70 [ + + ]: 13114 : if (nSinceLastTry < 60 * 10)
# 71 : 434 : fChance *= 0.01;
# 72 : :
# 73 : : // deprioritize 66% after each failed attempt, but at most 1/28th to avoid the search taking forever or overly penalizing outages.
# 74 : 13114 : fChance *= pow(0.66, std::min(nAttempts, 8));
# 75 : :
# 76 : 13114 : return fChance;
# 77 : 13114 : }
# 78 : :
# 79 : : CAddrInfo* CAddrMan::Find(const CNetAddr& addr, int* pnId)
# 80 : 26740 : {
# 81 : 26740 : std::map<CNetAddr, int>::iterator it = mapAddr.find(addr);
# 82 [ + + ]: 26740 : if (it == mapAddr.end())
# 83 : 25653 : return nullptr;
# 84 [ + + ]: 1087 : if (pnId)
# 85 : 895 : *pnId = (*it).second;
# 86 : 1087 : std::map<int, CAddrInfo>::iterator it2 = mapInfo.find((*it).second);
# 87 [ + - ]: 1087 : if (it2 != mapInfo.end())
# 88 : 1087 : return &(*it2).second;
# 89 : 0 : return nullptr;
# 90 : 0 : }
# 91 : :
# 92 : : CAddrInfo* CAddrMan::Create(const CAddress& addr, const CNetAddr& addrSource, int* pnId)
# 93 : 24231 : {
# 94 : 24231 : int nId = nIdCount++;
# 95 : 24231 : mapInfo[nId] = CAddrInfo(addr, addrSource);
# 96 : 24231 : mapAddr[addr] = nId;
# 97 : 24231 : mapInfo[nId].nRandomPos = vRandom.size();
# 98 : 24231 : vRandom.push_back(nId);
# 99 [ + - ]: 24231 : if (pnId)
# 100 : 24231 : *pnId = nId;
# 101 : 24231 : return &mapInfo[nId];
# 102 : 24231 : }
# 103 : :
# 104 : : void CAddrMan::SwapRandom(unsigned int nRndPos1, unsigned int nRndPos2)
# 105 : 48620 : {
# 106 [ + + ]: 48620 : if (nRndPos1 == nRndPos2)
# 107 : 1462 : return;
# 108 : :
# 109 : 47158 : assert(nRndPos1 < vRandom.size() && nRndPos2 < vRandom.size());
# 110 : :
# 111 : 47158 : int nId1 = vRandom[nRndPos1];
# 112 : 47158 : int nId2 = vRandom[nRndPos2];
# 113 : :
# 114 : 47158 : assert(mapInfo.count(nId1) == 1);
# 115 : 47158 : assert(mapInfo.count(nId2) == 1);
# 116 : :
# 117 : 47158 : mapInfo[nId1].nRandomPos = nRndPos2;
# 118 : 47158 : mapInfo[nId2].nRandomPos = nRndPos1;
# 119 : :
# 120 : 47158 : vRandom[nRndPos1] = nId2;
# 121 : 47158 : vRandom[nRndPos2] = nId1;
# 122 : 47158 : }
# 123 : :
# 124 : : void CAddrMan::Delete(int nId)
# 125 : 1394 : {
# 126 : 1394 : assert(mapInfo.count(nId) != 0);
# 127 : 1394 : CAddrInfo& info = mapInfo[nId];
# 128 : 1394 : assert(!info.fInTried);
# 129 : 1394 : assert(info.nRefCount == 0);
# 130 : :
# 131 : 1394 : SwapRandom(info.nRandomPos, vRandom.size() - 1);
# 132 : 1394 : vRandom.pop_back();
# 133 : 1394 : mapAddr.erase(info);
# 134 : 1394 : mapInfo.erase(nId);
# 135 : 1394 : nNew--;
# 136 : 1394 : }
# 137 : :
# 138 : : void CAddrMan::ClearNew(int nUBucket, int nUBucketPos)
# 139 : 22843 : {
# 140 : : // if there is an entry in the specified bucket, delete it.
# 141 [ + + ]: 22843 : if (vvNew[nUBucket][nUBucketPos] != -1) {
# 142 : 4 : int nIdDelete = vvNew[nUBucket][nUBucketPos];
# 143 : 4 : CAddrInfo& infoDelete = mapInfo[nIdDelete];
# 144 : 4 : assert(infoDelete.nRefCount > 0);
# 145 : 4 : infoDelete.nRefCount--;
# 146 : 4 : vvNew[nUBucket][nUBucketPos] = -1;
# 147 [ + - ]: 4 : if (infoDelete.nRefCount == 0) {
# 148 : 4 : Delete(nIdDelete);
# 149 : 4 : }
# 150 : 4 : }
# 151 : 22843 : }
# 152 : :
# 153 : : void CAddrMan::MakeTried(CAddrInfo& info, int nId)
# 154 : 802 : {
# 155 : : // remove the entry from all new buckets
# 156 [ + + ]: 822050 : for (int bucket = 0; bucket < ADDRMAN_NEW_BUCKET_COUNT; bucket++) {
# 157 : 821248 : int pos = info.GetBucketPosition(nKey, true, bucket);
# 158 [ + + ]: 821248 : if (vvNew[bucket][pos] == nId) {
# 159 : 802 : vvNew[bucket][pos] = -1;
# 160 : 802 : info.nRefCount--;
# 161 : 802 : }
# 162 : 821248 : }
# 163 : 802 : nNew--;
# 164 : :
# 165 : 802 : assert(info.nRefCount == 0);
# 166 : :
# 167 : : // which tried bucket to move the entry to
# 168 : 802 : int nKBucket = info.GetTriedBucket(nKey, m_asmap);
# 169 : 802 : int nKBucketPos = info.GetBucketPosition(nKey, false, nKBucket);
# 170 : :
# 171 : : // first make space to add it (the existing tried entry there is moved to new, deleting whatever is there).
# 172 [ + + ]: 802 : if (vvTried[nKBucket][nKBucketPos] != -1) {
# 173 : : // find an item to evict
# 174 : 2 : int nIdEvict = vvTried[nKBucket][nKBucketPos];
# 175 : 2 : assert(mapInfo.count(nIdEvict) == 1);
# 176 : 2 : CAddrInfo& infoOld = mapInfo[nIdEvict];
# 177 : :
# 178 : : // Remove the to-be-evicted item from the tried set.
# 179 : 2 : infoOld.fInTried = false;
# 180 : 2 : vvTried[nKBucket][nKBucketPos] = -1;
# 181 : 2 : nTried--;
# 182 : :
# 183 : : // find which new bucket it belongs to
# 184 : 2 : int nUBucket = infoOld.GetNewBucket(nKey, m_asmap);
# 185 : 2 : int nUBucketPos = infoOld.GetBucketPosition(nKey, true, nUBucket);
# 186 : 2 : ClearNew(nUBucket, nUBucketPos);
# 187 : 2 : assert(vvNew[nUBucket][nUBucketPos] == -1);
# 188 : :
# 189 : : // Enter it into the new set again.
# 190 : 2 : infoOld.nRefCount = 1;
# 191 : 2 : vvNew[nUBucket][nUBucketPos] = nIdEvict;
# 192 : 2 : nNew++;
# 193 : 2 : }
# 194 : 802 : assert(vvTried[nKBucket][nKBucketPos] == -1);
# 195 : :
# 196 : 802 : vvTried[nKBucket][nKBucketPos] = nId;
# 197 : 802 : nTried++;
# 198 : 802 : info.fInTried = true;
# 199 : 802 : }
# 200 : :
# 201 : : void CAddrMan::Good_(const CService& addr, bool test_before_evict, int64_t nTime)
# 202 : 1248 : {
# 203 : 1248 : int nId;
# 204 : :
# 205 : 1248 : nLastGood = nTime;
# 206 : :
# 207 : 1248 : CAddrInfo* pinfo = Find(addr, &nId);
# 208 : :
# 209 : : // if not found, bail out
# 210 [ + + ]: 1248 : if (!pinfo)
# 211 : 366 : return;
# 212 : :
# 213 : 882 : CAddrInfo& info = *pinfo;
# 214 : :
# 215 : : // check whether we are talking about the exact same CService (including same port)
# 216 [ + + ]: 882 : if (info != addr)
# 217 : 2 : return;
# 218 : :
# 219 : : // update info
# 220 : 880 : info.nLastSuccess = nTime;
# 221 : 880 : info.nLastTry = nTime;
# 222 : 880 : info.nAttempts = 0;
# 223 : : // nTime is not updated here, to avoid leaking information about
# 224 : : // currently-connected peers.
# 225 : :
# 226 : : // if it is already in the tried set, don't do anything else
# 227 [ + + ]: 880 : if (info.fInTried)
# 228 : 48 : return;
# 229 : :
# 230 : : // find a bucket it is in now
# 231 : 832 : int nRnd = insecure_rand.randrange(ADDRMAN_NEW_BUCKET_COUNT);
# 232 : 832 : int nUBucket = -1;
# 233 [ + - ]: 434108 : for (unsigned int n = 0; n < ADDRMAN_NEW_BUCKET_COUNT; n++) {
# 234 : 434108 : int nB = (n + nRnd) % ADDRMAN_NEW_BUCKET_COUNT;
# 235 : 434108 : int nBpos = info.GetBucketPosition(nKey, true, nB);
# 236 [ + + ]: 434108 : if (vvNew[nB][nBpos] == nId) {
# 237 : 832 : nUBucket = nB;
# 238 : 832 : break;
# 239 : 832 : }
# 240 : 434108 : }
# 241 : :
# 242 : : // if no bucket is found, something bad happened;
# 243 : : // TODO: maybe re-add the node, but for now, just bail out
# 244 [ - + ]: 832 : if (nUBucket == -1)
# 245 : 0 : return;
# 246 : :
# 247 : : // which tried bucket to move the entry to
# 248 : 832 : int tried_bucket = info.GetTriedBucket(nKey, m_asmap);
# 249 : 832 : int tried_bucket_pos = info.GetBucketPosition(nKey, false, tried_bucket);
# 250 : :
# 251 : : // Will moving this address into tried evict another entry?
# 252 [ + + ][ + + ]: 832 : if (test_before_evict && (vvTried[tried_bucket][tried_bucket_pos] != -1)) {
# 253 : : // Output the entry we'd be colliding with, for debugging purposes
# 254 : 30 : auto colliding_entry = mapInfo.find(vvTried[tried_bucket][tried_bucket_pos]);
# 255 [ + - ][ + - ]: 30 : LogPrint(BCLog::ADDRMAN, "Collision inserting element into tried table (%s), moving %s to m_tried_collisions=%d\n", colliding_entry != mapInfo.end() ? colliding_entry->second.ToString() : "", addr.ToString(), m_tried_collisions.size());
# 256 [ + - ]: 30 : if (m_tried_collisions.size() < ADDRMAN_SET_TRIED_COLLISION_SIZE) {
# 257 : 30 : m_tried_collisions.insert(nId);
# 258 : 30 : }
# 259 : 802 : } else {
# 260 [ + - ]: 802 : LogPrint(BCLog::ADDRMAN, "Moving %s to tried\n", addr.ToString());
# 261 : :
# 262 : : // move nId to the tried tables
# 263 : 802 : MakeTried(info, nId);
# 264 : 802 : }
# 265 : 832 : }
# 266 : :
# 267 : : bool CAddrMan::Add_(const CAddress& addr, const CNetAddr& source, int64_t nTimePenalty)
# 268 : 25569 : {
# 269 [ + + ]: 25569 : if (!addr.IsRoutable())
# 270 : 1329 : return false;
# 271 : :
# 272 : 24240 : bool fNew = false;
# 273 : 24240 : int nId;
# 274 : 24240 : CAddrInfo* pinfo = Find(addr, &nId);
# 275 : :
# 276 : : // Do not set a penalty for a source's self-announcement
# 277 [ + + ]: 24240 : if (addr == source) {
# 278 : 23775 : nTimePenalty = 0;
# 279 : 23775 : }
# 280 : :
# 281 [ + + ]: 24240 : if (pinfo) {
# 282 : : // periodically update nTime
# 283 : 13 : bool fCurrentlyOnline = (GetAdjustedTime() - addr.nTime < 24 * 60 * 60);
# 284 [ + + ]: 13 : int64_t nUpdateInterval = (fCurrentlyOnline ? 60 * 60 : 24 * 60 * 60);
# 285 [ + - ][ - + ]: 13 : if (addr.nTime && (!pinfo->nTime || pinfo->nTime < addr.nTime - nUpdateInterval - nTimePenalty))
# [ - + ]
# 286 : 0 : pinfo->nTime = std::max((int64_t)0, addr.nTime - nTimePenalty);
# 287 : :
# 288 : : // add services
# 289 : 13 : pinfo->nServices = ServiceFlags(pinfo->nServices | addr.nServices);
# 290 : :
# 291 : : // do not update if no new information is present
# 292 [ - + ][ + - ]: 13 : if (!addr.nTime || (pinfo->nTime && addr.nTime <= pinfo->nTime))
# [ + - ]
# 293 : 13 : return false;
# 294 : :
# 295 : : // do not update if the entry was already in the "tried" table
# 296 [ # # ]: 0 : if (pinfo->fInTried)
# 297 : 0 : return false;
# 298 : :
# 299 : : // do not update if the max reference count is reached
# 300 [ # # ]: 0 : if (pinfo->nRefCount == ADDRMAN_NEW_BUCKETS_PER_ADDRESS)
# 301 : 0 : return false;
# 302 : :
# 303 : : // stochastic test: previous nRefCount == N: 2^N times harder to increase it
# 304 : 0 : int nFactor = 1;
# 305 [ # # ]: 0 : for (int n = 0; n < pinfo->nRefCount; n++)
# 306 : 0 : nFactor *= 2;
# 307 [ # # ][ # # ]: 0 : if (nFactor > 1 && (insecure_rand.randrange(nFactor) != 0))
# 308 : 0 : return false;
# 309 : 24227 : } else {
# 310 : 24227 : pinfo = Create(addr, source, &nId);
# 311 : 24227 : pinfo->nTime = std::max((int64_t)0, (int64_t)pinfo->nTime - nTimePenalty);
# 312 : 24227 : nNew++;
# 313 : 24227 : fNew = true;
# 314 : 24227 : }
# 315 : :
# 316 : 24240 : int nUBucket = pinfo->GetNewBucket(nKey, source, m_asmap);
# 317 : 24227 : int nUBucketPos = pinfo->GetBucketPosition(nKey, true, nUBucket);
# 318 [ + - ]: 24227 : if (vvNew[nUBucket][nUBucketPos] != nId) {
# 319 : 24227 : bool fInsert = vvNew[nUBucket][nUBucketPos] == -1;
# 320 [ + + ]: 24227 : if (!fInsert) {
# 321 : 1390 : CAddrInfo& infoExisting = mapInfo[vvNew[nUBucket][nUBucketPos]];
# 322 [ + + ][ - + ]: 1390 : if (infoExisting.IsTerrible() || (infoExisting.nRefCount > 1 && pinfo->nRefCount == 0)) {
# [ # # ]
# 323 : : // Overwrite the existing new table entry.
# 324 : 2 : fInsert = true;
# 325 : 2 : }
# 326 : 1390 : }
# 327 [ + + ]: 24227 : if (fInsert) {
# 328 : 22839 : ClearNew(nUBucket, nUBucketPos);
# 329 : 22839 : pinfo->nRefCount++;
# 330 : 22839 : vvNew[nUBucket][nUBucketPos] = nId;
# 331 : 22839 : } else {
# 332 [ + - ]: 1388 : if (pinfo->nRefCount == 0) {
# 333 : 1388 : Delete(nId);
# 334 : 1388 : }
# 335 : 1388 : }
# 336 : 24227 : }
# 337 : 24227 : return fNew;
# 338 : 24240 : }
# 339 : :
# 340 : : void CAddrMan::Attempt_(const CService& addr, bool fCountFailure, int64_t nTime)
# 341 : 546 : {
# 342 : 546 : CAddrInfo* pinfo = Find(addr);
# 343 : :
# 344 : : // if not found, bail out
# 345 [ + + ]: 546 : if (!pinfo)
# 346 : 362 : return;
# 347 : :
# 348 : 184 : CAddrInfo& info = *pinfo;
# 349 : :
# 350 : : // check whether we are talking about the exact same CService (including same port)
# 351 [ - + ]: 184 : if (info != addr)
# 352 : 0 : return;
# 353 : :
# 354 : : // update info
# 355 : 184 : info.nLastTry = nTime;
# 356 [ - + ][ # # ]: 184 : if (fCountFailure && info.nLastCountAttempt < nLastGood) {
# 357 : 0 : info.nLastCountAttempt = nTime;
# 358 : 0 : info.nAttempts++;
# 359 : 0 : }
# 360 : 184 : }
# 361 : :
# 362 : : CAddrInfo CAddrMan::Select_(bool newOnly)
# 363 : 31338 : {
# 364 [ + + ]: 31338 : if (size() == 0)
# 365 : 18632 : return CAddrInfo();
# 366 : :
# 367 [ + + ][ + + ]: 12706 : if (newOnly && nNew == 0)
# 368 : 2 : return CAddrInfo();
# 369 : :
# 370 : : // Use a 50% chance for choosing between tried and new table entries.
# 371 [ + + ]: 12704 : if (!newOnly &&
# 372 [ + + ][ + + ]: 12704 : (nTried > 0 && (nNew == 0 || insecure_rand.randbool() == 0))) {
# [ + + ]
# 373 : : // use a tried node
# 374 : 24 : double fChanceFactor = 1.0;
# 375 : 378 : while (1) {
# 376 : 378 : int nKBucket = insecure_rand.randrange(ADDRMAN_TRIED_BUCKET_COUNT);
# 377 : 378 : int nKBucketPos = insecure_rand.randrange(ADDRMAN_BUCKET_SIZE);
# 378 [ + + ]: 1575728 : while (vvTried[nKBucket][nKBucketPos] == -1) {
# 379 : 1575350 : nKBucket = (nKBucket + insecure_rand.randbits(ADDRMAN_TRIED_BUCKET_COUNT_LOG2)) % ADDRMAN_TRIED_BUCKET_COUNT;
# 380 : 1575350 : nKBucketPos = (nKBucketPos + insecure_rand.randbits(ADDRMAN_BUCKET_SIZE_LOG2)) % ADDRMAN_BUCKET_SIZE;
# 381 : 1575350 : }
# 382 : 378 : int nId = vvTried[nKBucket][nKBucketPos];
# 383 : 378 : assert(mapInfo.count(nId) == 1);
# 384 : 378 : CAddrInfo& info = mapInfo[nId];
# 385 [ + + ]: 378 : if (insecure_rand.randbits(30) < fChanceFactor * info.GetChance() * (1 << 30))
# 386 : 24 : return info;
# 387 : 354 : fChanceFactor *= 1.2;
# 388 : 354 : }
# 389 : 12680 : } else {
# 390 : : // use a new node
# 391 : 12680 : double fChanceFactor = 1.0;
# 392 : 12736 : while (1) {
# 393 : 12736 : int nUBucket = insecure_rand.randrange(ADDRMAN_NEW_BUCKET_COUNT);
# 394 : 12736 : int nUBucketPos = insecure_rand.randrange(ADDRMAN_BUCKET_SIZE);
# 395 [ + + ]: 229325778 : while (vvNew[nUBucket][nUBucketPos] == -1) {
# 396 : 229313042 : nUBucket = (nUBucket + insecure_rand.randbits(ADDRMAN_NEW_BUCKET_COUNT_LOG2)) % ADDRMAN_NEW_BUCKET_COUNT;
# 397 : 229313042 : nUBucketPos = (nUBucketPos + insecure_rand.randbits(ADDRMAN_BUCKET_SIZE_LOG2)) % ADDRMAN_BUCKET_SIZE;
# 398 : 229313042 : }
# 399 : 12736 : int nId = vvNew[nUBucket][nUBucketPos];
# 400 : 12736 : assert(mapInfo.count(nId) == 1);
# 401 : 12736 : CAddrInfo& info = mapInfo[nId];
# 402 [ + + ]: 12736 : if (insecure_rand.randbits(30) < fChanceFactor * info.GetChance() * (1 << 30))
# 403 : 12680 : return info;
# 404 : 56 : fChanceFactor *= 1.2;
# 405 : 56 : }
# 406 : 12680 : }
# 407 : 12704 : }
# 408 : :
# 409 : : #ifdef DEBUG_ADDRMAN
# 410 : : int CAddrMan::Check_()
# 411 : 157667 : {
# 412 : 157667 : std::set<int> setTried;
# 413 : 157667 : std::map<int, int> mapNew;
# 414 : :
# 415 [ - + ]: 157667 : if (vRandom.size() != (size_t)(nTried + nNew))
# 416 : 0 : return -7;
# 417 : :
# 418 [ + + ]: 285553633 : for (const auto& entry : mapInfo) {
# 419 : 285553633 : int n = entry.first;
# 420 : 285553633 : const CAddrInfo& info = entry.second;
# 421 [ + + ]: 285553633 : if (info.fInTried) {
# 422 [ - + ]: 1177484 : if (!info.nLastSuccess)
# 423 : 0 : return -1;
# 424 [ - + ]: 1177484 : if (info.nRefCount)
# 425 : 0 : return -2;
# 426 : 1177484 : setTried.insert(n);
# 427 : 284376149 : } else {
# 428 [ - + ][ - + ]: 284376149 : if (info.nRefCount < 0 || info.nRefCount > ADDRMAN_NEW_BUCKETS_PER_ADDRESS)
# 429 : 284376149 : return -3;
# 430 [ - + ]: 284376149 : if (!info.nRefCount)
# 431 : 0 : return -4;
# 432 : 284376149 : mapNew[n] = info.nRefCount;
# 433 : 284376149 : }
# 434 [ - + ]: 285553633 : if (mapAddr[info] != n)
# 435 : 0 : return -5;
# 436 [ - + ][ - + ]: 285553633 : if (info.nRandomPos < 0 || (size_t)info.nRandomPos >= vRandom.size() || vRandom[info.nRandomPos] != n)
# [ - + ]
# 437 : 0 : return -14;
# 438 [ - + ]: 285553633 : if (info.nLastTry < 0)
# 439 : 0 : return -6;
# 440 [ - + ]: 285553633 : if (info.nLastSuccess < 0)
# 441 : 0 : return -8;
# 442 : 285553633 : }
# 443 : :
# 444 [ - + ]: 157667 : if (setTried.size() != (size_t)nTried)
# 445 : 0 : return -9;
# 446 [ - + ]: 157667 : if (mapNew.size() != (size_t)nNew)
# 447 : 0 : return -10;
# 448 : :
# 449 [ + + ]: 40520419 : for (int n = 0; n < ADDRMAN_TRIED_BUCKET_COUNT; n++) {
# 450 [ + + ]: 2623578880 : for (int i = 0; i < ADDRMAN_BUCKET_SIZE; i++) {
# 451 [ + + ]: 2583216128 : if (vvTried[n][i] != -1) {
# 452 [ - + ]: 1177484 : if (!setTried.count(vvTried[n][i]))
# 453 : 0 : return -11;
# 454 [ - + ]: 1177484 : if (mapInfo[vvTried[n][i]].GetTriedBucket(nKey, m_asmap) != n)
# 455 : 0 : return -17;
# 456 [ - + ]: 1177484 : if (mapInfo[vvTried[n][i]].GetBucketPosition(nKey, false, n) != i)
# 457 : 0 : return -18;
# 458 : 1177484 : setTried.erase(vvTried[n][i]);
# 459 : 1177484 : }
# 460 : 2583216128 : }
# 461 : 40362752 : }
# 462 : :
# 463 [ + + ]: 161608675 : for (int n = 0; n < ADDRMAN_NEW_BUCKET_COUNT; n++) {
# 464 [ + + ]:10494315520 : for (int i = 0; i < ADDRMAN_BUCKET_SIZE; i++) {
# 465 [ + + ]:10332864512 : if (vvNew[n][i] != -1) {
# 466 [ - + ]: 284376149 : if (!mapNew.count(vvNew[n][i]))
# 467 : 0 : return -12;
# 468 [ - + ]: 284376149 : if (mapInfo[vvNew[n][i]].GetBucketPosition(nKey, true, n) != i)
# 469 : 0 : return -19;
# 470 [ + - ]: 284376149 : if (--mapNew[vvNew[n][i]] == 0)
# 471 : 284376149 : mapNew.erase(vvNew[n][i]);
# 472 : 284376149 : }
# 473 :10332864512 : }
# 474 : 161451008 : }
# 475 : :
# 476 [ - + ]: 157667 : if (setTried.size())
# 477 : 0 : return -13;
# 478 [ - + ]: 157667 : if (mapNew.size())
# 479 : 0 : return -15;
# 480 [ + + ]: 157667 : if (nKey.IsNull())
# 481 : 11474 : return -16;
# 482 : :
# 483 : 146193 : return 0;
# 484 : 146193 : }
# 485 : : #endif
# 486 : :
# 487 : : void CAddrMan::GetAddr_(std::vector<CAddress>& vAddr, size_t max_addresses, size_t max_pct, std::optional<Network> network)
# 488 : 257 : {
# 489 : 257 : size_t nNodes = vRandom.size();
# 490 [ + + ]: 257 : if (max_pct != 0) {
# 491 : 236 : nNodes = max_pct * nNodes / 100;
# 492 : 236 : }
# 493 [ + + ]: 257 : if (max_addresses != 0) {
# 494 : 239 : nNodes = std::min(nNodes, max_addresses);
# 495 : 239 : }
# 496 : :
# 497 : : // gather a list of random nodes, skipping those of low quality
# 498 : 257 : const int64_t now{GetAdjustedTime()};
# 499 [ + + ]: 47481 : for (unsigned int n = 0; n < vRandom.size(); n++) {
# 500 [ + + ]: 47236 : if (vAddr.size() >= nNodes)
# 501 : 12 : break;
# 502 : :
# 503 : 47224 : int nRndPos = insecure_rand.randrange(vRandom.size() - n) + n;
# 504 : 47224 : SwapRandom(n, nRndPos);
# 505 : 47224 : assert(mapInfo.count(vRandom[n]) == 1);
# 506 : :
# 507 : 47224 : const CAddrInfo& ai = mapInfo[vRandom[n]];
# 508 : :
# 509 : : // Filter by network (optional)
# 510 [ + + ][ + + ]: 47224 : if (network != std::nullopt && ai.GetNetClass() != network) continue;
# [ + + ]
# 511 : :
# 512 : : // Filter for quality
# 513 [ - + ]: 20790 : if (ai.IsTerrible(now)) continue;
# 514 : :
# 515 : 20790 : vAddr.push_back(ai);
# 516 : 20790 : }
# 517 : 257 : }
# 518 : :
# 519 : : void CAddrMan::Connected_(const CService& addr, int64_t nTime)
# 520 : 350 : {
# 521 : 350 : CAddrInfo* pinfo = Find(addr);
# 522 : :
# 523 : : // if not found, bail out
# 524 [ + - ]: 350 : if (!pinfo)
# 525 : 350 : return;
# 526 : :
# 527 : 0 : CAddrInfo& info = *pinfo;
# 528 : :
# 529 : : // check whether we are talking about the exact same CService (including same port)
# 530 [ # # ]: 0 : if (info != addr)
# 531 : 0 : return;
# 532 : :
# 533 : : // update info
# 534 : 0 : int64_t nUpdateInterval = 20 * 60;
# 535 [ # # ]: 0 : if (nTime - info.nTime > nUpdateInterval)
# 536 : 0 : info.nTime = nTime;
# 537 : 0 : }
# 538 : :
# 539 : : void CAddrMan::SetServices_(const CService& addr, ServiceFlags nServices)
# 540 : 346 : {
# 541 : 346 : CAddrInfo* pinfo = Find(addr);
# 542 : :
# 543 : : // if not found, bail out
# 544 [ + - ]: 346 : if (!pinfo)
# 545 : 346 : return;
# 546 : :
# 547 : 0 : CAddrInfo& info = *pinfo;
# 548 : :
# 549 : : // check whether we are talking about the exact same CService (including same port)
# 550 [ # # ]: 0 : if (info != addr)
# 551 : 0 : return;
# 552 : :
# 553 : : // update info
# 554 : 0 : info.nServices = nServices;
# 555 : 0 : }
# 556 : :
# 557 : : void CAddrMan::ResolveCollisions_()
# 558 : 18856 : {
# 559 [ + + ]: 18866 : for (std::set<int>::iterator it = m_tried_collisions.begin(); it != m_tried_collisions.end();) {
# 560 : 10 : int id_new = *it;
# 561 : :
# 562 : 10 : bool erase_collision = false;
# 563 : :
# 564 : : // If id_new not found in mapInfo remove it from m_tried_collisions
# 565 [ - + ]: 10 : if (mapInfo.count(id_new) != 1) {
# 566 : 0 : erase_collision = true;
# 567 : 10 : } else {
# 568 : 10 : CAddrInfo& info_new = mapInfo[id_new];
# 569 : :
# 570 : : // Which tried bucket to move the entry to.
# 571 : 10 : int tried_bucket = info_new.GetTriedBucket(nKey, m_asmap);
# 572 : 10 : int tried_bucket_pos = info_new.GetBucketPosition(nKey, false, tried_bucket);
# 573 [ - + ]: 10 : if (!info_new.IsValid()) { // id_new may no longer map to a valid address
# 574 : 0 : erase_collision = true;
# 575 [ + - ]: 10 : } else if (vvTried[tried_bucket][tried_bucket_pos] != -1) { // The position in the tried bucket is not empty
# 576 : :
# 577 : : // Get the to-be-evicted address that is being tested
# 578 : 10 : int id_old = vvTried[tried_bucket][tried_bucket_pos];
# 579 : 10 : CAddrInfo& info_old = mapInfo[id_old];
# 580 : :
# 581 : : // Has successfully connected in last X hours
# 582 [ + + ]: 10 : if (GetAdjustedTime() - info_old.nLastSuccess < ADDRMAN_REPLACEMENT_HOURS*(60*60)) {
# 583 : 8 : erase_collision = true;
# 584 [ + - ]: 8 : } else if (GetAdjustedTime() - info_old.nLastTry < ADDRMAN_REPLACEMENT_HOURS*(60*60)) { // attempted to connect and failed in last X hours
# 585 : :
# 586 : : // Give address at least 60 seconds to successfully connect
# 587 [ + - ]: 2 : if (GetAdjustedTime() - info_old.nLastTry > 60) {
# 588 [ + - ]: 2 : LogPrint(BCLog::ADDRMAN, "Replacing %s with %s in tried table\n", info_old.ToString(), info_new.ToString());
# 589 : :
# 590 : : // Replaces an existing address already in the tried table with the new address
# 591 : 2 : Good_(info_new, false, GetAdjustedTime());
# 592 : 2 : erase_collision = true;
# 593 : 2 : }
# 594 [ # # ]: 2 : } else if (GetAdjustedTime() - info_new.nLastSuccess > ADDRMAN_TEST_WINDOW) {
# 595 : : // If the collision hasn't resolved in some reasonable amount of time,
# 596 : : // just evict the old entry -- we must not be able to
# 597 : : // connect to it for some reason.
# 598 [ # # ]: 0 : LogPrint(BCLog::ADDRMAN, "Unable to test; replacing %s with %s in tried table anyway\n", info_old.ToString(), info_new.ToString());
# 599 : 0 : Good_(info_new, false, GetAdjustedTime());
# 600 : 0 : erase_collision = true;
# 601 : 0 : }
# 602 : 10 : } else { // Collision is not actually a collision anymore
# 603 : 0 : Good_(info_new, false, GetAdjustedTime());
# 604 : 0 : erase_collision = true;
# 605 : 0 : }
# 606 : 10 : }
# 607 : :
# 608 [ + - ]: 10 : if (erase_collision) {
# 609 : 10 : m_tried_collisions.erase(it++);
# 610 : 10 : } else {
# 611 : 0 : it++;
# 612 : 0 : }
# 613 : 10 : }
# 614 : 18856 : }
# 615 : :
# 616 : : CAddrInfo CAddrMan::SelectTriedCollision_()
# 617 : 218 : {
# 618 [ + + ]: 218 : if (m_tried_collisions.size() == 0) return CAddrInfo();
# 619 : :
# 620 : 10 : std::set<int>::iterator it = m_tried_collisions.begin();
# 621 : :
# 622 : : // Selects a random element from m_tried_collisions
# 623 : 10 : std::advance(it, insecure_rand.randrange(m_tried_collisions.size()));
# 624 : 10 : int id_new = *it;
# 625 : :
# 626 : : // If id_new not found in mapInfo remove it from m_tried_collisions
# 627 [ - + ]: 10 : if (mapInfo.count(id_new) != 1) {
# 628 : 0 : m_tried_collisions.erase(it);
# 629 : 0 : return CAddrInfo();
# 630 : 0 : }
# 631 : :
# 632 : 10 : const CAddrInfo& newInfo = mapInfo[id_new];
# 633 : :
# 634 : : // which tried bucket to move the entry to
# 635 : 10 : int tried_bucket = newInfo.GetTriedBucket(nKey, m_asmap);
# 636 : 10 : int tried_bucket_pos = newInfo.GetBucketPosition(nKey, false, tried_bucket);
# 637 : :
# 638 : 10 : int id_old = vvTried[tried_bucket][tried_bucket_pos];
# 639 : :
# 640 : 10 : return mapInfo[id_old];
# 641 : 10 : }
# 642 : :
# 643 : : std::vector<bool> CAddrMan::DecodeAsmap(fs::path path)
# 644 : 5 : {
# 645 : 5 : std::vector<bool> bits;
# 646 : 5 : FILE *filestr = fsbridge::fopen(path, "rb");
# 647 : 5 : CAutoFile file(filestr, SER_DISK, CLIENT_VERSION);
# 648 [ - + ]: 5 : if (file.IsNull()) {
# 649 : 0 : LogPrintf("Failed to open asmap file from disk\n");
# 650 : 0 : return bits;
# 651 : 0 : }
# 652 : 5 : fseek(filestr, 0, SEEK_END);
# 653 : 5 : int length = ftell(filestr);
# 654 : 5 : LogPrintf("Opened asmap file %s (%d bytes) from disk\n", path, length);
# 655 : 5 : fseek(filestr, 0, SEEK_SET);
# 656 : 5 : char cur_byte;
# 657 [ + + ]: 241 : for (int i = 0; i < length; ++i) {
# 658 : 236 : file >> cur_byte;
# 659 [ + + ]: 2124 : for (int bit = 0; bit < 8; ++bit) {
# 660 : 1888 : bits.push_back((cur_byte >> bit) & 1);
# 661 : 1888 : }
# 662 : 236 : }
# 663 [ + + ]: 5 : if (!SanityCheckASMap(bits)) {
# 664 : 1 : LogPrintf("Sanity check of asmap file %s failed\n", path);
# 665 : 1 : return {};
# 666 : 1 : }
# 667 : 4 : return bits;
# 668 : 4 : }
# 669 : :
# 670 : : void CAddrMan::ResetI2PPorts()
# 671 : 261 : {
# 672 [ + + ]: 267525 : for (int bucket = 0; bucket < ADDRMAN_NEW_BUCKET_COUNT; ++bucket) {
# 673 [ + + ]: 17372160 : for (int i = 0; i < ADDRMAN_BUCKET_SIZE; ++i) {
# 674 : 17104896 : const auto id = vvNew[bucket][i];
# 675 [ + + ]: 17104896 : if (id == -1) {
# 676 : 17104108 : continue;
# 677 : 17104108 : }
# 678 : 788 : auto& addr_info = mapInfo[id];
# 679 [ + + ][ + + ]: 788 : if (!addr_info.IsI2P() || addr_info.GetPort() == i2p::sam::PORT_SAM31) {
# 680 : 782 : continue;
# 681 : 782 : }
# 682 : :
# 683 : 6 : auto addr_info_newport = addr_info;
# 684 : : // The below changes addr_info_newport.GetKey() which is used in finding
# 685 : : // a bucket and a position within that bucket. So a re-bucketing may be necessary.
# 686 : 6 : addr_info_newport.port = i2p::sam::PORT_SAM31;
# 687 : :
# 688 : : // Reposition entries of vvNew within the same bucket because we don't know the source
# 689 : : // address which led to the decision to store the entry in vvNew[bucket] so we can't
# 690 : : // re-evaluate that decision, but even if we could, CAddrInfo::GetNewBucket() does not
# 691 : : // use CAddrInfo::GetKey() so it would end up in the same bucket as before the port
# 692 : : // change.
# 693 : 6 : const auto i_target = addr_info_newport.GetBucketPosition(nKey, true, bucket);
# 694 : :
# 695 [ + + ]: 6 : if (i_target == i) { // No need to re-position.
# 696 : 2 : addr_info = addr_info_newport;
# 697 : 2 : continue;
# 698 : 2 : }
# 699 : :
# 700 : : // Reposition from i to i_target or delete if the target position is occupied.
# 701 : :
# 702 [ + + ]: 4 : if (vvNew[bucket][i_target] == -1) {
# 703 : 2 : vvNew[bucket][i_target] = id;
# 704 : 2 : vvNew[bucket][i] = -1;
# 705 : 2 : addr_info = addr_info_newport;
# 706 : 2 : } else {
# 707 : 2 : ClearNew(bucket, i);
# 708 : 2 : }
# 709 : 4 : }
# 710 : 267264 : }
# 711 : :
# 712 [ + + ]: 67077 : for (int bucket = 0; bucket < ADDRMAN_TRIED_BUCKET_COUNT; ++bucket) {
# 713 [ + + ]: 4343040 : for (int i = 0; i < ADDRMAN_BUCKET_SIZE; ++i) {
# 714 : 4276224 : const auto id = vvTried[bucket][i];
# 715 [ + + ]: 4276224 : if (id == -1) {
# 716 : 4276214 : continue;
# 717 : 4276214 : }
# 718 : 10 : auto& addr_info = mapInfo[id];
# 719 [ + + ][ + + ]: 10 : if (!addr_info.IsI2P() || addr_info.GetPort() == i2p::sam::PORT_SAM31) {
# 720 : 4 : continue;
# 721 : 4 : }
# 722 : :
# 723 : 6 : auto addr_info_newport = addr_info;
# 724 : : // The below changes addr_info_newport.GetKey() which is used in finding
# 725 : : // a bucket and a position within that bucket. So a re-bucketing may be necessary.
# 726 : 6 : addr_info_newport.port = i2p::sam::PORT_SAM31;
# 727 : :
# 728 : 6 : const auto bucket_target = addr_info_newport.GetTriedBucket(nKey, m_asmap);
# 729 : 6 : const auto i_target = addr_info_newport.GetBucketPosition(nKey, false, bucket_target);
# 730 : :
# 731 [ + + ][ + - ]: 6 : if (bucket_target == bucket && i_target == i) { // No need to re-position.
# 732 : 2 : addr_info = addr_info_newport;
# 733 : 2 : continue;
# 734 : 2 : }
# 735 : :
# 736 : : // Reposition from (bucket, i) to (bucket_target, i_target) or delete if the target
# 737 : : // position is occupied.
# 738 : :
# 739 [ + + ]: 4 : if (vvTried[bucket_target][i_target] == -1) {
# 740 : 2 : vvTried[bucket_target][i_target] = id;
# 741 : 2 : vvTried[bucket][i] = -1;
# 742 : 2 : addr_info = addr_info_newport;
# 743 : 2 : } else {
# 744 : 2 : vvTried[bucket][i] = -1;
# 745 : 2 : SwapRandom(addr_info.nRandomPos, vRandom.size() - 1);
# 746 : 2 : vRandom.pop_back();
# 747 : 2 : mapAddr.erase(addr_info);
# 748 : 2 : mapInfo.erase(id);
# 749 : 2 : --nTried;
# 750 : 2 : }
# 751 : 4 : }
# 752 : 66816 : }
# 753 : 261 : }
|