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