]> Git Repo - VerusCoin.git/blob - src/addrman.h
Merge pull request #5941
[VerusCoin.git] / src / addrman.h
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 "netbase.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
20 /** 
21  * Extended statistics about a CAddress 
22  */
23 class CAddrInfo : public CAddress
24 {
25 private:
26     //! where knowledge about this address first came from
27     CNetAddr source;
28
29     //! last successful connection by us
30     int64_t nLastSuccess;
31
32     //! last try whatsoever by us:
33     // int64_t CAddress::nLastTry
34
35     //! connection attempts since last successful attempt
36     int nAttempts;
37
38     //! reference count in new sets (memory only)
39     int nRefCount;
40
41     //! in tried set? (memory only)
42     bool fInTried;
43
44     //! position in vRandom
45     int nRandomPos;
46
47     friend class CAddrMan;
48
49 public:
50
51     ADD_SERIALIZE_METHODS;
52
53     template <typename Stream, typename Operation>
54     inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
55         READWRITE(*(CAddress*)this);
56         READWRITE(source);
57         READWRITE(nLastSuccess);
58         READWRITE(nAttempts);
59     }
60
61     void Init()
62     {
63         nLastSuccess = 0;
64         nLastTry = 0;
65         nAttempts = 0;
66         nRefCount = 0;
67         fInTried = false;
68         nRandomPos = -1;
69     }
70
71     CAddrInfo(const CAddress &addrIn, const CNetAddr &addrSource) : CAddress(addrIn), source(addrSource)
72     {
73         Init();
74     }
75
76     CAddrInfo() : CAddress(), source()
77     {
78         Init();
79     }
80
81     //! Calculate in which "tried" bucket this entry belongs
82     int GetTriedBucket(const uint256 &nKey) const;
83
84     //! Calculate in which "new" bucket this entry belongs, given a certain source
85     int GetNewBucket(const uint256 &nKey, const CNetAddr& src) const;
86
87     //! Calculate in which "new" bucket this entry belongs, using its default source
88     int GetNewBucket(const uint256 &nKey) const
89     {
90         return GetNewBucket(nKey, source);
91     }
92
93     //! Calculate in which position of a bucket to store this entry.
94     int GetBucketPosition(const uint256 &nKey, bool fNew, int nBucket) const;
95
96     //! Determine whether the statistics about this entry are bad enough so that it can just be deleted
97     bool IsTerrible(int64_t nNow = GetAdjustedTime()) const;
98
99     //! Calculate the relative chance this entry should be given when selecting nodes to connect to
100     double GetChance(int64_t nNow = GetAdjustedTime()) const;
101
102 };
103
104 /** Stochastic address manager
105  *
106  * Design goals:
107  *  * Keep the address tables in-memory, and asynchronously dump the entire to able in peers.dat.
108  *  * Make sure no (localized) attacker can fill the entire table with his nodes/addresses.
109  *
110  * To that end:
111  *  * Addresses are organized into buckets.
112  *    * Address that have not yet been tried go into 1024 "new" buckets.
113  *      * Based on the address range (/16 for IPv4) of source of the information, 64 buckets are selected at random
114  *      * The actual bucket is chosen from one of these, based on the range the address itself is located.
115  *      * One single address can occur in up to 8 different buckets, to increase selection chances for addresses that
116  *        are seen frequently. The chance for increasing this multiplicity decreases exponentially.
117  *      * When adding a new address to a full bucket, a randomly chosen entry (with a bias favoring less recently seen
118  *        ones) is removed from it first.
119  *    * Addresses of nodes that are known to be accessible go into 256 "tried" buckets.
120  *      * Each address range selects at random 8 of these buckets.
121  *      * The actual bucket is chosen from one of these, based on the full address.
122  *      * When adding a new good address to a full bucket, a randomly chosen entry (with a bias favoring less recently
123  *        tried ones) is evicted from it, back to the "new" buckets.
124  *    * Bucket selection is based on cryptographic hashing, using a randomly-generated 256-bit key, which should not
125  *      be observable by adversaries.
126  *    * Several indexes are kept for high performance. Defining DEBUG_ADDRMAN will introduce frequent (and expensive)
127  *      consistency checks for the entire data structure.
128  */
129
130 //! total number of buckets for tried addresses
131 #define ADDRMAN_TRIED_BUCKET_COUNT 256
132
133 //! total number of buckets for new addresses
134 #define ADDRMAN_NEW_BUCKET_COUNT 1024
135
136 //! maximum allowed number of entries in buckets for new and tried addresses
137 #define ADDRMAN_BUCKET_SIZE 64
138
139 //! over how many buckets entries with tried addresses from a single group (/16 for IPv4) are spread
140 #define ADDRMAN_TRIED_BUCKETS_PER_GROUP 8
141
142 //! over how many buckets entries with new addresses originating from a single group are spread
143 #define ADDRMAN_NEW_BUCKETS_PER_SOURCE_GROUP 64
144
145 //! in how many buckets for entries with new addresses a single address may occur
146 #define ADDRMAN_NEW_BUCKETS_PER_ADDRESS 8
147
148 //! how old addresses can maximally be
149 #define ADDRMAN_HORIZON_DAYS 30
150
151 //! after how many failed attempts we give up on a new node
152 #define ADDRMAN_RETRIES 3
153
154 //! how many successive failures are allowed ...
155 #define ADDRMAN_MAX_FAILURES 10
156
157 //! ... in at least this many days
158 #define ADDRMAN_MIN_FAIL_DAYS 7
159
160 //! the maximum percentage of nodes to return in a getaddr call
161 #define ADDRMAN_GETADDR_MAX_PCT 23
162
163 //! the maximum number of nodes to return in a getaddr call
164 #define ADDRMAN_GETADDR_MAX 2500
165
166 /** 
167  * Stochastical (IP) address manager 
168  */
169 class CAddrMan
170 {
171 private:
172     //! critical section to protect the inner data structures
173     mutable CCriticalSection cs;
174
175     //! secret key to randomize bucket select with
176     uint256 nKey;
177
178     //! last used nId
179     int nIdCount;
180
181     //! table with information about all nIds
182     std::map<int, CAddrInfo> mapInfo;
183
184     //! find an nId based on its network address
185     std::map<CNetAddr, int> mapAddr;
186
187     //! randomly-ordered vector of all nIds
188     std::vector<int> vRandom;
189
190     // number of "tried" entries
191     int nTried;
192
193     //! list of "tried" buckets
194     int vvTried[ADDRMAN_TRIED_BUCKET_COUNT][ADDRMAN_BUCKET_SIZE];
195
196     //! number of (unique) "new" entries
197     int nNew;
198
199     //! list of "new" buckets
200     int vvNew[ADDRMAN_NEW_BUCKET_COUNT][ADDRMAN_BUCKET_SIZE];
201
202 protected:
203
204     //! Find an entry.
205     CAddrInfo* Find(const CNetAddr& addr, int *pnId = NULL);
206
207     //! find an entry, creating it if necessary.
208     //! nTime and nServices of the found node are updated, if necessary.
209     CAddrInfo* Create(const CAddress &addr, const CNetAddr &addrSource, int *pnId = NULL);
210
211     //! Swap two elements in vRandom.
212     void SwapRandom(unsigned int nRandomPos1, unsigned int nRandomPos2);
213
214     //! Move an entry from the "new" table(s) to the "tried" table
215     void MakeTried(CAddrInfo& info, int nId);
216
217     //! Delete an entry. It must not be in tried, and have refcount 0.
218     void Delete(int nId);
219
220     //! Clear a position in a "new" table. This is the only place where entries are actually deleted.
221     void ClearNew(int nUBucket, int nUBucketPos);
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_();
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
244     //! Mark an entry as currently-connected-to.
245     void Connected_(const CService &addr, int64_t nTime);
246
247 public:
248     /**
249      * serialized format:
250      * * version byte (currently 1)
251      * * 0x20 + nKey (serialized as if it were a vector, for backward compatibility)
252      * * nNew
253      * * nTried
254      * * number of "new" buckets XOR 2**30
255      * * all nNew addrinfos in vvNew
256      * * all nTried addrinfos in vvTried
257      * * for each bucket:
258      *   * number of elements
259      *   * for each element: index
260      *
261      * 2**30 is xorred with the number of buckets to make addrman deserializer v0 detect it
262      * as incompatible. This is necessary because it did not check the version number on
263      * deserialization.
264      *
265      * Notice that vvTried, mapAddr and vVector are never encoded explicitly;
266      * they are instead reconstructed from the other information.
267      *
268      * vvNew is serialized, but only used if ADDRMAN_UNKOWN_BUCKET_COUNT didn't change,
269      * otherwise it is reconstructed as well.
270      *
271      * This format is more complex, but significantly smaller (at most 1.5 MiB), and supports
272      * changes to the ADDRMAN_ parameters without breaking the on-disk structure.
273      *
274      * We don't use ADD_SERIALIZE_METHODS since the serialization and deserialization code has
275      * very little in common.
276      */
277     template<typename Stream>
278     void Serialize(Stream &s, int nType, int nVersionDummy) const
279     {
280         LOCK(cs);
281
282         unsigned char nVersion = 1;
283         s << nVersion;
284         s << ((unsigned char)32);
285         s << nKey;
286         s << nNew;
287         s << nTried;
288
289         int nUBuckets = ADDRMAN_NEW_BUCKET_COUNT ^ (1 << 30);
290         s << nUBuckets;
291         std::map<int, int> mapUnkIds;
292         int nIds = 0;
293         for (std::map<int, CAddrInfo>::const_iterator it = mapInfo.begin(); it != mapInfo.end(); it++) {
294             mapUnkIds[(*it).first] = nIds;
295             const CAddrInfo &info = (*it).second;
296             if (info.nRefCount) {
297                 assert(nIds != nNew); // this means nNew was wrong, oh ow
298                 s << info;
299                 nIds++;
300             }
301         }
302         nIds = 0;
303         for (std::map<int, CAddrInfo>::const_iterator it = mapInfo.begin(); it != mapInfo.end(); it++) {
304             const CAddrInfo &info = (*it).second;
305             if (info.fInTried) {
306                 assert(nIds != nTried); // this means nTried was wrong, oh ow
307                 s << info;
308                 nIds++;
309             }
310         }
311         for (int bucket = 0; bucket < ADDRMAN_NEW_BUCKET_COUNT; bucket++) {
312             int nSize = 0;
313             for (int i = 0; i < ADDRMAN_BUCKET_SIZE; i++) {
314                 if (vvNew[bucket][i] != -1)
315                     nSize++;
316             }
317             s << nSize;
318             for (int i = 0; i < ADDRMAN_BUCKET_SIZE; i++) {
319                 if (vvNew[bucket][i] != -1) {
320                     int nIndex = mapUnkIds[vvNew[bucket][i]];
321                     s << nIndex;
322                 }
323             }
324         }
325     }
326
327     template<typename Stream>
328     void Unserialize(Stream& s, int nType, int nVersionDummy)
329     {
330         LOCK(cs);
331
332         Clear();
333
334         unsigned char nVersion;
335         s >> nVersion;
336         unsigned char nKeySize;
337         s >> nKeySize;
338         if (nKeySize != 32) throw std::ios_base::failure("Incorrect keysize in addrman deserialization");
339         s >> nKey;
340         s >> nNew;
341         s >> nTried;
342         int nUBuckets = 0;
343         s >> nUBuckets;
344         if (nVersion != 0) {
345             nUBuckets ^= (1 << 30);
346         }
347
348         // Deserialize entries from the new table.
349         for (int n = 0; n < nNew; n++) {
350             CAddrInfo &info = mapInfo[n];
351             s >> info;
352             mapAddr[info] = n;
353             info.nRandomPos = vRandom.size();
354             vRandom.push_back(n);
355             if (nVersion != 1 || nUBuckets != ADDRMAN_NEW_BUCKET_COUNT) {
356                 // In case the new table data cannot be used (nVersion unknown, or bucket count wrong),
357                 // immediately try to give them a reference based on their primary source address.
358                 int nUBucket = info.GetNewBucket(nKey);
359                 int nUBucketPos = info.GetBucketPosition(nKey, true, nUBucket);
360                 if (vvNew[nUBucket][nUBucketPos] == -1) {
361                     vvNew[nUBucket][nUBucketPos] = n;
362                     info.nRefCount++;
363                 }
364             }
365         }
366         nIdCount = nNew;
367
368         // Deserialize entries from the tried table.
369         int nLost = 0;
370         for (int n = 0; n < nTried; n++) {
371             CAddrInfo info;
372             s >> info;
373             int nKBucket = info.GetTriedBucket(nKey);
374             int nKBucketPos = info.GetBucketPosition(nKey, false, nKBucket);
375             if (vvTried[nKBucket][nKBucketPos] == -1) {
376                 info.nRandomPos = vRandom.size();
377                 info.fInTried = true;
378                 vRandom.push_back(nIdCount);
379                 mapInfo[nIdCount] = info;
380                 mapAddr[info] = nIdCount;
381                 vvTried[nKBucket][nKBucketPos] = nIdCount;
382                 nIdCount++;
383             } else {
384                 nLost++;
385             }
386         }
387         nTried -= nLost;
388
389         // Deserialize positions in the new table (if possible).
390         for (int bucket = 0; bucket < nUBuckets; bucket++) {
391             int nSize = 0;
392             s >> nSize;
393             for (int n = 0; n < nSize; n++) {
394                 int nIndex = 0;
395                 s >> nIndex;
396                 if (nIndex >= 0 && nIndex < nNew) {
397                     CAddrInfo &info = mapInfo[nIndex];
398                     int nUBucketPos = info.GetBucketPosition(nKey, true, bucket);
399                     if (nVersion == 1 && nUBuckets == ADDRMAN_NEW_BUCKET_COUNT && vvNew[bucket][nUBucketPos] == -1 && info.nRefCount < ADDRMAN_NEW_BUCKETS_PER_ADDRESS) {
400                         info.nRefCount++;
401                         vvNew[bucket][nUBucketPos] = nIndex;
402                     }
403                 }
404             }
405         }
406
407         // Prune new entries with refcount 0 (as a result of collisions).
408         int nLostUnk = 0;
409         for (std::map<int, CAddrInfo>::const_iterator it = mapInfo.begin(); it != mapInfo.end(); ) {
410             if (it->second.fInTried == false && it->second.nRefCount == 0) {
411                 std::map<int, CAddrInfo>::const_iterator itCopy = it++;
412                 Delete(itCopy->first);
413                 nLostUnk++;
414             } else {
415                 it++;
416             }
417         }
418         if (nLost + nLostUnk > 0) {
419             LogPrint("addrman", "addrman lost %i new and %i tried addresses due to collisions\n", nLostUnk, nLost);
420         }
421
422         Check();
423     }
424
425     unsigned int GetSerializeSize(int nType, int nVersion) const
426     {
427         return (CSizeComputer(nType, nVersion) << *this).size();
428     }
429
430     void Clear()
431     {
432         std::vector<int>().swap(vRandom);
433         nKey = GetRandHash();
434         for (size_t bucket = 0; bucket < ADDRMAN_NEW_BUCKET_COUNT; bucket++) {
435             for (size_t entry = 0; entry < ADDRMAN_BUCKET_SIZE; entry++) {
436                 vvNew[bucket][entry] = -1;
437             }
438         }
439         for (size_t bucket = 0; bucket < ADDRMAN_TRIED_BUCKET_COUNT; bucket++) {
440             for (size_t entry = 0; entry < ADDRMAN_BUCKET_SIZE; entry++) {
441                 vvTried[bucket][entry] = -1;
442             }
443         }
444
445         nIdCount = 0;
446         nTried = 0;
447         nNew = 0;
448     }
449
450     CAddrMan()
451     {
452         Clear();
453     }
454
455     ~CAddrMan()
456     {
457         nKey.SetNull();
458     }
459
460     //! Return the number of (unique) addresses in all tables.
461     int size()
462     {
463         return vRandom.size();
464     }
465
466     //! Consistency check
467     void Check()
468     {
469 #ifdef DEBUG_ADDRMAN
470         {
471             LOCK(cs);
472             int err;
473             if ((err=Check_()))
474                 LogPrintf("ADDRMAN CONSISTENCY CHECK FAILED!!! err=%i\n", err);
475         }
476 #endif
477     }
478
479     //! Add a single address.
480     bool Add(const CAddress &addr, const CNetAddr& source, int64_t nTimePenalty = 0)
481     {
482         bool fRet = false;
483         {
484             LOCK(cs);
485             Check();
486             fRet |= Add_(addr, source, nTimePenalty);
487             Check();
488         }
489         if (fRet)
490             LogPrint("addrman", "Added %s from %s: %i tried, %i new\n", addr.ToStringIPPort(), source.ToString(), nTried, nNew);
491         return fRet;
492     }
493
494     //! Add multiple addresses.
495     bool Add(const std::vector<CAddress> &vAddr, const CNetAddr& source, int64_t nTimePenalty = 0)
496     {
497         int nAdd = 0;
498         {
499             LOCK(cs);
500             Check();
501             for (std::vector<CAddress>::const_iterator it = vAddr.begin(); it != vAddr.end(); it++)
502                 nAdd += Add_(*it, source, nTimePenalty) ? 1 : 0;
503             Check();
504         }
505         if (nAdd)
506             LogPrint("addrman", "Added %i addresses from %s: %i tried, %i new\n", nAdd, source.ToString(), nTried, nNew);
507         return nAdd > 0;
508     }
509
510     //! Mark an entry as accessible.
511     void Good(const CService &addr, int64_t nTime = GetAdjustedTime())
512     {
513         {
514             LOCK(cs);
515             Check();
516             Good_(addr, nTime);
517             Check();
518         }
519     }
520
521     //! Mark an entry as connection attempted to.
522     void Attempt(const CService &addr, int64_t nTime = GetAdjustedTime())
523     {
524         {
525             LOCK(cs);
526             Check();
527             Attempt_(addr, nTime);
528             Check();
529         }
530     }
531
532     /**
533      * Choose an address to connect to.
534      * nUnkBias determines how much "new" entries are favored over "tried" ones (0-100).
535      */
536     CAddress Select()
537     {
538         CAddress addrRet;
539         {
540             LOCK(cs);
541             Check();
542             addrRet = Select_();
543             Check();
544         }
545         return addrRet;
546     }
547
548     //! Return a bunch of addresses, selected at random.
549     std::vector<CAddress> GetAddr()
550     {
551         Check();
552         std::vector<CAddress> vAddr;
553         {
554             LOCK(cs);
555             GetAddr_(vAddr);
556         }
557         Check();
558         return vAddr;
559     }
560
561     //! Mark an entry as currently-connected-to.
562     void Connected(const CService &addr, int64_t nTime = GetAdjustedTime())
563     {
564         {
565             LOCK(cs);
566             Check();
567             Connected_(addr, nTime);
568             Check();
569         }
570     }
571 };
572
573 #endif // BITCOIN_ADDRMAN_H
This page took 0.054367 seconds and 4 git commands to generate.