Includes cleanup
[novacoin.git] / src / addrman.h
1 // Copyright (c) 2012 Pieter Wuille
2 // Distributed under the MIT/X11 software license, see the accompanying
3 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4 #ifndef _BITCOIN_ADDRMAN
5 #define _BITCOIN_ADDRMAN 1
6
7 #include "netbase.h"
8 #include "protocol.h"
9 #include "timedata.h"
10 #include "util.h"
11 #include "sync.h"
12
13
14 #include <map>
15 #include <vector>
16
17 #include <openssl/rand.h>
18
19
20 /** Extended statistics about a CAddress */
21 class CAddrInfo : public CAddress
22 {
23 private:
24     // where knowledge about this address first came from
25     CNetAddr source;
26
27     // last successful connection by us
28     int64_t nLastSuccess;
29
30     // last try whatsoever by us:
31     // int64_t CAddress::nLastTry
32
33     // connection attempts since last successful attempt
34     int nAttempts;
35
36     // reference count in new sets (memory only)
37     int nRefCount;
38
39     // in tried set? (memory only)
40     bool fInTried;
41
42     // position in vRandom
43     int nRandomPos;
44
45     friend class CAddrMan;
46
47 public:
48
49     IMPLEMENT_SERIALIZE(
50         CAddress* pthis = (CAddress*)(this);
51         READWRITE(*pthis);
52         READWRITE(source);
53         READWRITE(nLastSuccess);
54         READWRITE(nAttempts);
55     )
56
57     void Init()
58     {
59         nLastSuccess = 0;
60         nLastTry = 0;
61         nAttempts = 0;
62         nRefCount = 0;
63         fInTried = false;
64         nRandomPos = -1;
65     }
66
67     CAddrInfo(const CAddress &addrIn, const CNetAddr &addrSource) : CAddress(addrIn), source(addrSource)
68     {
69         Init();
70     }
71
72     CAddrInfo() : CAddress(), source()
73     {
74         Init();
75     }
76
77     // Calculate in which "tried" bucket this entry belongs
78     int GetTriedBucket(const std::vector<unsigned char> &nKey) const;
79
80     // Calculate in which "new" bucket this entry belongs, given a certain source
81     int GetNewBucket(const std::vector<unsigned char> &nKey, const CNetAddr& src) const;
82
83     // Calculate in which "new" bucket this entry belongs, using its default source
84     int GetNewBucket(const std::vector<unsigned char> &nKey) const
85     {
86         return GetNewBucket(nKey, source);
87     }
88
89     // Determine whether the statistics about this entry are bad enough so that it can just be deleted
90     bool IsTerrible(int64_t nNow = GetAdjustedTime()) const;
91
92     // Calculate the relative chance this entry should be given when selecting nodes to connect to
93     double GetChance(int64_t nNow = GetAdjustedTime()) const;
94
95 };
96
97 // Stochastic address manager
98 //
99 // Design goals:
100 //  * Only keep a limited number of addresses around, so that addr.dat and memory requirements do not grow without bound.
101 //  * Keep the address tables in-memory, and asynchronously dump the entire to able in addr.dat.
102 //  * Make sure no (localized) attacker can fill the entire table with his nodes/addresses.
103 //
104 // To that end:
105 //  * Addresses are organized into buckets.
106 //    * Address that have not yet been tried go into 256 "new" buckets.
107 //      * Based on the address range (/16 for IPv4) of source of the information, 32 buckets are selected at random
108 //      * The actual bucket is chosen from one of these, based on the range the address itself is located.
109 //      * One single address can occur in up to 4 different buckets, to increase selection chances for addresses that
110 //        are seen frequently. The chance for increasing this multiplicity decreases exponentially.
111 //      * When adding a new address to a full bucket, a randomly chosen entry (with a bias favoring less recently seen
112 //        ones) is removed from it first.
113 //    * Addresses of nodes that are known to be accessible go into 64 "tried" buckets.
114 //      * Each address range selects at random 4 of these buckets.
115 //      * The actual bucket is chosen from one of these, based on the full address.
116 //      * When adding a new good address to a full bucket, a randomly chosen entry (with a bias favoring less recently
117 //        tried ones) is evicted from it, back to the "new" buckets.
118 //    * Bucket selection is based on cryptographic hashing, using a randomly-generated 256-bit key, which should not
119 //      be observable by adversaries.
120 //    * Several indexes are kept for high performance. Defining DEBUG_ADDRMAN will introduce frequent (and expensive)
121 //      consistency checks for the entire data structure.
122
123 // total number of buckets for tried addresses
124 #define ADDRMAN_TRIED_BUCKET_COUNT 64
125
126 // maximum allowed number of entries in buckets for tried addresses
127 #define ADDRMAN_TRIED_BUCKET_SIZE 64
128
129 // total number of buckets for new addresses
130 #define ADDRMAN_NEW_BUCKET_COUNT 256
131
132 // maximum allowed number of entries in buckets for new addresses
133 #define ADDRMAN_NEW_BUCKET_SIZE 64
134
135 // over how many buckets entries with tried addresses from a single group (/16 for IPv4) are spread
136 #define ADDRMAN_TRIED_BUCKETS_PER_GROUP 4
137
138 // over how many buckets entries with new addresses originating from a single group are spread
139 #define ADDRMAN_NEW_BUCKETS_PER_SOURCE_GROUP 32
140
141 // in how many buckets for entries with new addresses a single address may occur
142 #define ADDRMAN_NEW_BUCKETS_PER_ADDRESS 4
143
144 // how many entries in a bucket with tried addresses are inspected, when selecting one to replace
145 #define ADDRMAN_TRIED_ENTRIES_INSPECT_ON_EVICT 4
146
147 // how old addresses can maximally be
148 #define ADDRMAN_HORIZON_DAYS 30
149
150 // after how many failed attempts we give up on a new node
151 #define ADDRMAN_RETRIES 3
152
153 // how many successive failures are allowed ...
154 #define ADDRMAN_MAX_FAILURES 10
155
156 // ... in at least this many days
157 #define ADDRMAN_MIN_FAIL_DAYS 7
158
159 // the maximum percentage of nodes to return in a getaddr call
160 #define ADDRMAN_GETADDR_MAX_PCT 23
161
162 // the maximum number of nodes to return in a getaddr call
163 #define ADDRMAN_GETADDR_MAX 2500
164
165 /** Stochastical (IP) address manager */
166 class CAddrMan
167 {
168 private:
169     // critical section to protect the inner data structures
170     mutable CCriticalSection cs;
171
172     // secret key to randomize bucket select with
173     std::vector<unsigned char> nKey;
174
175     // last used nId
176     int nIdCount;
177
178     // table with information about all nIds
179     std::map<int, CAddrInfo> mapInfo;
180
181     // find an nId based on its network address
182     std::map<CNetAddr, int> mapAddr;
183
184     // randomly-ordered vector of all nIds
185     std::vector<int> vRandom;
186
187     // number of "tried" entries
188     int nTried;
189
190     // list of "tried" buckets
191     std::vector<std::vector<int> > vvTried;
192
193     // number of (unique) "new" entries
194     int nNew;
195
196     // list of "new" buckets
197     std::vector<std::set<int> > vvNew;
198
199 protected:
200
201     // Find an entry.
202     CAddrInfo* Find(const CNetAddr& addr, int *pnId = NULL);
203
204     // find an entry, creating it if necessary.
205     // nTime and nServices of found node is updated, if necessary.
206     CAddrInfo* Create(const CAddress &addr, const CNetAddr &addrSource, int *pnId = NULL);
207
208     // Swap two elements in vRandom.
209     void SwapRandom(unsigned int nRandomPos1, unsigned int nRandomPos2);
210
211     // Return position in given bucket to replace.
212     int SelectTried(int nKBucket);
213
214     // Remove an element from a "new" bucket.
215     // This is the only place where actual deletes occur.
216     // They are never deleted while in the "tried" table, only possibly evicted back to the "new" table.
217     int ShrinkNew(int nUBucket);
218
219     // Move an entry from the "new" table(s) to the "tried" table
220     // @pre vvUnkown[nOrigin].count(nId) != 0
221     void MakeTried(CAddrInfo& info, int nId, int nOrigin);
222
223     // Mark an entry "good", possibly moving it from "new" to "tried".
224     void Good_(const CService &addr, int64_t nTime);
225
226     // Add an entry to the "new" table.
227     bool Add_(const CAddress &addr, const CNetAddr& source, int64_t nTimePenalty);
228
229     // Mark an entry as attempted to connect.
230     void Attempt_(const CService &addr, int64_t nTime);
231
232     // Select an address to connect to.
233     // nUnkBias determines how much to favor new addresses over tried ones (min=0, max=100)
234     CAddress Select_(int nUnkBias);
235
236 #ifdef DEBUG_ADDRMAN
237     // Perform consistency check. Returns an error code or zero.
238     int Check_();
239 #endif
240
241     // Select several addresses at once.
242     void GetAddr_(std::vector<CAddress> &vAddr);
243     void GetOnlineAddr_(std::vector<CAddrInfo> &vAddr);
244
245     // Mark an entry as currently-connected-to.
246     void Connected_(const CService &addr, int64_t nTime);
247
248 public:
249
250     typedef std::map<int, int> MapUnkIds; // For MSVC macro
251     IMPLEMENT_SERIALIZE
252             (
253             LOCK(cs);
254                 unsigned char 
255                     nVersion = 0;
256
257                 READWRITE(nVersion);
258                 READWRITE(nKey);
259                 READWRITE(nNew);
260                 READWRITE(nTried);
261
262                 CAddrMan
263                     *am = const_cast<CAddrMan*>(this);
264
265                 if (fWrite)
266                 {
267                     int
268                         nUBuckets = ADDRMAN_NEW_BUCKET_COUNT;
269
270                     READWRITE(nUBuckets);
271                     MapUnkIds mapUnkIds;
272                     int
273                         nIds = 0;
274                     for (std::map<int, CAddrInfo>::iterator it = am->mapInfo.begin(); it != am->mapInfo.end(); it++)
275                     {
276                         if (nIds == nNew)
277                             break; // this means nNew was wrong, oh ow
278                         mapUnkIds[(*it).first] = nIds;
279
280                         CAddrInfo
281                             &info = (*it).second;
282
283                         if (info.nRefCount)
284                         {
285                             READWRITE(info);
286                             nIds++;
287                         }
288                     }
289                     nIds = 0;
290                     for (std::map<int, CAddrInfo>::iterator it = am->mapInfo.begin(); it != am->mapInfo.end(); it++)
291                     {
292                         if (nIds == nTried) 
293                             break; /* this means nTried was wrong, oh ow */
294
295                         CAddrInfo 
296                             &info = (*it).second;
297
298                         if (info.fInTried)
299                         {
300                             READWRITE(info);
301                             nIds++;
302                         }
303                     }
304                     for (
305                          std::vector<std::set<int> >::iterator it = am->vvNew.begin(); 
306                          it != am->vvNew.end(); 
307                          it++
308                         )
309                     {
310                         std::set<int> 
311                             &vNew = (*it);
312
313                         int 
314                             nSize = int( vNew.size() );
315
316                         READWRITE(nSize);
317                         for (std::set<int>::iterator it2 = vNew.begin(); it2 != vNew.end(); it2++)
318                         {
319                         int 
320                             nIndex = mapUnkIds[*it2];
321                         READWRITE(nIndex);
322                         }
323                     }
324                 } 
325                 else 
326                 {
327                     int 
328                         nUBuckets = 0;
329
330                     READWRITE(nUBuckets);
331                     am->nIdCount = 0;
332                     am->mapInfo.clear();
333                     am->mapAddr.clear();
334                     am->vRandom.clear();
335                     am->vvTried = 
336                         std::vector<std::vector<int> >(
337                                                         ADDRMAN_TRIED_BUCKET_COUNT, 
338                                                         std::vector<int>(0)
339                                                       );
340                     am->vvNew = 
341                         std::vector<std::set<int> >(
342                                                     ADDRMAN_NEW_BUCKET_COUNT, 
343                                                     std::set<int>()
344                                                    );
345                     for (int n = 0; n < am->nNew; n++)
346                     {
347                         CAddrInfo 
348                             &info = am->mapInfo[n];
349
350                         READWRITE(info);
351                         am->mapAddr[info] = n;
352                         info.nRandomPos = int( vRandom.size() );
353                         am->vRandom.push_back(n);
354                         if (nUBuckets != ADDRMAN_NEW_BUCKET_COUNT)
355                         {
356                             am->vvNew[info.GetNewBucket(am->nKey)].insert(n);
357                             info.nRefCount++;
358                         }
359                     }
360                     am->nIdCount = am->nNew;
361
362                     int 
363                         nLost = 0;
364
365                     for (int n = 0; n < am->nTried; n++)
366                     {
367                         CAddrInfo 
368                             info;
369
370                         READWRITE(info);
371
372                         std::vector<int> 
373                             &vTried = am->vvTried[info.GetTriedBucket(am->nKey)];
374
375                         if (vTried.size() < ADDRMAN_TRIED_BUCKET_SIZE)
376                         {
377                             info.nRandomPos = int( vRandom.size() );
378                             info.fInTried = true;
379                             am->vRandom.push_back(am->nIdCount);
380                             am->mapInfo[am->nIdCount] = info;
381                             am->mapAddr[info] = am->nIdCount;
382                             vTried.push_back(am->nIdCount);
383                             am->nIdCount++;
384                         } 
385                         else 
386                         {
387                             nLost++;
388                         }
389                     }
390                     am->nTried -= nLost;
391                     for (int b = 0; b < nUBuckets; b++)
392                     {
393                         std::set<int> 
394                             &vNew = am->vvNew[b];
395
396                         int 
397                             nSize = 0;
398
399                         READWRITE(nSize);
400                         for (int n = 0; n < nSize; n++)
401                         {
402                             int 
403                                 nIndex = 0;
404
405                             READWRITE(nIndex);
406
407                             CAddrInfo 
408                                 &info = am->mapInfo[nIndex];
409
410                             if (
411                                 (nUBuckets == ADDRMAN_NEW_BUCKET_COUNT) && 
412                                 (info.nRefCount < ADDRMAN_NEW_BUCKETS_PER_ADDRESS)
413                                )
414                             {
415                                 info.nRefCount++;
416                                 vNew.insert(nIndex);
417                             }
418                         }
419                     }
420                 }
421             )
422
423
424     CAddrMan() : vRandom(0), vvTried(ADDRMAN_TRIED_BUCKET_COUNT, std::vector<int>(0)), vvNew(ADDRMAN_NEW_BUCKET_COUNT, std::set<int>())
425     {
426          nKey.resize(32);
427          RAND_bytes(&nKey[0], 32);
428
429          nIdCount = 0;
430          nTried = 0;
431          nNew = 0;
432     }
433
434     // Return the number of (unique) addresses in all tables.
435     int size()
436     {
437         return (int) vRandom.size();
438     }
439
440     // Consistency check
441     void Check()
442     {
443 #ifdef DEBUG_ADDRMAN
444         {
445             LOCK(cs);
446             int err;
447             if ((err=Check_()))
448                 printf("ADDRMAN CONSISTENCY CHECK FAILED!!! err=%i\n", err);
449         }
450 #endif
451     }
452
453     // Add a single address.
454     bool Add(const CAddress &addr, const CNetAddr& source, int64_t nTimePenalty = 0)
455     {
456         bool fRet = false;
457         {
458             LOCK(cs);
459             Check();
460             fRet |= Add_(addr, source, nTimePenalty);
461             Check();
462         }
463         if (fRet)
464             printf("Added %s from %s: %i tried, %i new\n", addr.ToStringIPPort().c_str(), source.ToString().c_str(), nTried, nNew);
465         return fRet;
466     }
467
468     // Add multiple addresses.
469     bool Add(const std::vector<CAddress> &vAddr, const CNetAddr& source, int64_t nTimePenalty = 0)
470     {
471         int nAdd = 0;
472         {
473             LOCK(cs);
474             Check();
475             for (std::vector<CAddress>::const_iterator it = vAddr.begin(); it != vAddr.end(); it++)
476                 nAdd += Add_(*it, source, nTimePenalty) ? 1 : 0;
477             Check();
478         }
479         if (nAdd)
480             printf("Added %i addresses from %s: %i tried, %i new\n", nAdd, source.ToString().c_str(), nTried, nNew);
481         return nAdd > 0;
482     }
483
484     // Mark an entry as accessible.
485     void Good(const CService &addr, int64_t nTime = GetAdjustedTime())
486     {
487         {
488             LOCK(cs);
489             Check();
490             Good_(addr, nTime);
491             Check();
492         }
493     }
494
495     // Mark an entry as connection attempted to.
496     void Attempt(const CService &addr, int64_t nTime = GetAdjustedTime())
497     {
498         {
499             LOCK(cs);
500             Check();
501             Attempt_(addr, nTime);
502             Check();
503         }
504     }
505
506     // Choose an address to connect to.
507     // nUnkBias determines how much "new" entries are favored over "tried" ones (0-100).
508     CAddress Select(int nUnkBias = 50)
509     {
510         CAddress addrRet;
511         {
512             LOCK(cs);
513             Check();
514             addrRet = Select_(nUnkBias);
515             Check();
516         }
517         return addrRet;
518     }
519
520     // Return a bunch of addresses, selected at random.
521     std::vector<CAddress> GetAddr()
522     {
523         Check();
524         std::vector<CAddress> vAddr;
525         {
526             LOCK(cs);
527             GetAddr_(vAddr);
528         }
529         Check();
530         return vAddr;
531     }
532
533     std::vector<CAddrInfo> GetOnlineAddr()
534     {
535         Check();
536         std::vector<CAddrInfo> vAddr;
537         {
538             LOCK(cs);
539             GetOnlineAddr_(vAddr);
540         }
541         Check();
542         return vAddr;
543     }
544
545     // Mark an entry as currently-connected-to.
546     void Connected(const CService &addr, int64_t nTime = GetAdjustedTime())
547     {
548         {
549             LOCK(cs);
550             Check();
551             Connected_(addr, nTime);
552             Check();
553         }
554     }
555 };
556
557 #endif