53 std::vector<unsigned char> vchSourceGroupKey = src.
GetGroup(asmap);
70 if (
nTime > nNow + 10 * 60)
88 int64_t nSinceLastTry = std::max<int64_t>(nNow -
nLastTry, 0);
91 if (nSinceLastTry < 60 * 10)
95 fChance *= pow(0.66, std::min(
nAttempts, 8));
101 : insecure_rand{deterministic}
102 , nKey{deterministic ?
uint256{1} : insecure_rand.rand256()}
103 , m_consistency_check_ratio{consistency_check_ratio}
104 , m_asmap{std::move(asmap)}
106 for (
auto& bucket : vvNew) {
107 for (
auto& entry : bucket) {
111 for (
auto& bucket : vvTried) {
112 for (
auto& entry : bucket) {
123template <
typename Stream>
174 static constexpr uint8_t lowest_compatible = Format::V4_MULTIPORT;
183 std::unordered_map<int, int> mapUnkIds;
185 for (
const auto& entry : mapInfo) {
186 mapUnkIds[entry.first] = nIds;
187 const AddrInfo& info = entry.second;
195 for (
const auto& entry : mapInfo) {
196 const AddrInfo& info = entry.second;
206 if (vvNew[bucket][i] != -1)
211 if (vvNew[bucket][i] != -1) {
212 int nIndex = mapUnkIds[vvNew[bucket][i]];
226template <
typename Stream>
234 s_ >> Using<CustomUintFormatter<1>>(
format);
236 int stream_version = s_.GetVersion();
237 if (
format >= Format::V3_BIP155) {
250 "Unsupported format of addrman database: %u. It is compatible with formats >=%u, "
251 "but the maximum supported by this version of %s is %u.",
260 if (
format >= Format::V1_DETERMINISTIC) {
261 nUBuckets ^= (1 << 30);
265 throw std::ios_base::failure(
266 strprintf(
"Corrupt AddrMan serialization: nNew=%d, should be in [0, %d]",
272 throw std::ios_base::failure(
273 strprintf(
"Corrupt AddrMan serialization: nTried=%d, should be in [0, %d]",
279 for (
int n = 0; n < nNew; n++) {
284 vRandom.push_back(n);
290 for (
int n = 0; n < nTried; n++) {
296 && vvTried[nKBucket][nKBucketPos] == -1) {
299 vRandom.push_back(nIdCount);
300 mapInfo[nIdCount] = info;
301 mapAddr[info] = nIdCount;
302 vvTried[nKBucket][nKBucketPos] = nIdCount;
313 std::vector<std::pair<int, int>> bucket_entries;
315 for (
int bucket = 0; bucket < nUBuckets; ++bucket) {
318 for (
int n = 0; n < num_entries; ++n) {
321 if (entry_index >= 0 && entry_index < nNew) {
322 bucket_entries.emplace_back(bucket, entry_index);
330 uint256 supplied_asmap_checksum;
334 uint256 serialized_asmap_checksum;
335 if (
format >= Format::V2_ASMAP) {
336 s >> serialized_asmap_checksum;
339 serialized_asmap_checksum == supplied_asmap_checksum};
341 if (!restore_bucketing) {
345 for (
auto bucket_entry : bucket_entries) {
346 int bucket{bucket_entry.first};
347 const int entry_index{bucket_entry.second};
348 AddrInfo& info = mapInfo[entry_index];
359 if (restore_bucketing && vvNew[bucket][bucket_position] == -1) {
361 vvNew[bucket][bucket_position] = entry_index;
368 if (vvNew[bucket][bucket_position] == -1) {
369 vvNew[bucket][bucket_position] = entry_index;
377 for (
auto it = mapInfo.cbegin(); it != mapInfo.cend(); ) {
378 if (it->second.fInTried ==
false && it->second.nRefCount == 0) {
379 const auto itCopy = it++;
386 if (nLost + nLostUnk > 0) {
387 LogPrint(
BCLog::ADDRMAN,
"addrman lost %i new and %i tried addresses due to collisions or invalid addresses\n", nLostUnk, nLost);
391 if (check_code != 0) {
393 "Corrupt data. Consistency check failed with code %s",
402 const auto it = mapAddr.find(addr);
403 if (it == mapAddr.end())
406 *pnId = (*it).second;
407 const auto it2 = mapInfo.find((*it).second);
408 if (it2 != mapInfo.end())
409 return &(*it2).second;
417 int nId = nIdCount++;
418 mapInfo[nId] =
AddrInfo(addr, addrSource);
420 mapInfo[nId].nRandomPos = vRandom.size();
421 vRandom.push_back(nId);
424 return &mapInfo[nId];
431 if (nRndPos1 == nRndPos2)
434 assert(nRndPos1 < vRandom.size() && nRndPos2 < vRandom.size());
436 int nId1 = vRandom[nRndPos1];
437 int nId2 = vRandom[nRndPos2];
439 const auto it_1{mapInfo.find(nId1)};
440 const auto it_2{mapInfo.find(nId2)};
441 assert(it_1 != mapInfo.end());
442 assert(it_2 != mapInfo.end());
444 it_1->second.nRandomPos = nRndPos2;
445 it_2->second.nRandomPos = nRndPos1;
447 vRandom[nRndPos1] = nId2;
448 vRandom[nRndPos2] = nId1;
455 assert(mapInfo.count(nId) != 0);
472 if (vvNew[nUBucket][nUBucketPos] != -1) {
473 int nIdDelete = vvNew[nUBucket][nUBucketPos];
474 AddrInfo& infoDelete = mapInfo[nIdDelete];
477 vvNew[nUBucket][nUBucketPos] = -1;
494 if (vvNew[bucket][pos] == nId) {
495 vvNew[bucket][pos] = -1;
509 if (vvTried[nKBucket][nKBucketPos] != -1) {
511 int nIdEvict = vvTried[nKBucket][nKBucketPos];
512 assert(mapInfo.count(nIdEvict) == 1);
513 AddrInfo& infoOld = mapInfo[nIdEvict];
517 vvTried[nKBucket][nKBucketPos] = -1;
524 assert(vvNew[nUBucket][nUBucketPos] == -1);
528 vvNew[nUBucket][nUBucketPos] = nIdEvict;
531 infoOld.
ToString(), nKBucket, nKBucketPos, nUBucket, nUBucketPos);
533 assert(vvTried[nKBucket][nKBucketPos] == -1);
535 vvTried[nKBucket][nKBucketPos] = nId;
558 int64_t nUpdateInterval = (fCurrentlyOnline ? 60 * 60 : 24 * 60 * 60);
559 if (addr.
nTime && (!pinfo->
nTime || pinfo->
nTime < addr.
nTime - nUpdateInterval - nTimePenalty))
560 pinfo->
nTime = std::max((int64_t)0, addr.
nTime - nTimePenalty);
579 for (
int n = 0; n < pinfo->
nRefCount; n++)
581 if (nFactor > 1 && (insecure_rand.randrange(nFactor) != 0))
585 pinfo->
nTime = std::max((int64_t)0, (int64_t)pinfo->
nTime - nTimePenalty);
591 bool fInsert = vvNew[nUBucket][nUBucketPos] == -1;
592 if (vvNew[nUBucket][nUBucketPos] != nId) {
594 AddrInfo& infoExisting = mapInfo[vvNew[nUBucket][nUBucketPos]];
603 vvNew[nUBucket][nUBucketPos] = nId;
652 if (test_before_evict && (vvTried[tried_bucket][tried_bucket_pos] != -1)) {
657 auto colliding_entry = mapInfo.find(vvTried[tried_bucket][tried_bucket_pos]);
659 colliding_entry != mapInfo.end() ? colliding_entry->second.ToString() :
"",
673 for (std::vector<CAddress>::const_iterator it = vAddr.begin(); it != vAddr.end(); it++) {
677 LogPrint(
BCLog::ADDRMAN,
"Added %i addresses (of %i) from %s: %i tried, %i new\n", added, vAddr.size(),
source.ToString(), nTried, nNew);
706 if (vRandom.empty())
return {};
708 if (newOnly && nNew == 0)
return {};
712 (nTried > 0 && (nNew == 0 || insecure_rand.randbool() == 0))) {
714 double fChanceFactor = 1.0;
729 const auto it_found{mapInfo.find(nId)};
730 assert(it_found != mapInfo.end());
731 const AddrInfo& info{it_found->second};
733 if (insecure_rand.randbits(30) < fChanceFactor * info.GetChance() * (1 << 30)) {
735 return {info, info.nLastTry};
738 fChanceFactor *= 1.2;
742 double fChanceFactor = 1.0;
757 const auto it_found{mapInfo.find(nId)};
758 assert(it_found != mapInfo.end());
759 const AddrInfo& info{it_found->second};
761 if (insecure_rand.randbits(30) < fChanceFactor * info.GetChance() * (1 << 30)) {
763 return {info, info.nLastTry};
766 fChanceFactor *= 1.2;
771std::vector<CAddress>
AddrManImpl::GetAddr_(
size_t max_addresses,
size_t max_pct, std::optional<Network> network)
const
775 size_t nNodes = vRandom.size();
777 nNodes = max_pct * nNodes / 100;
779 if (max_addresses != 0) {
780 nNodes = std::min(nNodes, max_addresses);
785 std::vector<CAddress> addresses;
786 for (
unsigned int n = 0; n < vRandom.size(); n++) {
787 if (addresses.size() >= nNodes)
790 int nRndPos = insecure_rand.randrange(vRandom.size() - n) + n;
792 const auto it{mapInfo.find(vRandom[n])};
793 assert(it != mapInfo.end());
798 if (network != std::nullopt && ai.GetNetClass() != network)
continue;
801 if (ai.IsTerrible(now))
continue;
803 addresses.push_back(ai);
822 int64_t nUpdateInterval = 20 * 60;
823 if (nTime - info.
nTime > nUpdateInterval)
850 bool erase_collision =
false;
853 if (mapInfo.count(id_new) != 1) {
854 erase_collision =
true;
856 AddrInfo& info_new = mapInfo[id_new];
862 erase_collision =
true;
863 }
else if (vvTried[tried_bucket][tried_bucket_pos] != -1) {
866 int id_old = vvTried[tried_bucket][tried_bucket_pos];
867 AddrInfo& info_old = mapInfo[id_old];
871 erase_collision =
true;
880 erase_collision =
true;
888 erase_collision =
true;
892 erase_collision =
true;
896 if (erase_collision) {
917 if (mapInfo.count(id_new) != 1) {
922 const AddrInfo& newInfo = mapInfo[id_new];
928 const AddrInfo& info_old = mapInfo[vvTried[tried_bucket][tried_bucket_pos]];
929 return {info_old, info_old.
nLastTry};
942 LogPrintf(
"ADDRMAN CONSISTENCY CHECK FAILED!!! err=%i\n", err);
951 LogPrint(
BCLog::ADDRMAN,
"Addrman checks started: new %i, tried %i, total %u\n", nNew, nTried, vRandom.size());
953 std::unordered_set<int> setTried;
954 std::unordered_map<int, int> mapNew;
956 if (vRandom.size() != (
size_t)(nTried + nNew))
959 for (
const auto& entry : mapInfo) {
961 const AddrInfo& info = entry.second;
975 const auto it{mapAddr.find(info)};
976 if (it == mapAddr.end() || it->second != n) {
987 if (setTried.size() != (
size_t)nTried)
989 if (mapNew.size() != (
size_t)nNew)
994 if (vvTried[n][i] != -1) {
995 if (!setTried.count(vvTried[n][i]))
997 const auto it{mapInfo.find(vvTried[n][i])};
998 if (it == mapInfo.end() || it->second.GetTriedBucket(
nKey,
m_asmap) != n) {
1001 if (it->second.GetBucketPosition(
nKey,
false, n) != i) {
1004 setTried.erase(vvTried[n][i]);
1011 if (vvNew[n][i] != -1) {
1012 if (!mapNew.count(vvNew[n][i]))
1014 const auto it{mapInfo.find(vvNew[n][i])};
1015 if (it == mapInfo.end() || it->second.GetBucketPosition(
nKey,
true, n) != i) {
1018 if (--mapNew[vvNew[n][i]] == 0)
1019 mapNew.erase(vvNew[n][i]);
1024 if (setTried.size())
1038 return vRandom.size();
1045 auto ret =
Add_(vAddr,
source, nTimePenalty);
1054 Good_(addr,
true, nTime);
1062 Attempt_(addr, fCountFailure, nTime);
1087 const auto addrRet =
Select_(newOnly);
1092std::vector<CAddress>
AddrManImpl::GetAddr(
size_t max_addresses,
size_t max_pct, std::optional<Network> network)
const
1096 const auto addresses =
GetAddr_(max_addresses, max_pct, network);
1123 : m_impl(std::make_unique<
AddrManImpl>(std::move(asmap), deterministic, consistency_check_ratio)) {}
1127template <
typename Stream>
1130 m_impl->Serialize<Stream>(s_);
1133template <
typename Stream>
1136 m_impl->Unserialize<Stream>(s_);
1160 m_impl->Good(addr, nTime);
1165 m_impl->Attempt(addr, fCountFailure, nTime);
1170 m_impl->ResolveCollisions();
1175 return m_impl->SelectTriedCollision();
1180 return m_impl->Select(newOnly);
1183std::vector<CAddress>
AddrMan::GetAddr(
size_t max_addresses,
size_t max_pct, std::optional<Network> network)
const
1185 return m_impl->GetAddr(max_addresses, max_pct, network);
1190 m_impl->Connected(addr, nTime);
1195 m_impl->SetServices(addr, nServices);
1200 return m_impl->GetAsmap();
static constexpr uint32_t ADDRMAN_NEW_BUCKETS_PER_SOURCE_GROUP
Over how many buckets entries with new addresses originating from a single group are spread.
static constexpr int64_t ADDRMAN_HORIZON_DAYS
How old addresses can maximally be.
static constexpr int32_t ADDRMAN_MAX_FAILURES
How many successive failures are allowed ...
static constexpr int32_t ADDRMAN_RETRIES
After how many failed attempts we give up on a new node.
static constexpr size_t ADDRMAN_SET_TRIED_COLLISION_SIZE
The maximum number of tried addr collisions to store.
static constexpr int64_t ADDRMAN_REPLACEMENT_HOURS
How recent a successful connection should be before we allow an address to be evicted from tried.
static constexpr uint32_t ADDRMAN_TRIED_BUCKETS_PER_GROUP
Over how many buckets entries with tried addresses from a single group (/16 for IPv4) are spread.
static constexpr int64_t ADDRMAN_MIN_FAIL_DAYS
... in at least this many days
static constexpr int64_t ADDRMAN_TEST_WINDOW
The maximum time we'll spend trying to resolve a tried table collision, in seconds.
static constexpr int32_t ADDRMAN_NEW_BUCKETS_PER_ADDRESS
Maximum number of times an address can occur in the new table.
static constexpr int ADDRMAN_TRIED_BUCKET_COUNT
static constexpr int ADDRMAN_BUCKET_SIZE
static constexpr int ADDRMAN_NEW_BUCKET_COUNT
#define Assume(val)
Assume is the identity function.
Extended statistics about a CAddress.
int GetTriedBucket(const uint256 &nKey, const std::vector< bool > &asmap) const
Calculate in which "tried" bucket this entry belongs.
bool IsTerrible(int64_t nNow=GetAdjustedTime()) const
Determine whether the statistics about this entry are bad enough so that it can just be deleted.
int nRandomPos
position in vRandom
double GetChance(int64_t nNow=GetAdjustedTime()) const
Calculate the relative chance this entry should be given when selecting nodes to connect to.
bool fInTried
in tried set? (memory only)
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.
int64_t nLastCountAttempt
last counted attempt (memory only)
int64_t nLastTry
last try whatsoever by us (memory only)
int64_t nLastSuccess
last successful connection by us
int nRefCount
reference count in new sets (memory only)
int GetBucketPosition(const uint256 &nKey, bool fNew, int nBucket) const
Calculate in which position of a bucket to store this entry.
int nAttempts
connection attempts since last successful attempt
std::pair< CAddress, int64_t > SelectTriedCollision()
Randomly select an address in the tried table that another address is attempting to evict.
const std::unique_ptr< AddrManImpl > m_impl
std::vector< CAddress > GetAddr(size_t max_addresses, size_t max_pct, std::optional< Network > network) const
Return all or many randomly selected addresses, optionally by network.
const std::vector< bool > & GetAsmap() const
bool Add(const std::vector< CAddress > &vAddr, const CNetAddr &source, int64_t nTimePenalty=0)
Attempt to add one or more addresses to addrman's new table.
void Good(const CService &addr, int64_t nTime=GetAdjustedTime())
Mark an entry as accessible, possibly moving it from "new" to "tried".
void ResolveCollisions()
See if any to-be-evicted tried table entries have been tested and if so resolve the collisions.
void Connected(const CService &addr, int64_t nTime=GetAdjustedTime())
We have successfully connected to this peer.
void Attempt(const CService &addr, bool fCountFailure, int64_t nTime=GetAdjustedTime())
Mark an entry as connection attempted to.
void Serialize(Stream &s_) const
size_t size() const
Return the number of (unique) addresses in all tables.
void Unserialize(Stream &s_)
AddrMan(std::vector< bool > asmap, bool deterministic, int32_t consistency_check_ratio)
std::pair< CAddress, int64_t > Select(bool newOnly=false) const
Choose an address to connect to.
void SetServices(const CService &addr, ServiceFlags nServices)
Update an entry's service bits.
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.
int ForceCheckAddrman() const EXCLUSIVE_LOCKS_REQUIRED(cs)
Perform consistency check, regardless of m_consistency_check_ratio.
void Good(const CService &addr, int64_t nTime) EXCLUSIVE_LOCKS_REQUIRED(!cs)
static constexpr Format FILE_FORMAT
The maximum format this software knows it can unserialize.
void ResolveCollisions_() EXCLUSIVE_LOCKS_REQUIRED(cs)
Format
Serialization versions.
void Serialize(Stream &s_) const EXCLUSIVE_LOCKS_REQUIRED(!cs)
std::vector< CAddress > GetAddr(size_t max_addresses, size_t max_pct, std::optional< Network > network) const EXCLUSIVE_LOCKS_REQUIRED(!cs)
size_t size() const EXCLUSIVE_LOCKS_REQUIRED(!cs)
void MakeTried(AddrInfo &info, int nId) EXCLUSIVE_LOCKS_REQUIRED(cs)
Move an entry from the "new" table(s) to the "tried" table.
void SetServices(const CService &addr, ServiceFlags nServices) EXCLUSIVE_LOCKS_REQUIRED(!cs)
bool Add(const std::vector< CAddress > &vAddr, const CNetAddr &source, int64_t nTimePenalty) EXCLUSIVE_LOCKS_REQUIRED(!cs)
void Attempt(const CService &addr, bool fCountFailure, int64_t nTime) EXCLUSIVE_LOCKS_REQUIRED(!cs)
void SetServices_(const CService &addr, ServiceFlags nServices) EXCLUSIVE_LOCKS_REQUIRED(cs)
void Connected(const CService &addr, int64_t nTime) EXCLUSIVE_LOCKS_REQUIRED(!cs)
const int32_t m_consistency_check_ratio
Perform consistency checks every m_consistency_check_ratio operations (if non-zero).
const std::vector< bool > & GetAsmap() const
std::pair< CAddress, int64_t > Select(bool newOnly) const EXCLUSIVE_LOCKS_REQUIRED(!cs)
void Check() const EXCLUSIVE_LOCKS_REQUIRED(cs)
Consistency check, taking into account m_consistency_check_ratio. Will std::abort if an inconsistency...
AddrInfo * Find(const CService &addr, int *pnId=nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs)
Find an entry.
std::pair< CAddress, int64_t > SelectTriedCollision_() EXCLUSIVE_LOCKS_REQUIRED(cs)
Mutex cs
A mutex to protect the inner data structures.
void Good_(const CService &addr, bool test_before_evict, int64_t time) EXCLUSIVE_LOCKS_REQUIRED(cs)
std::pair< CAddress, int64_t > Select_(bool newOnly) const EXCLUSIVE_LOCKS_REQUIRED(cs)
AddrManImpl(std::vector< bool > &&asmap, bool deterministic, int32_t consistency_check_ratio)
static constexpr uint8_t INCOMPATIBILITY_BASE
The initial value of a field that is incremented every time an incompatible format change is made (su...
void Delete(int nId) EXCLUSIVE_LOCKS_REQUIRED(cs)
Delete an entry. It must not be in tried, and have refcount 0.
void SwapRandom(unsigned int nRandomPos1, unsigned int nRandomPos2) const EXCLUSIVE_LOCKS_REQUIRED(cs)
Swap two elements in vRandom.
void Connected_(const CService &addr, int64_t nTime) EXCLUSIVE_LOCKS_REQUIRED(cs)
bool AddSingle(const CAddress &addr, const CNetAddr &source, int64_t nTimePenalty) EXCLUSIVE_LOCKS_REQUIRED(cs)
Attempt to add a single address to addrman's new table.
void Unserialize(Stream &s_) EXCLUSIVE_LOCKS_REQUIRED(!cs)
std::set< int > m_tried_collisions
Holds addrs inserted into tried table that collide with existing entries. Test-before-evict disciplin...
const std::vector< bool > m_asmap
uint256 nKey
secret key to randomize bucket select with
void ResolveCollisions() EXCLUSIVE_LOCKS_REQUIRED(!cs)
AddrInfo * Create(const CAddress &addr, const CNetAddr &addrSource, int *pnId=nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs)
Create a new entry and add it to the internal data structures mapInfo, mapAddr and vRandom.
std::pair< CAddress, int64_t > SelectTriedCollision() EXCLUSIVE_LOCKS_REQUIRED(!cs)
bool Add_(const std::vector< CAddress > &vAddr, const CNetAddr &source, int64_t nTimePenalty) EXCLUSIVE_LOCKS_REQUIRED(cs)
std::vector< CAddress > GetAddr_(size_t max_addresses, size_t max_pct, std::optional< Network > network) const EXCLUSIVE_LOCKS_REQUIRED(cs)
void Attempt_(const CService &addr, bool fCountFailure, int64_t nTime) EXCLUSIVE_LOCKS_REQUIRED(cs)
A CService with information about it as peer.
ServiceFlags nServices
Serialized as uint64_t in V1, and as CompactSize in V2.
uint32_t nTime
Always included in serialization.
Non-refcounted RAII wrapper for FILE*.
Double ended buffer combining vector and stream-like interfaces.
Reads data from an underlying stream, while hashing the read data.
A writer stream (for serialization) that computes a 256-bit hash.
std::vector< unsigned char > GetGroup(const std::vector< bool > &asmap) const
Get the canonical identifier of our network group.
uint32_t GetMappedAS(const std::vector< bool > &asmap) const
A combination of a network address (CNetAddr) and a (TCP) port.
std::string ToString() const
std::vector< unsigned char > GetKey() const
uint256 SerializeHash(const T &obj, int nType=SER_GETHASH, int nVersion=PROTOCOL_VERSION)
Compute the 256-bit hash of an object's serialization.
#define LogPrint(category,...)
static constexpr int ADDRV2_FORMAT
A flag that is ORed into the protocol version to designate that addresses should be serialized in (un...
ServiceFlags
nServices flags
int64_t GetAdjustedTime()