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