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