PageRenderTime 47ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/src/addrman.cpp

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