Bitcoin Core  24.99.0
P2P Digital Currency
addrman.cpp
Go to the documentation of this file.
1 // Copyright (c) 2012 Pieter Wuille
2 // Copyright (c) 2012-2022 The Bitcoin Core developers
3 // Distributed under the MIT software license, see the accompanying
4 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
5 
6 #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  m_network_counts[info.GetNetwork()].n_new++;
295  }
296  nIdCount = nNew;
297 
298  // Deserialize entries from the tried table.
299  int nLost = 0;
300  for (int n = 0; n < nTried; n++) {
301  AddrInfo info;
302  s >> info;
303  int nKBucket = info.GetTriedBucket(nKey, m_netgroupman);
304  int nKBucketPos = info.GetBucketPosition(nKey, false, nKBucket);
305  if (info.IsValid()
306  && vvTried[nKBucket][nKBucketPos] == -1) {
307  info.nRandomPos = vRandom.size();
308  info.fInTried = true;
309  vRandom.push_back(nIdCount);
310  mapInfo[nIdCount] = info;
311  mapAddr[info] = nIdCount;
312  vvTried[nKBucket][nKBucketPos] = nIdCount;
313  nIdCount++;
314  m_network_counts[info.GetNetwork()].n_tried++;
315  } else {
316  nLost++;
317  }
318  }
319  nTried -= nLost;
320 
321  // Store positions in the new table buckets to apply later (if possible).
322  // An entry may appear in up to ADDRMAN_NEW_BUCKETS_PER_ADDRESS buckets,
323  // so we store all bucket-entry_index pairs to iterate through later.
324  std::vector<std::pair<int, int>> bucket_entries;
325 
326  for (int bucket = 0; bucket < nUBuckets; ++bucket) {
327  int num_entries{0};
328  s >> num_entries;
329  for (int n = 0; n < num_entries; ++n) {
330  int entry_index{0};
331  s >> entry_index;
332  if (entry_index >= 0 && entry_index < nNew) {
333  bucket_entries.emplace_back(bucket, entry_index);
334  }
335  }
336  }
337 
338  // If the bucket count and asmap checksum haven't changed, then attempt
339  // to restore the entries to the buckets/positions they were in before
340  // serialization.
341  uint256 supplied_asmap_checksum{m_netgroupman.GetAsmapChecksum()};
342  uint256 serialized_asmap_checksum;
343  if (format >= Format::V2_ASMAP) {
344  s >> serialized_asmap_checksum;
345  }
346  const bool restore_bucketing{nUBuckets == ADDRMAN_NEW_BUCKET_COUNT &&
347  serialized_asmap_checksum == supplied_asmap_checksum};
348 
349  if (!restore_bucketing) {
350  LogPrint(BCLog::ADDRMAN, "Bucketing method was updated, re-bucketing addrman entries from disk\n");
351  }
352 
353  for (auto bucket_entry : bucket_entries) {
354  int bucket{bucket_entry.first};
355  const int entry_index{bucket_entry.second};
356  AddrInfo& info = mapInfo[entry_index];
357 
358  // Don't store the entry in the new bucket if it's not a valid address for our addrman
359  if (!info.IsValid()) continue;
360 
361  // The entry shouldn't appear in more than
362  // ADDRMAN_NEW_BUCKETS_PER_ADDRESS. If it has already, just skip
363  // this bucket_entry.
364  if (info.nRefCount >= ADDRMAN_NEW_BUCKETS_PER_ADDRESS) continue;
365 
366  int bucket_position = info.GetBucketPosition(nKey, true, bucket);
367  if (restore_bucketing && vvNew[bucket][bucket_position] == -1) {
368  // Bucketing has not changed, using existing bucket positions for the new table
369  vvNew[bucket][bucket_position] = entry_index;
370  ++info.nRefCount;
371  } else {
372  // In case the new table data cannot be used (bucket count wrong or new asmap),
373  // try to give them a reference based on their primary source address.
374  bucket = info.GetNewBucket(nKey, m_netgroupman);
375  bucket_position = info.GetBucketPosition(nKey, true, bucket);
376  if (vvNew[bucket][bucket_position] == -1) {
377  vvNew[bucket][bucket_position] = entry_index;
378  ++info.nRefCount;
379  }
380  }
381  }
382 
383  // Prune new entries with refcount 0 (as a result of collisions or invalid address).
384  int nLostUnk = 0;
385  for (auto it = mapInfo.cbegin(); it != mapInfo.cend(); ) {
386  if (it->second.fInTried == false && it->second.nRefCount == 0) {
387  const auto itCopy = it++;
388  Delete(itCopy->first);
389  ++nLostUnk;
390  } else {
391  ++it;
392  }
393  }
394  if (nLost + nLostUnk > 0) {
395  LogPrint(BCLog::ADDRMAN, "addrman lost %i new and %i tried addresses due to collisions or invalid addresses\n", nLostUnk, nLost);
396  }
397 
398  const int check_code{CheckAddrman()};
399  if (check_code != 0) {
400  throw std::ios_base::failure(strprintf(
401  "Corrupt data. Consistency check failed with code %s",
402  check_code));
403  }
404 }
405 
406 AddrInfo* AddrManImpl::Find(const CService& addr, int* pnId)
407 {
409 
410  const auto it = mapAddr.find(addr);
411  if (it == mapAddr.end())
412  return nullptr;
413  if (pnId)
414  *pnId = (*it).second;
415  const auto it2 = mapInfo.find((*it).second);
416  if (it2 != mapInfo.end())
417  return &(*it2).second;
418  return nullptr;
419 }
420 
421 AddrInfo* AddrManImpl::Create(const CAddress& addr, const CNetAddr& addrSource, int* pnId)
422 {
424 
425  int nId = nIdCount++;
426  mapInfo[nId] = AddrInfo(addr, addrSource);
427  mapAddr[addr] = nId;
428  mapInfo[nId].nRandomPos = vRandom.size();
429  vRandom.push_back(nId);
430  nNew++;
431  m_network_counts[addr.GetNetwork()].n_new++;
432  if (pnId)
433  *pnId = nId;
434  return &mapInfo[nId];
435 }
436 
437 void AddrManImpl::SwapRandom(unsigned int nRndPos1, unsigned int nRndPos2) const
438 {
440 
441  if (nRndPos1 == nRndPos2)
442  return;
443 
444  assert(nRndPos1 < vRandom.size() && nRndPos2 < vRandom.size());
445 
446  int nId1 = vRandom[nRndPos1];
447  int nId2 = vRandom[nRndPos2];
448 
449  const auto it_1{mapInfo.find(nId1)};
450  const auto it_2{mapInfo.find(nId2)};
451  assert(it_1 != mapInfo.end());
452  assert(it_2 != mapInfo.end());
453 
454  it_1->second.nRandomPos = nRndPos2;
455  it_2->second.nRandomPos = nRndPos1;
456 
457  vRandom[nRndPos1] = nId2;
458  vRandom[nRndPos2] = nId1;
459 }
460 
461 void AddrManImpl::Delete(int nId)
462 {
464 
465  assert(mapInfo.count(nId) != 0);
466  AddrInfo& info = mapInfo[nId];
467  assert(!info.fInTried);
468  assert(info.nRefCount == 0);
469 
470  SwapRandom(info.nRandomPos, vRandom.size() - 1);
471  m_network_counts[info.GetNetwork()].n_new--;
472  vRandom.pop_back();
473  mapAddr.erase(info);
474  mapInfo.erase(nId);
475  nNew--;
476 }
477 
478 void AddrManImpl::ClearNew(int nUBucket, int nUBucketPos)
479 {
481 
482  // if there is an entry in the specified bucket, delete it.
483  if (vvNew[nUBucket][nUBucketPos] != -1) {
484  int nIdDelete = vvNew[nUBucket][nUBucketPos];
485  AddrInfo& infoDelete = mapInfo[nIdDelete];
486  assert(infoDelete.nRefCount > 0);
487  infoDelete.nRefCount--;
488  vvNew[nUBucket][nUBucketPos] = -1;
489  LogPrint(BCLog::ADDRMAN, "Removed %s from new[%i][%i]\n", infoDelete.ToStringAddrPort(), nUBucket, nUBucketPos);
490  if (infoDelete.nRefCount == 0) {
491  Delete(nIdDelete);
492  }
493  }
494 }
495 
496 void AddrManImpl::MakeTried(AddrInfo& info, int nId)
497 {
499 
500  // remove the entry from all new buckets
501  const int start_bucket{info.GetNewBucket(nKey, m_netgroupman)};
502  for (int n = 0; n < ADDRMAN_NEW_BUCKET_COUNT; ++n) {
503  const int bucket{(start_bucket + n) % ADDRMAN_NEW_BUCKET_COUNT};
504  const int pos{info.GetBucketPosition(nKey, true, bucket)};
505  if (vvNew[bucket][pos] == nId) {
506  vvNew[bucket][pos] = -1;
507  info.nRefCount--;
508  if (info.nRefCount == 0) break;
509  }
510  }
511  nNew--;
512  m_network_counts[info.GetNetwork()].n_new--;
513 
514  assert(info.nRefCount == 0);
515 
516  // which tried bucket to move the entry to
517  int nKBucket = info.GetTriedBucket(nKey, m_netgroupman);
518  int nKBucketPos = info.GetBucketPosition(nKey, false, nKBucket);
519 
520  // first make space to add it (the existing tried entry there is moved to new, deleting whatever is there).
521  if (vvTried[nKBucket][nKBucketPos] != -1) {
522  // find an item to evict
523  int nIdEvict = vvTried[nKBucket][nKBucketPos];
524  assert(mapInfo.count(nIdEvict) == 1);
525  AddrInfo& infoOld = mapInfo[nIdEvict];
526 
527  // Remove the to-be-evicted item from the tried set.
528  infoOld.fInTried = false;
529  vvTried[nKBucket][nKBucketPos] = -1;
530  nTried--;
531  m_network_counts[infoOld.GetNetwork()].n_tried--;
532 
533  // find which new bucket it belongs to
534  int nUBucket = infoOld.GetNewBucket(nKey, m_netgroupman);
535  int nUBucketPos = infoOld.GetBucketPosition(nKey, true, nUBucket);
536  ClearNew(nUBucket, nUBucketPos);
537  assert(vvNew[nUBucket][nUBucketPos] == -1);
538 
539  // Enter it into the new set again.
540  infoOld.nRefCount = 1;
541  vvNew[nUBucket][nUBucketPos] = nIdEvict;
542  nNew++;
543  m_network_counts[infoOld.GetNetwork()].n_new++;
544  LogPrint(BCLog::ADDRMAN, "Moved %s from tried[%i][%i] to new[%i][%i] to make space\n",
545  infoOld.ToStringAddrPort(), nKBucket, nKBucketPos, nUBucket, nUBucketPos);
546  }
547  assert(vvTried[nKBucket][nKBucketPos] == -1);
548 
549  vvTried[nKBucket][nKBucketPos] = nId;
550  nTried++;
551  info.fInTried = true;
552  m_network_counts[info.GetNetwork()].n_tried++;
553 }
554 
555 bool AddrManImpl::AddSingle(const CAddress& addr, const CNetAddr& source, std::chrono::seconds time_penalty)
556 {
558 
559  if (!addr.IsRoutable())
560  return false;
561 
562  int nId;
563  AddrInfo* pinfo = Find(addr, &nId);
564 
565  // Do not set a penalty for a source's self-announcement
566  if (addr == source) {
567  time_penalty = 0s;
568  }
569 
570  if (pinfo) {
571  // periodically update nTime
572  const bool currently_online{NodeClock::now() - addr.nTime < 24h};
573  const auto update_interval{currently_online ? 1h : 24h};
574  if (pinfo->nTime < addr.nTime - update_interval - time_penalty) {
575  pinfo->nTime = std::max(NodeSeconds{0s}, addr.nTime - time_penalty);
576  }
577 
578  // add services
579  pinfo->nServices = ServiceFlags(pinfo->nServices | addr.nServices);
580 
581  // do not update if no new information is present
582  if (addr.nTime <= pinfo->nTime) {
583  return false;
584  }
585 
586  // do not update if the entry was already in the "tried" table
587  if (pinfo->fInTried)
588  return false;
589 
590  // do not update if the max reference count is reached
592  return false;
593 
594  // stochastic test: previous nRefCount == N: 2^N times harder to increase it
595  int nFactor = 1;
596  for (int n = 0; n < pinfo->nRefCount; n++)
597  nFactor *= 2;
598  if (nFactor > 1 && (insecure_rand.randrange(nFactor) != 0))
599  return false;
600  } else {
601  pinfo = Create(addr, source, &nId);
602  pinfo->nTime = std::max(NodeSeconds{0s}, pinfo->nTime - time_penalty);
603  }
604 
605  int nUBucket = pinfo->GetNewBucket(nKey, source, m_netgroupman);
606  int nUBucketPos = pinfo->GetBucketPosition(nKey, true, nUBucket);
607  bool fInsert = vvNew[nUBucket][nUBucketPos] == -1;
608  if (vvNew[nUBucket][nUBucketPos] != nId) {
609  if (!fInsert) {
610  AddrInfo& infoExisting = mapInfo[vvNew[nUBucket][nUBucketPos]];
611  if (infoExisting.IsTerrible() || (infoExisting.nRefCount > 1 && pinfo->nRefCount == 0)) {
612  // Overwrite the existing new table entry.
613  fInsert = true;
614  }
615  }
616  if (fInsert) {
617  ClearNew(nUBucket, nUBucketPos);
618  pinfo->nRefCount++;
619  vvNew[nUBucket][nUBucketPos] = nId;
620  LogPrint(BCLog::ADDRMAN, "Added %s mapped to AS%i to new[%i][%i]\n",
621  addr.ToStringAddrPort(), m_netgroupman.GetMappedAS(addr), nUBucket, nUBucketPos);
622  } else {
623  if (pinfo->nRefCount == 0) {
624  Delete(nId);
625  }
626  }
627  }
628  return fInsert;
629 }
630 
631 bool AddrManImpl::Good_(const CService& addr, bool test_before_evict, NodeSeconds time)
632 {
634 
635  int nId;
636 
637  m_last_good = time;
638 
639  AddrInfo* pinfo = Find(addr, &nId);
640 
641  // if not found, bail out
642  if (!pinfo) return false;
643 
644  AddrInfo& info = *pinfo;
645 
646  // update info
647  info.m_last_success = time;
648  info.m_last_try = time;
649  info.nAttempts = 0;
650  // nTime is not updated here, to avoid leaking information about
651  // currently-connected peers.
652 
653  // if it is already in the tried set, don't do anything else
654  if (info.fInTried) return false;
655 
656  // if it is not in new, something bad happened
657  if (!Assume(info.nRefCount > 0)) return false;
658 
659 
660  // which tried bucket to move the entry to
661  int tried_bucket = info.GetTriedBucket(nKey, m_netgroupman);
662  int tried_bucket_pos = info.GetBucketPosition(nKey, false, tried_bucket);
663 
664  // Will moving this address into tried evict another entry?
665  if (test_before_evict && (vvTried[tried_bucket][tried_bucket_pos] != -1)) {
667  m_tried_collisions.insert(nId);
668  }
669  // Output the entry we'd be colliding with, for debugging purposes
670  auto colliding_entry = mapInfo.find(vvTried[tried_bucket][tried_bucket_pos]);
671  LogPrint(BCLog::ADDRMAN, "Collision with %s while attempting to move %s to tried table. Collisions=%d\n",
672  colliding_entry != mapInfo.end() ? colliding_entry->second.ToStringAddrPort() : "",
673  addr.ToStringAddrPort(),
674  m_tried_collisions.size());
675  return false;
676  } else {
677  // move nId to the tried tables
678  MakeTried(info, nId);
679  LogPrint(BCLog::ADDRMAN, "Moved %s mapped to AS%i to tried[%i][%i]\n",
680  addr.ToStringAddrPort(), m_netgroupman.GetMappedAS(addr), tried_bucket, tried_bucket_pos);
681  return true;
682  }
683 }
684 
685 bool AddrManImpl::Add_(const std::vector<CAddress>& vAddr, const CNetAddr& source, std::chrono::seconds time_penalty)
686 {
687  int added{0};
688  for (std::vector<CAddress>::const_iterator it = vAddr.begin(); it != vAddr.end(); it++) {
689  added += AddSingle(*it, source, time_penalty) ? 1 : 0;
690  }
691  if (added > 0) {
692  LogPrint(BCLog::ADDRMAN, "Added %i addresses (of %i) from %s: %i tried, %i new\n", added, vAddr.size(), source.ToStringAddr(), nTried, nNew);
693  }
694  return added > 0;
695 }
696 
697 void AddrManImpl::Attempt_(const CService& addr, bool fCountFailure, NodeSeconds time)
698 {
700 
701  AddrInfo* pinfo = Find(addr);
702 
703  // if not found, bail out
704  if (!pinfo)
705  return;
706 
707  AddrInfo& info = *pinfo;
708 
709  // update info
710  info.m_last_try = time;
711  if (fCountFailure && info.m_last_count_attempt < m_last_good) {
712  info.m_last_count_attempt = time;
713  info.nAttempts++;
714  }
715 }
716 
717 std::pair<CAddress, NodeSeconds> AddrManImpl::Select_(bool newOnly) const
718 {
720 
721  if (vRandom.empty()) return {};
722 
723  if (newOnly && nNew == 0) return {};
724 
725  // Use a 50% chance for choosing between tried and new table entries.
726  if (!newOnly &&
727  (nTried > 0 && (nNew == 0 || insecure_rand.randbool() == 0))) {
728  // use a tried node
729  double fChanceFactor = 1.0;
730  while (1) {
731  // Pick a tried bucket, and an initial position in that bucket.
732  int nKBucket = insecure_rand.randrange(ADDRMAN_TRIED_BUCKET_COUNT);
733  int nKBucketPos = insecure_rand.randrange(ADDRMAN_BUCKET_SIZE);
734  // Iterate over the positions of that bucket, starting at the initial one,
735  // and looping around.
736  int i;
737  for (i = 0; i < ADDRMAN_BUCKET_SIZE; ++i) {
738  if (vvTried[nKBucket][(nKBucketPos + i) % ADDRMAN_BUCKET_SIZE] != -1) break;
739  }
740  // If the bucket is entirely empty, start over with a (likely) different one.
741  if (i == ADDRMAN_BUCKET_SIZE) continue;
742  // Find the entry to return.
743  int nId = vvTried[nKBucket][(nKBucketPos + i) % ADDRMAN_BUCKET_SIZE];
744  const auto it_found{mapInfo.find(nId)};
745  assert(it_found != mapInfo.end());
746  const AddrInfo& info{it_found->second};
747  // With probability GetChance() * fChanceFactor, return the entry.
748  if (insecure_rand.randbits(30) < fChanceFactor * info.GetChance() * (1 << 30)) {
749  LogPrint(BCLog::ADDRMAN, "Selected %s from tried\n", info.ToStringAddrPort());
750  return {info, info.m_last_try};
751  }
752  // Otherwise start over with a (likely) different bucket, and increased chance factor.
753  fChanceFactor *= 1.2;
754  }
755  } else {
756  // use a new node
757  double fChanceFactor = 1.0;
758  while (1) {
759  // Pick a new bucket, and an initial position in that bucket.
760  int nUBucket = insecure_rand.randrange(ADDRMAN_NEW_BUCKET_COUNT);
761  int nUBucketPos = insecure_rand.randrange(ADDRMAN_BUCKET_SIZE);
762  // Iterate over the positions of that bucket, starting at the initial one,
763  // and looping around.
764  int i;
765  for (i = 0; i < ADDRMAN_BUCKET_SIZE; ++i) {
766  if (vvNew[nUBucket][(nUBucketPos + i) % ADDRMAN_BUCKET_SIZE] != -1) break;
767  }
768  // If the bucket is entirely empty, start over with a (likely) different one.
769  if (i == ADDRMAN_BUCKET_SIZE) continue;
770  // Find the entry to return.
771  int nId = vvNew[nUBucket][(nUBucketPos + i) % ADDRMAN_BUCKET_SIZE];
772  const auto it_found{mapInfo.find(nId)};
773  assert(it_found != mapInfo.end());
774  const AddrInfo& info{it_found->second};
775  // With probability GetChance() * fChanceFactor, return the entry.
776  if (insecure_rand.randbits(30) < fChanceFactor * info.GetChance() * (1 << 30)) {
777  LogPrint(BCLog::ADDRMAN, "Selected %s from new\n", info.ToStringAddrPort());
778  return {info, info.m_last_try};
779  }
780  // Otherwise start over with a (likely) different bucket, and increased chance factor.
781  fChanceFactor *= 1.2;
782  }
783  }
784 }
785 
786 std::vector<CAddress> AddrManImpl::GetAddr_(size_t max_addresses, size_t max_pct, std::optional<Network> network) const
787 {
789 
790  size_t nNodes = vRandom.size();
791  if (max_pct != 0) {
792  nNodes = max_pct * nNodes / 100;
793  }
794  if (max_addresses != 0) {
795  nNodes = std::min(nNodes, max_addresses);
796  }
797 
798  // gather a list of random nodes, skipping those of low quality
799  const auto now{Now<NodeSeconds>()};
800  std::vector<CAddress> addresses;
801  for (unsigned int n = 0; n < vRandom.size(); n++) {
802  if (addresses.size() >= nNodes)
803  break;
804 
805  int nRndPos = insecure_rand.randrange(vRandom.size() - n) + n;
806  SwapRandom(n, nRndPos);
807  const auto it{mapInfo.find(vRandom[n])};
808  assert(it != mapInfo.end());
809 
810  const AddrInfo& ai{it->second};
811 
812  // Filter by network (optional)
813  if (network != std::nullopt && ai.GetNetClass() != network) continue;
814 
815  // Filter for quality
816  if (ai.IsTerrible(now)) continue;
817 
818  addresses.push_back(ai);
819  }
820  LogPrint(BCLog::ADDRMAN, "GetAddr returned %d random addresses\n", addresses.size());
821  return addresses;
822 }
823 
825 {
827 
828  AddrInfo* pinfo = Find(addr);
829 
830  // if not found, bail out
831  if (!pinfo)
832  return;
833 
834  AddrInfo& info = *pinfo;
835 
836  // update info
837  const auto update_interval{20min};
838  if (time - info.nTime > update_interval) {
839  info.nTime = time;
840  }
841 }
842 
843 void AddrManImpl::SetServices_(const CService& addr, ServiceFlags nServices)
844 {
846 
847  AddrInfo* pinfo = Find(addr);
848 
849  // if not found, bail out
850  if (!pinfo)
851  return;
852 
853  AddrInfo& info = *pinfo;
854 
855  // update info
856  info.nServices = nServices;
857 }
858 
860 {
862 
863  for (std::set<int>::iterator it = m_tried_collisions.begin(); it != m_tried_collisions.end();) {
864  int id_new = *it;
865 
866  bool erase_collision = false;
867 
868  // If id_new not found in mapInfo remove it from m_tried_collisions
869  if (mapInfo.count(id_new) != 1) {
870  erase_collision = true;
871  } else {
872  AddrInfo& info_new = mapInfo[id_new];
873 
874  // Which tried bucket to move the entry to.
875  int tried_bucket = info_new.GetTriedBucket(nKey, m_netgroupman);
876  int tried_bucket_pos = info_new.GetBucketPosition(nKey, false, tried_bucket);
877  if (!info_new.IsValid()) { // id_new may no longer map to a valid address
878  erase_collision = true;
879  } else if (vvTried[tried_bucket][tried_bucket_pos] != -1) { // The position in the tried bucket is not empty
880 
881  // Get the to-be-evicted address that is being tested
882  int id_old = vvTried[tried_bucket][tried_bucket_pos];
883  AddrInfo& info_old = mapInfo[id_old];
884 
885  const auto current_time{Now<NodeSeconds>()};
886 
887  // Has successfully connected in last X hours
888  if (current_time - info_old.m_last_success < ADDRMAN_REPLACEMENT) {
889  erase_collision = true;
890  } else if (current_time - info_old.m_last_try < ADDRMAN_REPLACEMENT) { // attempted to connect and failed in last X hours
891 
892  // Give address at least 60 seconds to successfully connect
893  if (current_time - info_old.m_last_try > 60s) {
894  LogPrint(BCLog::ADDRMAN, "Replacing %s with %s in tried table\n", info_old.ToStringAddrPort(), info_new.ToStringAddrPort());
895 
896  // Replaces an existing address already in the tried table with the new address
897  Good_(info_new, false, current_time);
898  erase_collision = true;
899  }
900  } else if (current_time - info_new.m_last_success > ADDRMAN_TEST_WINDOW) {
901  // If the collision hasn't resolved in some reasonable amount of time,
902  // just evict the old entry -- we must not be able to
903  // connect to it for some reason.
904  LogPrint(BCLog::ADDRMAN, "Unable to test; replacing %s with %s in tried table anyway\n", info_old.ToStringAddrPort(), info_new.ToStringAddrPort());
905  Good_(info_new, false, current_time);
906  erase_collision = true;
907  }
908  } else { // Collision is not actually a collision anymore
909  Good_(info_new, false, Now<NodeSeconds>());
910  erase_collision = true;
911  }
912  }
913 
914  if (erase_collision) {
915  m_tried_collisions.erase(it++);
916  } else {
917  it++;
918  }
919  }
920 }
921 
922 std::pair<CAddress, NodeSeconds> AddrManImpl::SelectTriedCollision_()
923 {
925 
926  if (m_tried_collisions.size() == 0) return {};
927 
928  std::set<int>::iterator it = m_tried_collisions.begin();
929 
930  // Selects a random element from m_tried_collisions
931  std::advance(it, insecure_rand.randrange(m_tried_collisions.size()));
932  int id_new = *it;
933 
934  // If id_new not found in mapInfo remove it from m_tried_collisions
935  if (mapInfo.count(id_new) != 1) {
936  m_tried_collisions.erase(it);
937  return {};
938  }
939 
940  const AddrInfo& newInfo = mapInfo[id_new];
941 
942  // which tried bucket to move the entry to
943  int tried_bucket = newInfo.GetTriedBucket(nKey, m_netgroupman);
944  int tried_bucket_pos = newInfo.GetBucketPosition(nKey, false, tried_bucket);
945 
946  const AddrInfo& info_old = mapInfo[vvTried[tried_bucket][tried_bucket_pos]];
947  return {info_old, info_old.m_last_try};
948 }
949 
950 std::optional<AddressPosition> AddrManImpl::FindAddressEntry_(const CAddress& addr)
951 {
953 
954  AddrInfo* addr_info = Find(addr);
955 
956  if (!addr_info) return std::nullopt;
957 
958  if(addr_info->fInTried) {
959  int bucket{addr_info->GetTriedBucket(nKey, m_netgroupman)};
960  return AddressPosition(/*tried_in=*/true,
961  /*multiplicity_in=*/1,
962  /*bucket_in=*/bucket,
963  /*position_in=*/addr_info->GetBucketPosition(nKey, false, bucket));
964  } else {
965  int bucket{addr_info->GetNewBucket(nKey, m_netgroupman)};
966  return AddressPosition(/*tried_in=*/false,
967  /*multiplicity_in=*/addr_info->nRefCount,
968  /*bucket_in=*/bucket,
969  /*position_in=*/addr_info->GetBucketPosition(nKey, true, bucket));
970  }
971 }
972 
973 size_t AddrManImpl::Size_(std::optional<Network> net, std::optional<bool> in_new) const
974 {
976 
977  if (!net.has_value()) {
978  if (in_new.has_value()) {
979  return *in_new ? nNew : nTried;
980  } else {
981  return vRandom.size();
982  }
983  }
984  if (auto it = m_network_counts.find(*net); it != m_network_counts.end()) {
985  auto net_count = it->second;
986  if (in_new.has_value()) {
987  return *in_new ? net_count.n_new : net_count.n_tried;
988  } else {
989  return net_count.n_new + net_count.n_tried;
990  }
991  }
992  return 0;
993 }
994 
995 void AddrManImpl::Check() const
996 {
998 
999  // Run consistency checks 1 in m_consistency_check_ratio times if enabled
1000  if (m_consistency_check_ratio == 0) return;
1001  if (insecure_rand.randrange(m_consistency_check_ratio) >= 1) return;
1002 
1003  const int err{CheckAddrman()};
1004  if (err) {
1005  LogPrintf("ADDRMAN CONSISTENCY CHECK FAILED!!! err=%i\n", err);
1006  assert(false);
1007  }
1008 }
1009 
1011 {
1012  AssertLockHeld(cs);
1013 
1015  strprintf("new %i, tried %i, total %u", nNew, nTried, vRandom.size()), BCLog::ADDRMAN);
1016 
1017  std::unordered_set<int> setTried;
1018  std::unordered_map<int, int> mapNew;
1019  std::unordered_map<Network, NewTriedCount> local_counts;
1020 
1021  if (vRandom.size() != (size_t)(nTried + nNew))
1022  return -7;
1023 
1024  for (const auto& entry : mapInfo) {
1025  int n = entry.first;
1026  const AddrInfo& info = entry.second;
1027  if (info.fInTried) {
1028  if (!TicksSinceEpoch<std::chrono::seconds>(info.m_last_success)) {
1029  return -1;
1030  }
1031  if (info.nRefCount)
1032  return -2;
1033  setTried.insert(n);
1034  local_counts[info.GetNetwork()].n_tried++;
1035  } else {
1036  if (info.nRefCount < 0 || info.nRefCount > ADDRMAN_NEW_BUCKETS_PER_ADDRESS)
1037  return -3;
1038  if (!info.nRefCount)
1039  return -4;
1040  mapNew[n] = info.nRefCount;
1041  local_counts[info.GetNetwork()].n_new++;
1042  }
1043  const auto it{mapAddr.find(info)};
1044  if (it == mapAddr.end() || it->second != n) {
1045  return -5;
1046  }
1047  if (info.nRandomPos < 0 || (size_t)info.nRandomPos >= vRandom.size() || vRandom[info.nRandomPos] != n)
1048  return -14;
1049  if (info.m_last_try < NodeSeconds{0s}) {
1050  return -6;
1051  }
1052  if (info.m_last_success < NodeSeconds{0s}) {
1053  return -8;
1054  }
1055  }
1056 
1057  if (setTried.size() != (size_t)nTried)
1058  return -9;
1059  if (mapNew.size() != (size_t)nNew)
1060  return -10;
1061 
1062  for (int n = 0; n < ADDRMAN_TRIED_BUCKET_COUNT; n++) {
1063  for (int i = 0; i < ADDRMAN_BUCKET_SIZE; i++) {
1064  if (vvTried[n][i] != -1) {
1065  if (!setTried.count(vvTried[n][i]))
1066  return -11;
1067  const auto it{mapInfo.find(vvTried[n][i])};
1068  if (it == mapInfo.end() || it->second.GetTriedBucket(nKey, m_netgroupman) != n) {
1069  return -17;
1070  }
1071  if (it->second.GetBucketPosition(nKey, false, n) != i) {
1072  return -18;
1073  }
1074  setTried.erase(vvTried[n][i]);
1075  }
1076  }
1077  }
1078 
1079  for (int n = 0; n < ADDRMAN_NEW_BUCKET_COUNT; n++) {
1080  for (int i = 0; i < ADDRMAN_BUCKET_SIZE; i++) {
1081  if (vvNew[n][i] != -1) {
1082  if (!mapNew.count(vvNew[n][i]))
1083  return -12;
1084  const auto it{mapInfo.find(vvNew[n][i])};
1085  if (it == mapInfo.end() || it->second.GetBucketPosition(nKey, true, n) != i) {
1086  return -19;
1087  }
1088  if (--mapNew[vvNew[n][i]] == 0)
1089  mapNew.erase(vvNew[n][i]);
1090  }
1091  }
1092  }
1093 
1094  if (setTried.size())
1095  return -13;
1096  if (mapNew.size())
1097  return -15;
1098  if (nKey.IsNull())
1099  return -16;
1100 
1101  // It's possible that m_network_counts may have all-zero entries that local_counts
1102  // doesn't have if addrs from a network were being added and then removed again in the past.
1103  if (m_network_counts.size() < local_counts.size()) {
1104  return -20;
1105  }
1106  for (const auto& [net, count] : m_network_counts) {
1107  if (local_counts[net].n_new != count.n_new || local_counts[net].n_tried != count.n_tried) {
1108  return -21;
1109  }
1110  }
1111 
1112  return 0;
1113 }
1114 
1115 size_t AddrManImpl::Size(std::optional<Network> net, std::optional<bool> in_new) const
1116 {
1117  LOCK(cs);
1118  Check();
1119  auto ret = Size_(net, in_new);
1120  Check();
1121  return ret;
1122 }
1123 
1124 bool AddrManImpl::Add(const std::vector<CAddress>& vAddr, const CNetAddr& source, std::chrono::seconds time_penalty)
1125 {
1126  LOCK(cs);
1127  Check();
1128  auto ret = Add_(vAddr, source, time_penalty);
1129  Check();
1130  return ret;
1131 }
1132 
1133 bool AddrManImpl::Good(const CService& addr, NodeSeconds time)
1134 {
1135  LOCK(cs);
1136  Check();
1137  auto ret = Good_(addr, /*test_before_evict=*/true, time);
1138  Check();
1139  return ret;
1140 }
1141 
1142 void AddrManImpl::Attempt(const CService& addr, bool fCountFailure, NodeSeconds time)
1143 {
1144  LOCK(cs);
1145  Check();
1146  Attempt_(addr, fCountFailure, time);
1147  Check();
1148 }
1149 
1151 {
1152  LOCK(cs);
1153  Check();
1155  Check();
1156 }
1157 
1158 std::pair<CAddress, NodeSeconds> AddrManImpl::SelectTriedCollision()
1159 {
1160  LOCK(cs);
1161  Check();
1162  auto ret = SelectTriedCollision_();
1163  Check();
1164  return ret;
1165 }
1166 
1167 std::pair<CAddress, NodeSeconds> AddrManImpl::Select(bool newOnly) const
1168 {
1169  LOCK(cs);
1170  Check();
1171  auto addrRet = Select_(newOnly);
1172  Check();
1173  return addrRet;
1174 }
1175 
1176 std::vector<CAddress> AddrManImpl::GetAddr(size_t max_addresses, size_t max_pct, std::optional<Network> network) const
1177 {
1178  LOCK(cs);
1179  Check();
1180  auto addresses = GetAddr_(max_addresses, max_pct, network);
1181  Check();
1182  return addresses;
1183 }
1184 
1186 {
1187  LOCK(cs);
1188  Check();
1189  Connected_(addr, time);
1190  Check();
1191 }
1192 
1193 void AddrManImpl::SetServices(const CService& addr, ServiceFlags nServices)
1194 {
1195  LOCK(cs);
1196  Check();
1197  SetServices_(addr, nServices);
1198  Check();
1199 }
1200 
1201 std::optional<AddressPosition> AddrManImpl::FindAddressEntry(const CAddress& addr)
1202 {
1203  LOCK(cs);
1204  Check();
1205  auto entry = FindAddressEntry_(addr);
1206  Check();
1207  return entry;
1208 }
1209 
1210 AddrMan::AddrMan(const NetGroupManager& netgroupman, bool deterministic, int32_t consistency_check_ratio)
1211  : m_impl(std::make_unique<AddrManImpl>(netgroupman, deterministic, consistency_check_ratio)) {}
1212 
1213 AddrMan::~AddrMan() = default;
1214 
1215 template <typename Stream>
1216 void AddrMan::Serialize(Stream& s_) const
1217 {
1218  m_impl->Serialize<Stream>(s_);
1219 }
1220 
1221 template <typename Stream>
1222 void AddrMan::Unserialize(Stream& s_)
1223 {
1224  m_impl->Unserialize<Stream>(s_);
1225 }
1226 
1227 // explicit instantiation
1228 template void AddrMan::Serialize(HashedSourceWriter<CAutoFile>& s) const;
1229 template void AddrMan::Serialize(CDataStream& s) const;
1230 template void AddrMan::Unserialize(CAutoFile& s);
1232 template void AddrMan::Unserialize(CDataStream& s);
1234 
1235 size_t AddrMan::Size(std::optional<Network> net, std::optional<bool> in_new) const
1236 {
1237  return m_impl->Size(net, in_new);
1238 }
1239 
1240 bool AddrMan::Add(const std::vector<CAddress>& vAddr, const CNetAddr& source, std::chrono::seconds time_penalty)
1241 {
1242  return m_impl->Add(vAddr, source, time_penalty);
1243 }
1244 
1245 bool AddrMan::Good(const CService& addr, NodeSeconds time)
1246 {
1247  return m_impl->Good(addr, time);
1248 }
1249 
1250 void AddrMan::Attempt(const CService& addr, bool fCountFailure, NodeSeconds time)
1251 {
1252  m_impl->Attempt(addr, fCountFailure, time);
1253 }
1254 
1256 {
1257  m_impl->ResolveCollisions();
1258 }
1259 
1260 std::pair<CAddress, NodeSeconds> AddrMan::SelectTriedCollision()
1261 {
1262  return m_impl->SelectTriedCollision();
1263 }
1264 
1265 std::pair<CAddress, NodeSeconds> AddrMan::Select(bool newOnly) const
1266 {
1267  return m_impl->Select(newOnly);
1268 }
1269 
1270 std::vector<CAddress> AddrMan::GetAddr(size_t max_addresses, size_t max_pct, std::optional<Network> network) const
1271 {
1272  return m_impl->GetAddr(max_addresses, max_pct, network);
1273 }
1274 
1275 void AddrMan::Connected(const CService& addr, NodeSeconds time)
1276 {
1277  m_impl->Connected(addr, time);
1278 }
1279 
1280 void AddrMan::SetServices(const CService& addr, ServiceFlags nServices)
1281 {
1282  m_impl->SetServices(addr, nServices);
1283 }
1284 
1285 std::optional<AddressPosition> AddrMan::FindAddressEntry(const CAddress& addr)
1286 {
1287  return m_impl->FindAddressEntry(addr);
1288 }
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
static constexpr auto ADDRMAN_HORIZON
How old addresses can maximally be.
Definition: addrman.cpp:32
static constexpr int32_t ADDRMAN_MAX_FAILURES
How many successive failures are allowed ...
Definition: addrman.cpp:36
static constexpr auto ADDRMAN_MIN_FAIL
...
Definition: addrman.cpp:38
static constexpr auto ADDRMAN_TEST_WINDOW
The maximum time we'll spend trying to resolve a tried table collision.
Definition: addrman.cpp:44
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
static constexpr int32_t ADDRMAN_RETRIES
After how many failed attempts we give up on a new node.
Definition: addrman.cpp:34
static constexpr size_t ADDRMAN_SET_TRIED_COLLISION_SIZE
The maximum number of tried addr collisions to store.
Definition: addrman.cpp:42
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
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
static constexpr int ADDRMAN_TRIED_BUCKET_COUNT
Definition: addrman_impl.h:28
static constexpr int ADDRMAN_BUCKET_SIZE
Definition: addrman_impl.h:34
static constexpr int ADDRMAN_NEW_BUCKET_COUNT
Definition: addrman_impl.h:31
int ret
#define PACKAGE_NAME
#define Assume(val)
Assume is the identity function.
Definition: check.h:85
Extended statistics about a CAddress.
Definition: addrman_impl.h:40
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
int GetTriedBucket(const uint256 &nKey, const NetGroupManager &netgroupman) const
Calculate in which "tried" bucket this entry belongs.
Definition: addrman.cpp:46
int nRandomPos
position in vRandom
Definition: addrman_impl.h:64
bool fInTried
in tried set? (memory only)
Definition: addrman_impl.h:61
NodeSeconds m_last_success
last successful connection by us
Definition: addrman_impl.h:52
NodeSeconds m_last_count_attempt
last counted attempt (memory only)
Definition: addrman_impl.h:46
NodeSeconds m_last_try
last try whatsoever by us (memory only)
Definition: addrman_impl.h:43
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
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
int nRefCount
reference count in new sets (memory only)
Definition: addrman_impl.h:58
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 Connected(const CService &addr, NodeSeconds time=Now< NodeSeconds >())
We have successfully connected to this peer.
Definition: addrman.cpp:1275
const std::unique_ptr< AddrManImpl > m_impl
Definition: addrman.h:89
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:1270
void Attempt(const CService &addr, bool fCountFailure, NodeSeconds time=Now< NodeSeconds >())
Mark an entry as connection attempted to.
Definition: addrman.cpp:1250
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:1235
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:1285
std::pair< CAddress, NodeSeconds > Select(bool newOnly=false) const
Choose an address to connect to.
Definition: addrman.cpp:1265
void ResolveCollisions()
See if any to-be-evicted tried table entries have been tested and if so resolve the collisions.
Definition: addrman.cpp:1255
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:1245
void Serialize(Stream &s_) const
Definition: addrman.cpp:1216
void Unserialize(Stream &s_)
Definition: addrman.cpp:1222
AddrMan(const NetGroupManager &netgroupman, bool deterministic, int32_t consistency_check_ratio)
Definition: addrman.cpp:1210
std::pair< CAddress, NodeSeconds > SelectTriedCollision()
Randomly select an address in the tried table that another address is attempting to evict.
Definition: addrman.cpp:1260
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:1240
void SetServices(const CService &addr, ServiceFlags nServices)
Update an entry's service bits.
Definition: addrman.cpp:1280
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:478
std::pair< CAddress, NodeSeconds > Select(bool newOnly) const EXCLUSIVE_LOCKS_REQUIRED(!cs)
Definition: addrman.cpp:1167
void Connected_(const CService &addr, NodeSeconds time) EXCLUSIVE_LOCKS_REQUIRED(cs)
Definition: addrman.cpp:824
void Attempt_(const CService &addr, bool fCountFailure, NodeSeconds time) EXCLUSIVE_LOCKS_REQUIRED(cs)
Definition: addrman.cpp:697
static constexpr Format FILE_FORMAT
The maximum format this software knows it can unserialize.
Definition: addrman_impl.h:171
void ResolveCollisions_() EXCLUSIVE_LOCKS_REQUIRED(cs)
Definition: addrman.cpp:859
Format
Serialization versions.
Definition: addrman_impl.h:158
void Serialize(Stream &s_) const EXCLUSIVE_LOCKS_REQUIRED(!cs)
Definition: addrman.cpp:131
void Connected(const CService &addr, NodeSeconds time) EXCLUSIVE_LOCKS_REQUIRED(!cs)
Definition: addrman.cpp:1185
std::vector< CAddress > GetAddr(size_t max_addresses, size_t max_pct, std::optional< Network > network) const EXCLUSIVE_LOCKS_REQUIRED(!cs)
Definition: addrman.cpp:1176
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:496
void SetServices(const CService &addr, ServiceFlags nServices) EXCLUSIVE_LOCKS_REQUIRED(!cs)
Definition: addrman.cpp:1193
std::optional< AddressPosition > FindAddressEntry_(const CAddress &addr) EXCLUSIVE_LOCKS_REQUIRED(cs)
Definition: addrman.cpp:950
void SetServices_(const CService &addr, ServiceFlags nServices) EXCLUSIVE_LOCKS_REQUIRED(cs)
Definition: addrman.cpp:843
AddrManImpl(const NetGroupManager &netgroupman, bool deterministic, int32_t consistency_check_ratio)
Definition: addrman.cpp:107
const int32_t m_consistency_check_ratio
Perform consistency checks every m_consistency_check_ratio operations (if non-zero).
Definition: addrman_impl.h:213
void Check() const EXCLUSIVE_LOCKS_REQUIRED(cs)
Consistency check, taking into account m_consistency_check_ratio.
Definition: addrman.cpp:995
int CheckAddrman() const EXCLUSIVE_LOCKS_REQUIRED(cs)
Perform consistency check, regardless of m_consistency_check_ratio.
Definition: addrman.cpp:1010
bool Add(const std::vector< CAddress > &vAddr, const CNetAddr &source, std::chrono::seconds time_penalty) EXCLUSIVE_LOCKS_REQUIRED(!cs)
Definition: addrman.cpp:1124
std::optional< AddressPosition > FindAddressEntry(const CAddress &addr) EXCLUSIVE_LOCKS_REQUIRED(!cs)
Definition: addrman.cpp:1201
AddrInfo * Find(const CService &addr, int *pnId=nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs)
Find an entry.
Definition: addrman.cpp:406
bool Good_(const CService &addr, bool test_before_evict, NodeSeconds time) EXCLUSIVE_LOCKS_REQUIRED(cs)
Definition: addrman.cpp:631
Mutex cs
A mutex to protect the inner data structures.
Definition: addrman_impl.h:149
size_t Size_(std::optional< Network > net, std::optional< bool > in_new) const EXCLUSIVE_LOCKS_REQUIRED(cs)
Definition: addrman.cpp:973
std::pair< CAddress, NodeSeconds > SelectTriedCollision_() EXCLUSIVE_LOCKS_REQUIRED(cs)
Definition: addrman.cpp:922
std::pair< CAddress, NodeSeconds > Select_(bool newOnly) const EXCLUSIVE_LOCKS_REQUIRED(cs)
Definition: addrman.cpp:717
std::pair< CAddress, NodeSeconds > SelectTriedCollision() EXCLUSIVE_LOCKS_REQUIRED(!cs)
Definition: addrman.cpp:1158
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
void Delete(int nId) EXCLUSIVE_LOCKS_REQUIRED(cs)
Delete an entry. It must not be in tried, and have refcount 0.
Definition: addrman.cpp:461
void SwapRandom(unsigned int nRandomPos1, unsigned int nRandomPos2) const EXCLUSIVE_LOCKS_REQUIRED(cs)
Swap two elements in vRandom.
Definition: addrman.cpp:437
void Attempt(const CService &addr, bool fCountFailure, NodeSeconds time) EXCLUSIVE_LOCKS_REQUIRED(!cs)
Definition: addrman.cpp:1142
void Unserialize(Stream &s_) EXCLUSIVE_LOCKS_REQUIRED(!cs)
Definition: addrman.cpp:230
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
uint256 nKey
secret key to randomize bucket select with
Definition: addrman_impl.h:155
void ResolveCollisions() EXCLUSIVE_LOCKS_REQUIRED(!cs)
Definition: addrman.cpp:1150
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:421
const NetGroupManager & m_netgroupman
Reference to the netgroup manager.
Definition: addrman_impl.h:216
std::vector< CAddress > GetAddr_(size_t max_addresses, size_t max_pct, std::optional< Network > network) const EXCLUSIVE_LOCKS_REQUIRED(cs)
Definition: addrman.cpp:786
bool Add_(const std::vector< CAddress > &vAddr, const CNetAddr &source, std::chrono::seconds time_penalty) EXCLUSIVE_LOCKS_REQUIRED(cs)
Definition: addrman.cpp:685
bool Good(const CService &addr, NodeSeconds time) EXCLUSIVE_LOCKS_REQUIRED(!cs)
Definition: addrman.cpp:1133
size_t Size(std::optional< Network > net, std::optional< bool > in_new) const EXCLUSIVE_LOCKS_REQUIRED(!cs)
Definition: addrman.cpp:1115
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:555
A CService with information about it as peer.
Definition: protocol.h:361
ServiceFlags nServices
Serialized as uint64_t in V1, and as CompactSize in V2.
Definition: protocol.h:443
NodeSeconds nTime
Always included in serialization. The behavior is unspecified if the value is not representable as ui...
Definition: protocol.h:441
Network address.
Definition: netaddress.h:120
bool IsRoutable() const
Definition: netaddress.cpp:484
bool IsValid() const
Definition: netaddress.cpp:445
enum Network GetNetwork() const
Definition: netaddress.cpp:518
A combination of a network address (CNetAddr) and a (TCP) port.
Definition: netaddress.h:520
std::string ToStringAddrPort() const
Definition: netaddress.cpp:914
std::vector< unsigned char > GetKey() const
Definition: netaddress.cpp:906
Writes data to an underlying source stream, while hashing the written data.
Definition: hash.h:240
Netgroup manager.
Definition: netgroup.h:16
uint256 GetAsmapChecksum() const
Get a checksum identifying the asmap being used.
Definition: netgroup.cpp:10
std::vector< unsigned char > GetGroup(const CNetAddr &address) const
Get the canonical identifier of the network group for address.
Definition: netgroup.cpp:17
uint32_t GetMappedAS(const CNetAddr &address) const
Get the autonomous system on the BGP path to address.
Definition: netgroup.cpp:80
constexpr bool IsNull() const
Definition: uint256.h:41
constexpr void SetNull()
Definition: uint256.h:48
256-bit opaque blob.
Definition: uint256.h:105
#define LogPrint(category,...)
Definition: logging.h:245
#define LogPrintf(...)
Definition: logging.h:236
@ ADDRMAN
Definition: logging.h:49
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:1060
static constexpr int ADDRV2_FORMAT
A flag that is ORed into the protocol version to designate that addresses should be serialized in (un...
Definition: netaddress.h:33
ServiceFlags
nServices flags
Definition: protocol.h:273
const char * source
Definition: rpcconsole.cpp:58
@ SER_GETHASH
Definition: serialize.h:133
Test-only struct, capturing info about an address in AddrMan.
Definition: addrman.h:33
static time_point now() noexcept
Return current system time or mocked time, if set.
Definition: time.cpp:70
#define LOCK(cs)
Definition: sync.h:258
static int count
std::chrono::time_point< NodeClock, std::chrono::seconds > NodeSeconds
Definition: time.h:25
#define LOG_TIME_MILLIS_WITH_CATEGORY_MSG_ONCE(end_msg, log_category)
Definition: timer.h:105
#define strprintf
Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for...
Definition: tinyformat.h:1162
AssertLockHeld(pool.cs)
assert(!tx.IsCoinBase())