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