15 const std::vector<bool> &asmap)
const {
30 const std::vector<bool> &asmap)
const {
31 std::vector<uint8_t> vchSourceGroupKey = src.
GetGroup(asmap);
33 << nKey <<
GetGroup(asmap) << vchSourceGroupKey)
36 << nKey << vchSourceGroupKey
49 << nKey << (fNew ?
'N' :
'K') << nBucket <<
GetKey())
61 if (
nTime > nNow + 10 * 60) {
86 int64_t nSinceLastTry = std::max<int64_t>(nNow -
nLastTry, 0);
89 if (nSinceLastTry < 60 * 10) {
95 fChance *= std::pow(0.66, std::min(
nAttempts, 8));
101 std::map<CNetAddr, int>::iterator it = mapAddr.find(addr);
102 if (it == mapAddr.end()) {
106 *pnId = (*it).second;
108 std::map<int, CAddrInfo>::iterator it2 = mapInfo.find((*it).second);
109 if (it2 != mapInfo.end()) {
110 return &(*it2).second;
117 int nId = nIdCount++;
118 mapInfo[nId] =
CAddrInfo(addr, addrSource);
120 mapInfo[nId].nRandomPos = vRandom.size();
121 vRandom.push_back(nId);
125 return &mapInfo[nId];
129 if (nRndPos1 == nRndPos2) {
133 assert(nRndPos1 < vRandom.size() && nRndPos2 < vRandom.size());
135 int nId1 = vRandom[nRndPos1];
136 int nId2 = vRandom[nRndPos2];
138 assert(mapInfo.count(nId1) == 1);
139 assert(mapInfo.count(nId2) == 1);
141 mapInfo[nId1].nRandomPos = nRndPos2;
142 mapInfo[nId2].nRandomPos = nRndPos1;
144 vRandom[nRndPos1] = nId2;
145 vRandom[nRndPos2] = nId1;
149 assert(mapInfo.count(nId) != 0);
151 assert(!info.fInTried);
152 assert(info.nRefCount == 0);
154 SwapRandom(info.nRandomPos, vRandom.size() - 1);
163 if (vvNew[nUBucket][nUBucketPos] != -1) {
164 int nIdDelete = vvNew[nUBucket][nUBucketPos];
165 CAddrInfo &infoDelete = mapInfo[nIdDelete];
168 vvNew[nUBucket][nUBucketPos] = -1;
179 if (vvNew[bucket][pos] == nId) {
180 vvNew[bucket][pos] = -1;
194 if (vvTried[nKBucket][nKBucketPos] != -1) {
196 int nIdEvict = vvTried[nKBucket][nKBucketPos];
197 assert(mapInfo.count(nIdEvict) == 1);
202 vvTried[nKBucket][nKBucketPos] = -1;
206 int nUBucket = infoOld.GetNewBucket(nKey, m_asmap);
207 int nUBucketPos = infoOld.GetBucketPosition(nKey,
true, nUBucket);
208 ClearNew(nUBucket, nUBucketPos);
209 assert(vvNew[nUBucket][nUBucketPos] == -1);
212 infoOld.nRefCount = 1;
213 vvNew[nUBucket][nUBucketPos] = nIdEvict;
216 assert(vvTried[nKBucket][nKBucketPos] == -1);
218 vvTried[nKBucket][nKBucketPos] = nId;
260 int nB = (n + nRnd) % ADDRMAN_NEW_BUCKET_COUNT;
262 if (vvNew[nB][nBpos] == nId) {
270 if (nUBucket == -1) {
279 if (test_before_evict && (vvTried[tried_bucket][tried_bucket_pos] != -1)) {
281 auto colliding_entry =
282 mapInfo.find(vvTried[tried_bucket][tried_bucket_pos]);
284 "Collision inserting element into tried table (%s), moving %s " 285 "to m_tried_collisions=%d\n",
286 colliding_entry != mapInfo.end()
287 ? colliding_entry->second.ToString()
289 addr.
ToString(), m_tried_collisions.size());
291 m_tried_collisions.insert(nId);
297 MakeTried(info, nId);
302 int64_t nTimePenalty) {
312 if (addr == source) {
319 int64_t nUpdateInterval = (fCurrentlyOnline ? 60 * 60 : 24 * 60 * 60);
322 pinfo->
nTime < addr.
nTime - nUpdateInterval - nTimePenalty)) {
323 pinfo->
nTime = std::max((int64_t)0, addr.
nTime - nTimePenalty);
347 for (
int n = 0; n < pinfo->
nRefCount; n++) {
351 if (nFactor > 1 && (insecure_rand.randrange(nFactor) != 0)) {
355 pinfo = Create(addr, source, &nId);
357 std::max((int64_t)0, (int64_t)pinfo->
nTime - nTimePenalty);
362 int nUBucket = pinfo->
GetNewBucket(nKey, source, m_asmap);
364 if (vvNew[nUBucket][nUBucketPos] != nId) {
365 bool fInsert = vvNew[nUBucket][nUBucketPos] == -1;
367 CAddrInfo &infoExisting = mapInfo[vvNew[nUBucket][nUBucketPos]];
375 ClearNew(nUBucket, nUBucketPos);
377 vvNew[nUBucket][nUBucketPos] = nId;
415 if (newOnly && nNew == 0) {
421 (nTried > 0 && (nNew == 0 || insecure_rand.randbool() == 0))) {
423 double fChanceFactor = 1.0;
427 while (vvTried[nKBucket][nKBucketPos] == -1) {
428 nKBucket = (nKBucket + insecure_rand.randbits(
431 nKBucketPos = (nKBucketPos + insecure_rand.randbits(
435 int nId = vvTried[nKBucket][nKBucketPos];
436 assert(mapInfo.count(nId) == 1);
438 if (insecure_rand.randbits(30) <
439 fChanceFactor * info.GetChance() * (1 << 30)) {
442 fChanceFactor *= 1.2;
446 double fChanceFactor = 1.0;
450 while (vvNew[nUBucket][nUBucketPos] == -1) {
451 nUBucket = (nUBucket + insecure_rand.randbits(
454 nUBucketPos = (nUBucketPos + insecure_rand.randbits(
458 int nId = vvNew[nUBucket][nUBucketPos];
459 assert(mapInfo.count(nId) == 1);
461 if (insecure_rand.randbits(30) <
462 fChanceFactor * info.GetChance() * (1 << 30)) {
465 fChanceFactor *= 1.2;
471 int CAddrMan::Check_() {
472 std::set<int> setTried;
473 std::map<int, int> mapNew;
475 if (vRandom.size() != size_t(nTried + nNew)) {
479 for (
const auto &entry : mapInfo) {
500 if (mapAddr[info] != n) {
515 if (setTried.size() != size_t(nTried)) {
518 if (mapNew.size() != size_t(nNew)) {
524 if (vvTried[n][i] != -1) {
525 if (!setTried.count(vvTried[n][i])) {
535 setTried.erase(vvTried[n][i]);
542 if (vvNew[n][i] != -1) {
543 if (!mapNew.count(vvNew[n][i])) {
550 if (--mapNew[vvNew[n][i]] == 0) {
551 mapNew.erase(vvNew[n][i]);
557 if (setTried.size()) {
578 for (
unsigned int n = 0; n < vRandom.size(); n++) {
579 if (vAddr.size() >= nNodes) {
583 int nRndPos = insecure_rand.randrange(vRandom.size() - n) + n;
584 SwapRandom(n, nRndPos);
585 assert(mapInfo.count(vRandom[n]) == 1);
587 const CAddrInfo &ai = mapInfo[vRandom[n]];
588 if (!ai.IsTerrible()) {
611 int64_t nUpdateInterval = 20 * 60;
612 if (nTime - info.
nTime > nUpdateInterval) {
640 for (std::set<int>::iterator it = m_tried_collisions.begin();
641 it != m_tried_collisions.end();) {
644 bool erase_collision =
false;
647 auto id_new_it = mapInfo.find(id_new);
648 if (id_new_it == mapInfo.end()) {
649 erase_collision =
true;
655 int tried_bucket_pos =
659 erase_collision =
true;
660 }
else if (vvTried[tried_bucket][tried_bucket_pos] != -1) {
664 int id_old = vvTried[tried_bucket][tried_bucket_pos];
670 erase_collision =
true;
671 }
else if (adjustedTime - info_old.
nLastTry <
678 "Replacing %s with %s in tried table\n",
684 erase_collision =
true;
692 "Unable to test; replacing %s with %s in tried " 696 erase_collision =
true;
700 Good_(info_new,
false, adjustedTime);
701 erase_collision =
true;
705 if (erase_collision) {
706 m_tried_collisions.erase(it++);
714 if (m_tried_collisions.size() == 0) {
718 std::set<int>::iterator it = m_tried_collisions.begin();
721 std::advance(it, insecure_rand.randrange(m_tried_collisions.size()));
725 auto id_new_it = mapInfo.find(id_new);
726 if (id_new_it == mapInfo.end()) {
727 m_tried_collisions.erase(it);
737 int id_old = vvTried[tried_bucket][tried_bucket_pos];
739 return mapInfo[id_old];
743 std::vector<bool> bits;
747 LogPrintf(
"Failed to open asmap file from disk\n");
750 fseek(filestr, 0, SEEK_END);
751 int length = ftell(filestr);
752 LogPrintf(
"Opened asmap file %s (%d bytes) from disk\n", path, length);
753 fseek(filestr, 0, SEEK_SET);
755 for (
int i = 0; i < length; ++i) {
757 for (
int bit = 0; bit < 8; ++bit) {
758 bits.push_back((cur_byte >> bit) & 1);
762 LogPrintf(
"Sanity check of asmap file %s failed\n", path);
int nRefCount
reference count in new sets (memory only)
ServiceFlags
nServices flags.
void ResolveCollisions_() EXCLUSIVE_LOCKS_REQUIRED(cs)
See if any to-be-evicted tried table entries have been tested and if so resolve the collisions...
#define ADDRMAN_SET_TRIED_COLLISION_SIZE
the maximum number of tried addr collisions to store
#define LogPrint(category,...)
FILE * fopen(const fs::path &p, const char *mode)
int GetTriedBucket(const uint256 &nKey, const std::vector< bool > &asmap) const
Calculate in which "tried" bucket this entry belongs.
#define ADDRMAN_TRIED_BUCKET_COUNT_LOG2
Stochastic address manager.
CAddrInfo * Create(const CAddress &addr, const CNetAddr &addrSource, int *pnId=nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs)
find an entry, creating it if necessary.
#define ADDRMAN_TRIED_BUCKETS_PER_GROUP
over how many buckets entries with tried addresses from a single group (/16 for IPv4) are spread ...
std::vector< uint8_t > GetGroup(const std::vector< bool > &asmap) const
Get the canonical identifier of our network group.
void SetServices_(const CService &addr, ServiceFlags nServices) EXCLUSIVE_LOCKS_REQUIRED(cs)
Update an entry's service bits.
void Delete(int nId) EXCLUSIVE_LOCKS_REQUIRED(cs)
Delete an entry. It must not be in tried, and have refcount 0.
void Good_(const CService &addr, bool test_before_evict, int64_t time) EXCLUSIVE_LOCKS_REQUIRED(cs)
Mark an entry "good", possibly moving it from "new" to "tried".
#define ADDRMAN_MIN_FAIL_DAYS
... in at least this many days
void Attempt_(const CService &addr, bool fCountFailure, int64_t nTime) EXCLUSIVE_LOCKS_REQUIRED(cs)
Mark an entry as attempted to connect.
int nAttempts
connection attempts since last successful attempt
static void LogPrintf(const char *fmt, const Args &... args)
#define ADDRMAN_NEW_BUCKET_COUNT_LOG2
total number of buckets for new addresses
static const int64_t ADDRMAN_TEST_WINDOW
the maximum time we'll spend trying to resolve a tried table collision, in seconds (40 minutes) ...
bool Add_(const CAddress &addr, const CNetAddr &source, int64_t nTimePenalty) EXCLUSIVE_LOCKS_REQUIRED(cs)
Add an entry to the "new" table.
void SwapRandom(unsigned int nRandomPos1, unsigned int nRandomPos2) EXCLUSIVE_LOCKS_REQUIRED(cs)
Swap two elements in vRandom.
int nRandomPos
position in vRandom
CAddrInfo SelectTriedCollision_() EXCLUSIVE_LOCKS_REQUIRED(cs)
Return a random to-be-evicted tried table address.
#define ADDRMAN_TRIED_BUCKET_COUNT
Convenience.
bool fInTried
in tried set? (memory only)
bool IsNull() const
Return true if the wrapped FILE* is nullptr, false otherwise.
uint32_t GetMappedAS(const std::vector< bool > &asmap) const
Extended statistics about a CAddress.
#define ADDRMAN_REPLACEMENT_SECONDS
how recent a successful connection should be before we allow an address to be evicted from tried ...
#define ADDRMAN_BUCKET_SIZE_LOG2
maximum allowed number of entries in buckets for new and tried addresses
#define ADDRMAN_RETRIES
after how many failed attempts we give up on a new node
void MakeTried(CAddrInfo &info, int nId) EXCLUSIVE_LOCKS_REQUIRED(cs)
Move an entry from the "new" table(s) to the "tried" table.
std::string ToStringIP() const
A combination of a network address (CNetAddr) and a (TCP) port.
A CService with information about it as peer.
int GetNewBucket(const uint256 &nKey, const CNetAddr &src, const std::vector< bool > &asmap) const
Calculate in which "new" bucket this entry belongs, given a certain source.
#define ADDRMAN_BUCKET_SIZE
bool IsTerrible(int64_t nNow=GetAdjustedTime()) const
Determine whether the statistics about this entry are bad enough so that it can just be deleted...
void ClearNew(int nUBucket, int nUBucketPos) EXCLUSIVE_LOCKS_REQUIRED(cs)
Clear a position in a "new" table.
IP address (IPv6, or IPv4 using mapped IPv6 range (::FFFF:0:0/96))
#define ADDRMAN_GETADDR_MAX
the maximum number of nodes to return in a getaddr call
CAddrInfo Select_(bool newOnly) EXCLUSIVE_LOCKS_REQUIRED(cs)
Select an address to connect to, if newOnly is set to true, only the new table is selected from...
int GetBucketPosition(const uint256 &nKey, bool fNew, int nBucket) const
Calculate in which position of a bucket to store this entry.
static constexpr int CLIENT_VERSION
bitcoind-res.rc includes this file, but it cannot cope with real c++ code.
void Connected_(const CService &addr, int64_t nTime) EXCLUSIVE_LOCKS_REQUIRED(cs)
Mark an entry as currently-connected-to.
#define ADDRMAN_NEW_BUCKET_COUNT
int64_t GetAdjustedTime()
double GetChance(int64_t nNow=GetAdjustedTime()) const
Calculate the relative chance this entry should be given when selecting nodes to connect to...
int64_t nLastCountAttempt
last counted attempt (memory only)
void GetAddr_(std::vector< CAddress > &vAddr) EXCLUSIVE_LOCKS_REQUIRED(cs)
Select several addresses at once.
static std::vector< bool > DecodeAsmap(fs::path path)
A writer stream (for serialization) that computes a 256-bit hash.
std::string ToString() const
bool SanityCheckASMap(const std::vector< bool > &asmap)
#define ADDRMAN_NEW_BUCKETS_PER_SOURCE_GROUP
over how many buckets entries with new addresses originating from a single group are spread ...
std::vector< uint8_t > GetKey() const
#define ADDRMAN_HORIZON_DAYS
how old addresses can maximally be
#define ADDRMAN_MAX_FAILURES
how many successive failures are allowed ...
int64_t nLastSuccess
last successful connection by us
Non-refcounted RAII wrapper for FILE*.
#define ADDRMAN_GETADDR_MAX_PCT
the maximum percentage of nodes to return in a getaddr call
int64_t nLastTry
last try whatsoever by us (memory only)
#define ADDRMAN_NEW_BUCKETS_PER_ADDRESS
in how many buckets for entries with new addresses a single address may occur
CNetAddr source
where knowledge about this address first came from
CAddrInfo * Find(const CNetAddr &addr, int *pnId=nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs)
Find an entry.