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