56 const std::vector<bool> &asmap)
const {
65 const std::vector<bool> &asmap)
const {
66 std::vector<uint8_t> vchSourceGroupKey = src.
GetGroup(asmap);
80 (
HashWriter{} << nKey << (fNew ? uint8_t{
'N'} : uint8_t{
'K'}) << nBucket
93 if (
nTime > now + 10min) {
118 double fChance = 1.0;
127 fChance *= std::pow(0.66, std::min(
nAttempts, 8));
133 int32_t consistency_check_ratio)
134 : insecure_rand{deterministic},
135 nKey{deterministic ?
uint256{1} : insecure_rand.rand256()},
136 m_consistency_check_ratio{consistency_check_ratio},
137 m_asmap{
std::move(asmap)} {
138 for (
auto &bucket : vvNew) {
139 for (
auto &entry : bucket) {
143 for (
auto &bucket : vvTried) {
144 for (
auto &entry : bucket) {
205 static constexpr uint8_t lowest_compatible = Format::V4_MULTIPORT;
214 std::unordered_map<nid_type, int> mapUnkIds;
216 for (
const auto &entry : mapInfo) {
217 mapUnkIds[entry.first] = nIds;
218 const AddrInfo &info = entry.second;
227 for (
const auto &entry : mapInfo) {
228 const AddrInfo &info = entry.second;
239 if (vvNew[bucket][i] != -1) {
245 if (vvNew[bucket][i] != -1) {
246 int nIndex = mapUnkIds[vvNew[bucket][i]];
266 s_ >> Using<CustomUintFormatter<1>>(
format);
268 int stream_version = s_.GetVersion();
269 if (
format >= Format::V3_BIP155) {
281 throw std::ios_base::failure(
282 strprintf(
"Corrupted addrman database: The compat value (%u) "
283 "is lower than the expected minimum value %u.",
289 "Unsupported format of addrman database: %u. It is compatible with "
290 "formats >=%u, but the maximum supported by this version of %s is "
292 uint8_t{
format}, lowest_compatible, PACKAGE_NAME,
301 if (
format >= Format::V1_DETERMINISTIC) {
302 nUBuckets ^= (1 << 30);
307 "Corrupt AddrMan serialization: nNew=%d, should be in [0, %d]",
314 "Corrupt AddrMan serialization: nTried=%d, should be in [0, "
320 for (
int n = 0; n < nNew; n++) {
325 vRandom.push_back(n);
331 for (
int n = 0; n < nTried; n++) {
336 if (vvTried[nKBucket][nKBucketPos] == -1) {
339 vRandom.push_back(nIdCount);
340 mapInfo[nIdCount] = info;
341 mapAddr[info] = nIdCount;
342 vvTried[nKBucket][nKBucketPos] = nIdCount;
354 std::vector<std::pair<int, int>> bucket_entries;
356 for (
int bucket = 0; bucket < nUBuckets; ++bucket) {
359 for (
int n = 0; n < num_entries; ++n) {
362 if (entry_index >= 0 && entry_index < nNew) {
363 bucket_entries.emplace_back(bucket, entry_index);
371 uint256 supplied_asmap_checksum;
375 uint256 serialized_asmap_checksum;
376 if (
format >= Format::V2_ASMAP) {
377 s >> serialized_asmap_checksum;
380 serialized_asmap_checksum ==
381 supplied_asmap_checksum};
383 if (!restore_bucketing) {
385 "Bucketing method was updated, re-bucketing addrman "
386 "entries from disk\n");
389 for (
auto bucket_entry : bucket_entries) {
390 int bucket{bucket_entry.first};
391 const int entry_index{bucket_entry.second};
392 AddrInfo &info = mapInfo[entry_index];
402 if (restore_bucketing && vvNew[bucket][bucket_position] == -1) {
405 vvNew[bucket][bucket_position] = entry_index;
413 if (vvNew[bucket][bucket_position] == -1) {
414 vvNew[bucket][bucket_position] = entry_index;
422 for (
auto it = mapInfo.cbegin(); it != mapInfo.cend();) {
423 if (it->second.fInTried ==
false && it->second.nRefCount == 0) {
424 const auto itCopy = it++;
431 if (nLost + nLostUnk > 0) {
433 "addrman lost %i new and %i tried addresses due to "
439 if (check_code != 0) {
441 "Corrupt data. Consistency check failed with code %s", check_code));
448 const auto it = mapAddr.find(addr);
449 if (it == mapAddr.end()) {
453 *pnId = (*it).second;
455 const auto it2 = mapInfo.find((*it).second);
456 if (it2 != mapInfo.end()) {
457 return &(*it2).second;
467 mapInfo[nId] =
AddrInfo(addr, addrSource);
469 mapInfo[nId].nRandomPos = vRandom.size();
470 vRandom.push_back(nId);
474 return &mapInfo[nId];
478 unsigned int nRndPos2)
const {
481 if (nRndPos1 == nRndPos2) {
485 assert(nRndPos1 < vRandom.size() && nRndPos2 < vRandom.size());
490 const auto it_1{mapInfo.find(nId1)};
491 const auto it_2{mapInfo.find(nId2)};
492 assert(it_1 != mapInfo.end());
493 assert(it_2 != mapInfo.end());
495 it_1->second.nRandomPos = nRndPos2;
496 it_2->second.nRandomPos = nRndPos1;
498 vRandom[nRndPos1] = nId2;
499 vRandom[nRndPos2] = nId1;
505 assert(mapInfo.count(nId) != 0);
521 if (vvNew[nUBucket][nUBucketPos] != -1) {
522 nid_type nIdDelete = vvNew[nUBucket][nUBucketPos];
523 AddrInfo &infoDelete = mapInfo[nIdDelete];
526 vvNew[nUBucket][nUBucketPos] = -1;
528 infoDelete.
ToString(), nUBucket, nUBucketPos);
543 if (vvNew[bucket][pos] == nId) {
544 vvNew[bucket][pos] = -1;
561 if (vvTried[nKBucket][nKBucketPos] != -1) {
563 nid_type nIdEvict = vvTried[nKBucket][nKBucketPos];
564 assert(mapInfo.count(nIdEvict) == 1);
565 AddrInfo &infoOld = mapInfo[nIdEvict];
569 vvTried[nKBucket][nKBucketPos] = -1;
576 assert(vvNew[nUBucket][nUBucketPos] == -1);
580 vvNew[nUBucket][nUBucketPos] = nIdEvict;
583 "Moved %s from tried[%i][%i] to new[%i][%i] to make space\n",
584 infoOld.
ToString(), nKBucket, nKBucketPos, nUBucket,
587 assert(vvTried[nKBucket][nKBucketPos] == -1);
589 vvTried[nKBucket][nKBucketPos] = nId;
595 std::chrono::seconds time_penalty) {
613 const auto update_interval{currently_online ? 1h : 24h};
614 if (pinfo->
nTime < addr.
nTime - update_interval - time_penalty) {
639 for (
int n = 0; n < pinfo->
nRefCount; n++) {
643 if (nFactor > 1 && (insecure_rand.randrange(nFactor) != 0)) {
654 bool fInsert = vvNew[nUBucket][nUBucketPos] == -1;
655 if (vvNew[nUBucket][nUBucketPos] != nId) {
657 AddrInfo &infoExisting = mapInfo[vvNew[nUBucket][nUBucketPos]];
667 vvNew[nUBucket][nUBucketPos] = nId;
717 if (test_before_evict && (vvTried[tried_bucket][tried_bucket_pos] != -1)) {
722 auto colliding_entry =
723 mapInfo.find(vvTried[tried_bucket][tried_bucket_pos]);
725 "Collision with %s while attempting to move %s to tried "
726 "table. Collisions=%d\n",
727 colliding_entry != mapInfo.end()
728 ? colliding_entry->second.ToString()
742 std::chrono::seconds time_penalty) {
744 for (std::vector<CAddress>::const_iterator it = vAddr.begin();
745 it != vAddr.end(); it++) {
750 "Added %i addresses (of %i) from %s: %i tried, %i new\n",
751 added, vAddr.size(),
source.ToString(), nTried, nNew);
780 if (vRandom.empty()) {
784 if (newOnly && nNew == 0) {
790 (nTried > 0 && (nNew == 0 || insecure_rand.randbool() == 0))) {
792 double fChanceFactor = 1.0;
801 if (vvTried[nKBucket]
814 const auto it_found{mapInfo.find(nId)};
815 assert(it_found != mapInfo.end());
816 const AddrInfo &info{it_found->second};
818 if (insecure_rand.randbits(30) <
819 fChanceFactor * info.GetChance() * (1 << 30)) {
822 return {info, info.m_last_try};
826 fChanceFactor *= 1.2;
830 double fChanceFactor = 1.0;
851 const auto it_found{mapInfo.find(nId)};
852 assert(it_found != mapInfo.end());
853 const AddrInfo &info{it_found->second};
855 if (insecure_rand.randbits(30) <
856 fChanceFactor * info.GetChance() * (1 << 30)) {
859 return {info, info.m_last_try};
863 fChanceFactor *= 1.2;
870 std::optional<Network> network)
const {
873 size_t nNodes = vRandom.size();
875 nNodes = max_pct * nNodes / 100;
877 if (max_addresses != 0) {
878 nNodes = std::min(nNodes, max_addresses);
882 const auto now{Now<NodeSeconds>()};
883 std::vector<CAddress> addresses;
884 for (
unsigned int n = 0; n < vRandom.size(); n++) {
885 if (addresses.size() >= nNodes) {
889 int nRndPos = insecure_rand.randrange(vRandom.size() - n) + n;
891 const auto it{mapInfo.find(vRandom[n])};
892 assert(it != mapInfo.end());
897 if (network != std::nullopt && ai.GetNetClass() != network) {
902 if (ai.IsTerrible(now)) {
906 addresses.push_back(ai);
926 const auto update_interval{20min};
927 if (time - info.
nTime > update_interval) {
951 const auto current_time{Now<NodeSeconds>()};
957 bool erase_collision =
false;
961 auto id_new_it = mapInfo.find(id_new);
962 if (id_new_it == mapInfo.end()) {
963 erase_collision =
true;
965 AddrInfo &info_new = mapInfo[id_new];
969 int tried_bucket_pos =
973 erase_collision =
true;
974 }
else if (vvTried[tried_bucket][tried_bucket_pos] != -1) {
978 nid_type id_old = vvTried[tried_bucket][tried_bucket_pos];
979 AddrInfo &info_old = mapInfo[id_old];
984 erase_collision =
true;
985 }
else if (current_time - info_old.
m_last_try <
991 if (current_time - info_old.
m_last_try > 60s) {
993 "Replacing %s with %s in tried table\n",
998 Good_(info_new,
false, current_time);
999 erase_collision =
true;
1008 "Unable to test; replacing %s with %s in tried "
1011 Good_(info_new,
false, current_time);
1012 erase_collision =
true;
1016 Good_(info_new,
false, current_time);
1017 erase_collision =
true;
1021 if (erase_collision) {
1043 auto id_new_it = mapInfo.find(id_new);
1044 if (id_new_it == mapInfo.end()) {
1049 const AddrInfo &newInfo = id_new_it->second;
1055 const AddrInfo &info_old = mapInfo[vvTried[tried_bucket][tried_bucket_pos]];
1072 LogPrintf(
"ADDRMAN CONSISTENCY CHECK FAILED!!! err=%i\n", err);
1081 strprintf(
"new %i, tried %i, total %u", nNew, nTried, vRandom.size()),
1084 std::unordered_set<nid_type> setTried;
1085 std::unordered_map<nid_type, int> mapNew;
1087 if (vRandom.size() !=
size_t(nTried + nNew)) {
1091 for (
const auto &entry : mapInfo) {
1093 const AddrInfo &info = entry.second;
1095 if (!TicksSinceEpoch<std::chrono::seconds>(info.
m_last_success)) {
1112 const auto it{mapAddr.find(info)};
1113 if (it == mapAddr.end() || it->second != n) {
1128 if (setTried.size() !=
size_t(nTried)) {
1131 if (mapNew.size() !=
size_t(nNew)) {
1137 if (vvTried[n][i] != -1) {
1138 if (!setTried.count(vvTried[n][i])) {
1141 const auto it{mapInfo.find(vvTried[n][i])};
1142 if (it == mapInfo.end() ||
1146 if (it->second.GetBucketPosition(
nKey,
false, n) != i) {
1149 setTried.erase(vvTried[n][i]);
1156 if (vvNew[n][i] != -1) {
1157 if (!mapNew.count(vvNew[n][i])) {
1160 const auto it{mapInfo.find(vvNew[n][i])};
1161 if (it == mapInfo.end() ||
1162 it->second.GetBucketPosition(
nKey,
true, n) != i) {
1165 if (--mapNew[vvNew[n][i]] == 0) {
1166 mapNew.erase(vvNew[n][i]);
1172 if (setTried.size()) {
1175 if (mapNew.size()) {
1188 return vRandom.size();
1193 std::chrono::seconds time_penalty) {
1196 auto ret =
Add_(vAddr,
source, time_penalty);
1205 Good_(addr, test_before_evict, time);
1213 Attempt_(addr, fCountFailure, time);
1235 auto addrRet =
Select_(newOnly);
1240std::vector<CAddress>
1242 std::optional<Network> network)
const {
1245 auto addresses =
GetAddr_(max_addresses, max_pct, network);
1269 int32_t consistency_check_ratio)
1271 consistency_check_ratio)) {}
1276 m_impl->Serialize<Stream>(s_);
1280 m_impl->Unserialize<Stream>(s_);
1296 std::chrono::seconds time_penalty) {
1302 m_impl->Good(addr, test_before_evict, time);
1307 m_impl->Attempt(addr, fCountFailure, time);
1311 m_impl->ResolveCollisions();
1315 return m_impl->SelectTriedCollision();
1319 return m_impl->Select(newOnly);
1323 std::optional<Network> network)
const {
1324 return m_impl->GetAddr(max_addresses, max_pct, network);
1328 m_impl->Connected(addr, time);
1332 m_impl->SetServices(addr, nServices);
1336 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 auto ADDRMAN_HORIZON
How old addresses can maximally be.
static constexpr int32_t ADDRMAN_MAX_FAILURES
How many successive failures are allowed ...
static constexpr auto ADDRMAN_MIN_FAIL
... in at least this duration
static constexpr auto ADDRMAN_TEST_WINDOW
The maximum time we'll spend trying to resolve a tried table collision.
static constexpr auto ADDRMAN_REPLACEMENT
How recent a successful connection should be before we allow an address to be evicted from tried.
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 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 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
int64_t nid_type
User-defined type for the internally used nIds This used to be int, making it feasible for attackers ...
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.
int nRandomPos
position in vRandom
bool fInTried
in tried set? (memory only)
NodeSeconds m_last_success
last successful connection by us
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.
NodeSeconds m_last_count_attempt
last counted attempt (memory only)
NodeSeconds m_last_try
last try whatsoever by us (memory only)
double GetChance(NodeSeconds now=Now< NodeSeconds >()) const
Calculate the relative chance this entry should be given when selecting nodes to connect to.
bool IsTerrible(NodeSeconds now=Now< NodeSeconds >()) const
Determine whether the statistics about this entry are bad enough so that it can just be deleted.
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
void Connected(const CService &addr, NodeSeconds time=Now< NodeSeconds >())
We have successfully connected to this peer.
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
void Attempt(const CService &addr, bool fCountFailure, NodeSeconds time=Now< NodeSeconds >())
Mark an entry as connection attempted to.
std::pair< CAddress, NodeSeconds > Select(bool newOnly=false) const
Choose an address to connect to.
void ResolveCollisions()
See if any to-be-evicted tried table entries have been tested and if so resolve the collisions.
void Serialize(Stream &s_) const
size_t size() const
Return the number of (unique) addresses in all tables.
void Unserialize(Stream &s_)
void Good(const CService &addr, bool test_before_evict=true, NodeSeconds time=Now< NodeSeconds >())
Mark an entry as accessible, possibly moving it from "new" to "tried".
std::pair< CAddress, NodeSeconds > SelectTriedCollision()
Randomly select an address in the tried table that another address is attempting to evict.
bool Add(const std::vector< CAddress > &vAddr, const CNetAddr &source, std::chrono::seconds time_penalty=0s)
Attempt to add one or more addresses to addrman's new table.
AddrMan(std::vector< bool > asmap, bool deterministic, int32_t consistency_check_ratio)
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.
AddrInfo * Create(const CAddress &addr, const CNetAddr &addrSource, nid_type *pnId=nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs)
find an entry, creating it if necessary.
std::pair< CAddress, NodeSeconds > Select(bool newOnly) const EXCLUSIVE_LOCKS_REQUIRED(!cs)
void Connected_(const CService &addr, NodeSeconds time) EXCLUSIVE_LOCKS_REQUIRED(cs)
void Attempt_(const CService &addr, bool fCountFailure, NodeSeconds time) 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)
void Delete(nid_type nId) EXCLUSIVE_LOCKS_REQUIRED(cs)
Delete an entry. It must not be in tried, and have refcount 0.
void Connected(const CService &addr, NodeSeconds time) 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 SetServices(const CService &addr, ServiceFlags nServices) EXCLUSIVE_LOCKS_REQUIRED(!cs)
void MakeTried(AddrInfo &info, nid_type 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)
AddrInfo * Find(const CService &addr, nid_type *pnId=nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs)
Find an entry.
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
void Check() const EXCLUSIVE_LOCKS_REQUIRED(cs)
Consistency check, taking into account m_consistency_check_ratio.
int CheckAddrman() const EXCLUSIVE_LOCKS_REQUIRED(cs)
Perform consistency check, regardless of m_consistency_check_ratio.
bool Add(const std::vector< CAddress > &vAddr, const CNetAddr &source, std::chrono::seconds time_penalty) EXCLUSIVE_LOCKS_REQUIRED(!cs)
Mutex cs
A mutex to protect the inner data structures.
std::pair< CAddress, NodeSeconds > SelectTriedCollision_() EXCLUSIVE_LOCKS_REQUIRED(cs)
std::pair< CAddress, NodeSeconds > Select_(bool newOnly) const EXCLUSIVE_LOCKS_REQUIRED(cs)
std::pair< CAddress, NodeSeconds > SelectTriedCollision() EXCLUSIVE_LOCKS_REQUIRED(!cs)
std::set< nid_type > m_tried_collisions
Holds addrs inserted into tried table that collide with existing entries.
void Good_(const CService &addr, bool test_before_evict, NodeSeconds time) 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 SwapRandom(unsigned int nRandomPos1, unsigned int nRandomPos2) const EXCLUSIVE_LOCKS_REQUIRED(cs)
Swap two elements in vRandom.
void Attempt(const CService &addr, bool fCountFailure, NodeSeconds time) EXCLUSIVE_LOCKS_REQUIRED(!cs)
void Good(const CService &addr, bool test_before_evict, NodeSeconds time) EXCLUSIVE_LOCKS_REQUIRED(!cs)
void Unserialize(Stream &s_) EXCLUSIVE_LOCKS_REQUIRED(!cs)
const std::vector< bool > m_asmap
uint256 nKey
secret key to randomize bucket select with
void ResolveCollisions() 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)
bool Add_(const std::vector< CAddress > &vAddr, const CNetAddr &source, std::chrono::seconds time_penalty) EXCLUSIVE_LOCKS_REQUIRED(cs)
bool AddSingle(const CAddress &addr, const CNetAddr &source, std::chrono::seconds time_penalty) EXCLUSIVE_LOCKS_REQUIRED(cs)
Attempt to add a single address to addrman's new table.
A CService with information about it as peer.
ServiceFlags nServices
Serialized as uint64_t in V1, and as CompactSize in V2.
NodeSeconds nTime
Always included in serialization, except in the network format on INIT_PROTO_VERSION.
Reads data from an underlying stream, while hashing the read data.
std::vector< uint8_t > 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< uint8_t > GetKey() const
A writer stream (for serialization) that computes a 256-bit hash.
Writes data to an underlying source stream, while hashing the written data.
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,...)
Implement std::hash so RCUPtr can be used as a key for maps or sets.
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.
static time_point now() noexcept
Return current system time or mocked time, if set.
std::chrono::time_point< NodeClock, std::chrono::seconds > NodeSeconds
#define LOG_TIME_MILLIS_WITH_CATEGORY_MSG_ONCE(end_msg, log_category)