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.
10 void CAddrInfo::Init()
20 CAddrInfo::CAddrInfo(const CAddress &addrIn, const CNetAddr &addrSource) : CAddress(addrIn), source(addrSource)
25 CAddrInfo::CAddrInfo() : CAddress(), source()
30 int CAddrInfo::GetTriedBucket(const std::vector<unsigned char> &nKey) const
32 CDataStream ss1(SER_GETHASH, 0);
33 std::vector<unsigned char> vchKey = GetKey();
34 ss1 << nKey << vchKey;
35 auto hash1 = Hash(ss1.begin(), ss1.end()).Get64();
37 CDataStream ss2(SER_GETHASH, 0);
38 std::vector<unsigned char> vchGroupKey = GetGroup();
39 ss2 << nKey << vchGroupKey << (hash1 % ADDRMAN_TRIED_BUCKETS_PER_GROUP);
40 auto hash2 = Hash(ss2.begin(), ss2.end()).Get64();
41 return hash2 % ADDRMAN_TRIED_BUCKET_COUNT;
44 int CAddrInfo::GetNewBucket(const std::vector<unsigned char> &nKey, const CNetAddr& src) const
46 CDataStream ss1(SER_GETHASH, 0);
47 std::vector<unsigned char> vchGroupKey = GetGroup();
48 std::vector<unsigned char> vchSourceGroupKey = src.GetGroup();
49 ss1 << nKey << vchGroupKey << vchSourceGroupKey;
50 auto hash1 = Hash(ss1.begin(), ss1.end()).Get64();
52 CDataStream ss2(SER_GETHASH, 0);
53 ss2 << nKey << vchSourceGroupKey << (hash1 % ADDRMAN_NEW_BUCKETS_PER_SOURCE_GROUP);
54 auto hash2 = Hash(ss2.begin(), ss2.end()).Get64();
55 return hash2 % ADDRMAN_NEW_BUCKET_COUNT;
58 int CAddrInfo::GetNewBucket(const std::vector<unsigned char> &nKey) const
60 return GetNewBucket(nKey, source);
63 bool CAddrInfo::IsTerrible(int64_t nNow) const
65 if (nLastTry && nLastTry >= nNow-60) // never remove things tried the last minute
68 if (nTime > nNow + 10*60) // came in a flying DeLorean
71 if (nTime==0 || nNow-nTime > ADDRMAN_HORIZON_DAYS*nOneDay) // not seen in over a month
74 if (nLastSuccess==0 && nAttempts>=ADDRMAN_RETRIES) // tried three times and never a success
77 if (nNow-nLastSuccess > ADDRMAN_MIN_FAIL_DAYS*nOneDay && nAttempts>=ADDRMAN_MAX_FAILURES) // 10 successive failures in the last week
83 double CAddrInfo::GetChance(int64_t nNow) const
87 auto nSinceLastSeen = nNow - nTime;
88 auto nSinceLastTry = nNow - nLastTry;
90 if (nSinceLastSeen < 0) nSinceLastSeen = 0;
91 if (nSinceLastTry < 0) nSinceLastTry = 0;
93 fChance *= 600.0 / (600.0 + nSinceLastSeen);
95 // deprioritize very recent attempts away
96 if (nSinceLastTry < 60*10)
99 // deprioritize 50% after each failed attempt
100 for (int n=0; n<nAttempts; n++)
106 CAddrInfo* CAddrMan::Find(const CNetAddr& addr, int *pnId)
108 auto it = mapAddr.find(addr);
109 if (it == mapAddr.end())
112 *pnId = (*it).second;
113 auto it2 = mapInfo.find((*it).second);
114 if (it2 != mapInfo.end())
115 return &(*it2).second;
119 CAddrInfo* CAddrMan::Create(const CAddress &addr, const CNetAddr &addrSource, int *pnId)
121 int nId = nIdCount++;
122 mapInfo[nId] = CAddrInfo(addr, addrSource);
124 mapInfo[nId].nRandomPos = vRandom.size();
125 vRandom.push_back(nId);
128 return &mapInfo[nId];
131 void CAddrMan::SwapRandom(unsigned int nRndPos1, unsigned int nRndPos2)
133 if (nRndPos1 == nRndPos2)
136 assert(nRndPos1 < vRandom.size() && nRndPos2 < vRandom.size());
138 int nId1 = vRandom[nRndPos1];
139 int nId2 = vRandom[nRndPos2];
141 assert(mapInfo.count(nId1) == 1);
142 assert(mapInfo.count(nId2) == 1);
144 mapInfo[nId1].nRandomPos = nRndPos2;
145 mapInfo[nId2].nRandomPos = nRndPos1;
147 vRandom[nRndPos1] = nId2;
148 vRandom[nRndPos2] = nId1;
151 int CAddrMan::SelectTried(int nKBucket)
153 std::vector<int> &vTried = vvTried[nKBucket];
155 // random shuffle the first few elements (using the entire list)
156 // find the least recently tried among them
157 int64_t nOldest = -1;
159 for (unsigned int i = 0; i < ADDRMAN_TRIED_ENTRIES_INSPECT_ON_EVICT && i < vTried.size(); i++)
161 int nPos = GetRandInt(vTried.size() - i) + i;
162 int nTemp = vTried[nPos];
163 vTried[nPos] = vTried[i];
165 assert(nOldest == -1 || mapInfo.count(nTemp) == 1);
166 if (nOldest == -1 || mapInfo[nTemp].nLastSuccess < mapInfo[nOldest].nLastSuccess) {
175 int CAddrMan::ShrinkNew(int nUBucket)
177 assert(nUBucket >= 0 && (unsigned int)nUBucket < vvNew.size());
178 std::set<int> &vNew = vvNew[nUBucket];
180 // first look for deletable items
181 for (auto it = vNew.begin(); it != vNew.end(); it++)
183 assert(mapInfo.count(*it));
184 CAddrInfo &info = mapInfo[*it];
185 if (info.IsTerrible())
187 if (--info.nRefCount == 0)
189 SwapRandom(info.nRandomPos, vRandom.size()-1);
200 // otherwise, select four randomly, and pick the oldest of those to replace
201 int n[4] = {GetRandInt(vNew.size()), GetRandInt(vNew.size()), GetRandInt(vNew.size()), GetRandInt(vNew.size())};
204 for (auto it = vNew.begin(); it != vNew.end(); it++)
206 if (nI == n[0] || nI == n[1] || nI == n[2] || nI == n[3])
208 assert(nOldest == -1 || mapInfo.count(*it) == 1);
209 if (nOldest == -1 || mapInfo[*it].nTime < mapInfo[nOldest].nTime)
214 assert(mapInfo.count(nOldest) == 1);
215 CAddrInfo &info = mapInfo[nOldest];
216 if (--info.nRefCount == 0)
218 SwapRandom(info.nRandomPos, vRandom.size()-1);
221 mapInfo.erase(nOldest);
229 void CAddrMan::MakeTried(CAddrInfo& info, int nId, int nOrigin)
231 assert(vvNew[nOrigin].count(nId) == 1);
233 // remove the entry from all new buckets
234 for (auto it = vvNew.begin(); it != vvNew.end(); it++)
236 if ((*it).erase(nId))
241 assert(info.nRefCount == 0);
243 // what tried bucket to move the entry to
244 int nKBucket = info.GetTriedBucket(nKey);
245 std::vector<int> &vTried = vvTried[nKBucket];
247 // first check whether there is place to just add it
248 if (vTried.size() < ADDRMAN_TRIED_BUCKET_SIZE)
250 vTried.push_back(nId);
252 info.fInTried = true;
256 // otherwise, find an item to evict
257 int nPos = SelectTried(nKBucket);
259 // find which new bucket it belongs to
260 assert(mapInfo.count(vTried[nPos]) == 1);
261 int nUBucket = mapInfo[vTried[nPos]].GetNewBucket(nKey);
262 std::set<int> &vNew = vvNew[nUBucket];
264 // remove the to-be-replaced tried entry from the tried set
265 CAddrInfo& infoOld = mapInfo[vTried[nPos]];
266 infoOld.fInTried = false;
267 infoOld.nRefCount = 1;
268 // do not update nTried, as we are going to move something else there immediately
270 // check whether there is place in that one,
271 if (vNew.size() < ADDRMAN_NEW_BUCKET_SIZE)
273 // if so, move it back there
274 vNew.insert(vTried[nPos]);
276 // otherwise, move it to the new bucket nId came from (there is certainly place there)
277 vvNew[nOrigin].insert(vTried[nPos]);
282 // we just overwrote an entry in vTried; no need to update nTried
283 info.fInTried = true;
287 void CAddrMan::Good_(const CService &addr, int64_t nTime)
289 // printf("Good: addr=%s\n", addr.ToString().c_str());
292 CAddrInfo *pinfo = Find(addr, &nId);
294 // if not found, bail out
298 CAddrInfo &info = *pinfo;
300 // check whether we are talking about the exact same CService (including same port)
305 info.nLastSuccess = nTime;
306 info.nLastTry = nTime;
310 // if it is already in the tried set, don't do anything else
314 // find a bucket it is in now
315 int nRnd = GetRandInt(vvNew.size());
317 for (unsigned int n = 0; n < vvNew.size(); n++)
319 int nB = (n+nRnd) % vvNew.size();
320 std::set<int> &vNew = vvNew[nB];
328 // if no bucket is found, something bad happened;
329 // TODO: maybe re-add the node, but for now, just bail out
330 if (nUBucket == -1) return;
332 printf("Moving %s to tried\n", addr.ToString().c_str());
334 // move nId to the tried tables
335 MakeTried(info, nId, nUBucket);
338 bool CAddrMan::Add_(const CAddress &addr, const CNetAddr& source, int64_t nTimePenalty)
340 if (!addr.IsRoutable())
345 CAddrInfo *pinfo = Find(addr, &nId);
349 // periodically update nTime
350 bool fCurrentlyOnline = (GetAdjustedTime() - addr.nTime < nOneDay);
351 int64_t nUpdateInterval = (fCurrentlyOnline ? nOneHour : nOneDay);
352 if (addr.nTime && (!pinfo->nTime || pinfo->nTime < addr.nTime - nUpdateInterval - nTimePenalty))
353 pinfo->nTime = max((int64_t)0, addr.nTime - nTimePenalty);
356 pinfo->nServices |= addr.nServices;
358 // do not update if no new information is present
359 if (!addr.nTime || (pinfo->nTime && addr.nTime <= pinfo->nTime))
362 // do not update if the entry was already in the "tried" table
366 // do not update if the max reference count is reached
367 if (pinfo->nRefCount == ADDRMAN_NEW_BUCKETS_PER_ADDRESS)
370 // stochastic test: previous nRefCount == N: 2^N times harder to increase it
372 for (int n=0; n<pinfo->nRefCount; n++)
374 if (nFactor > 1 && (GetRandInt(nFactor) != 0))
377 pinfo = Create(addr, source, &nId);
378 pinfo->nTime = max((int64_t)0, (int64_t)pinfo->nTime - nTimePenalty);
379 // printf("Added %s [nTime=%fhr]\n", pinfo->ToString().c_str(), (GetAdjustedTime() - pinfo->nTime) / 3600.0);
384 int nUBucket = pinfo->GetNewBucket(nKey, source);
385 std::set<int> &vNew = vvNew[nUBucket];
386 if (!vNew.count(nId))
389 if (vNew.size() == ADDRMAN_NEW_BUCKET_SIZE)
391 vvNew[nUBucket].insert(nId);
396 void CAddrMan::Attempt_(const CService &addr, int64_t nTime)
398 CAddrInfo *pinfo = Find(addr);
400 // if not found, bail out
404 CAddrInfo &info = *pinfo;
406 // check whether we are talking about the exact same CService (including same port)
411 info.nLastTry = nTime;
415 CAddress CAddrMan::Select_(int nUnkBias)
420 double nCorTried = sqrt(nTried) * (100.0 - nUnkBias);
421 double nCorNew = sqrt(nNew) * nUnkBias;
422 if ((nCorTried + nCorNew)*GetRandInt(1<<30)/(1<<30) < nCorTried)
425 double fChanceFactor = 1.0;
428 int nKBucket = GetRandInt(vvTried.size());
429 std::vector<int> &vTried = vvTried[nKBucket];
430 if (vTried.size() == 0) continue;
431 int nPos = GetRandInt(vTried.size());
432 assert(mapInfo.count(vTried[nPos]) == 1);
433 CAddrInfo &info = mapInfo[vTried[nPos]];
434 if (GetRandInt(1<<30) < fChanceFactor*info.GetChance()*(1<<30))
436 fChanceFactor *= 1.2;
440 double fChanceFactor = 1.0;
443 int nUBucket = GetRandInt(vvNew.size());
444 std::set<int> &vNew = vvNew[nUBucket];
445 if (vNew.size() == 0) continue;
446 int nPos = GetRandInt(vNew.size());
447 auto it = vNew.begin();
450 assert(mapInfo.count(*it) == 1);
451 CAddrInfo &info = mapInfo[*it];
452 if (GetRandInt(1<<30) < fChanceFactor*info.GetChance()*(1<<30))
454 fChanceFactor *= 1.2;
459 void CAddrMan::GetAddr_(std::vector<CAddress> &vAddr)
461 size_t nNodes = ADDRMAN_GETADDR_MAX_PCT*vRandom.size()/100;
462 if (nNodes > ADDRMAN_GETADDR_MAX)
463 nNodes = ADDRMAN_GETADDR_MAX;
465 // perform a random shuffle over the first nNodes elements of vRandom (selecting from all)
466 for (unsigned int n = 0; n<nNodes; n++)
468 int nRndPos = GetRandInt(vRandom.size() - n) + n;
469 SwapRandom(n, nRndPos);
470 assert(mapInfo.count(vRandom[n]) == 1);
471 vAddr.push_back(mapInfo[vRandom[n]]);
475 void CAddrMan::GetOnlineAddr_(std::vector<CAddrInfo> &vAddr)
477 for (auto it = mapInfo.begin(); it != mapInfo.end(); it++)
479 auto addr = it->second;
480 bool fCurrentlyOnline = (GetAdjustedTime() - addr.nTime < nOneDay);
481 if (fCurrentlyOnline)
482 vAddr.push_back(addr);
486 void CAddrMan::Connected_(const CService &addr, int64_t nTime)
488 CAddrInfo *pinfo = Find(addr);
490 // if not found, bail out
494 CAddrInfo &info = *pinfo;
496 // check whether we are talking about the exact same CService (including same port)
501 int64_t nUpdateInterval = 20 * 60;
502 if (nTime - info.nTime > nUpdateInterval)
506 CAddrMan::CAddrMan() : vRandom(0), vvTried(ADDRMAN_TRIED_BUCKET_COUNT, std::vector<int>(0)), vvNew(ADDRMAN_NEW_BUCKET_COUNT, std::set<int>())
509 RAND_bytes(&nKey[0], 32);
518 return (int) vRandom.size();
521 bool CAddrMan::Add(const CAddress &addr, const CNetAddr& source, int64_t nTimePenalty)
526 fRet |= Add_(addr, source, nTimePenalty);
529 printf("Added %s from %s: %i tried, %i new\n", addr.ToStringIPPort().c_str(), source.ToString().c_str(), nTried, nNew);
533 bool CAddrMan::Add(const std::vector<CAddress> &vAddr, const CNetAddr& source, int64_t nTimePenalty)
538 for (auto it = vAddr.begin(); it != vAddr.end(); it++)
539 nAdd += Add_(*it, source, nTimePenalty) ? 1 : 0;
542 printf("Added %i addresses from %s: %i tried, %i new\n", nAdd, source.ToString().c_str(), nTried, nNew);
546 void CAddrMan::Good(const CService &addr, int64_t nTime)
554 void CAddrMan::Attempt(const CService &addr, int64_t nTime)
558 Attempt_(addr, nTime);
562 CAddress CAddrMan::Select(int nUnkBias)
567 addrRet = Select_(nUnkBias);
572 std::vector<CAddress> CAddrMan::GetAddr()
574 std::vector<CAddress> vAddr;
582 std::vector<CAddrInfo> CAddrMan::GetOnlineAddr()
584 std::vector<CAddrInfo> vAddr;
587 GetOnlineAddr_(vAddr);
592 void CAddrMan::Connected(const CService &addr, int64_t nTime)
596 Connected_(addr, nTime);