Bitcoin ABC  0.22.13
P2P Digital Currency
addrman.h
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 #ifndef BITCOIN_ADDRMAN_H
7 #define BITCOIN_ADDRMAN_H
8 
9 #include <clientversion.h>
10 #include <fs.h>
11 #include <hash.h>
12 #include <netaddress.h>
13 #include <protocol.h>
14 #include <random.h>
15 #include <streams.h>
16 #include <sync.h>
17 #include <timedata.h>
18 #include <util/system.h>
19 
20 #include <cstdint>
21 #include <iostream>
22 #include <map>
23 #include <set>
24 #include <vector>
25 
29 class CAddrInfo : public CAddress {
30 public:
32  int64_t nLastTry{0};
33 
35  int64_t nLastCountAttempt{0};
36 
37 private:
40 
42  int64_t nLastSuccess{0};
43 
45  int nAttempts{0};
46 
48  int nRefCount{0};
49 
51  bool fInTried{false};
52 
54  int nRandomPos{-1};
55 
56  friend class CAddrMan;
57 
58 public:
60  READWRITEAS(CAddress, obj);
61  READWRITE(obj.source, obj.nLastSuccess, obj.nAttempts);
62  }
63 
64  CAddrInfo(const CAddress &addrIn, const CNetAddr &addrSource)
65  : CAddress(addrIn), source(addrSource) {}
66 
67  CAddrInfo() : CAddress(), source() {}
68 
70  int GetTriedBucket(const uint256 &nKey,
71  const std::vector<bool> &asmap) const;
72 
75  int GetNewBucket(const uint256 &nKey, const CNetAddr &src,
76  const std::vector<bool> &asmap) const;
77 
80  int GetNewBucket(const uint256 &nKey,
81  const std::vector<bool> &asmap) const {
82  return GetNewBucket(nKey, source, asmap);
83  }
84 
86  int GetBucketPosition(const uint256 &nKey, bool fNew, int nBucket) const;
87 
90  bool IsTerrible(int64_t nNow = GetAdjustedTime()) const;
91 
94  double GetChance(int64_t nNow = GetAdjustedTime()) const;
95 };
96 
135 #define ADDRMAN_TRIED_BUCKET_COUNT_LOG2 8
137 
139 #define ADDRMAN_NEW_BUCKET_COUNT_LOG2 10
140 
142 #define ADDRMAN_BUCKET_SIZE_LOG2 6
143 
146 #define ADDRMAN_TRIED_BUCKETS_PER_GROUP 8
147 
150 #define ADDRMAN_NEW_BUCKETS_PER_SOURCE_GROUP 64
151 
154 #define ADDRMAN_NEW_BUCKETS_PER_ADDRESS 8
155 
157 #define ADDRMAN_HORIZON_DAYS 30
158 
160 #define ADDRMAN_RETRIES 3
161 
163 #define ADDRMAN_MAX_FAILURES 10
164 
166 #define ADDRMAN_MIN_FAIL_DAYS 7
167 
170 #define ADDRMAN_REPLACEMENT_SECONDS (4 * 60 * 60)
171 
173 #define ADDRMAN_GETADDR_MAX_PCT 23
174 
176 #define ADDRMAN_GETADDR_MAX 2500
177 
179 #define ADDRMAN_TRIED_BUCKET_COUNT (1 << ADDRMAN_TRIED_BUCKET_COUNT_LOG2)
180 #define ADDRMAN_NEW_BUCKET_COUNT (1 << ADDRMAN_NEW_BUCKET_COUNT_LOG2)
181 #define ADDRMAN_BUCKET_SIZE (1 << ADDRMAN_BUCKET_SIZE_LOG2)
182 
184 #define ADDRMAN_SET_TRIED_COLLISION_SIZE 10
185 
188 static const int64_t ADDRMAN_TEST_WINDOW = 40 * 60;
189 
193 class CAddrMan {
194  friend class CAddrManTest;
195 
196 protected:
199 
200 private:
202  int nIdCount GUARDED_BY(cs);
203 
205  std::map<int, CAddrInfo> mapInfo GUARDED_BY(cs);
206 
208  std::map<CNetAddr, int> mapAddr GUARDED_BY(cs);
209 
211  std::vector<int> vRandom GUARDED_BY(cs);
212 
213  // number of "tried" entries
214  int nTried GUARDED_BY(cs);
215 
218 
220  int nNew GUARDED_BY(cs);
221 
224 
226  int64_t nLastGood GUARDED_BY(cs);
227 
230  std::set<int> m_tried_collisions;
231 
232 protected:
235 
238 
240  CAddrInfo *Find(const CNetAddr &addr, int *pnId = nullptr)
242 
245  CAddrInfo *Create(const CAddress &addr, const CNetAddr &addrSource,
246  int *pnId = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs);
247 
249  void SwapRandom(unsigned int nRandomPos1, unsigned int nRandomPos2)
251 
253  void MakeTried(CAddrInfo &info, int nId) EXCLUSIVE_LOCKS_REQUIRED(cs);
254 
256  void Delete(int nId) EXCLUSIVE_LOCKS_REQUIRED(cs);
257 
260  void ClearNew(int nUBucket, int nUBucketPos) EXCLUSIVE_LOCKS_REQUIRED(cs);
261 
263  void Good_(const CService &addr, bool test_before_evict, int64_t time)
265 
267  bool Add_(const CAddress &addr, const CNetAddr &source,
268  int64_t nTimePenalty) EXCLUSIVE_LOCKS_REQUIRED(cs);
269 
271  void Attempt_(const CService &addr, bool fCountFailure, int64_t nTime)
273 
276  CAddrInfo Select_(bool newOnly) EXCLUSIVE_LOCKS_REQUIRED(cs);
277 
280  void ResolveCollisions_() EXCLUSIVE_LOCKS_REQUIRED(cs);
281 
283  CAddrInfo SelectTriedCollision_() EXCLUSIVE_LOCKS_REQUIRED(cs);
284 
285 #ifdef DEBUG_ADDRMAN
286  int Check_() EXCLUSIVE_LOCKS_REQUIRED(cs);
288 #endif
289 
291  void GetAddr_(std::vector<CAddress> &vAddr) EXCLUSIVE_LOCKS_REQUIRED(cs);
292 
294  void Connected_(const CService &addr, int64_t nTime)
296 
298  void SetServices_(const CService &addr, ServiceFlags nServices)
300 
301 public:
302  // Compressed IP->ASN mapping, loaded from a file when a node starts.
303  // Should be always empty if no file was provided.
304  // This mapping is then used for bucketing nodes in Addrman.
305  //
306  // If asmap is provided, nodes will be bucketed by
307  // AS they belong to, in order to make impossible for a node
308  // to connect to several nodes hosted in a single AS.
309  // This is done in response to Erebus attack, but also to generally
310  // diversify the connections every node creates,
311  // especially useful when a large fraction of nodes
312  // operate under a couple of cloud providers.
313  //
314  // If a new asmap was provided, the existing records
315  // would be re-bucketed accordingly.
316  std::vector<bool> m_asmap;
317 
318  // Read asmap from provided binary file
319  static std::vector<bool> DecodeAsmap(fs::path path);
320 
353  template <typename Stream> void Serialize(Stream &s) const {
354  LOCK(cs);
355 
356  uint8_t nVersion = 2;
357  s << nVersion;
358  s << uint8_t(32);
359  s << nKey;
360  s << nNew;
361  s << nTried;
362 
363  int nUBuckets = ADDRMAN_NEW_BUCKET_COUNT ^ (1 << 30);
364  s << nUBuckets;
365  std::map<int, int> mapUnkIds;
366  int nIds = 0;
367  for (const auto &entry : mapInfo) {
368  mapUnkIds[entry.first] = nIds;
369  const CAddrInfo &info = entry.second;
370  if (info.nRefCount) {
371  // this means nNew was wrong, oh ow
372  assert(nIds != nNew);
373  s << info;
374  nIds++;
375  }
376  }
377  nIds = 0;
378  for (const auto &entry : mapInfo) {
379  const CAddrInfo &info = entry.second;
380  if (info.fInTried) {
381  // this means nTried was wrong, oh ow
382  assert(nIds != nTried);
383  s << info;
384  nIds++;
385  }
386  }
387  for (int bucket = 0; bucket < ADDRMAN_NEW_BUCKET_COUNT; bucket++) {
388  int nSize = 0;
389  for (int i = 0; i < ADDRMAN_BUCKET_SIZE; i++) {
390  if (vvNew[bucket][i] != -1) nSize++;
391  }
392  s << nSize;
393  for (int i = 0; i < ADDRMAN_BUCKET_SIZE; i++) {
394  if (vvNew[bucket][i] != -1) {
395  int nIndex = mapUnkIds[vvNew[bucket][i]];
396  s << nIndex;
397  }
398  }
399  }
400  // Store asmap version after bucket entries so that it
401  // can be ignored by older clients for backward compatibility.
402  uint256 asmap_version;
403  if (m_asmap.size() != 0) {
404  asmap_version = SerializeHash(m_asmap);
405  }
406  s << asmap_version;
407  }
408 
409  template <typename Stream> void Unserialize(Stream &s) {
410  LOCK(cs);
411 
412  Clear();
413  uint8_t nVersion;
414  s >> nVersion;
415  uint8_t nKeySize;
416  s >> nKeySize;
417  if (nKeySize != 32) {
418  throw std::ios_base::failure(
419  "Incorrect keysize in addrman deserialization");
420  }
421 
422  s >> nKey;
423  s >> nNew;
424  s >> nTried;
425  int nUBuckets = 0;
426  s >> nUBuckets;
427  if (nVersion != 0) {
428  nUBuckets ^= (1 << 30);
429  }
430 
432  throw std::ios_base::failure(
433  "Corrupt CAddrMan serialization, nNew exceeds limit.");
434  }
435 
436  if (nTried > ADDRMAN_TRIED_BUCKET_COUNT * ADDRMAN_BUCKET_SIZE) {
437  throw std::ios_base::failure(
438  "Corrupt CAddrMan serialization, nTried exceeds limit.");
439  }
440 
441  // Deserialize entries from the new table.
442  for (int n = 0; n < nNew; n++) {
443  CAddrInfo &info = mapInfo[n];
444  s >> info;
445  mapAddr[info] = n;
446  info.nRandomPos = vRandom.size();
447  vRandom.push_back(n);
448  }
449  nIdCount = nNew;
450 
451  // Deserialize entries from the tried table.
452  int nLost = 0;
453  for (int n = 0; n < nTried; n++) {
454  CAddrInfo info;
455  s >> info;
456  int nKBucket = info.GetTriedBucket(nKey, m_asmap);
457  int nKBucketPos = info.GetBucketPosition(nKey, false, nKBucket);
458  if (vvTried[nKBucket][nKBucketPos] == -1) {
459  info.nRandomPos = vRandom.size();
460  info.fInTried = true;
461  vRandom.push_back(nIdCount);
462  mapInfo[nIdCount] = info;
463  mapAddr[info] = nIdCount;
464  vvTried[nKBucket][nKBucketPos] = nIdCount;
465  nIdCount++;
466  } else {
467  nLost++;
468  }
469  }
470  nTried -= nLost;
471 
472  // Store positions in the new table buckets to apply later (if
473  // possible). Represents which entry belonged to which bucket when
474  // serializing
475  std::map<int, int> entryToBucket;
476 
477  for (int bucket = 0; bucket < nUBuckets; bucket++) {
478  int nSize = 0;
479  s >> nSize;
480  for (int n = 0; n < nSize; n++) {
481  int nIndex = 0;
482  s >> nIndex;
483  if (nIndex >= 0 && nIndex < nNew) {
484  entryToBucket[nIndex] = bucket;
485  }
486  }
487  }
488 
489  uint256 supplied_asmap_version;
490  if (m_asmap.size() != 0) {
491  supplied_asmap_version = SerializeHash(m_asmap);
492  }
493  uint256 serialized_asmap_version;
494  if (nVersion > 1) {
495  s >> serialized_asmap_version;
496  }
497 
498  for (int n = 0; n < nNew; n++) {
499  CAddrInfo &info = mapInfo[n];
500  int bucket = entryToBucket[n];
501  int nUBucketPos = info.GetBucketPosition(nKey, true, bucket);
502  if (nVersion == 2 && nUBuckets == ADDRMAN_NEW_BUCKET_COUNT &&
503  vvNew[bucket][nUBucketPos] == -1 &&
505  serialized_asmap_version == supplied_asmap_version) {
506  // Bucketing has not changed, using existing bucket positions
507  // for the new table
508  vvNew[bucket][nUBucketPos] = n;
509  info.nRefCount++;
510  } else {
511  // In case the new table data cannot be used (nVersion unknown,
512  // bucket count wrong or new asmap), try to give them a
513  // reference based on their primary source address.
515  "Bucketing method was updated, re-bucketing addrman "
516  "entries from disk\n");
517  bucket = info.GetNewBucket(nKey, m_asmap);
518  nUBucketPos = info.GetBucketPosition(nKey, true, bucket);
519  if (vvNew[bucket][nUBucketPos] == -1) {
520  vvNew[bucket][nUBucketPos] = n;
521  info.nRefCount++;
522  }
523  }
524  }
525 
526  // Prune new entries with refcount 0 (as a result of collisions).
527  int nLostUnk = 0;
528  for (std::map<int, CAddrInfo>::const_iterator it = mapInfo.begin();
529  it != mapInfo.end();) {
530  if (it->second.fInTried == false && it->second.nRefCount == 0) {
531  std::map<int, CAddrInfo>::const_iterator itCopy = it++;
532  Delete(itCopy->first);
533  nLostUnk++;
534  } else {
535  it++;
536  }
537  }
538  if (nLost + nLostUnk > 0) {
540  "addrman lost %i new and %i tried addresses due to "
541  "collisions\n",
542  nLostUnk, nLost);
543  }
544 
545  Check();
546  }
547 
548  void Clear() {
549  LOCK(cs);
550  std::vector<int>().swap(vRandom);
551  nKey = insecure_rand.rand256();
552  for (size_t bucket = 0; bucket < ADDRMAN_NEW_BUCKET_COUNT; bucket++) {
553  for (size_t entry = 0; entry < ADDRMAN_BUCKET_SIZE; entry++) {
554  vvNew[bucket][entry] = -1;
555  }
556  }
557  for (size_t bucket = 0; bucket < ADDRMAN_TRIED_BUCKET_COUNT; bucket++) {
558  for (size_t entry = 0; entry < ADDRMAN_BUCKET_SIZE; entry++) {
559  vvTried[bucket][entry] = -1;
560  }
561  }
562 
563  nIdCount = 0;
564  nTried = 0;
565  nNew = 0;
566  // Initially at 1 so that "never" is strictly worse.
567  nLastGood = 1;
568  mapInfo.clear();
569  mapAddr.clear();
570  }
571 
572  CAddrMan() { Clear(); }
573 
574  ~CAddrMan() { nKey.SetNull(); }
575 
577  size_t size() const {
578  // TODO: Cache this in an atomic to avoid this overhead
579  LOCK(cs);
580  return vRandom.size();
581  }
582 
584  void Check() {
585 #ifdef DEBUG_ADDRMAN
586  {
587  LOCK(cs);
588  int err;
589  if ((err = Check_())) {
590  LogPrintf("ADDRMAN CONSISTENCY CHECK FAILED!!! err=%i\n", err);
591  }
592  }
593 #endif
594  }
595 
597  bool Add(const CAddress &addr, const CNetAddr &source,
598  int64_t nTimePenalty = 0) {
599  LOCK(cs);
600  bool fRet = false;
601  Check();
602  fRet |= Add_(addr, source, nTimePenalty);
603  Check();
604  if (fRet) {
605  LogPrint(BCLog::ADDRMAN, "Added %s from %s: %i tried, %i new\n",
606  addr.ToStringIPPort(), source.ToString(), nTried, nNew);
607  }
608  return fRet;
609  }
610 
612  bool Add(const std::vector<CAddress> &vAddr, const CNetAddr &source,
613  int64_t nTimePenalty = 0) {
614  LOCK(cs);
615  int nAdd = 0;
616  Check();
617  for (const CAddress &a : vAddr) {
618  nAdd += Add_(a, source, nTimePenalty) ? 1 : 0;
619  }
620  Check();
621  if (nAdd) {
623  "Added %i addresses from %s: %i tried, %i new\n", nAdd,
624  source.ToString(), nTried, nNew);
625  }
626  return nAdd > 0;
627  }
628 
630  void Good(const CService &addr, bool test_before_evict = true,
631  int64_t nTime = GetAdjustedTime()) {
632  LOCK(cs);
633  Check();
634  Good_(addr, test_before_evict, nTime);
635  Check();
636  }
637 
639  void Attempt(const CService &addr, bool fCountFailure,
640  int64_t nTime = GetAdjustedTime()) {
641  LOCK(cs);
642  Check();
643  Attempt_(addr, fCountFailure, nTime);
644  Check();
645  }
646 
650  LOCK(cs);
651  Check();
652  ResolveCollisions_();
653  Check();
654  }
655 
659  CAddrInfo ret;
660  {
661  LOCK(cs);
662  Check();
663  ret = SelectTriedCollision_();
664  Check();
665  }
666  return ret;
667  }
668 
672  CAddrInfo Select(bool newOnly = false) {
673  CAddrInfo addrRet;
674  {
675  LOCK(cs);
676  Check();
677  addrRet = Select_(newOnly);
678  Check();
679  }
680  return addrRet;
681  }
682 
684  std::vector<CAddress> GetAddr() {
685  Check();
686  std::vector<CAddress> vAddr;
687  {
688  LOCK(cs);
689  GetAddr_(vAddr);
690  }
691  Check();
692  return vAddr;
693  }
694 
696  void Connected(const CService &addr, int64_t nTime = GetAdjustedTime()) {
697  LOCK(cs);
698  Check();
699  Connected_(addr, nTime);
700  Check();
701  }
702 
704  LOCK(cs);
705  Check();
706  SetServices_(addr, nServices);
707  Check();
708  }
709 };
710 
711 #endif // BITCOIN_ADDRMAN_H
int nRefCount
reference count in new sets (memory only)
Definition: addrman.h:48
ServiceFlags
nServices flags.
Definition: protocol.h:320
CAddrInfo Select(bool newOnly=false)
Choose an address to connect to.
Definition: addrman.h:672
void SetNull()
Definition: uint256.h:35
#define LogPrint(category,...)
Definition: logging.h:192
void Attempt(const CService &addr, bool fCountFailure, int64_t nTime=GetAdjustedTime())
Mark an entry as connection attempted to.
Definition: addrman.h:639
int GetTriedBucket(const uint256 &nKey, const std::vector< bool > &asmap) const
Calculate in which "tried" bucket this entry belongs.
Definition: addrman.cpp:14
~CAddrMan()
Definition: addrman.h:574
int nAttempts
connection attempts since last successful attempt
Definition: addrman.h:45
uint256 rand256() noexcept
generate a random uint256.
Definition: random.cpp:681
static void LogPrintf(const char *fmt, const Args &... args)
Definition: logging.h:174
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
std::set< int > m_tried_collisions
Holds addrs inserted into tried table that collide with existing entries.
Definition: addrman.h:230
std::string ToString() const
Definition: netaddress.cpp:364
int nRandomPos
position in vRandom
Definition: addrman.h:54
bool Add(const CAddress &addr, const CNetAddr &source, int64_t nTimePenalty=0)
Add a single address.
Definition: addrman.h:597
#define ADDRMAN_TRIED_BUCKET_COUNT
Convenience.
Definition: addrman.h:179
#define READWRITEAS(type, obj)
Definition: serialize.h:192
bool fInTried
in tried set? (memory only)
Definition: addrman.h:51
Stochastical (IP) address manager.
Definition: addrman.h:193
RecursiveMutex cs
critical section to protect the inner data structures
Definition: addrman.h:198
uint256 SerializeHash(const T &obj, int nType=SER_GETHASH, int nVersion=PROTOCOL_VERSION)
Compute the 256-bit hash of an object&#39;s serialization.
Definition: hash.h:196
std::vector< CAddress > GetAddr()
Return a bunch of addresses, selected at random.
Definition: addrman.h:684
Extended statistics about a CAddress.
Definition: addrman.h:29
FastRandomContext insecure_rand
Source of random numbers for randomization in inner loops.
Definition: addrman.h:237
void Unserialize(Stream &s)
Definition: addrman.h:409
#define LOCK(cs)
Definition: sync.h:230
A combination of a network address (CNetAddr) and a (TCP) port.
Definition: netaddress.h:179
Fast randomness source.
Definition: random.h:113
void Check()
Consistency check.
Definition: addrman.h:584
int GetNewBucket(const uint256 &nKey, const std::vector< bool > &asmap) const
Calculate in which "new" bucket this entry belongs, using its default source.
Definition: addrman.h:80
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 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
CAddrInfo()
Definition: addrman.h:67
IP address (IPv6, or IPv4 using mapped IPv6 range (::FFFF:0:0/96))
Definition: netaddress.h:30
size_t size() const
Return the number of (unique) addresses in all tables.
Definition: addrman.h:577
256-bit opaque blob.
Definition: uint256.h:120
unsigned int nTime
Definition: protocol.h:466
ServiceFlags nServices
Definition: protocol.h:463
#define EXCLUSIVE_LOCKS_REQUIRED(...)
Definition: threadsafety.h:56
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
SERIALIZE_METHODS(CAddrInfo, obj)
Definition: addrman.h:59
void Clear()
Definition: addrman.h:548
CAddrMan()
Definition: addrman.h:572
CAddrInfo(const CAddress &addrIn, const CNetAddr &addrSource)
Definition: addrman.h:64
#define ADDRMAN_NEW_BUCKET_COUNT
Definition: addrman.h:180
int64_t GetAdjustedTime()
Definition: timedata.cpp:34
CAddrInfo SelectTriedCollision()
Randomly select an address in tried that another address is attempting to evict.
Definition: addrman.h:658
std::string ToStringIPPort() const
Definition: netaddress.cpp:772
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 ResolveCollisions()
See if any to-be-evicted tried table entries have been tested and if so resolve the collisions...
Definition: addrman.h:649
#define GUARDED_BY(x)
Definition: threadsafety.h:45
std::vector< bool > m_asmap
Definition: addrman.h:316
bool Add(const std::vector< CAddress > &vAddr, const CNetAddr &source, int64_t nTimePenalty=0)
Add multiple addresses.
Definition: addrman.h:612
void Good(const CService &addr, bool test_before_evict=true, int64_t nTime=GetAdjustedTime())
Mark an entry as accessible.
Definition: addrman.h:630
void SetServices(const CService &addr, ServiceFlags nServices)
Definition: addrman.h:703
uint256 nKey
secret key to randomize bucket select with
Definition: addrman.h:234
void Serialize(Stream &s) const
serialized format:
Definition: addrman.h:353
#define READWRITE(...)
Definition: serialize.h:191
void Connected(const CService &addr, int64_t nTime=GetAdjustedTime())
Mark an entry as currently-connected-to.
Definition: addrman.h:696
int64_t nLastSuccess
last successful connection by us
Definition: addrman.h:42
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