This check is excessive since we don't support walletless mode for now.
[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 "util.h"
10 #include "sync.h"
11
12 #include <map>
13 #include <vector>
14
15 #include <openssl/rand.h>
16
17 // Extended statistics about a CAddress
18 class CAddrInfo : public CAddress
19 {
20 private:
21     // where knowledge about this address first came from
22     CNetAddr source;
23
24     // last successful connection by us
25     int64_t nLastSuccess;
26
27     // last try whatsoever by us:
28     // int64_t CAddress::nLastTry
29
30     // connection attempts since last successful attempt
31     int nAttempts;
32
33     // reference count in new sets (memory only)
34     int nRefCount;
35
36     // in tried set? (memory only)
37     bool fInTried;
38
39     // position in vRandom
40     int nRandomPos;
41
42     friend class CAddrMan;
43
44 public:
45
46     IMPLEMENT_SERIALIZE(
47         CAddress* pthis = (CAddress*)(this);
48         READWRITE(*pthis);
49         READWRITE(source);
50         READWRITE(nLastSuccess);
51         READWRITE(nAttempts);
52     )
53
54     void Init();
55
56     CAddrInfo(const CAddress &addrIn, const CNetAddr &addrSource);
57     CAddrInfo();
58
59     // Calculate in which "tried" bucket this entry belongs
60     int GetTriedBucket(const std::vector<unsigned char> &nKey) const;
61
62     // Calculate in which "new" bucket this entry belongs, given a certain source
63     int GetNewBucket(const std::vector<unsigned char> &nKey, const CNetAddr& src) const;
64
65     // Calculate in which "new" bucket this entry belongs, using its default source
66     int GetNewBucket(const std::vector<unsigned char> &nKey) const;
67
68     // Determine whether the statistics about this entry are bad enough so that it can just be deleted
69     bool IsTerrible(int64_t nNow = GetAdjustedTime()) const;
70
71     // Calculate the relative chance this entry should be given when selecting nodes to connect to
72     double GetChance(int64_t nNow = GetAdjustedTime()) const;
73
74 };
75
76 // Stochastic address manager
77 //
78 // Design goals:
79 //  * Only keep a limited number of addresses around, so that addr.dat and memory requirements do not grow without bound.
80 //  * Keep the address tables in-memory, and asynchronously dump the entire to able in addr.dat.
81 //  * Make sure no (localized) attacker can fill the entire table with his nodes/addresses.
82 //
83 // To that end:
84 //  * Addresses are organized into buckets.
85 //    * Address that have not yet been tried go into 256 "new" buckets.
86 //      * Based on the address range (/16 for IPv4) of source of the information, 32 buckets are selected at random
87 //      * The actual bucket is chosen from one of these, based on the range the address itself is located.
88 //      * One single address can occur in up to 4 different buckets, to increase selection chances for addresses that
89 //        are seen frequently. The chance for increasing this multiplicity decreases exponentially.
90 //      * When adding a new address to a full bucket, a randomly chosen entry (with a bias favoring less recently seen
91 //        ones) is removed from it first.
92 //    * Addresses of nodes that are known to be accessible go into 64 "tried" buckets.
93 //      * Each address range selects at random 4 of these buckets.
94 //      * The actual bucket is chosen from one of these, based on the full address.
95 //      * When adding a new good address to a full bucket, a randomly chosen entry (with a bias favoring less recently
96 //        tried ones) is evicted from it, back to the "new" buckets.
97 //    * Bucket selection is based on cryptographic hashing, using a randomly-generated 256-bit key, which should not
98 //      be observable by adversaries.
99 //    * Several indexes are kept for high performance.
100
101 // total number of buckets for tried addresses
102 #define ADDRMAN_TRIED_BUCKET_COUNT 64
103
104 // maximum allowed number of entries in buckets for tried addresses
105 #define ADDRMAN_TRIED_BUCKET_SIZE 64
106
107 // total number of buckets for new addresses
108 #define ADDRMAN_NEW_BUCKET_COUNT 256
109
110 // maximum allowed number of entries in buckets for new addresses
111 #define ADDRMAN_NEW_BUCKET_SIZE 64
112
113 // over how many buckets entries with tried addresses from a single group (/16 for IPv4) are spread
114 #define ADDRMAN_TRIED_BUCKETS_PER_GROUP 4
115
116 // over how many buckets entries with new addresses originating from a single group are spread
117 #define ADDRMAN_NEW_BUCKETS_PER_SOURCE_GROUP 32
118
119 // in how many buckets for entries with new addresses a single address may occur
120 #define ADDRMAN_NEW_BUCKETS_PER_ADDRESS 4
121
122 // how many entries in a bucket with tried addresses are inspected, when selecting one to replace
123 #define ADDRMAN_TRIED_ENTRIES_INSPECT_ON_EVICT 4
124
125 // how old addresses can maximally be
126 #define ADDRMAN_HORIZON_DAYS 30
127
128 // after how many failed attempts we give up on a new node
129 #define ADDRMAN_RETRIES 3
130
131 // how many successive failures are allowed ...
132 #define ADDRMAN_MAX_FAILURES 10
133
134 // ... in at least this many days
135 #define ADDRMAN_MIN_FAIL_DAYS 7
136
137 // the maximum percentage of nodes to return in a getaddr call
138 #define ADDRMAN_GETADDR_MAX_PCT 23
139
140 // the maximum number of nodes to return in a getaddr call
141 #define ADDRMAN_GETADDR_MAX 2500
142
143 // Stochastical (IP) address manager
144 class CAddrMan
145 {
146 private:
147     // critical section to protect the inner data structures
148     mutable CCriticalSection cs;
149
150     // secret key to randomize bucket select with
151     std::vector<unsigned char> nKey;
152
153     // last used nId
154     int nIdCount;
155
156     // table with information about all nIds
157     std::map<int, CAddrInfo> mapInfo;
158
159     // find an nId based on its network address
160     std::map<CNetAddr, int> mapAddr;
161
162     // randomly-ordered vector of all nIds
163     std::vector<int> vRandom;
164
165     // number of "tried" entries
166     int nTried;
167
168     // list of "tried" buckets
169     std::vector<std::vector<int> > vvTried;
170
171     // number of (unique) "new" entries
172     int nNew;
173
174     // list of "new" buckets
175     std::vector<std::set<int> > vvNew;
176
177 protected:
178
179     // Find an entry.
180     CAddrInfo* Find(const CNetAddr& addr, int *pnId = NULL);
181
182     // find an entry, creating it if necessary.
183     // nTime and nServices of found node is updated, if necessary.
184     CAddrInfo* Create(const CAddress &addr, const CNetAddr &addrSource, int *pnId = NULL);
185
186     // Swap two elements in vRandom.
187     void SwapRandom(unsigned int nRandomPos1, unsigned int nRandomPos2);
188
189     // Return position in given bucket to replace.
190     int SelectTried(int nKBucket);
191
192     // Remove an element from a "new" bucket.
193     // This is the only place where actual deletes occur.
194     // They are never deleted while in the "tried" table, only possibly evicted back to the "new" table.
195     int ShrinkNew(int nUBucket);
196
197     // Move an entry from the "new" table(s) to the "tried" table
198     // @pre vvUnkown[nOrigin].count(nId) != 0
199     void MakeTried(CAddrInfo& info, int nId, int nOrigin);
200
201     // Mark an entry "good", possibly moving it from "new" to "tried".
202     void Good_(const CService &addr, int64_t nTime);
203
204     // Add an entry to the "new" table.
205     bool Add_(const CAddress &addr, const CNetAddr& source, int64_t nTimePenalty);
206
207     // Mark an entry as attempted to connect.
208     void Attempt_(const CService &addr, int64_t nTime);
209
210     // Select an address to connect to.
211     // nUnkBias determines how much to favor new addresses over tried ones (min=0, max=100)
212     CAddress Select_(int nUnkBias);
213
214     // Select several addresses at once.
215     void GetAddr_(std::vector<CAddress> &vAddr);
216     void GetOnlineAddr_(std::vector<CAddrInfo> &vAddr);
217
218     // Mark an entry as currently-connected-to.
219     void Connected_(const CService &addr, int64_t nTime);
220
221 public:
222
223     typedef std::map<int, int> MapUnkIds; // For MSVC macro
224     IMPLEMENT_SERIALIZE
225             (
226             LOCK(cs);
227                 unsigned char 
228                     nVersion = 0;
229
230                 READWRITE(nVersion);
231                 READWRITE(nKey);
232                 READWRITE(nNew);
233                 READWRITE(nTried);
234
235                 CAddrMan
236                     *am = const_cast<CAddrMan*>(this);
237
238                 if (fWrite)
239                 {
240                     int
241                         nUBuckets = ADDRMAN_NEW_BUCKET_COUNT;
242
243                     READWRITE(nUBuckets);
244                     MapUnkIds mapUnkIds;
245                     int
246                         nIds = 0;
247                     for (auto it = am->mapInfo.begin(); it != am->mapInfo.end(); it++)
248                     {
249                         if (nIds == nNew)
250                             break; // this means nNew was wrong, oh ow
251                         mapUnkIds[(*it).first] = nIds;
252
253                         CAddrInfo
254                             &info = (*it).second;
255
256                         if (info.nRefCount)
257                         {
258                             READWRITE(info);
259                             nIds++;
260                         }
261                     }
262                     nIds = 0;
263                     for (auto it = am->mapInfo.begin(); it != am->mapInfo.end(); it++)
264                     {
265                         if (nIds == nTried) 
266                             break; /* this means nTried was wrong, oh ow */
267
268                         CAddrInfo 
269                             &info = (*it).second;
270
271                         if (info.fInTried)
272                         {
273                             READWRITE(info);
274                             nIds++;
275                         }
276                     }
277                     for (
278                          auto it = am->vvNew.begin(); 
279                          it != am->vvNew.end(); 
280                          it++
281                         )
282                     {
283                         std::set<int> 
284                             &vNew = (*it);
285
286                         int 
287                             nSize = int( vNew.size() );
288
289                         READWRITE(nSize);
290                         for (auto it2 = vNew.begin(); it2 != vNew.end(); it2++)
291                         {
292                         int 
293                             nIndex = mapUnkIds[*it2];
294                         READWRITE(nIndex);
295                         }
296                     }
297                 } 
298                 else 
299                 {
300                     int 
301                         nUBuckets = 0;
302
303                     READWRITE(nUBuckets);
304                     am->nIdCount = 0;
305                     am->mapInfo.clear();
306                     am->mapAddr.clear();
307                     am->vRandom.clear();
308                     am->vvTried = 
309                         std::vector<std::vector<int> >(
310                                                         ADDRMAN_TRIED_BUCKET_COUNT, 
311                                                         std::vector<int>(0)
312                                                       );
313                     am->vvNew = 
314                         std::vector<std::set<int> >(
315                                                     ADDRMAN_NEW_BUCKET_COUNT, 
316                                                     std::set<int>()
317                                                    );
318                     for (int n = 0; n < am->nNew; n++)
319                     {
320                         CAddrInfo 
321                             &info = am->mapInfo[n];
322
323                         READWRITE(info);
324                         am->mapAddr[info] = n;
325                         info.nRandomPos = int( vRandom.size() );
326                         am->vRandom.push_back(n);
327                         if (nUBuckets != ADDRMAN_NEW_BUCKET_COUNT)
328                         {
329                             am->vvNew[info.GetNewBucket(am->nKey)].insert(n);
330                             info.nRefCount++;
331                         }
332                     }
333                     am->nIdCount = am->nNew;
334
335                     int 
336                         nLost = 0;
337
338                     for (int n = 0; n < am->nTried; n++)
339                     {
340                         CAddrInfo 
341                             info;
342
343                         READWRITE(info);
344
345                         std::vector<int> 
346                             &vTried = am->vvTried[info.GetTriedBucket(am->nKey)];
347
348                         if (vTried.size() < ADDRMAN_TRIED_BUCKET_SIZE)
349                         {
350                             info.nRandomPos = int( vRandom.size() );
351                             info.fInTried = true;
352                             am->vRandom.push_back(am->nIdCount);
353                             am->mapInfo[am->nIdCount] = info;
354                             am->mapAddr[info] = am->nIdCount;
355                             vTried.push_back(am->nIdCount);
356                             am->nIdCount++;
357                         } 
358                         else 
359                         {
360                             nLost++;
361                         }
362                     }
363                     am->nTried -= nLost;
364                     for (int b = 0; b < nUBuckets; b++)
365                     {
366                         std::set<int> 
367                             &vNew = am->vvNew[b];
368
369                         int 
370                             nSize = 0;
371
372                         READWRITE(nSize);
373                         for (int n = 0; n < nSize; n++)
374                         {
375                             int 
376                                 nIndex = 0;
377
378                             READWRITE(nIndex);
379
380                             CAddrInfo 
381                                 &info = am->mapInfo[nIndex];
382
383                             if (
384                                 (nUBuckets == ADDRMAN_NEW_BUCKET_COUNT) && 
385                                 (info.nRefCount < ADDRMAN_NEW_BUCKETS_PER_ADDRESS)
386                                )
387                             {
388                                 info.nRefCount++;
389                                 vNew.insert(nIndex);
390                             }
391                         }
392                     }
393                 }
394             )
395
396     CAddrMan();
397
398     // Return the number of (unique) addresses in all tables.
399     int size();
400
401     // Add a single address.
402     bool Add(const CAddress &addr, const CNetAddr& source, int64_t nTimePenalty = 0);
403
404     // Add multiple addresses.
405     bool Add(const std::vector<CAddress> &vAddr, const CNetAddr& source, int64_t nTimePenalty = 0);
406
407     // Mark an entry as accessible.
408     void Good(const CService &addr, int64_t nTime = GetAdjustedTime());
409
410     // Mark an entry as connection attempted to.
411     void Attempt(const CService &addr, int64_t nTime = GetAdjustedTime());
412
413     // Choose an address to connect to.
414     // nUnkBias determines how much "new" entries are favored over "tried" ones (0-100).
415     CAddress Select(int nUnkBias = 50);
416
417     // Return a bunch of addresses, selected at random.
418     std::vector<CAddress> GetAddr();
419
420     std::vector<CAddrInfo> GetOnlineAddr();
421
422     // Mark an entry as currently-connected-to.
423     void Connected(const CService &addr, int64_t nTime = GetAdjustedTime());
424 };
425
426 #endif