Raven Core  3.0.0
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 // Copyright (c) 2017-2019 The Raven Core developers
4 // Distributed under the MIT software license, see the accompanying
5 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
6 
7 #ifndef RAVEN_ADDRMAN_H
8 #define RAVEN_ADDRMAN_H
9 
10 #include "netaddress.h"
11 #include "protocol.h"
12 #include "random.h"
13 #include "sync.h"
14 #include "timedata.h"
15 #include "util.h"
16 
17 #include <map>
18 #include <set>
19 #include <stdint.h>
20 #include <vector>
21 
25 class CAddrInfo : public CAddress
26 {
27 
28 
29 public:
31  int64_t nLastTry;
32 
35 
36 private:
39 
41  int64_t nLastSuccess;
42 
44  int nAttempts;
45 
47  int nRefCount;
48 
50  bool fInTried;
51 
54 
55  friend class CAddrMan;
56 
57 public:
58 
60 
61  template <typename Stream, typename Operation>
62  inline void SerializationOp(Stream& s, Operation ser_action) {
63  READWRITE(*(CAddress*)this);
64  READWRITE(source);
65  READWRITE(nLastSuccess);
66  READWRITE(nAttempts);
67  }
68 
69  void Init()
70  {
71  nLastSuccess = 0;
72  nLastTry = 0;
73  nLastCountAttempt = 0;
74  nAttempts = 0;
75  nRefCount = 0;
76  fInTried = false;
77  nRandomPos = -1;
78  }
79 
80  CAddrInfo(const CAddress &addrIn, const CNetAddr &addrSource) : CAddress(addrIn), source(addrSource)
81  {
82  Init();
83  }
84 
85  CAddrInfo() : CAddress(), source()
86  {
87  Init();
88  }
89 
91  int GetTriedBucket(const uint256 &nKey) const;
92 
94  int GetNewBucket(const uint256 &nKey, const CNetAddr& src) const;
95 
97  int GetNewBucket(const uint256 &nKey) const
98  {
99  return GetNewBucket(nKey, source);
100  }
101 
103  int GetBucketPosition(const uint256 &nKey, bool fNew, int nBucket) const;
104 
106  bool IsTerrible(int64_t nNow = GetAdjustedTime()) const;
107 
109  double GetChance(int64_t nNow = GetAdjustedTime()) const;
110 
111 };
112 
139 #define ADDRMAN_TRIED_BUCKET_COUNT_LOG2 8
141 
143 #define ADDRMAN_NEW_BUCKET_COUNT_LOG2 10
144 
146 #define ADDRMAN_BUCKET_SIZE_LOG2 6
147 
149 #define ADDRMAN_TRIED_BUCKETS_PER_GROUP 8
150 
152 #define ADDRMAN_NEW_BUCKETS_PER_SOURCE_GROUP 64
153 
155 #define ADDRMAN_NEW_BUCKETS_PER_ADDRESS 8
156 
158 #define ADDRMAN_HORIZON_DAYS 30
159 
161 #define ADDRMAN_RETRIES 3
162 
164 #define ADDRMAN_MAX_FAILURES 10
165 
167 #define ADDRMAN_MIN_FAIL_DAYS 7
168 
170 #define ADDRMAN_GETADDR_MAX_PCT 23
171 
173 #define ADDRMAN_GETADDR_MAX 2500
174 
176 #define ADDRMAN_TRIED_BUCKET_COUNT (1 << ADDRMAN_TRIED_BUCKET_COUNT_LOG2)
177 #define ADDRMAN_NEW_BUCKET_COUNT (1 << ADDRMAN_NEW_BUCKET_COUNT_LOG2)
178 #define ADDRMAN_BUCKET_SIZE (1 << ADDRMAN_BUCKET_SIZE_LOG2)
179 
183 class CAddrMan
184 {
185 private:
188 
190  int nIdCount;
191 
193  std::map<int, CAddrInfo> mapInfo;
194 
196  std::map<CNetAddr, int> mapAddr;
197 
199  std::vector<int> vRandom;
200 
201  // number of "tried" entries
202  int nTried;
203 
206 
208  int nNew;
209 
212 
214  int64_t nLastGood;
215 
216 protected:
219 
222 
224  CAddrInfo* Find(const CNetAddr& addr, int *pnId = nullptr);
225 
228  CAddrInfo* Create(const CAddress &addr, const CNetAddr &addrSource, int *pnId = nullptr);
229 
231  void SwapRandom(unsigned int nRandomPos1, unsigned int nRandomPos2);
232 
234  void MakeTried(CAddrInfo& info, int nId);
235 
237  void Delete(int nId);
238 
240  void ClearNew(int nUBucket, int nUBucketPos);
241 
243  void Good_(const CService &addr, int64_t nTime);
244 
246  bool Add_(const CAddress &addr, const CNetAddr& source, int64_t nTimePenalty);
247 
249  void Attempt_(const CService &addr, bool fCountFailure, int64_t nTime);
250 
252  CAddrInfo Select_(bool newOnly);
253 
255  virtual int RandomInt(int nMax);
256 
257 #ifdef DEBUG_ADDRMAN
258  int Check_();
260 #endif
261 
263  void GetAddr_(std::vector<CAddress> &vAddr);
264 
266  void Connected_(const CService &addr, int64_t nTime);
267 
269  void SetServices_(const CService &addr, ServiceFlags nServices);
270 
271 public:
301  template<typename Stream>
302  void Serialize(Stream &s) const
303  {
304  LOCK(cs);
305 
306  unsigned char nVersion = 1;
307  s << nVersion;
308  s << ((unsigned char)32);
309  s << nKey;
310  s << nNew;
311  s << nTried;
312 
313  int nUBuckets = ADDRMAN_NEW_BUCKET_COUNT ^ (1 << 30);
314  s << nUBuckets;
315  std::map<int, int> mapUnkIds;
316  int nIds = 0;
317  for (std::map<int, CAddrInfo>::const_iterator it = mapInfo.begin(); it != mapInfo.end(); it++) {
318  mapUnkIds[(*it).first] = nIds;
319  const CAddrInfo &info = (*it).second;
320  if (info.nRefCount) {
321  assert(nIds != nNew); // this means nNew was wrong, oh ow
322  s << info;
323  nIds++;
324  }
325  }
326  nIds = 0;
327  for (std::map<int, CAddrInfo>::const_iterator it = mapInfo.begin(); it != mapInfo.end(); it++) {
328  const CAddrInfo &info = (*it).second;
329  if (info.fInTried) {
330  assert(nIds != nTried); // this means nTried was wrong, oh ow
331  s << info;
332  nIds++;
333  }
334  }
335  for (int bucket = 0; bucket < ADDRMAN_NEW_BUCKET_COUNT; bucket++) {
336  int nSize = 0;
337  for (int i = 0; i < ADDRMAN_BUCKET_SIZE; i++) {
338  if (vvNew[bucket][i] != -1)
339  nSize++;
340  }
341  s << nSize;
342  for (int i = 0; i < ADDRMAN_BUCKET_SIZE; i++) {
343  if (vvNew[bucket][i] != -1) {
344  int nIndex = mapUnkIds[vvNew[bucket][i]];
345  s << nIndex;
346  }
347  }
348  }
349  }
350 
351  template<typename Stream>
352  void Unserialize(Stream& s)
353  {
354  LOCK(cs);
355 
356  Clear();
357 
358  unsigned char nVersion;
359  s >> nVersion;
360  unsigned char nKeySize;
361  s >> nKeySize;
362  if (nKeySize != 32) throw std::ios_base::failure("Incorrect keysize in addrman deserialization");
363  s >> nKey;
364  s >> nNew;
365  s >> nTried;
366  int nUBuckets = 0;
367  s >> nUBuckets;
368  if (nVersion != 0) {
369  nUBuckets ^= (1 << 30);
370  }
371 
373  throw std::ios_base::failure("Corrupt CAddrMan serialization, nNew exceeds limit.");
374  }
375 
376  if (nTried > ADDRMAN_TRIED_BUCKET_COUNT * ADDRMAN_BUCKET_SIZE) {
377  throw std::ios_base::failure("Corrupt CAddrMan serialization, nTried exceeds limit.");
378  }
379 
380  // Deserialize entries from the new table.
381  for (int n = 0; n < nNew; n++) {
382  CAddrInfo &info = mapInfo[n];
383  s >> info;
384  mapAddr[info] = n;
385  info.nRandomPos = vRandom.size();
386  vRandom.push_back(n);
387  if (nVersion != 1 || nUBuckets != ADDRMAN_NEW_BUCKET_COUNT) {
388  // In case the new table data cannot be used (nVersion unknown, or bucket count wrong),
389  // immediately try to give them a reference based on their primary source address.
390  int nUBucket = info.GetNewBucket(nKey);
391  int nUBucketPos = info.GetBucketPosition(nKey, true, nUBucket);
392  if (vvNew[nUBucket][nUBucketPos] == -1) {
393  vvNew[nUBucket][nUBucketPos] = n;
394  info.nRefCount++;
395  }
396  }
397  }
398  nIdCount = nNew;
399 
400  // Deserialize entries from the tried table.
401  int nLost = 0;
402  for (int n = 0; n < nTried; n++) {
403  CAddrInfo info;
404  s >> info;
405  int nKBucket = info.GetTriedBucket(nKey);
406  int nKBucketPos = info.GetBucketPosition(nKey, false, nKBucket);
407  if (vvTried[nKBucket][nKBucketPos] == -1) {
408  info.nRandomPos = vRandom.size();
409  info.fInTried = true;
410  vRandom.push_back(nIdCount);
411  mapInfo[nIdCount] = info;
412  mapAddr[info] = nIdCount;
413  vvTried[nKBucket][nKBucketPos] = nIdCount;
414  nIdCount++;
415  } else {
416  nLost++;
417  }
418  }
419  nTried -= nLost;
420 
421  // Deserialize positions in the new table (if possible).
422  for (int bucket = 0; bucket < nUBuckets; bucket++) {
423  int nSize = 0;
424  s >> nSize;
425  for (int n = 0; n < nSize; n++) {
426  int nIndex = 0;
427  s >> nIndex;
428  if (nIndex >= 0 && nIndex < nNew) {
429  CAddrInfo &info = mapInfo[nIndex];
430  int nUBucketPos = info.GetBucketPosition(nKey, true, bucket);
431  if (nVersion == 1 && nUBuckets == ADDRMAN_NEW_BUCKET_COUNT && vvNew[bucket][nUBucketPos] == -1 && info.nRefCount < ADDRMAN_NEW_BUCKETS_PER_ADDRESS) {
432  info.nRefCount++;
433  vvNew[bucket][nUBucketPos] = nIndex;
434  }
435  }
436  }
437  }
438 
439  // Prune new entries with refcount 0 (as a result of collisions).
440  int nLostUnk = 0;
441  for (std::map<int, CAddrInfo>::const_iterator it = mapInfo.begin(); it != mapInfo.end(); ) {
442  if (it->second.fInTried == false && it->second.nRefCount == 0) {
443  std::map<int, CAddrInfo>::const_iterator itCopy = it++;
444  Delete(itCopy->first);
445  nLostUnk++;
446  } else {
447  it++;
448  }
449  }
450  if (nLost + nLostUnk > 0) {
451  LogPrint(BCLog::ADDRMAN, "addrman lost %i new and %i tried addresses due to collisions\n", nLostUnk, nLost);
452  }
453 
454  Check();
455  }
456 
457  void Clear()
458  {
459  std::vector<int>().swap(vRandom);
460  nKey = GetRandHash();
461  for (size_t bucket = 0; bucket < ADDRMAN_NEW_BUCKET_COUNT; bucket++) {
462  for (size_t entry = 0; entry < ADDRMAN_BUCKET_SIZE; entry++) {
463  vvNew[bucket][entry] = -1;
464  }
465  }
466  for (size_t bucket = 0; bucket < ADDRMAN_TRIED_BUCKET_COUNT; bucket++) {
467  for (size_t entry = 0; entry < ADDRMAN_BUCKET_SIZE; entry++) {
468  vvTried[bucket][entry] = -1;
469  }
470  }
471 
472  nIdCount = 0;
473  nTried = 0;
474  nNew = 0;
475  nLastGood = 1; //Initially at 1 so that "never" is strictly worse.
476  mapInfo.clear();
477  mapAddr.clear();
478  }
479 
481  {
482  Clear();
483  }
484 
486  {
487  nKey.SetNull();
488  }
489 
491  size_t size() const
492  {
493  LOCK(cs); // TODO: Cache this in an atomic to avoid this overhead
494  return vRandom.size();
495  }
496 
498  void Check()
499  {
500 #ifdef DEBUG_ADDRMAN
501  {
502  LOCK(cs);
503  int err;
504  if ((err=Check_()))
505  LogPrintf("ADDRMAN CONSISTENCY CHECK FAILED!!! err=%i\n", err);
506  }
507 #endif
508  }
509 
511  bool Add(const CAddress &addr, const CNetAddr& source, int64_t nTimePenalty = 0)
512  {
513  LOCK(cs);
514  bool fRet = false;
515  Check();
516  fRet |= Add_(addr, source, nTimePenalty);
517  Check();
518  if (fRet) {
519  LogPrint(BCLog::ADDRMAN, "Added %s from %s: %i tried, %i new\n", addr.ToStringIPPort(), source.ToString(), nTried, nNew);
520  }
521  return fRet;
522  }
523 
524  CAddrInfo* ById(unsigned long nId);
525 
527  bool Add(const std::vector<CAddress> &vAddr, const CNetAddr& source, int64_t nTimePenalty = 0)
528  {
529  LOCK(cs);
530  int nAdd = 0;
531  Check();
532  for (std::vector<CAddress>::const_iterator it = vAddr.begin(); it != vAddr.end(); it++)
533  nAdd += Add_(*it, source, nTimePenalty) ? 1 : 0;
534  Check();
535  if (nAdd) {
536  LogPrint(BCLog::ADDRMAN, "Added %i addresses from %s: %i tried, %i new\n", nAdd, source.ToString(), nTried, nNew);
537  }
538  return nAdd > 0;
539  }
540 
542  void Good(const CService &addr, int64_t nTime = GetAdjustedTime())
543  {
544  LOCK(cs);
545  Check();
546  Good_(addr, nTime);
547  Check();
548  }
549 
551  void Attempt(const CService &addr, bool fCountFailure, int64_t nTime = GetAdjustedTime())
552  {
553  LOCK(cs);
554  Check();
555  Attempt_(addr, fCountFailure, nTime);
556  Check();
557  }
558 
562  CAddrInfo Select(bool newOnly = false)
563  {
564  CAddrInfo addrRet;
565  {
566  LOCK(cs);
567  Check();
568  addrRet = Select_(newOnly);
569  Check();
570  }
571  return addrRet;
572  }
573 
575  std::vector<CAddress> GetAddr()
576  {
577  Check();
578  std::vector<CAddress> vAddr;
579  {
580  LOCK(cs);
581  GetAddr_(vAddr);
582  }
583  Check();
584  return vAddr;
585  }
586 
588  void Connected(const CService &addr, int64_t nTime = GetAdjustedTime())
589  {
590  LOCK(cs);
591  Check();
592  Connected_(addr, nTime);
593  Check();
594  }
595 
597  {
598  LOCK(cs);
599  Check();
600  SetServices_(addr, nServices);
601  Check();
602  }
603 
604 };
605 
606 #endif // RAVEN_ADDRMAN_H
int nRefCount
reference count in new sets (memory only)
Definition: addrman.h:47
uint256 GetRandHash()
Definition: random.cpp:373
ServiceFlags
nServices flags
Definition: protocol.h:271
CAddrInfo Select(bool newOnly=false)
Choose an address to connect to.
Definition: addrman.h:562
void SetNull()
Definition: uint256.h:41
void Attempt(const CService &addr, bool fCountFailure, int64_t nTime=GetAdjustedTime())
Mark an entry as connection attempted to.
Definition: addrman.h:551
#define READWRITE(obj)
Definition: serialize.h:163
~CAddrMan()
Definition: addrman.h:485
int64_t nLastGood
last time Good was called (memory only)
Definition: addrman.h:214
int nAttempts
connection attempts since last successful attempt
Definition: addrman.h:44
std::string ToString() const
Definition: netaddress.cpp:288
int nRandomPos
position in vRandom
Definition: addrman.h:53
bool Add(const CAddress &addr, const CNetAddr &source, int64_t nTimePenalty=0)
Add a single address.
Definition: addrman.h:511
#define ADDRMAN_TRIED_BUCKET_COUNT
Convenience.
Definition: addrman.h:176
bool fInTried
in tried set? (memory only)
Definition: addrman.h:50
Stochastical (IP) address manager.
Definition: addrman.h:183
std::vector< CAddress > GetAddr()
Return a bunch of addresses, selected at random.
Definition: addrman.h:575
Extended statistics about a CAddress.
Definition: addrman.h:25
std::vector< int > vRandom
randomly-ordered vector of all nIds
Definition: addrman.h:199
int GetNewBucket(const uint256 &nKey, const CNetAddr &src) const
Calculate in which "new" bucket this entry belongs, given a certain source.
Definition: addrman.cpp:20
int GetTriedBucket(const uint256 &nKey) const
Calculate in which "tried" bucket this entry belongs.
Definition: addrman.cpp:13
#define LogPrintf(...)
Definition: util.h:149
FastRandomContext insecure_rand
Source of random numbers for randomization in inner loops.
Definition: addrman.h:221
void Unserialize(Stream &s)
Definition: addrman.h:352
#define LOCK(cs)
Definition: sync.h:176
A combination of a network address (CNetAddr) and a (TCP) port.
Definition: netaddress.h:141
Fast randomness source.
Definition: random.h:45
void Check()
Consistency check.
Definition: addrman.h:498
A CService with information about it as peer.
Definition: protocol.h:340
int nTried
Definition: addrman.h:202
ADD_SERIALIZE_METHODS
Definition: addrman.h:59
#define ADDRMAN_BUCKET_SIZE
Definition: addrman.h:178
int nIdCount
last used nId
Definition: addrman.h:190
std::map< CNetAddr, int > mapAddr
find an nId based on its network address
Definition: addrman.h:196
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:34
CAddrInfo()
Definition: addrman.h:85
#define LogPrint(category,...)
Definition: util.h:160
IP address (IPv6, or IPv4 using mapped IPv6 range (::FFFF:0:0/96))
Definition: netaddress.h:32
size_t size() const
Return the number of (unique) addresses in all tables.
Definition: addrman.h:491
256-bit opaque blob.
Definition: uint256.h:123
unsigned int nTime
Definition: protocol.h:372
ServiceFlags nServices
Definition: protocol.h:369
void Init()
Definition: addrman.h:69
int GetBucketPosition(const uint256 &nKey, bool fNew, int nBucket) const
Calculate in which position of a bucket to store this entry.
Definition: addrman.cpp:28
void Clear()
Definition: addrman.h:457
CAddrMan()
Definition: addrman.h:480
CAddrInfo(const CAddress &addrIn, const CNetAddr &addrSource)
Definition: addrman.h:80
#define ADDRMAN_NEW_BUCKET_COUNT
Definition: addrman.h:177
int64_t GetAdjustedTime()
Definition: timedata.cpp:36
std::string ToStringIPPort() const
Definition: netaddress.cpp:588
int GetNewBucket(const uint256 &nKey) const
Calculate in which "new" bucket this entry belongs, using its default source.
Definition: addrman.h:97
void SerializationOp(Stream &s, Operation ser_action)
Definition: addrman.h:62
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:54
int64_t nLastCountAttempt
last counted attempt (memory only)
Definition: addrman.h:34
std::map< int, CAddrInfo > mapInfo
table with information about all nIds
Definition: addrman.h:193
bool Add(const std::vector< CAddress > &vAddr, const CNetAddr &source, int64_t nTimePenalty=0)
Add multiple addresses.
Definition: addrman.h:527
void SetServices(const CService &addr, ServiceFlags nServices)
Definition: addrman.h:596
uint256 nKey
secret key to randomize bucket select with
Definition: addrman.h:218
void Serialize(Stream &s) const
serialized format:
Definition: addrman.h:302
int nNew
number of (unique) "new" entries
Definition: addrman.h:208
void Connected(const CService &addr, int64_t nTime=GetAdjustedTime())
Mark an entry as currently-connected-to.
Definition: addrman.h:588
int64_t nLastSuccess
last successful connection by us
Definition: addrman.h:41
CCriticalSection cs
critical section to protect the inner data structures
Definition: addrman.h:187
Wrapped boost mutex: supports recursive locking, but no waiting TODO: We should move away from using ...
Definition: sync.h:92
int64_t nLastTry
last try whatsoever by us (memory only)
Definition: addrman.h:31
#define ADDRMAN_NEW_BUCKETS_PER_ADDRESS
in how many buckets for entries with new addresses a single address may occur
Definition: addrman.h:155
CNetAddr source
where knowledge about this address first came from
Definition: addrman.h:38
void Good(const CService &addr, int64_t nTime=GetAdjustedTime())
Mark an entry as accessible.
Definition: addrman.h:542