Bitcoin Core  21.99.0
P2P Digital Currency
addrman.cpp
Go to the documentation of this file.
1 // Copyright (c) 2012 Pieter Wuille
2 // Copyright (c) 2012-2020 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 
8 #include <hash.h>
9 #include <logging.h>
10 #include <netaddress.h>
11 #include <serialize.h>
12 
13 #include <cmath>
14 #include <optional>
15 #include <unordered_map>
16 #include <unordered_set>
17 
18 int CAddrInfo::GetTriedBucket(const uint256& nKey, const std::vector<bool> &asmap) const
19 {
20  uint64_t hash1 = (CHashWriter(SER_GETHASH, 0) << nKey << GetKey()).GetCheapHash();
21  uint64_t hash2 = (CHashWriter(SER_GETHASH, 0) << nKey << GetGroup(asmap) << (hash1 % ADDRMAN_TRIED_BUCKETS_PER_GROUP)).GetCheapHash();
22  int tried_bucket = hash2 % ADDRMAN_TRIED_BUCKET_COUNT;
23  uint32_t mapped_as = GetMappedAS(asmap);
24  LogPrint(BCLog::NET, "IP %s mapped to AS%i belongs to tried bucket %i\n", ToStringIP(), mapped_as, tried_bucket);
25  return tried_bucket;
26 }
27 
28 int CAddrInfo::GetNewBucket(const uint256& nKey, const CNetAddr& src, const std::vector<bool> &asmap) const
29 {
30  std::vector<unsigned char> vchSourceGroupKey = src.GetGroup(asmap);
31  uint64_t hash1 = (CHashWriter(SER_GETHASH, 0) << nKey << GetGroup(asmap) << vchSourceGroupKey).GetCheapHash();
32  uint64_t hash2 = (CHashWriter(SER_GETHASH, 0) << nKey << vchSourceGroupKey << (hash1 % ADDRMAN_NEW_BUCKETS_PER_SOURCE_GROUP)).GetCheapHash();
33  int new_bucket = hash2 % ADDRMAN_NEW_BUCKET_COUNT;
34  uint32_t mapped_as = GetMappedAS(asmap);
35  LogPrint(BCLog::NET, "IP %s mapped to AS%i belongs to new bucket %i\n", ToStringIP(), mapped_as, new_bucket);
36  return new_bucket;
37 }
38 
39 int CAddrInfo::GetBucketPosition(const uint256 &nKey, bool fNew, int nBucket) const
40 {
41  uint64_t hash1 = (CHashWriter(SER_GETHASH, 0) << nKey << (fNew ? uint8_t{'N'} : uint8_t{'K'}) << nBucket << GetKey()).GetCheapHash();
42  return hash1 % ADDRMAN_BUCKET_SIZE;
43 }
44 
45 bool CAddrInfo::IsTerrible(int64_t nNow) const
46 {
47  if (nLastTry && nLastTry >= nNow - 60) // never remove things tried in the last minute
48  return false;
49 
50  if (nTime > nNow + 10 * 60) // came in a flying DeLorean
51  return true;
52 
53  if (nTime == 0 || nNow - nTime > ADDRMAN_HORIZON_DAYS * 24 * 60 * 60) // not seen in recent history
54  return true;
55 
56  if (nLastSuccess == 0 && nAttempts >= ADDRMAN_RETRIES) // tried N times and never a success
57  return true;
58 
59  if (nNow - nLastSuccess > ADDRMAN_MIN_FAIL_DAYS * 24 * 60 * 60 && nAttempts >= ADDRMAN_MAX_FAILURES) // N successive failures in the last week
60  return true;
61 
62  return false;
63 }
64 
65 double CAddrInfo::GetChance(int64_t nNow) const
66 {
67  double fChance = 1.0;
68  int64_t nSinceLastTry = std::max<int64_t>(nNow - nLastTry, 0);
69 
70  // deprioritize very recent attempts away
71  if (nSinceLastTry < 60 * 10)
72  fChance *= 0.01;
73 
74  // deprioritize 66% after each failed attempt, but at most 1/28th to avoid the search taking forever or overly penalizing outages.
75  fChance *= pow(0.66, std::min(nAttempts, 8));
76 
77  return fChance;
78 }
79 
80 CAddrInfo* CAddrMan::Find(const CNetAddr& addr, int* pnId)
81 {
83 
84  const auto it = mapAddr.find(addr);
85  if (it == mapAddr.end())
86  return nullptr;
87  if (pnId)
88  *pnId = (*it).second;
89  const auto it2 = mapInfo.find((*it).second);
90  if (it2 != mapInfo.end())
91  return &(*it2).second;
92  return nullptr;
93 }
94 
95 CAddrInfo* CAddrMan::Create(const CAddress& addr, const CNetAddr& addrSource, int* pnId)
96 {
98 
99  int nId = nIdCount++;
100  mapInfo[nId] = CAddrInfo(addr, addrSource);
101  mapAddr[addr] = nId;
102  mapInfo[nId].nRandomPos = vRandom.size();
103  vRandom.push_back(nId);
104  if (pnId)
105  *pnId = nId;
106  return &mapInfo[nId];
107 }
108 
109 void CAddrMan::SwapRandom(unsigned int nRndPos1, unsigned int nRndPos2)
110 {
112 
113  if (nRndPos1 == nRndPos2)
114  return;
115 
116  assert(nRndPos1 < vRandom.size() && nRndPos2 < vRandom.size());
117 
118  int nId1 = vRandom[nRndPos1];
119  int nId2 = vRandom[nRndPos2];
120 
121  assert(mapInfo.count(nId1) == 1);
122  assert(mapInfo.count(nId2) == 1);
123 
124  mapInfo[nId1].nRandomPos = nRndPos2;
125  mapInfo[nId2].nRandomPos = nRndPos1;
126 
127  vRandom[nRndPos1] = nId2;
128  vRandom[nRndPos2] = nId1;
129 }
130 
131 void CAddrMan::Delete(int nId)
132 {
134 
135  assert(mapInfo.count(nId) != 0);
136  CAddrInfo& info = mapInfo[nId];
137  assert(!info.fInTried);
138  assert(info.nRefCount == 0);
139 
140  SwapRandom(info.nRandomPos, vRandom.size() - 1);
141  vRandom.pop_back();
142  mapAddr.erase(info);
143  mapInfo.erase(nId);
144  nNew--;
145 }
146 
147 void CAddrMan::ClearNew(int nUBucket, int nUBucketPos)
148 {
150 
151  // if there is an entry in the specified bucket, delete it.
152  if (vvNew[nUBucket][nUBucketPos] != -1) {
153  int nIdDelete = vvNew[nUBucket][nUBucketPos];
154  CAddrInfo& infoDelete = mapInfo[nIdDelete];
155  assert(infoDelete.nRefCount > 0);
156  infoDelete.nRefCount--;
157  vvNew[nUBucket][nUBucketPos] = -1;
158  if (infoDelete.nRefCount == 0) {
159  Delete(nIdDelete);
160  }
161  }
162 }
163 
164 void CAddrMan::MakeTried(CAddrInfo& info, int nId)
165 {
167 
168  // remove the entry from all new buckets
169  for (int bucket = 0; bucket < ADDRMAN_NEW_BUCKET_COUNT; bucket++) {
170  int pos = info.GetBucketPosition(nKey, true, bucket);
171  if (vvNew[bucket][pos] == nId) {
172  vvNew[bucket][pos] = -1;
173  info.nRefCount--;
174  }
175  }
176  nNew--;
177 
178  assert(info.nRefCount == 0);
179 
180  // which tried bucket to move the entry to
181  int nKBucket = info.GetTriedBucket(nKey, m_asmap);
182  int nKBucketPos = info.GetBucketPosition(nKey, false, nKBucket);
183 
184  // first make space to add it (the existing tried entry there is moved to new, deleting whatever is there).
185  if (vvTried[nKBucket][nKBucketPos] != -1) {
186  // find an item to evict
187  int nIdEvict = vvTried[nKBucket][nKBucketPos];
188  assert(mapInfo.count(nIdEvict) == 1);
189  CAddrInfo& infoOld = mapInfo[nIdEvict];
190 
191  // Remove the to-be-evicted item from the tried set.
192  infoOld.fInTried = false;
193  vvTried[nKBucket][nKBucketPos] = -1;
194  nTried--;
195 
196  // find which new bucket it belongs to
197  int nUBucket = infoOld.GetNewBucket(nKey, m_asmap);
198  int nUBucketPos = infoOld.GetBucketPosition(nKey, true, nUBucket);
199  ClearNew(nUBucket, nUBucketPos);
200  assert(vvNew[nUBucket][nUBucketPos] == -1);
201 
202  // Enter it into the new set again.
203  infoOld.nRefCount = 1;
204  vvNew[nUBucket][nUBucketPos] = nIdEvict;
205  nNew++;
206  }
207  assert(vvTried[nKBucket][nKBucketPos] == -1);
208 
209  vvTried[nKBucket][nKBucketPos] = nId;
210  nTried++;
211  info.fInTried = true;
212 }
213 
214 void CAddrMan::Good_(const CService& addr, bool test_before_evict, int64_t nTime)
215 {
217 
218  int nId;
219 
220  nLastGood = nTime;
221 
222  CAddrInfo* pinfo = Find(addr, &nId);
223 
224  // if not found, bail out
225  if (!pinfo)
226  return;
227 
228  CAddrInfo& info = *pinfo;
229 
230  // check whether we are talking about the exact same CService (including same port)
231  if (info != addr)
232  return;
233 
234  // update info
235  info.nLastSuccess = nTime;
236  info.nLastTry = nTime;
237  info.nAttempts = 0;
238  // nTime is not updated here, to avoid leaking information about
239  // currently-connected peers.
240 
241  // if it is already in the tried set, don't do anything else
242  if (info.fInTried)
243  return;
244 
245  // find a bucket it is in now
247  int nUBucket = -1;
248  for (unsigned int n = 0; n < ADDRMAN_NEW_BUCKET_COUNT; n++) {
249  int nB = (n + nRnd) % ADDRMAN_NEW_BUCKET_COUNT;
250  int nBpos = info.GetBucketPosition(nKey, true, nB);
251  if (vvNew[nB][nBpos] == nId) {
252  nUBucket = nB;
253  break;
254  }
255  }
256 
257  // if no bucket is found, something bad happened;
258  // TODO: maybe re-add the node, but for now, just bail out
259  if (nUBucket == -1)
260  return;
261 
262  // which tried bucket to move the entry to
263  int tried_bucket = info.GetTriedBucket(nKey, m_asmap);
264  int tried_bucket_pos = info.GetBucketPosition(nKey, false, tried_bucket);
265 
266  // Will moving this address into tried evict another entry?
267  if (test_before_evict && (vvTried[tried_bucket][tried_bucket_pos] != -1)) {
268  // Output the entry we'd be colliding with, for debugging purposes
269  auto colliding_entry = mapInfo.find(vvTried[tried_bucket][tried_bucket_pos]);
270  LogPrint(BCLog::ADDRMAN, "Collision inserting element into tried table (%s), moving %s to m_tried_collisions=%d\n", colliding_entry != mapInfo.end() ? colliding_entry->second.ToString() : "", addr.ToString(), m_tried_collisions.size());
272  m_tried_collisions.insert(nId);
273  }
274  } else {
275  LogPrint(BCLog::ADDRMAN, "Moving %s to tried\n", addr.ToString());
276 
277  // move nId to the tried tables
278  MakeTried(info, nId);
279  }
280 }
281 
282 bool CAddrMan::Add_(const CAddress& addr, const CNetAddr& source, int64_t nTimePenalty)
283 {
285 
286  if (!addr.IsRoutable())
287  return false;
288 
289  bool fNew = false;
290  int nId;
291  CAddrInfo* pinfo = Find(addr, &nId);
292 
293  // Do not set a penalty for a source's self-announcement
294  if (addr == source) {
295  nTimePenalty = 0;
296  }
297 
298  if (pinfo) {
299  // periodically update nTime
300  bool fCurrentlyOnline = (GetAdjustedTime() - addr.nTime < 24 * 60 * 60);
301  int64_t nUpdateInterval = (fCurrentlyOnline ? 60 * 60 : 24 * 60 * 60);
302  if (addr.nTime && (!pinfo->nTime || pinfo->nTime < addr.nTime - nUpdateInterval - nTimePenalty))
303  pinfo->nTime = std::max((int64_t)0, addr.nTime - nTimePenalty);
304 
305  // add services
306  pinfo->nServices = ServiceFlags(pinfo->nServices | addr.nServices);
307 
308  // do not update if no new information is present
309  if (!addr.nTime || (pinfo->nTime && addr.nTime <= pinfo->nTime))
310  return false;
311 
312  // do not update if the entry was already in the "tried" table
313  if (pinfo->fInTried)
314  return false;
315 
316  // do not update if the max reference count is reached
318  return false;
319 
320  // stochastic test: previous nRefCount == N: 2^N times harder to increase it
321  int nFactor = 1;
322  for (int n = 0; n < pinfo->nRefCount; n++)
323  nFactor *= 2;
324  if (nFactor > 1 && (insecure_rand.randrange(nFactor) != 0))
325  return false;
326  } else {
327  pinfo = Create(addr, source, &nId);
328  pinfo->nTime = std::max((int64_t)0, (int64_t)pinfo->nTime - nTimePenalty);
329  nNew++;
330  fNew = true;
331  }
332 
333  int nUBucket = pinfo->GetNewBucket(nKey, source, m_asmap);
334  int nUBucketPos = pinfo->GetBucketPosition(nKey, true, nUBucket);
335  if (vvNew[nUBucket][nUBucketPos] != nId) {
336  bool fInsert = vvNew[nUBucket][nUBucketPos] == -1;
337  if (!fInsert) {
338  CAddrInfo& infoExisting = mapInfo[vvNew[nUBucket][nUBucketPos]];
339  if (infoExisting.IsTerrible() || (infoExisting.nRefCount > 1 && pinfo->nRefCount == 0)) {
340  // Overwrite the existing new table entry.
341  fInsert = true;
342  }
343  }
344  if (fInsert) {
345  ClearNew(nUBucket, nUBucketPos);
346  pinfo->nRefCount++;
347  vvNew[nUBucket][nUBucketPos] = nId;
348  } else {
349  if (pinfo->nRefCount == 0) {
350  Delete(nId);
351  }
352  }
353  }
354  return fNew;
355 }
356 
357 void CAddrMan::Attempt_(const CService& addr, bool fCountFailure, int64_t nTime)
358 {
360 
361  CAddrInfo* pinfo = Find(addr);
362 
363  // if not found, bail out
364  if (!pinfo)
365  return;
366 
367  CAddrInfo& info = *pinfo;
368 
369  // check whether we are talking about the exact same CService (including same port)
370  if (info != addr)
371  return;
372 
373  // update info
374  info.nLastTry = nTime;
375  if (fCountFailure && info.nLastCountAttempt < nLastGood) {
376  info.nLastCountAttempt = nTime;
377  info.nAttempts++;
378  }
379 }
380 
382 {
384 
385  if (vRandom.empty())
386  return CAddrInfo();
387 
388  if (newOnly && nNew == 0)
389  return CAddrInfo();
390 
391  // Use a 50% chance for choosing between tried and new table entries.
392  if (!newOnly &&
393  (nTried > 0 && (nNew == 0 || insecure_rand.randbool() == 0))) {
394  // use a tried node
395  double fChanceFactor = 1.0;
396  while (1) {
398  int nKBucketPos = insecure_rand.randrange(ADDRMAN_BUCKET_SIZE);
399  while (vvTried[nKBucket][nKBucketPos] == -1) {
401  nKBucketPos = (nKBucketPos + insecure_rand.randbits(ADDRMAN_BUCKET_SIZE_LOG2)) % ADDRMAN_BUCKET_SIZE;
402  }
403  int nId = vvTried[nKBucket][nKBucketPos];
404  assert(mapInfo.count(nId) == 1);
405  CAddrInfo& info = mapInfo[nId];
406  if (insecure_rand.randbits(30) < fChanceFactor * info.GetChance() * (1 << 30))
407  return info;
408  fChanceFactor *= 1.2;
409  }
410  } else {
411  // use a new node
412  double fChanceFactor = 1.0;
413  while (1) {
415  int nUBucketPos = insecure_rand.randrange(ADDRMAN_BUCKET_SIZE);
416  while (vvNew[nUBucket][nUBucketPos] == -1) {
418  nUBucketPos = (nUBucketPos + insecure_rand.randbits(ADDRMAN_BUCKET_SIZE_LOG2)) % ADDRMAN_BUCKET_SIZE;
419  }
420  int nId = vvNew[nUBucket][nUBucketPos];
421  assert(mapInfo.count(nId) == 1);
422  CAddrInfo& info = mapInfo[nId];
423  if (insecure_rand.randbits(30) < fChanceFactor * info.GetChance() * (1 << 30))
424  return info;
425  fChanceFactor *= 1.2;
426  }
427  }
428 }
429 
430 #ifdef DEBUG_ADDRMAN
431 int CAddrMan::Check_()
432 {
434 
435  std::unordered_set<int> setTried;
436  std::unordered_map<int, int> mapNew;
437 
438  if (vRandom.size() != (size_t)(nTried + nNew))
439  return -7;
440 
441  for (const auto& entry : mapInfo) {
442  int n = entry.first;
443  const CAddrInfo& info = entry.second;
444  if (info.fInTried) {
445  if (!info.nLastSuccess)
446  return -1;
447  if (info.nRefCount)
448  return -2;
449  setTried.insert(n);
450  } else {
451  if (info.nRefCount < 0 || info.nRefCount > ADDRMAN_NEW_BUCKETS_PER_ADDRESS)
452  return -3;
453  if (!info.nRefCount)
454  return -4;
455  mapNew[n] = info.nRefCount;
456  }
457  if (mapAddr[info] != n)
458  return -5;
459  if (info.nRandomPos < 0 || (size_t)info.nRandomPos >= vRandom.size() || vRandom[info.nRandomPos] != n)
460  return -14;
461  if (info.nLastTry < 0)
462  return -6;
463  if (info.nLastSuccess < 0)
464  return -8;
465  }
466 
467  if (setTried.size() != (size_t)nTried)
468  return -9;
469  if (mapNew.size() != (size_t)nNew)
470  return -10;
471 
472  for (int n = 0; n < ADDRMAN_TRIED_BUCKET_COUNT; n++) {
473  for (int i = 0; i < ADDRMAN_BUCKET_SIZE; i++) {
474  if (vvTried[n][i] != -1) {
475  if (!setTried.count(vvTried[n][i]))
476  return -11;
477  if (mapInfo[vvTried[n][i]].GetTriedBucket(nKey, m_asmap) != n)
478  return -17;
479  if (mapInfo[vvTried[n][i]].GetBucketPosition(nKey, false, n) != i)
480  return -18;
481  setTried.erase(vvTried[n][i]);
482  }
483  }
484  }
485 
486  for (int n = 0; n < ADDRMAN_NEW_BUCKET_COUNT; n++) {
487  for (int i = 0; i < ADDRMAN_BUCKET_SIZE; i++) {
488  if (vvNew[n][i] != -1) {
489  if (!mapNew.count(vvNew[n][i]))
490  return -12;
491  if (mapInfo[vvNew[n][i]].GetBucketPosition(nKey, true, n) != i)
492  return -19;
493  if (--mapNew[vvNew[n][i]] == 0)
494  mapNew.erase(vvNew[n][i]);
495  }
496  }
497  }
498 
499  if (setTried.size())
500  return -13;
501  if (mapNew.size())
502  return -15;
503  if (nKey.IsNull())
504  return -16;
505 
506  return 0;
507 }
508 #endif
509 
510 void CAddrMan::GetAddr_(std::vector<CAddress>& vAddr, size_t max_addresses, size_t max_pct, std::optional<Network> network)
511 {
513 
514  size_t nNodes = vRandom.size();
515  if (max_pct != 0) {
516  nNodes = max_pct * nNodes / 100;
517  }
518  if (max_addresses != 0) {
519  nNodes = std::min(nNodes, max_addresses);
520  }
521 
522  // gather a list of random nodes, skipping those of low quality
523  const int64_t now{GetAdjustedTime()};
524  for (unsigned int n = 0; n < vRandom.size(); n++) {
525  if (vAddr.size() >= nNodes)
526  break;
527 
528  int nRndPos = insecure_rand.randrange(vRandom.size() - n) + n;
529  SwapRandom(n, nRndPos);
530  assert(mapInfo.count(vRandom[n]) == 1);
531 
532  const CAddrInfo& ai = mapInfo[vRandom[n]];
533 
534  // Filter by network (optional)
535  if (network != std::nullopt && ai.GetNetClass() != network) continue;
536 
537  // Filter for quality
538  if (ai.IsTerrible(now)) continue;
539 
540  vAddr.push_back(ai);
541  }
542 }
543 
544 void CAddrMan::Connected_(const CService& addr, int64_t nTime)
545 {
547 
548  CAddrInfo* pinfo = Find(addr);
549 
550  // if not found, bail out
551  if (!pinfo)
552  return;
553 
554  CAddrInfo& info = *pinfo;
555 
556  // check whether we are talking about the exact same CService (including same port)
557  if (info != addr)
558  return;
559 
560  // update info
561  int64_t nUpdateInterval = 20 * 60;
562  if (nTime - info.nTime > nUpdateInterval)
563  info.nTime = nTime;
564 }
565 
566 void CAddrMan::SetServices_(const CService& addr, ServiceFlags nServices)
567 {
569 
570  CAddrInfo* pinfo = Find(addr);
571 
572  // if not found, bail out
573  if (!pinfo)
574  return;
575 
576  CAddrInfo& info = *pinfo;
577 
578  // check whether we are talking about the exact same CService (including same port)
579  if (info != addr)
580  return;
581 
582  // update info
583  info.nServices = nServices;
584 }
585 
587 {
589 
590  for (std::set<int>::iterator it = m_tried_collisions.begin(); it != m_tried_collisions.end();) {
591  int id_new = *it;
592 
593  bool erase_collision = false;
594 
595  // If id_new not found in mapInfo remove it from m_tried_collisions
596  if (mapInfo.count(id_new) != 1) {
597  erase_collision = true;
598  } else {
599  CAddrInfo& info_new = mapInfo[id_new];
600 
601  // Which tried bucket to move the entry to.
602  int tried_bucket = info_new.GetTriedBucket(nKey, m_asmap);
603  int tried_bucket_pos = info_new.GetBucketPosition(nKey, false, tried_bucket);
604  if (!info_new.IsValid()) { // id_new may no longer map to a valid address
605  erase_collision = true;
606  } else if (vvTried[tried_bucket][tried_bucket_pos] != -1) { // The position in the tried bucket is not empty
607 
608  // Get the to-be-evicted address that is being tested
609  int id_old = vvTried[tried_bucket][tried_bucket_pos];
610  CAddrInfo& info_old = mapInfo[id_old];
611 
612  // Has successfully connected in last X hours
613  if (GetAdjustedTime() - info_old.nLastSuccess < ADDRMAN_REPLACEMENT_HOURS*(60*60)) {
614  erase_collision = true;
615  } else if (GetAdjustedTime() - info_old.nLastTry < ADDRMAN_REPLACEMENT_HOURS*(60*60)) { // attempted to connect and failed in last X hours
616 
617  // Give address at least 60 seconds to successfully connect
618  if (GetAdjustedTime() - info_old.nLastTry > 60) {
619  LogPrint(BCLog::ADDRMAN, "Replacing %s with %s in tried table\n", info_old.ToString(), info_new.ToString());
620 
621  // Replaces an existing address already in the tried table with the new address
622  Good_(info_new, false, GetAdjustedTime());
623  erase_collision = true;
624  }
625  } else if (GetAdjustedTime() - info_new.nLastSuccess > ADDRMAN_TEST_WINDOW) {
626  // If the collision hasn't resolved in some reasonable amount of time,
627  // just evict the old entry -- we must not be able to
628  // connect to it for some reason.
629  LogPrint(BCLog::ADDRMAN, "Unable to test; replacing %s with %s in tried table anyway\n", info_old.ToString(), info_new.ToString());
630  Good_(info_new, false, GetAdjustedTime());
631  erase_collision = true;
632  }
633  } else { // Collision is not actually a collision anymore
634  Good_(info_new, false, GetAdjustedTime());
635  erase_collision = true;
636  }
637  }
638 
639  if (erase_collision) {
640  m_tried_collisions.erase(it++);
641  } else {
642  it++;
643  }
644  }
645 }
646 
648 {
650 
651  if (m_tried_collisions.size() == 0) return CAddrInfo();
652 
653  std::set<int>::iterator it = m_tried_collisions.begin();
654 
655  // Selects a random element from m_tried_collisions
656  std::advance(it, insecure_rand.randrange(m_tried_collisions.size()));
657  int id_new = *it;
658 
659  // If id_new not found in mapInfo remove it from m_tried_collisions
660  if (mapInfo.count(id_new) != 1) {
661  m_tried_collisions.erase(it);
662  return CAddrInfo();
663  }
664 
665  const CAddrInfo& newInfo = mapInfo[id_new];
666 
667  // which tried bucket to move the entry to
668  int tried_bucket = newInfo.GetTriedBucket(nKey, m_asmap);
669  int tried_bucket_pos = newInfo.GetBucketPosition(nKey, false, tried_bucket);
670 
671  int id_old = vvTried[tried_bucket][tried_bucket_pos];
672 
673  return mapInfo[id_old];
674 }
675 
676 std::vector<bool> CAddrMan::DecodeAsmap(fs::path path)
677 {
678  std::vector<bool> bits;
679  FILE *filestr = fsbridge::fopen(path, "rb");
680  CAutoFile file(filestr, SER_DISK, CLIENT_VERSION);
681  if (file.IsNull()) {
682  LogPrintf("Failed to open asmap file from disk\n");
683  return bits;
684  }
685  fseek(filestr, 0, SEEK_END);
686  int length = ftell(filestr);
687  LogPrintf("Opened asmap file %s (%d bytes) from disk\n", path, length);
688  fseek(filestr, 0, SEEK_SET);
689  uint8_t cur_byte;
690  for (int i = 0; i < length; ++i) {
691  file >> cur_byte;
692  for (int bit = 0; bit < 8; ++bit) {
693  bits.push_back((cur_byte >> bit) & 1);
694  }
695  }
696  if (!SanityCheckASMap(bits)) {
697  LogPrintf("Sanity check of asmap file %s failed\n", path);
698  return {};
699  }
700  return bits;
701 }
CAddrMan::Create
CAddrInfo * Create(const CAddress &addr, const CNetAddr &addrSource, int *pnId=nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs)
find an entry, creating it if necessary.
Definition: addrman.cpp:95
CService
A combination of a network address (CNetAddr) and a (TCP) port.
Definition: netaddress.h:557
CAddrMan::nKey
uint256 nKey
secret key to randomize bucket select with
Definition: addrman.h:623
CAddrInfo::GetNewBucket
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:28
CAddrMan::Good_
void Good_(const CService &addr, bool test_before_evict, int64_t time) EXCLUSIVE_LOCKS_REQUIRED(cs)
Mark an entry "good", possibly moving it from "new" to "tried".
Definition: addrman.cpp:214
ADDRMAN_MIN_FAIL_DAYS
#define ADDRMAN_MIN_FAIL_DAYS
... in at least this many days
Definition: addrman.h:154
ADDRMAN_REPLACEMENT_HOURS
#define ADDRMAN_REPLACEMENT_HOURS
how recent a successful connection should be before we allow an address to be evicted from tried
Definition: addrman.h:157
CNetAddr::GetGroup
std::vector< unsigned char > GetGroup(const std::vector< bool > &asmap) const
Get the canonical identifier of our network group.
Definition: netaddress.cpp:766
assert
assert(!tx.IsCoinBase())
SER_DISK
@ SER_DISK
Definition: serialize.h:139
ADDRMAN_MAX_FAILURES
#define ADDRMAN_MAX_FAILURES
how many successive failures are allowed ...
Definition: addrman.h:151
ADDRMAN_TEST_WINDOW
static const int64_t ADDRMAN_TEST_WINDOW
the maximum time we'll spend trying to resolve a tried table collision, in seconds
Definition: addrman.h:168
ADDRMAN_TRIED_BUCKET_COUNT_LOG2
#define ADDRMAN_TRIED_BUCKET_COUNT_LOG2
Stochastic address manager.
Definition: addrman.h:127
CAddrMan::Add_
bool Add_(const CAddress &addr, const CNetAddr &source, int64_t nTimePenalty) EXCLUSIVE_LOCKS_REQUIRED(cs)
Add an entry to the "new" table.
Definition: addrman.cpp:282
ADDRMAN_TRIED_BUCKETS_PER_GROUP
#define ADDRMAN_TRIED_BUCKETS_PER_GROUP
over how many buckets entries with tried addresses from a single group (/16 for IPv4) are spread
Definition: addrman.h:136
CAddrMan::m_tried_collisions
std::set< int > m_tried_collisions
Holds addrs inserted into tried table that collide with existing entries. Test-before-evict disciplin...
Definition: addrman.h:682
CAddrMan::SwapRandom
void SwapRandom(unsigned int nRandomPos1, unsigned int nRandomPos2) EXCLUSIVE_LOCKS_REQUIRED(cs)
Swap two elements in vRandom.
Definition: addrman.cpp:109
fsbridge::fopen
FILE * fopen(const fs::path &p, const char *mode)
Definition: fs.cpp:24
ADDRMAN_NEW_BUCKETS_PER_ADDRESS
#define ADDRMAN_NEW_BUCKETS_PER_ADDRESS
in how many buckets for entries with new addresses a single address may occur
Definition: addrman.h:142
CNetAddr::GetNetClass
Network GetNetClass() const
Definition: netaddress.cpp:707
CAddrMan::Delete
void Delete(int nId) EXCLUSIVE_LOCKS_REQUIRED(cs)
Delete an entry. It must not be in tried, and have refcount 0.
Definition: addrman.cpp:131
ADDRMAN_SET_TRIED_COLLISION_SIZE
#define ADDRMAN_SET_TRIED_COLLISION_SIZE
the maximum number of tried addr collisions to store
Definition: addrman.h:165
CAddrMan::GetAddr_
void GetAddr_(std::vector< CAddress > &vAddr, size_t max_addresses, size_t max_pct, std::optional< Network > network) EXCLUSIVE_LOCKS_REQUIRED(cs)
Return all or many randomly selected addresses, optionally by network.
Definition: addrman.cpp:510
FastRandomContext::randbool
bool randbool() noexcept
Generate a random boolean.
Definition: random.h:211
CNetAddr
Network address.
Definition: netaddress.h:118
source
const char * source
Definition: rpcconsole.cpp:62
CAddrInfo::nLastSuccess
int64_t nLastSuccess
last successful connection by us
Definition: addrman.h:46
CAddrInfo::GetChance
double GetChance(int64_t nNow=GetAdjustedTime()) const
Calculate the relative chance this entry should be given when selecting nodes to connect to.
Definition: addrman.cpp:65
CAddrMan::ResolveCollisions_
void ResolveCollisions_() EXCLUSIVE_LOCKS_REQUIRED(cs)
See if any to-be-evicted tried table entries have been tested and if so resolve the collisions.
Definition: addrman.cpp:586
ServiceFlags
ServiceFlags
nServices flags
Definition: protocol.h:270
CAddrInfo::IsTerrible
bool IsTerrible(int64_t nNow=GetAdjustedTime()) const
Determine whether the statistics about this entry are bad enough so that it can just be deleted.
Definition: addrman.cpp:45
CAddress::nServices
ServiceFlags nServices
Definition: protocol.h:397
CAddrMan::Attempt_
void Attempt_(const CService &addr, bool fCountFailure, int64_t nTime) EXCLUSIVE_LOCKS_REQUIRED(cs)
Mark an entry as attempted to connect.
Definition: addrman.cpp:357
CAddress::nTime
uint32_t nTime
Definition: protocol.h:395
ADDRMAN_NEW_BUCKET_COUNT_LOG2
#define ADDRMAN_NEW_BUCKET_COUNT_LOG2
total number of buckets for new addresses
Definition: addrman.h:130
ADDRMAN_RETRIES
#define ADDRMAN_RETRIES
after how many failed attempts we give up on a new node
Definition: addrman.h:148
SanityCheckASMap
bool SanityCheckASMap(const std::vector< bool > &asmap)
Definition: netaddress.cpp:1246
CAddrMan::SetServices_
void SetServices_(const CService &addr, ServiceFlags nServices) EXCLUSIVE_LOCKS_REQUIRED(cs)
Update an entry's service bits.
Definition: addrman.cpp:566
FastRandomContext::randbits
uint64_t randbits(int bits) noexcept
Generate a random (bits)-bit integer.
Definition: random.h:172
CNetAddr::ToStringIP
std::string ToStringIP() const
Definition: netaddress.cpp:608
CAutoFile
Non-refcounted RAII wrapper for FILE*.
Definition: streams.h:564
CAddrInfo
Extended statistics about a CAddress.
Definition: addrman.h:32
CAddrInfo::nLastCountAttempt
int64_t nLastCountAttempt
last counted attempt (memory only)
Definition: addrman.h:39
CService::ToString
std::string ToString() const
Definition: netaddress.cpp:1037
CAddrMan::SelectTriedCollision_
CAddrInfo SelectTriedCollision_() EXCLUSIVE_LOCKS_REQUIRED(cs)
Return a random to-be-evicted tried table address.
Definition: addrman.cpp:647
ADDRMAN_BUCKET_SIZE
#define ADDRMAN_BUCKET_SIZE
Definition: addrman.h:162
ADDRMAN_TRIED_BUCKET_COUNT
#define ADDRMAN_TRIED_BUCKET_COUNT
Convenience.
Definition: addrman.h:160
CNetAddr::IsRoutable
bool IsRoutable() const
Definition: netaddress.cpp:490
netaddress.h
CAddrMan::ClearNew
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:147
CAddrInfo::nRefCount
int nRefCount
reference count in new sets (memory only)
Definition: addrman.h:52
CNetAddr::GetMappedAS
uint32_t GetMappedAS(const std::vector< bool > &asmap) const
Definition: netaddress.cpp:725
LogPrintf
#define LogPrintf(...)
Definition: logging.h:184
CAddrMan::insecure_rand
FastRandomContext insecure_rand
Source of random numbers for randomization in inner loops.
Definition: addrman.h:626
CNetAddr::IsValid
bool IsValid() const
Definition: netaddress.cpp:451
CAddrInfo::nRandomPos
int nRandomPos
position in vRandom
Definition: addrman.h:58
CAddrInfo::nLastTry
int64_t nLastTry
last try whatsoever by us (memory only)
Definition: addrman.h:36
CAutoFile::IsNull
bool IsNull() const
Return true if the wrapped FILE* is nullptr, false otherwise.
Definition: streams.h:609
AssertLockHeld
#define AssertLockHeld(cs)
Definition: sync.h:81
CAddrInfo::GetBucketPosition
int GetBucketPosition(const uint256 &nKey, bool fNew, int nBucket) const
Calculate in which position of a bucket to store this entry.
Definition: addrman.cpp:39
uint256
256-bit opaque blob.
Definition: uint256.h:124
LogPrint
#define LogPrint(category,...)
Definition: logging.h:188
ADDRMAN_NEW_BUCKET_COUNT
#define ADDRMAN_NEW_BUCKET_COUNT
Definition: addrman.h:161
ADDRMAN_BUCKET_SIZE_LOG2
#define ADDRMAN_BUCKET_SIZE_LOG2
maximum allowed number of entries in buckets for new and tried addresses
Definition: addrman.h:133
CAddrMan::MakeTried
void MakeTried(CAddrInfo &info, int nId) EXCLUSIVE_LOCKS_REQUIRED(cs)
Move an entry from the "new" table(s) to the "tried" table.
Definition: addrman.cpp:164
CAddrInfo::GetTriedBucket
int GetTriedBucket(const uint256 &nKey, const std::vector< bool > &asmap) const
Calculate in which "tried" bucket this entry belongs.
Definition: addrman.cpp:18
CAddrMan::Connected_
void Connected_(const CService &addr, int64_t nTime) EXCLUSIVE_LOCKS_REQUIRED(cs)
We have successfully connected to this peer.
Definition: addrman.cpp:544
CAddrMan::Select_
CAddrInfo Select_(bool newOnly) EXCLUSIVE_LOCKS_REQUIRED(cs)
Select an address to connect to, if newOnly is set to true, only the new table is selected from.
Definition: addrman.cpp:381
CAddrMan::m_asmap
std::vector< bool > m_asmap
Definition: addrman.h:190
GetAdjustedTime
int64_t GetAdjustedTime()
Definition: timedata.cpp:34
base_blob::IsNull
bool IsNull() const
Definition: uint256.h:31
CAddress
A CService with information about it as peer.
Definition: protocol.h:357
CAddrInfo::nAttempts
int nAttempts
connection attempts since last successful attempt
Definition: addrman.h:49
CLIENT_VERSION
static const int CLIENT_VERSION
bitcoind-res.rc includes this file, but it cannot cope with real c++ code.
Definition: clientversion.h:33
CHashWriter
A writer stream (for serialization) that computes a 256-bit hash.
Definition: hash.h:100
logging.h
FastRandomContext::randrange
uint64_t randrange(uint64_t range) noexcept
Generate a random integer in the range [0..range).
Definition: random.h:190
hash.h
serialize.h
addrman.h
BCLog::ADDRMAN
@ ADDRMAN
Definition: logging.h:47
CAddrMan::Find
CAddrInfo * Find(const CNetAddr &addr, int *pnId=nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs)
Find an entry.
Definition: addrman.cpp:80
CAddrInfo::fInTried
bool fInTried
in tried set? (memory only)
Definition: addrman.h:55
ADDRMAN_NEW_BUCKETS_PER_SOURCE_GROUP
#define ADDRMAN_NEW_BUCKETS_PER_SOURCE_GROUP
over how many buckets entries with new addresses originating from a single group are spread
Definition: addrman.h:139
ADDRMAN_HORIZON_DAYS
#define ADDRMAN_HORIZON_DAYS
how old addresses can maximally be
Definition: addrman.h:145
BCLog::NET
@ NET
Definition: logging.h:38
CAddrMan::DecodeAsmap
static std::vector< bool > DecodeAsmap(fs::path path)
Definition: addrman.cpp:676
SER_GETHASH
@ SER_GETHASH
Definition: serialize.h:140
CAddrMan::cs
Mutex cs
A mutex to protect the inner data structures.
Definition: addrman.h:630
it
auto it
Definition: validation.cpp:355
CService::GetKey
std::vector< unsigned char > GetKey() const
Definition: netaddress.cpp:1015