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