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