Dash Core  0.12.2.1
P2P Digital Currency
addrman.h
Go to the documentation of this file.
1 // Copyright (c) 2012 Pieter Wuille
2 // Distributed under the MIT software license, see the accompanying
3 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4 
5 #ifndef BITCOIN_ADDRMAN_H
6 #define BITCOIN_ADDRMAN_H
7 
8 #include "netaddress.h"
9 #include "protocol.h"
10 #include "random.h"
11 #include "sync.h"
12 #include "timedata.h"
13 #include "util.h"
14 
15 #include <map>
16 #include <set>
17 #include <stdint.h>
18 #include <vector>
19 
23 class CAddrInfo : public CAddress
24 {
25 
26 
27 public:
29  int64_t nLastTry;
30 
31 private:
34 
36  int64_t nLastSuccess;
37 
39  int nAttempts;
40 
42  int nRefCount;
43 
45  bool fInTried;
46 
49 
50  friend class CAddrMan;
51 
52 public:
53 
55 
56  template <typename Stream, typename Operation>
57  inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
58  READWRITE(*(CAddress*)this);
62  }
63 
64  void Init()
65  {
66  nLastSuccess = 0;
67  nLastTry = 0;
68  nAttempts = 0;
69  nRefCount = 0;
70  fInTried = false;
71  nRandomPos = -1;
72  }
73 
74  CAddrInfo(const CAddress &addrIn, const CNetAddr &addrSource) : CAddress(addrIn), source(addrSource)
75  {
76  Init();
77  }
78 
80  {
81  Init();
82  }
83 
85  int GetTriedBucket(const uint256 &nKey) const;
86 
88  int GetNewBucket(const uint256 &nKey, const CNetAddr& src) const;
89 
91  int GetNewBucket(const uint256 &nKey) const
92  {
93  return GetNewBucket(nKey, source);
94  }
95 
97  int GetBucketPosition(const uint256 &nKey, bool fNew, int nBucket) const;
98 
100  bool IsTerrible(int64_t nNow = GetAdjustedTime()) const;
101 
103  double GetChance(int64_t nNow = GetAdjustedTime()) const;
104 
105 };
106 
133 #define ADDRMAN_TRIED_BUCKET_COUNT 256
135 
137 #define ADDRMAN_NEW_BUCKET_COUNT 1024
138 
140 #define ADDRMAN_BUCKET_SIZE 64
141 
143 #define ADDRMAN_TRIED_BUCKETS_PER_GROUP 8
144 
146 #define ADDRMAN_NEW_BUCKETS_PER_SOURCE_GROUP 64
147 
149 #define ADDRMAN_NEW_BUCKETS_PER_ADDRESS 8
150 
152 #define ADDRMAN_HORIZON_DAYS 30
153 
155 #define ADDRMAN_RETRIES 3
156 
158 #define ADDRMAN_MAX_FAILURES 10
159 
161 #define ADDRMAN_MIN_FAIL_DAYS 7
162 
164 #define ADDRMAN_GETADDR_MAX_PCT 23
165 
167 #define ADDRMAN_GETADDR_MAX 2500
168 
172 class CAddrMan
173 {
174 private:
177 
179  int nIdCount;
180 
182  std::map<int, CAddrInfo> mapInfo;
183 
185  std::map<CNetAddr, int> mapAddr;
186 
188  std::vector<int> vRandom;
189 
190  // number of "tried" entries
191  int nTried;
192 
195 
197  int nNew;
198 
201 
202 protected:
205 
207  CAddrInfo* Find(const CNetAddr& addr, int *pnId = NULL);
208 
211  CAddrInfo* Create(const CAddress &addr, const CNetAddr &addrSource, int *pnId = NULL);
212 
214  void SwapRandom(unsigned int nRandomPos1, unsigned int nRandomPos2);
215 
217  void MakeTried(CAddrInfo& info, int nId);
218 
220  void Delete(int nId);
221 
223  void ClearNew(int nUBucket, int nUBucketPos);
224 
226  void Good_(const CService &addr, int64_t nTime);
227 
229  bool Add_(const CAddress &addr, const CNetAddr& source, int64_t nTimePenalty);
230 
232  void Attempt_(const CService &addr, int64_t nTime);
233 
235  CAddrInfo Select_(bool newOnly);
236 
238  virtual int RandomInt(int nMax);
239 
240 #ifdef DEBUG_ADDRMAN
241  int Check_();
243 #endif
244 
246  void GetAddr_(std::vector<CAddress> &vAddr);
247 
249  void Connected_(const CService &addr, int64_t nTime);
250 
252  void SetServices_(const CService &addr, ServiceFlags nServices);
253 
254 public:
284  template<typename Stream>
285  void Serialize(Stream &s, int nType, int nVersionDummy) const
286  {
287  LOCK(cs);
288 
289  unsigned char nVersion = 1;
290  s << nVersion;
291  s << ((unsigned char)32);
292  s << nKey;
293  s << nNew;
294  s << nTried;
295 
296  int nUBuckets = ADDRMAN_NEW_BUCKET_COUNT ^ (1 << 30);
297  s << nUBuckets;
298  std::map<int, int> mapUnkIds;
299  int nIds = 0;
300  for (std::map<int, CAddrInfo>::const_iterator it = mapInfo.begin(); it != mapInfo.end(); it++) {
301  mapUnkIds[(*it).first] = nIds;
302  const CAddrInfo &info = (*it).second;
303  if (info.nRefCount) {
304  assert(nIds != nNew); // this means nNew was wrong, oh ow
305  s << info;
306  nIds++;
307  }
308  }
309  nIds = 0;
310  for (std::map<int, CAddrInfo>::const_iterator it = mapInfo.begin(); it != mapInfo.end(); it++) {
311  const CAddrInfo &info = (*it).second;
312  if (info.fInTried) {
313  assert(nIds != nTried); // this means nTried was wrong, oh ow
314  s << info;
315  nIds++;
316  }
317  }
318  for (int bucket = 0; bucket < ADDRMAN_NEW_BUCKET_COUNT; bucket++) {
319  int nSize = 0;
320  for (int i = 0; i < ADDRMAN_BUCKET_SIZE; i++) {
321  if (vvNew[bucket][i] != -1)
322  nSize++;
323  }
324  s << nSize;
325  for (int i = 0; i < ADDRMAN_BUCKET_SIZE; i++) {
326  if (vvNew[bucket][i] != -1) {
327  int nIndex = mapUnkIds[vvNew[bucket][i]];
328  s << nIndex;
329  }
330  }
331  }
332  }
333 
334  template<typename Stream>
335  void Unserialize(Stream& s, int nType, int nVersionDummy)
336  {
337  LOCK(cs);
338 
339  Clear();
340 
341  unsigned char nVersion;
342  s >> nVersion;
343  unsigned char nKeySize;
344  s >> nKeySize;
345  if (nKeySize != 32) throw std::ios_base::failure("Incorrect keysize in addrman deserialization");
346  s >> nKey;
347  s >> nNew;
348  s >> nTried;
349  int nUBuckets = 0;
350  s >> nUBuckets;
351  if (nVersion != 0) {
352  nUBuckets ^= (1 << 30);
353  }
354 
355  // Deserialize entries from the new table.
356  for (int n = 0; n < nNew; n++) {
357  CAddrInfo &info = mapInfo[n];
358  s >> info;
359  mapAddr[info] = n;
360  info.nRandomPos = vRandom.size();
361  vRandom.push_back(n);
362  if (nVersion != 1 || nUBuckets != ADDRMAN_NEW_BUCKET_COUNT) {
363  // In case the new table data cannot be used (nVersion unknown, or bucket count wrong),
364  // immediately try to give them a reference based on their primary source address.
365  int nUBucket = info.GetNewBucket(nKey);
366  int nUBucketPos = info.GetBucketPosition(nKey, true, nUBucket);
367  if (vvNew[nUBucket][nUBucketPos] == -1) {
368  vvNew[nUBucket][nUBucketPos] = n;
369  info.nRefCount++;
370  }
371  }
372  }
373  nIdCount = nNew;
374 
375  // Deserialize entries from the tried table.
376  int nLost = 0;
377  for (int n = 0; n < nTried; n++) {
378  CAddrInfo info;
379  s >> info;
380  int nKBucket = info.GetTriedBucket(nKey);
381  int nKBucketPos = info.GetBucketPosition(nKey, false, nKBucket);
382  if (vvTried[nKBucket][nKBucketPos] == -1) {
383  info.nRandomPos = vRandom.size();
384  info.fInTried = true;
385  vRandom.push_back(nIdCount);
386  mapInfo[nIdCount] = info;
387  mapAddr[info] = nIdCount;
388  vvTried[nKBucket][nKBucketPos] = nIdCount;
389  nIdCount++;
390  } else {
391  nLost++;
392  }
393  }
394  nTried -= nLost;
395 
396  // Deserialize positions in the new table (if possible).
397  for (int bucket = 0; bucket < nUBuckets; bucket++) {
398  int nSize = 0;
399  s >> nSize;
400  for (int n = 0; n < nSize; n++) {
401  int nIndex = 0;
402  s >> nIndex;
403  if (nIndex >= 0 && nIndex < nNew) {
404  CAddrInfo &info = mapInfo[nIndex];
405  int nUBucketPos = info.GetBucketPosition(nKey, true, bucket);
406  if (nVersion == 1 && nUBuckets == ADDRMAN_NEW_BUCKET_COUNT && vvNew[bucket][nUBucketPos] == -1 && info.nRefCount < ADDRMAN_NEW_BUCKETS_PER_ADDRESS) {
407  info.nRefCount++;
408  vvNew[bucket][nUBucketPos] = nIndex;
409  }
410  }
411  }
412  }
413 
414  // Prune new entries with refcount 0 (as a result of collisions).
415  int nLostUnk = 0;
416  for (std::map<int, CAddrInfo>::const_iterator it = mapInfo.begin(); it != mapInfo.end(); ) {
417  if (it->second.fInTried == false && it->second.nRefCount == 0) {
418  std::map<int, CAddrInfo>::const_iterator itCopy = it++;
419  Delete(itCopy->first);
420  nLostUnk++;
421  } else {
422  it++;
423  }
424  }
425  if (nLost + nLostUnk > 0) {
426  LogPrint("addrman", "addrman lost %i new and %i tried addresses due to collisions\n", nLostUnk, nLost);
427  }
428 
429  Check();
430  }
431 
432  unsigned int GetSerializeSize(int nType, int nVersion) const
433  {
434  return (CSizeComputer(nType, nVersion) << *this).size();
435  }
436 
437  void Clear()
438  {
439  std::vector<int>().swap(vRandom);
440  nKey = GetRandHash();
441  for (size_t bucket = 0; bucket < ADDRMAN_NEW_BUCKET_COUNT; bucket++) {
442  for (size_t entry = 0; entry < ADDRMAN_BUCKET_SIZE; entry++) {
443  vvNew[bucket][entry] = -1;
444  }
445  }
446  for (size_t bucket = 0; bucket < ADDRMAN_TRIED_BUCKET_COUNT; bucket++) {
447  for (size_t entry = 0; entry < ADDRMAN_BUCKET_SIZE; entry++) {
448  vvTried[bucket][entry] = -1;
449  }
450  }
451 
452  nIdCount = 0;
453  nTried = 0;
454  nNew = 0;
455  }
456 
458  {
459  Clear();
460  }
461 
463  {
464  nKey.SetNull();
465  }
466 
468  size_t size() const
469  {
470  return vRandom.size();
471  }
472 
474  void Check()
475  {
476 #ifdef DEBUG_ADDRMAN
477  {
478  LOCK(cs);
479  int err;
480  if ((err=Check_()))
481  LogPrintf("ADDRMAN CONSISTENCY CHECK FAILED!!! err=%i\n", err);
482  }
483 #endif
484  }
485 
487  bool Add(const CAddress &addr, const CNetAddr& source, int64_t nTimePenalty = 0)
488  {
489  bool fRet = false;
490  {
491  LOCK(cs);
492  Check();
493  fRet |= Add_(addr, source, nTimePenalty);
494  Check();
495  }
496  if (fRet)
497  LogPrint("addrman", "Added %s from %s: %i tried, %i new\n", addr.ToStringIPPort(), source.ToString(), nTried, nNew);
498  return fRet;
499  }
500 
502  bool Add(const std::vector<CAddress> &vAddr, const CNetAddr& source, int64_t nTimePenalty = 0)
503  {
504  int nAdd = 0;
505  {
506  LOCK(cs);
507  Check();
508  for (std::vector<CAddress>::const_iterator it = vAddr.begin(); it != vAddr.end(); it++)
509  nAdd += Add_(*it, source, nTimePenalty) ? 1 : 0;
510  Check();
511  }
512  if (nAdd)
513  LogPrint("addrman", "Added %i addresses from %s: %i tried, %i new\n", nAdd, source.ToString(), nTried, nNew);
514  return nAdd > 0;
515  }
516 
518  void Good(const CService &addr, int64_t nTime = GetAdjustedTime())
519  {
520  {
521  LOCK(cs);
522  Check();
523  Good_(addr, nTime);
524  Check();
525  }
526  }
527 
529  void Attempt(const CService &addr, int64_t nTime = GetAdjustedTime())
530  {
531  {
532  LOCK(cs);
533  Check();
534  Attempt_(addr, nTime);
535  Check();
536  }
537  }
538 
542  CAddrInfo Select(bool newOnly = false)
543  {
544  CAddrInfo addrRet;
545  {
546  LOCK(cs);
547  Check();
548  addrRet = Select_(newOnly);
549  Check();
550  }
551  return addrRet;
552  }
553 
555  std::vector<CAddress> GetAddr()
556  {
557  Check();
558  std::vector<CAddress> vAddr;
559  {
560  LOCK(cs);
561  GetAddr_(vAddr);
562  }
563  Check();
564  return vAddr;
565  }
566 
568  void Connected(const CService &addr, int64_t nTime = GetAdjustedTime())
569  {
570  {
571  LOCK(cs);
572  Check();
573  Connected_(addr, nTime);
574  Check();
575  }
576  }
577 
578  void SetServices(const CService &addr, ServiceFlags nServices)
579  {
580  LOCK(cs);
581  Check();
582  SetServices_(addr, nServices);
583  Check();
584  }
585 
586 };
587 
588 #endif // BITCOIN_ADDRMAN_H
std::vector< CAddress > GetAddr()
Return a bunch of addresses, selected at random.
Definition: addrman.h:555
int nNew
number of (unique) "new" entries
Definition: addrman.h:197
uint256 GetRandHash()
Definition: random.cpp:114
ServiceFlags
Definition: protocol.h:253
void SetNull()
Definition: uint256.h:41
uint256 nKey
secret key to randomize bucket select with
Definition: addrman.h:204
#define READWRITE(obj)
Definition: serialize.h:175
void SerializationOp(Stream &s, Operation ser_action, int nType, int nVersion)
Definition: addrman.h:57
CAddrInfo(const CAddress &addrIn, const CNetAddr &addrSource)
Definition: addrman.h:74
CAddrInfo Select_(bool newOnly)
Select an address to connect to, if newOnly is set to true, only the new table is selected from...
Definition: addrman.cpp:332
void Clear()
Definition: addrman.h:437
ADD_SERIALIZE_METHODS
Definition: addrman.h:54
int nIdCount
last used nId
Definition: addrman.h:179
void Connected_(const CService &addr, int64_t nTime)
Mark an entry as currently-connected-to.
Definition: addrman.cpp:478
size_t size() const
Return the number of (unique) addresses in all tables.
Definition: addrman.h:468
void Good(const CService &addr, int64_t nTime=GetAdjustedTime())
Mark an entry as accessible.
Definition: addrman.h:518
void Attempt_(const CService &addr, int64_t nTime)
Mark an entry as attempted to connect.
Definition: addrman.cpp:313
std::map< int, CAddrInfo > mapInfo
table with information about all nIds
Definition: addrman.h:182
void ClearNew(int nUBucket, int nUBucketPos)
Clear a position in a "new" table. This is the only place where entries are actually deleted...
Definition: addrman.cpp:133
void SwapRandom(unsigned int nRandomPos1, unsigned int nRandomPos2)
Swap two elements in vRandom.
Definition: addrman.cpp:99
#define ADDRMAN_TRIED_BUCKET_COUNT
total number of buckets for tried addresses
Definition: addrman.h:134
bool Add(const CAddress &addr, const CNetAddr &source, int64_t nTimePenalty=0)
Add a single address.
Definition: addrman.h:487
int nRandomPos
position in vRandom
Definition: addrman.h:48
~CAddrMan()
Definition: addrman.h:462
CAddrInfo()
Definition: addrman.h:79
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:32
int64_t nLastTry
last try whatsoever by us (memory only)
Definition: addrman.h:29
void Good_(const CService &addr, int64_t nTime)
Mark an entry "good", possibly moving it from "new" to "tried".
Definition: addrman.cpp:196
CAddrInfo Select(bool newOnly=false)
Definition: addrman.h:542
CAddrMan()
Definition: addrman.h:457
bool fInTried
in tried set? (memory only)
Definition: addrman.h:45
CCriticalSection cs
critical section to protect the inner data structures
Definition: addrman.h:176
const char * source
Definition: rpcconsole.cpp:63
#define LogPrintf(...)
Definition: util.h:98
std::string ToStringIPPort(bool fUseGetnameinfo=true) const
Definition: netaddress.cpp:559
std::map< CNetAddr, int > mapAddr
find an nId based on its network address
Definition: addrman.h:185
static int LogPrint(const char *category, const char *format)
Definition: util.h:126
int GetBucketPosition(const uint256 &nKey, bool fNew, int nBucket) const
Calculate in which position of a bucket to store this entry.
Definition: addrman.cpp:26
#define LOCK(cs)
Definition: sync.h:168
void Serialize(Stream &s, int nType, int nVersionDummy) const
Definition: addrman.h:285
bool Add_(const CAddress &addr, const CNetAddr &source, int64_t nTimePenalty)
Add an entry to the "new" table.
Definition: addrman.cpp:245
void SetServices_(const CService &addr, ServiceFlags nServices)
Update an entry&#39;s service bits.
Definition: addrman.cpp:498
std::vector< int > vRandom
randomly-ordered vector of all nIds
Definition: addrman.h:188
#define ADDRMAN_BUCKET_SIZE
maximum allowed number of entries in buckets for new and tried addresses
Definition: addrman.h:140
int GetNewBucket(const uint256 &nKey, const CNetAddr &src) const
Calculate in which "new" bucket this entry belongs, given a certain source.
Definition: addrman.cpp:18
int GetNewBucket(const uint256 &nKey) const
Calculate in which "new" bucket this entry belongs, using its default source.
Definition: addrman.h:91
CAddrInfo * Find(const CNetAddr &addr, int *pnId=NULL)
Find an entry.
Definition: addrman.cpp:74
void SetServices(const CService &addr, ServiceFlags nServices)
Definition: addrman.h:578
void Delete(int nId)
Delete an entry. It must not be in tried, and have refcount 0.
Definition: addrman.cpp:119
int vvTried[ADDRMAN_TRIED_BUCKET_COUNT][ADDRMAN_BUCKET_SIZE]
list of "tried" buckets
Definition: addrman.h:194
void Check()
Consistency check.
Definition: addrman.h:474
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:52
void Connected(const CService &addr, int64_t nTime=GetAdjustedTime())
Mark an entry as currently-connected-to.
Definition: addrman.h:568
virtual int RandomInt(int nMax)
Wraps GetRandInt to allow tests to override RandomInt and make it determinismistic.
Definition: addrman.cpp:516
void Unserialize(Stream &s, int nType, int nVersionDummy)
Definition: addrman.h:335
void MakeTried(CAddrInfo &info, int nId)
Move an entry from the "new" table(s) to the "tried" table.
Definition: addrman.cpp:148
unsigned int GetSerializeSize(int nType, int nVersion) const
Definition: addrman.h:432
void GetAddr_(std::vector< CAddress > &vAddr)
Select several addresses at once.
Definition: addrman.cpp:457
#define ADDRMAN_NEW_BUCKET_COUNT
total number of buckets for new addresses
Definition: addrman.h:137
int64_t GetAdjustedTime()
Definition: timedata.cpp:33
void Init()
Definition: addrman.h:64
int64_t nLastSuccess
last successful connection by us
Definition: addrman.h:36
CNetAddr source
where knowledge about this address first came from
Definition: addrman.h:33
int nRefCount
reference count in new sets (memory only)
Definition: addrman.h:42
int nTried
Definition: addrman.h:191
bool Add(const std::vector< CAddress > &vAddr, const CNetAddr &source, int64_t nTimePenalty=0)
Add multiple addresses.
Definition: addrman.h:502
int nAttempts
connection attempts since last successful attempt
Definition: addrman.h:39
void Attempt(const CService &addr, int64_t nTime=GetAdjustedTime())
Mark an entry as connection attempted to.
Definition: addrman.h:529
CAddrInfo * Create(const CAddress &addr, const CNetAddr &addrSource, int *pnId=NULL)
Definition: addrman.cpp:87
#define ADDRMAN_NEW_BUCKETS_PER_ADDRESS
in how many buckets for entries with new addresses a single address may occur
Definition: addrman.h:149
int vvNew[ADDRMAN_NEW_BUCKET_COUNT][ADDRMAN_BUCKET_SIZE]
list of "new" buckets
Definition: addrman.h:200
int GetTriedBucket(const uint256 &nKey) const
Calculate in which "tried" bucket this entry belongs.
Definition: addrman.cpp:11