15 #include <unordered_map> 16 #include <unordered_set> 30 std::vector<unsigned char> vchSourceGroupKey = src.
GetGroup(asmap);
50 if (
nTime > nNow + 10 * 60)
68 int64_t nSinceLastTry = std::max<int64_t>(nNow -
nLastTry, 0);
71 if (nSinceLastTry < 60 * 10)
75 fChance *= pow(0.66, std::min(
nAttempts, 8));
84 const auto id = vvNew[bucket][i];
85 if (
id != -1 && !mapInfo[
id].IsValid()) {
93 const auto id = vvTried[bucket][i];
97 const auto& addr_info = mapInfo[id];
98 if (addr_info.IsValid()) {
101 vvTried[bucket][i] = -1;
103 SwapRandom(addr_info.nRandomPos, vRandom.size() - 1);
105 mapAddr.erase(addr_info);
116 const auto it = mapAddr.find(addr);
117 if (it == mapAddr.end())
120 *pnId = (*it).second;
121 const auto it2 = mapInfo.find((*it).second);
122 if (it2 != mapInfo.end())
123 return &(*it2).second;
131 int nId = nIdCount++;
132 mapInfo[nId] =
CAddrInfo(addr, addrSource);
134 mapInfo[nId].nRandomPos = vRandom.size();
135 vRandom.push_back(nId);
138 return &mapInfo[nId];
145 if (nRndPos1 == nRndPos2)
148 assert(nRndPos1 < vRandom.size() && nRndPos2 < vRandom.size());
150 int nId1 = vRandom[nRndPos1];
151 int nId2 = vRandom[nRndPos2];
153 assert(mapInfo.count(nId1) == 1);
154 assert(mapInfo.count(nId2) == 1);
156 mapInfo[nId1].nRandomPos = nRndPos2;
157 mapInfo[nId2].nRandomPos = nRndPos1;
159 vRandom[nRndPos1] = nId2;
160 vRandom[nRndPos2] = nId1;
167 assert(mapInfo.count(nId) != 0);
170 assert(info.nRefCount == 0);
172 SwapRandom(info.nRandomPos, vRandom.size() - 1);
184 if (vvNew[nUBucket][nUBucketPos] != -1) {
185 int nIdDelete = vvNew[nUBucket][nUBucketPos];
186 CAddrInfo& infoDelete = mapInfo[nIdDelete];
189 vvNew[nUBucket][nUBucketPos] = -1;
203 if (vvNew[bucket][pos] == nId) {
204 vvNew[bucket][pos] = -1;
217 if (vvTried[nKBucket][nKBucketPos] != -1) {
219 int nIdEvict = vvTried[nKBucket][nKBucketPos];
220 assert(mapInfo.count(nIdEvict) == 1);
225 vvTried[nKBucket][nKBucketPos] = -1;
229 int nUBucket = infoOld.GetNewBucket(
nKey,
m_asmap);
230 int nUBucketPos = infoOld.GetBucketPosition(
nKey,
true, nUBucket);
232 assert(vvNew[nUBucket][nUBucketPos] == -1);
235 infoOld.nRefCount = 1;
236 vvNew[nUBucket][nUBucketPos] = nIdEvict;
239 assert(vvTried[nKBucket][nKBucketPos] == -1);
241 vvTried[nKBucket][nKBucketPos] = nId;
283 if (vvNew[nB][nBpos] == nId) {
299 if (test_before_evict && (vvTried[tried_bucket][tried_bucket_pos] != -1)) {
301 auto colliding_entry = mapInfo.find(vvTried[tried_bucket][tried_bucket_pos]);
302 LogPrint(
BCLog::ADDRMAN,
"Collision inserting element into tried table (%s), moving %s to m_tried_collisions=%d\n", colliding_entry != mapInfo.end() ? colliding_entry->second.ToString() :
"", addr.
ToString(),
m_tried_collisions.size());
333 int64_t nUpdateInterval = (fCurrentlyOnline ? 60 * 60 : 24 * 60 * 60);
334 if (addr.
nTime && (!pinfo->
nTime || pinfo->
nTime < addr.
nTime - nUpdateInterval - nTimePenalty))
335 pinfo->
nTime = std::max((int64_t)0, addr.
nTime - nTimePenalty);
354 for (
int n = 0; n < pinfo->
nRefCount; n++)
360 pinfo->
nTime = std::max((int64_t)0, (int64_t)pinfo->
nTime - nTimePenalty);
367 if (vvNew[nUBucket][nUBucketPos] != nId) {
368 bool fInsert = vvNew[nUBucket][nUBucketPos] == -1;
370 CAddrInfo& infoExisting = mapInfo[vvNew[nUBucket][nUBucketPos]];
379 vvNew[nUBucket][nUBucketPos] = nId;
420 if (newOnly && nNew == 0)
427 double fChanceFactor = 1.0;
431 while (vvTried[nKBucket][nKBucketPos] == -1) {
435 int nId = vvTried[nKBucket][nKBucketPos];
436 assert(mapInfo.count(nId) == 1);
440 fChanceFactor *= 1.2;
444 double fChanceFactor = 1.0;
448 while (vvNew[nUBucket][nUBucketPos] == -1) {
452 int nId = vvNew[nUBucket][nUBucketPos];
453 assert(mapInfo.count(nId) == 1);
457 fChanceFactor *= 1.2;
463 int CAddrMan::Check_()
467 std::unordered_set<int> setTried;
468 std::unordered_map<int, int> mapNew;
470 if (vRandom.size() != (size_t)(nTried + nNew))
473 for (
const auto& entry : mapInfo) {
489 if (mapAddr[info] != n)
499 if (setTried.size() != (size_t)nTried)
501 if (mapNew.size() != (size_t)nNew)
506 if (vvTried[n][i] != -1) {
507 if (!setTried.count(vvTried[n][i]))
509 if (mapInfo[vvTried[n][i]].GetTriedBucket(
nKey,
m_asmap) != n)
511 if (mapInfo[vvTried[n][i]].GetBucketPosition(
nKey,
false, n) != i)
513 setTried.erase(vvTried[n][i]);
520 if (vvNew[n][i] != -1) {
521 if (!mapNew.count(vvNew[n][i]))
523 if (mapInfo[vvNew[n][i]].GetBucketPosition(
nKey,
true, n) != i)
525 if (--mapNew[vvNew[n][i]] == 0)
526 mapNew.erase(vvNew[n][i]);
542 void CAddrMan::GetAddr_(std::vector<CAddress>& vAddr,
size_t max_addresses,
size_t max_pct, std::optional<Network> network)
546 size_t nNodes = vRandom.size();
548 nNodes = max_pct * nNodes / 100;
550 if (max_addresses != 0) {
551 nNodes = std::min(nNodes, max_addresses);
556 for (
unsigned int n = 0; n < vRandom.size(); n++) {
557 if (vAddr.size() >= nNodes)
562 assert(mapInfo.count(vRandom[n]) == 1);
564 const CAddrInfo& ai = mapInfo[vRandom[n]];
567 if (network != std::nullopt && ai.GetNetClass() != network)
continue;
570 if (ai.IsTerrible(now))
continue;
593 int64_t nUpdateInterval = 20 * 60;
594 if (nTime - info.
nTime > nUpdateInterval)
625 bool erase_collision =
false;
628 if (mapInfo.count(id_new) != 1) {
629 erase_collision =
true;
637 erase_collision =
true;
638 }
else if (vvTried[tried_bucket][tried_bucket_pos] != -1) {
641 int id_old = vvTried[tried_bucket][tried_bucket_pos];
646 erase_collision =
true;
655 erase_collision =
true;
663 erase_collision =
true;
667 erase_collision =
true;
671 if (erase_collision) {
692 if (mapInfo.count(id_new) != 1) {
697 const CAddrInfo& newInfo = mapInfo[id_new];
703 int id_old = vvTried[tried_bucket][tried_bucket_pos];
705 return mapInfo[id_old];
710 std::vector<bool> bits;
714 LogPrintf(
"Failed to open asmap file from disk\n");
717 fseek(filestr, 0, SEEK_END);
718 int length = ftell(filestr);
719 LogPrintf(
"Opened asmap file %s (%d bytes) from disk\n", path, length);
720 fseek(filestr, 0, SEEK_SET);
722 for (
int i = 0; i < length; ++i) {
724 for (
int bit = 0; bit < 8; ++bit) {
725 bits.push_back((cur_byte >> bit) & 1);
729 LogPrintf(
"Sanity check of asmap file %s failed\n", path);
std::vector< unsigned char > GetGroup(const std::vector< bool > &asmap) const
Get the canonical identifier of our network group.
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,...)
void GetAddr_(std::vector< CAddress > &vAddr, size_t max_addresses, size_t max_pct, std::optional< Network > network) EXCLUSIVE_LOCKS_REQUIRED(cs)
Return all or many randomly selected addresses, optionally by network.
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.
Mutex cs
A mutex to protect the inner data structures.
#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 ...
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
#define ADDRMAN_REPLACEMENT_HOURS
how recent a successful connection should be before we allow an address to be evicted from tried ...
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
#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 ...
bool Add_(const CAddress &addr, const CNetAddr &source, int64_t nTimePenalty) EXCLUSIVE_LOCKS_REQUIRED(cs)
Add an entry to the "new" table.
std::set< int > m_tried_collisions
Holds addrs inserted into tried table that collide with existing entries. Test-before-evict disciplin...
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
uint64_t randbits(int bits) noexcept
Generate a random (bits)-bit integer.
Extended statistics about a CAddress.
#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
FastRandomContext insecure_rand
Source of random numbers for randomization in inner loops.
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.
std::vector< unsigned char > GetKey() const
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. This is the only place where entries are actually deleted...
ServiceFlags nServices
Serialized as uint64_t in V1, and as CompactSize in V2.
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.
void Connected_(const CService &addr, int64_t nTime) EXCLUSIVE_LOCKS_REQUIRED(cs)
We have successfully connected to this peer.
void RemoveInvalid() EXCLUSIVE_LOCKS_REQUIRED(cs)
Remove invalid addresses.
#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)
bool randbool() noexcept
Generate a random boolean.
static std::vector< bool > DecodeAsmap(fs::path path)
A writer stream (for serialization) that computes a 256-bit hash.
std::vector< bool > m_asmap
std::string ToString() const
uint256 nKey
secret key to randomize bucket select with
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 ...
#define ADDRMAN_HORIZON_DAYS
how old addresses can maximally be
static const int CLIENT_VERSION
bitcoind-res.rc includes this file, but it cannot cope with real c++ code.
#define ADDRMAN_MAX_FAILURES
how many successive failures are allowed ...
uint32_t nTime
Always included in serialization, except in the network format on INIT_PROTO_VERSION.
int64_t nLastSuccess
last successful connection by us
uint64_t randrange(uint64_t range) noexcept
Generate a random integer in the range [0..range).
Non-refcounted RAII wrapper for FILE*.
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
CAddrInfo * Find(const CNetAddr &addr, int *pnId=nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs)
Find an entry.