addrman.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526
  1. // ECOin - Copyright (c) - 2014/2021 - GPLv3 - epsylon@riseup.net (https://03c8.net)
  2. #include "addrman.h"
  3. using namespace std;
  4. int CAddrInfo::GetTriedBucket(const std::vector<unsigned char> &nKey) const
  5. {
  6. CDataStream ss1(SER_GETHASH, 0);
  7. std::vector<unsigned char> vchKey = GetKey();
  8. ss1 << nKey << vchKey;
  9. uint64 hash1 = Hash(ss1.begin(), ss1.end()).Get64();
  10. CDataStream ss2(SER_GETHASH, 0);
  11. std::vector<unsigned char> vchGroupKey = GetGroup();
  12. ss2 << nKey << vchGroupKey << (hash1 % ADDRMAN_TRIED_BUCKETS_PER_GROUP);
  13. uint64 hash2 = Hash(ss2.begin(), ss2.end()).Get64();
  14. return hash2 % ADDRMAN_TRIED_BUCKET_COUNT;
  15. }
  16. int CAddrInfo::GetNewBucket(const std::vector<unsigned char> &nKey, const CNetAddr& src) const
  17. {
  18. CDataStream ss1(SER_GETHASH, 0);
  19. std::vector<unsigned char> vchGroupKey = GetGroup();
  20. std::vector<unsigned char> vchSourceGroupKey = src.GetGroup();
  21. ss1 << nKey << vchGroupKey << vchSourceGroupKey;
  22. uint64 hash1 = Hash(ss1.begin(), ss1.end()).Get64();
  23. CDataStream ss2(SER_GETHASH, 0);
  24. ss2 << nKey << vchSourceGroupKey << (hash1 % ADDRMAN_NEW_BUCKETS_PER_SOURCE_GROUP);
  25. uint64 hash2 = Hash(ss2.begin(), ss2.end()).Get64();
  26. return hash2 % ADDRMAN_NEW_BUCKET_COUNT;
  27. }
  28. bool CAddrInfo::IsTerrible(int64 nNow) const
  29. {
  30. if (nLastTry && nLastTry >= nNow-60) // never remove things tried the last minute
  31. return false;
  32. if (nTime > nNow + 10*60) // came in a flying DeLorean
  33. return true;
  34. if (nTime==0 || nNow-nTime > ADDRMAN_HORIZON_DAYS*86400) // not seen in over a month
  35. return true;
  36. if (nLastSuccess==0 && nAttempts>=ADDRMAN_RETRIES) // tried three times and never a success
  37. return true;
  38. if (nNow-nLastSuccess > ADDRMAN_MIN_FAIL_DAYS*86400 && nAttempts>=ADDRMAN_MAX_FAILURES) // 10 successive failures in the last week
  39. return true;
  40. return false;
  41. }
  42. double CAddrInfo::GetChance(int64 nNow) const
  43. {
  44. double fChance = 1.0;
  45. int64 nSinceLastSeen = nNow - nTime;
  46. int64 nSinceLastTry = nNow - nLastTry;
  47. if (nSinceLastSeen < 0) nSinceLastSeen = 0;
  48. if (nSinceLastTry < 0) nSinceLastTry = 0;
  49. fChance *= 600.0 / (600.0 + nSinceLastSeen);
  50. // deprioritize very recent attempts away
  51. if (nSinceLastTry < 60*10)
  52. fChance *= 0.01;
  53. // deprioritize 50% after each failed attempt
  54. for (int n=0; n<nAttempts; n++)
  55. fChance /= 1.5;
  56. return fChance;
  57. }
  58. CAddrInfo* CAddrMan::Find(const CNetAddr& addr, int *pnId)
  59. {
  60. std::map<CNetAddr, int>::iterator it = mapAddr.find(addr);
  61. if (it == mapAddr.end())
  62. return NULL;
  63. if (pnId)
  64. *pnId = (*it).second;
  65. std::map<int, CAddrInfo>::iterator it2 = mapInfo.find((*it).second);
  66. if (it2 != mapInfo.end())
  67. return &(*it2).second;
  68. return NULL;
  69. }
  70. CAddrInfo* CAddrMan::Create(const CAddress &addr, const CNetAddr &addrSource, int *pnId)
  71. {
  72. int nId = nIdCount++;
  73. mapInfo[nId] = CAddrInfo(addr, addrSource);
  74. mapAddr[addr] = nId;
  75. mapInfo[nId].nRandomPos = vRandom.size();
  76. vRandom.push_back(nId);
  77. if (pnId)
  78. *pnId = nId;
  79. return &mapInfo[nId];
  80. }
  81. void CAddrMan::SwapRandom(unsigned int nRndPos1, unsigned int nRndPos2)
  82. {
  83. if (nRndPos1 == nRndPos2)
  84. return;
  85. assert(nRndPos1 < vRandom.size() && nRndPos2 < vRandom.size());
  86. int nId1 = vRandom[nRndPos1];
  87. int nId2 = vRandom[nRndPos2];
  88. assert(mapInfo.count(nId1) == 1);
  89. assert(mapInfo.count(nId2) == 1);
  90. mapInfo[nId1].nRandomPos = nRndPos2;
  91. mapInfo[nId2].nRandomPos = nRndPos1;
  92. vRandom[nRndPos1] = nId2;
  93. vRandom[nRndPos2] = nId1;
  94. }
  95. int CAddrMan::SelectTried(int nKBucket)
  96. {
  97. std::vector<int> &vTried = vvTried[nKBucket];
  98. // random shuffle the first few elements (using the entire list)
  99. // find the least recently tried among them
  100. int64 nOldest = -1;
  101. int nOldestPos = -1;
  102. for (unsigned int i = 0; i < ADDRMAN_TRIED_ENTRIES_INSPECT_ON_EVICT && i < vTried.size(); i++)
  103. {
  104. int nPos = GetRandInt(vTried.size() - i) + i;
  105. int nTemp = vTried[nPos];
  106. vTried[nPos] = vTried[i];
  107. vTried[i] = nTemp;
  108. assert(nOldest == -1 || mapInfo.count(nTemp) == 1);
  109. if (nOldest == -1 || mapInfo[nTemp].nLastSuccess < mapInfo[nOldest].nLastSuccess) {
  110. nOldest = nTemp;
  111. nOldestPos = nPos;
  112. }
  113. }
  114. return nOldestPos;
  115. }
  116. int CAddrMan::ShrinkNew(int nUBucket)
  117. {
  118. assert(nUBucket >= 0 && (unsigned int)nUBucket < vvNew.size());
  119. std::set<int> &vNew = vvNew[nUBucket];
  120. // first look for deletable items
  121. for (std::set<int>::iterator it = vNew.begin(); it != vNew.end(); it++)
  122. {
  123. assert(mapInfo.count(*it));
  124. CAddrInfo &info = mapInfo[*it];
  125. if (info.IsTerrible())
  126. {
  127. if (--info.nRefCount == 0)
  128. {
  129. SwapRandom(info.nRandomPos, vRandom.size()-1);
  130. vRandom.pop_back();
  131. mapAddr.erase(info);
  132. mapInfo.erase(*it);
  133. nNew--;
  134. }
  135. vNew.erase(it);
  136. return 0;
  137. }
  138. }
  139. // otherwise, select four randomly, and pick the oldest of those to replace
  140. int n[4] = {GetRandInt(vNew.size()), GetRandInt(vNew.size()), GetRandInt(vNew.size()), GetRandInt(vNew.size())};
  141. int nI = 0;
  142. int nOldest = -1;
  143. for (std::set<int>::iterator it = vNew.begin(); it != vNew.end(); it++)
  144. {
  145. if (nI == n[0] || nI == n[1] || nI == n[2] || nI == n[3])
  146. {
  147. assert(nOldest == -1 || mapInfo.count(*it) == 1);
  148. if (nOldest == -1 || mapInfo[*it].nTime < mapInfo[nOldest].nTime)
  149. nOldest = *it;
  150. }
  151. nI++;
  152. }
  153. assert(mapInfo.count(nOldest) == 1);
  154. CAddrInfo &info = mapInfo[nOldest];
  155. if (--info.nRefCount == 0)
  156. {
  157. SwapRandom(info.nRandomPos, vRandom.size()-1);
  158. vRandom.pop_back();
  159. mapAddr.erase(info);
  160. mapInfo.erase(nOldest);
  161. nNew--;
  162. }
  163. vNew.erase(nOldest);
  164. return 1;
  165. }
  166. void CAddrMan::MakeTried(CAddrInfo& info, int nId, int nOrigin)
  167. {
  168. assert(vvNew[nOrigin].count(nId) == 1);
  169. // remove the entry from all new buckets
  170. for (std::vector<std::set<int> >::iterator it = vvNew.begin(); it != vvNew.end(); it++)
  171. {
  172. if ((*it).erase(nId))
  173. info.nRefCount--;
  174. }
  175. nNew--;
  176. assert(info.nRefCount == 0);
  177. // what tried bucket to move the entry to
  178. int nKBucket = info.GetTriedBucket(nKey);
  179. std::vector<int> &vTried = vvTried[nKBucket];
  180. // first check whether there is place to just add it
  181. if (vTried.size() < ADDRMAN_TRIED_BUCKET_SIZE)
  182. {
  183. vTried.push_back(nId);
  184. nTried++;
  185. info.fInTried = true;
  186. return;
  187. }
  188. // otherwise, find an item to evict
  189. int nPos = SelectTried(nKBucket);
  190. // find which new bucket it belongs to
  191. assert(mapInfo.count(vTried[nPos]) == 1);
  192. int nUBucket = mapInfo[vTried[nPos]].GetNewBucket(nKey);
  193. std::set<int> &vNew = vvNew[nUBucket];
  194. // remove the to-be-replaced tried entry from the tried set
  195. CAddrInfo& infoOld = mapInfo[vTried[nPos]];
  196. infoOld.fInTried = false;
  197. infoOld.nRefCount = 1;
  198. // do not update nTried, as we are going to move something else there immediately
  199. // check whether there is place in that one,
  200. if (vNew.size() < ADDRMAN_NEW_BUCKET_SIZE)
  201. {
  202. // if so, move it back there
  203. vNew.insert(vTried[nPos]);
  204. } else {
  205. // otherwise, move it to the new bucket nId came from (there is certainly place there)
  206. vvNew[nOrigin].insert(vTried[nPos]);
  207. }
  208. nNew++;
  209. vTried[nPos] = nId;
  210. // we just overwrote an entry in vTried; no need to update nTried
  211. info.fInTried = true;
  212. return;
  213. }
  214. void CAddrMan::Good_(const CService &addr, int64 nTime)
  215. {
  216. // printf("Good: addr=%s\n", addr.ToString().c_str());
  217. int nId;
  218. CAddrInfo *pinfo = Find(addr, &nId);
  219. // if not found, bail out
  220. if (!pinfo)
  221. return;
  222. CAddrInfo &info = *pinfo;
  223. // check whether we are talking about the exact same CService (including same port)
  224. if (info != addr)
  225. return;
  226. // update info
  227. info.nLastSuccess = nTime;
  228. info.nLastTry = nTime;
  229. info.nTime = nTime;
  230. info.nAttempts = 0;
  231. // if it is already in the tried set, don't do anything else
  232. if (info.fInTried)
  233. return;
  234. // find a bucket it is in now
  235. int nRnd = GetRandInt(vvNew.size());
  236. int nUBucket = -1;
  237. for (unsigned int n = 0; n < vvNew.size(); n++)
  238. {
  239. int nB = (n+nRnd) % vvNew.size();
  240. std::set<int> &vNew = vvNew[nB];
  241. if (vNew.count(nId))
  242. {
  243. nUBucket = nB;
  244. break;
  245. }
  246. }
  247. // if no bucket is found, something bad happened;
  248. // TODO: maybe re-add the node, but for now, just bail out
  249. if (nUBucket == -1) return;
  250. printf("Moving %s to tried\n", addr.ToString().c_str());
  251. // move nId to the tried tables
  252. MakeTried(info, nId, nUBucket);
  253. }
  254. bool CAddrMan::Add_(const CAddress &addr, const CNetAddr& source, int64 nTimePenalty)
  255. {
  256. if (!addr.IsRoutable())
  257. return false;
  258. bool fNew = false;
  259. int nId;
  260. CAddrInfo *pinfo = Find(addr, &nId);
  261. if (pinfo)
  262. {
  263. // periodically update nTime
  264. bool fCurrentlyOnline = (GetAdjustedTime() - addr.nTime < 24 * 60 * 60);
  265. int64 nUpdateInterval = (fCurrentlyOnline ? 60 * 60 : 24 * 60 * 60);
  266. if (addr.nTime && (!pinfo->nTime || pinfo->nTime < addr.nTime - nUpdateInterval - nTimePenalty))
  267. pinfo->nTime = max((int64)0, addr.nTime - nTimePenalty);
  268. // add services
  269. pinfo->nServices |= addr.nServices;
  270. // do not update if no new information is present
  271. if (!addr.nTime || (pinfo->nTime && addr.nTime <= pinfo->nTime))
  272. return false;
  273. // do not update if the entry was already in the "tried" table
  274. if (pinfo->fInTried)
  275. return false;
  276. // do not update if the max reference count is reached
  277. if (pinfo->nRefCount == ADDRMAN_NEW_BUCKETS_PER_ADDRESS)
  278. return false;
  279. // stochastic test: previous nRefCount == N: 2^N times harder to increase it
  280. int nFactor = 1;
  281. for (int n=0; n<pinfo->nRefCount; n++)
  282. nFactor *= 2;
  283. if (nFactor > 1 && (GetRandInt(nFactor) != 0))
  284. return false;
  285. } else {
  286. pinfo = Create(addr, source, &nId);
  287. pinfo->nTime = max((int64)0, (int64)pinfo->nTime - nTimePenalty);
  288. // printf("Added %s [nTime=%fhr]\n", pinfo->ToString().c_str(), (GetAdjustedTime() - pinfo->nTime) / 3600.0);
  289. nNew++;
  290. fNew = true;
  291. }
  292. int nUBucket = pinfo->GetNewBucket(nKey, source);
  293. std::set<int> &vNew = vvNew[nUBucket];
  294. if (!vNew.count(nId))
  295. {
  296. pinfo->nRefCount++;
  297. if (vNew.size() == ADDRMAN_NEW_BUCKET_SIZE)
  298. ShrinkNew(nUBucket);
  299. vvNew[nUBucket].insert(nId);
  300. }
  301. return fNew;
  302. }
  303. void CAddrMan::Attempt_(const CService &addr, int64 nTime)
  304. {
  305. CAddrInfo *pinfo = Find(addr);
  306. // if not found, bail out
  307. if (!pinfo)
  308. return;
  309. CAddrInfo &info = *pinfo;
  310. // check whether we are talking about the exact same CService (including same port)
  311. if (info != addr)
  312. return;
  313. // update info
  314. info.nLastTry = nTime;
  315. info.nAttempts++;
  316. }
  317. CAddress CAddrMan::Select_(int nUnkBias)
  318. {
  319. if (size() == 0)
  320. return CAddress();
  321. double nCorTried = sqrt(nTried) * (100.0 - nUnkBias);
  322. double nCorNew = sqrt(nNew) * nUnkBias;
  323. if ((nCorTried + nCorNew)*GetRandInt(1<<30)/(1<<30) < nCorTried)
  324. {
  325. // use a tried node
  326. double fChanceFactor = 1.0;
  327. while(1)
  328. {
  329. int nKBucket = GetRandInt(vvTried.size());
  330. std::vector<int> &vTried = vvTried[nKBucket];
  331. if (vTried.size() == 0) continue;
  332. int nPos = GetRandInt(vTried.size());
  333. assert(mapInfo.count(vTried[nPos]) == 1);
  334. CAddrInfo &info = mapInfo[vTried[nPos]];
  335. if (GetRandInt(1<<30) < fChanceFactor*info.GetChance()*(1<<30))
  336. return info;
  337. fChanceFactor *= 1.2;
  338. }
  339. } else {
  340. // use a new node
  341. double fChanceFactor = 1.0;
  342. while(1)
  343. {
  344. int nUBucket = GetRandInt(vvNew.size());
  345. std::set<int> &vNew = vvNew[nUBucket];
  346. if (vNew.size() == 0) continue;
  347. int nPos = GetRandInt(vNew.size());
  348. std::set<int>::iterator it = vNew.begin();
  349. while (nPos--)
  350. it++;
  351. assert(mapInfo.count(*it) == 1);
  352. CAddrInfo &info = mapInfo[*it];
  353. if (GetRandInt(1<<30) < fChanceFactor*info.GetChance()*(1<<30))
  354. return info;
  355. fChanceFactor *= 1.2;
  356. }
  357. }
  358. }
  359. #ifdef DEBUG_ADDRMAN
  360. int CAddrMan::Check_()
  361. {
  362. std::set<int> setTried;
  363. std::map<int, int> mapNew;
  364. if (vRandom.size() != nTried + nNew) return -7;
  365. for (std::map<int, CAddrInfo>::iterator it = mapInfo.begin(); it != mapInfo.end(); it++)
  366. {
  367. int n = (*it).first;
  368. CAddrInfo &info = (*it).second;
  369. if (info.fInTried)
  370. {
  371. if (!info.nLastSuccess) return -1;
  372. if (info.nRefCount) return -2;
  373. setTried.insert(n);
  374. } else {
  375. if (info.nRefCount < 0 || info.nRefCount > ADDRMAN_NEW_BUCKETS_PER_ADDRESS) return -3;
  376. if (!info.nRefCount) return -4;
  377. mapNew[n] = info.nRefCount;
  378. }
  379. if (mapAddr[info] != n) return -5;
  380. if (info.nRandomPos<0 || info.nRandomPos>=vRandom.size() || vRandom[info.nRandomPos] != n) return -14;
  381. if (info.nLastTry < 0) return -6;
  382. if (info.nLastSuccess < 0) return -8;
  383. }
  384. if (setTried.size() != nTried) return -9;
  385. if (mapNew.size() != nNew) return -10;
  386. for (int n=0; n<vvTried.size(); n++)
  387. {
  388. std::vector<int> &vTried = vvTried[n];
  389. for (std::vector<int>::iterator it = vTried.begin(); it != vTried.end(); it++)
  390. {
  391. if (!setTried.count(*it)) return -11;
  392. setTried.erase(*it);
  393. }
  394. }
  395. for (int n=0; n<vvNew.size(); n++)
  396. {
  397. std::set<int> &vNew = vvNew[n];
  398. for (std::set<int>::iterator it = vNew.begin(); it != vNew.end(); it++)
  399. {
  400. if (!mapNew.count(*it)) return -12;
  401. if (--mapNew[*it] == 0)
  402. mapNew.erase(*it);
  403. }
  404. }
  405. if (setTried.size()) return -13;
  406. if (mapNew.size()) return -15;
  407. return 0;
  408. }
  409. #endif
  410. void CAddrMan::GetAddr_(std::vector<CAddress> &vAddr)
  411. {
  412. int nNodes = ADDRMAN_GETADDR_MAX_PCT*vRandom.size()/100;
  413. if (nNodes > ADDRMAN_GETADDR_MAX)
  414. nNodes = ADDRMAN_GETADDR_MAX;
  415. // perform a random shuffle over the first nNodes elements of vRandom (selecting from all)
  416. for (int n = 0; n<nNodes; n++)
  417. {
  418. int nRndPos = GetRandInt(vRandom.size() - n) + n;
  419. SwapRandom(n, nRndPos);
  420. assert(mapInfo.count(vRandom[n]) == 1);
  421. vAddr.push_back(mapInfo[vRandom[n]]);
  422. }
  423. }
  424. void CAddrMan::Connected_(const CService &addr, int64 nTime)
  425. {
  426. CAddrInfo *pinfo = Find(addr);
  427. // if not found, bail out
  428. if (!pinfo)
  429. return;
  430. CAddrInfo &info = *pinfo;
  431. // check whether we are talking about the exact same CService (including same port)
  432. if (info != addr)
  433. return;
  434. // update info
  435. int64 nUpdateInterval = 20 * 60;
  436. if (nTime - info.nTime > nUpdateInterval)
  437. info.nTime = nTime;
  438. }