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