aboutsummaryrefslogtreecommitdiff
path: root/src/addrman.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/addrman.cpp')
-rw-r--r--src/addrman.cpp535
1 files changed, 354 insertions, 181 deletions
diff --git a/src/addrman.cpp b/src/addrman.cpp
index 7c6b8fe64d..c364a7710b 100644
--- a/src/addrman.cpp
+++ b/src/addrman.cpp
@@ -4,25 +4,27 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <addrman.h>
+#include <addrman_impl.h>
-#include <clientversion.h>
#include <hash.h>
-#include <logging.h>
#include <netaddress.h>
+#include <protocol.h>
+#include <random.h>
#include <serialize.h>
#include <streams.h>
+#include <timedata.h>
+#include <tinyformat.h>
+#include <uint256.h>
#include <util/check.h>
#include <cmath>
#include <optional>
-#include <unordered_map>
-#include <unordered_set>
/** Over how many buckets entries with tried addresses from a single group (/16 for IPv4) are spread */
static constexpr uint32_t ADDRMAN_TRIED_BUCKETS_PER_GROUP{8};
/** Over how many buckets entries with new addresses originating from a single group are spread */
static constexpr uint32_t ADDRMAN_NEW_BUCKETS_PER_SOURCE_GROUP{64};
-/** Maximum number of times an address can be added to the new table */
+/** Maximum number of times an address can occur in the new table */
static constexpr int32_t ADDRMAN_NEW_BUCKETS_PER_ADDRESS{8};
/** How old addresses can maximally be */
static constexpr int64_t ADDRMAN_HORIZON_DAYS{30};
@@ -39,7 +41,7 @@ static constexpr size_t ADDRMAN_SET_TRIED_COLLISION_SIZE{10};
/** The maximum time we'll spend trying to resolve a tried table collision, in seconds */
static constexpr int64_t ADDRMAN_TEST_WINDOW{40*60}; // 40 minutes
-int CAddrInfo::GetTriedBucket(const uint256& nKey, const std::vector<bool> &asmap) const
+int AddrInfo::GetTriedBucket(const uint256& nKey, const std::vector<bool>& asmap) const
{
uint64_t hash1 = (CHashWriter(SER_GETHASH, 0) << nKey << GetKey()).GetCheapHash();
uint64_t hash2 = (CHashWriter(SER_GETHASH, 0) << nKey << GetGroup(asmap) << (hash1 % ADDRMAN_TRIED_BUCKETS_PER_GROUP)).GetCheapHash();
@@ -49,7 +51,7 @@ int CAddrInfo::GetTriedBucket(const uint256& nKey, const std::vector<bool> &asma
return tried_bucket;
}
-int CAddrInfo::GetNewBucket(const uint256& nKey, const CNetAddr& src, const std::vector<bool> &asmap) const
+int AddrInfo::GetNewBucket(const uint256& nKey, const CNetAddr& src, const std::vector<bool>& asmap) const
{
std::vector<unsigned char> vchSourceGroupKey = src.GetGroup(asmap);
uint64_t hash1 = (CHashWriter(SER_GETHASH, 0) << nKey << GetGroup(asmap) << vchSourceGroupKey).GetCheapHash();
@@ -60,13 +62,13 @@ int CAddrInfo::GetNewBucket(const uint256& nKey, const CNetAddr& src, const std:
return new_bucket;
}
-int CAddrInfo::GetBucketPosition(const uint256 &nKey, bool fNew, int nBucket) const
+int AddrInfo::GetBucketPosition(const uint256& nKey, bool fNew, int nBucket) const
{
uint64_t hash1 = (CHashWriter(SER_GETHASH, 0) << nKey << (fNew ? uint8_t{'N'} : uint8_t{'K'}) << nBucket << GetKey()).GetCheapHash();
return hash1 % ADDRMAN_BUCKET_SIZE;
}
-bool CAddrInfo::IsTerrible(int64_t nNow) const
+bool AddrInfo::IsTerrible(int64_t nNow) const
{
if (nLastTry && nLastTry >= nNow - 60) // never remove things tried in the last minute
return false;
@@ -86,7 +88,7 @@ bool CAddrInfo::IsTerrible(int64_t nNow) const
return false;
}
-double CAddrInfo::GetChance(int64_t nNow) const
+double AddrInfo::GetChance(int64_t nNow) const
{
double fChance = 1.0;
int64_t nSinceLastTry = std::max<int64_t>(nNow - nLastTry, 0);
@@ -101,7 +103,7 @@ double CAddrInfo::GetChance(int64_t nNow) const
return fChance;
}
-CAddrMan::CAddrMan(std::vector<bool> asmap, bool deterministic, int32_t consistency_check_ratio)
+AddrManImpl::AddrManImpl(std::vector<bool>&& asmap, bool deterministic, int32_t consistency_check_ratio)
: insecure_rand{deterministic}
, nKey{deterministic ? uint256{1} : insecure_rand.rand256()}
, m_consistency_check_ratio{consistency_check_ratio}
@@ -119,8 +121,13 @@ CAddrMan::CAddrMan(std::vector<bool> asmap, bool deterministic, int32_t consiste
}
}
+AddrManImpl::~AddrManImpl()
+{
+ nKey.SetNull();
+}
+
template <typename Stream>
-void CAddrMan::Serialize(Stream& s_) const
+void AddrManImpl::Serialize(Stream& s_) const
{
LOCK(cs);
@@ -183,7 +190,7 @@ void CAddrMan::Serialize(Stream& s_) const
int nIds = 0;
for (const auto& entry : mapInfo) {
mapUnkIds[entry.first] = nIds;
- const CAddrInfo &info = entry.second;
+ const AddrInfo& info = entry.second;
if (info.nRefCount) {
assert(nIds != nNew); // this means nNew was wrong, oh ow
s << info;
@@ -192,7 +199,7 @@ void CAddrMan::Serialize(Stream& s_) const
}
nIds = 0;
for (const auto& entry : mapInfo) {
- const CAddrInfo &info = entry.second;
+ const AddrInfo& info = entry.second;
if (info.fInTried) {
assert(nIds != nTried); // this means nTried was wrong, oh ow
s << info;
@@ -223,7 +230,7 @@ void CAddrMan::Serialize(Stream& s_) const
}
template <typename Stream>
-void CAddrMan::Unserialize(Stream& s_)
+void AddrManImpl::Unserialize(Stream& s_)
{
LOCK(cs);
@@ -262,21 +269,21 @@ void CAddrMan::Unserialize(Stream& s_)
if (nNew > ADDRMAN_NEW_BUCKET_COUNT * ADDRMAN_BUCKET_SIZE || nNew < 0) {
throw std::ios_base::failure(
- strprintf("Corrupt CAddrMan serialization: nNew=%d, should be in [0, %d]",
+ strprintf("Corrupt AddrMan serialization: nNew=%d, should be in [0, %d]",
nNew,
ADDRMAN_NEW_BUCKET_COUNT * ADDRMAN_BUCKET_SIZE));
}
if (nTried > ADDRMAN_TRIED_BUCKET_COUNT * ADDRMAN_BUCKET_SIZE || nTried < 0) {
throw std::ios_base::failure(
- strprintf("Corrupt CAddrMan serialization: nTried=%d, should be in [0, %d]",
+ strprintf("Corrupt AddrMan serialization: nTried=%d, should be in [0, %d]",
nTried,
ADDRMAN_TRIED_BUCKET_COUNT * ADDRMAN_BUCKET_SIZE));
}
// Deserialize entries from the new table.
for (int n = 0; n < nNew; n++) {
- CAddrInfo &info = mapInfo[n];
+ AddrInfo& info = mapInfo[n];
s >> info;
mapAddr[info] = n;
info.nRandomPos = vRandom.size();
@@ -287,7 +294,7 @@ void CAddrMan::Unserialize(Stream& s_)
// Deserialize entries from the tried table.
int nLost = 0;
for (int n = 0; n < nTried; n++) {
- CAddrInfo info;
+ AddrInfo info;
s >> info;
int nKBucket = info.GetTriedBucket(nKey, m_asmap);
int nKBucketPos = info.GetBucketPosition(nKey, false, nKBucket);
@@ -344,7 +351,7 @@ void CAddrMan::Unserialize(Stream& s_)
for (auto bucket_entry : bucket_entries) {
int bucket{bucket_entry.first};
const int entry_index{bucket_entry.second};
- CAddrInfo& info = mapInfo[entry_index];
+ AddrInfo& info = mapInfo[entry_index];
// Don't store the entry in the new bucket if it's not a valid address for our addrman
if (!info.IsValid()) continue;
@@ -394,16 +401,7 @@ void CAddrMan::Unserialize(Stream& s_)
}
}
-// explicit instantiation
-template void CAddrMan::Serialize(CHashWriter& s) const;
-template void CAddrMan::Serialize(CAutoFile& s) const;
-template void CAddrMan::Serialize(CDataStream& s) const;
-template void CAddrMan::Unserialize(CAutoFile& s);
-template void CAddrMan::Unserialize(CHashVerifier<CAutoFile>& s);
-template void CAddrMan::Unserialize(CDataStream& s);
-template void CAddrMan::Unserialize(CHashVerifier<CDataStream>& s);
-
-CAddrInfo* CAddrMan::Find(const CNetAddr& addr, int* pnId)
+AddrInfo* AddrManImpl::Find(const CNetAddr& addr, int* pnId)
{
AssertLockHeld(cs);
@@ -418,12 +416,12 @@ CAddrInfo* CAddrMan::Find(const CNetAddr& addr, int* pnId)
return nullptr;
}
-CAddrInfo* CAddrMan::Create(const CAddress& addr, const CNetAddr& addrSource, int* pnId)
+AddrInfo* AddrManImpl::Create(const CAddress& addr, const CNetAddr& addrSource, int* pnId)
{
AssertLockHeld(cs);
int nId = nIdCount++;
- mapInfo[nId] = CAddrInfo(addr, addrSource);
+ mapInfo[nId] = AddrInfo(addr, addrSource);
mapAddr[addr] = nId;
mapInfo[nId].nRandomPos = vRandom.size();
vRandom.push_back(nId);
@@ -432,7 +430,7 @@ CAddrInfo* CAddrMan::Create(const CAddress& addr, const CNetAddr& addrSource, in
return &mapInfo[nId];
}
-void CAddrMan::SwapRandom(unsigned int nRndPos1, unsigned int nRndPos2) const
+void AddrManImpl::SwapRandom(unsigned int nRndPos1, unsigned int nRndPos2) const
{
AssertLockHeld(cs);
@@ -456,12 +454,12 @@ void CAddrMan::SwapRandom(unsigned int nRndPos1, unsigned int nRndPos2) const
vRandom[nRndPos2] = nId1;
}
-void CAddrMan::Delete(int nId)
+void AddrManImpl::Delete(int nId)
{
AssertLockHeld(cs);
assert(mapInfo.count(nId) != 0);
- CAddrInfo& info = mapInfo[nId];
+ AddrInfo& info = mapInfo[nId];
assert(!info.fInTried);
assert(info.nRefCount == 0);
@@ -472,14 +470,14 @@ void CAddrMan::Delete(int nId)
nNew--;
}
-void CAddrMan::ClearNew(int nUBucket, int nUBucketPos)
+void AddrManImpl::ClearNew(int nUBucket, int nUBucketPos)
{
AssertLockHeld(cs);
// if there is an entry in the specified bucket, delete it.
if (vvNew[nUBucket][nUBucketPos] != -1) {
int nIdDelete = vvNew[nUBucket][nUBucketPos];
- CAddrInfo& infoDelete = mapInfo[nIdDelete];
+ AddrInfo& infoDelete = mapInfo[nIdDelete];
assert(infoDelete.nRefCount > 0);
infoDelete.nRefCount--;
vvNew[nUBucket][nUBucketPos] = -1;
@@ -489,7 +487,7 @@ void CAddrMan::ClearNew(int nUBucket, int nUBucketPos)
}
}
-void CAddrMan::MakeTried(CAddrInfo& info, int nId)
+void AddrManImpl::MakeTried(AddrInfo& info, int nId)
{
AssertLockHeld(cs);
@@ -517,7 +515,7 @@ void CAddrMan::MakeTried(CAddrInfo& info, int nId)
// find an item to evict
int nIdEvict = vvTried[nKBucket][nKBucketPos];
assert(mapInfo.count(nIdEvict) == 1);
- CAddrInfo& infoOld = mapInfo[nIdEvict];
+ AddrInfo& infoOld = mapInfo[nIdEvict];
// Remove the to-be-evicted item from the tried set.
infoOld.fInTried = false;
@@ -542,7 +540,7 @@ void CAddrMan::MakeTried(CAddrInfo& info, int nId)
info.fInTried = true;
}
-void CAddrMan::Good_(const CService& addr, bool test_before_evict, int64_t nTime)
+void AddrManImpl::Good_(const CService& addr, bool test_before_evict, int64_t nTime)
{
AssertLockHeld(cs);
@@ -550,13 +548,13 @@ void CAddrMan::Good_(const CService& addr, bool test_before_evict, int64_t nTime
nLastGood = nTime;
- CAddrInfo* pinfo = Find(addr, &nId);
+ AddrInfo* pinfo = Find(addr, &nId);
// if not found, bail out
if (!pinfo)
return;
- CAddrInfo& info = *pinfo;
+ AddrInfo& info = *pinfo;
// check whether we are talking about the exact same CService (including same port)
if (info != addr)
@@ -598,7 +596,7 @@ void CAddrMan::Good_(const CService& addr, bool test_before_evict, int64_t nTime
}
}
-bool CAddrMan::Add_(const CAddress& addr, const CNetAddr& source, int64_t nTimePenalty)
+bool AddrManImpl::Add_(const CAddress& addr, const CNetAddr& source, int64_t nTimePenalty)
{
AssertLockHeld(cs);
@@ -607,7 +605,7 @@ bool CAddrMan::Add_(const CAddress& addr, const CNetAddr& source, int64_t nTimeP
bool fNew = false;
int nId;
- CAddrInfo* pinfo = Find(addr, &nId);
+ AddrInfo* pinfo = Find(addr, &nId);
// Do not set a penalty for a source's self-announcement
if (addr == source) {
@@ -654,7 +652,7 @@ bool CAddrMan::Add_(const CAddress& addr, const CNetAddr& source, int64_t nTimeP
if (vvNew[nUBucket][nUBucketPos] != nId) {
bool fInsert = vvNew[nUBucket][nUBucketPos] == -1;
if (!fInsert) {
- CAddrInfo& infoExisting = mapInfo[vvNew[nUBucket][nUBucketPos]];
+ AddrInfo& infoExisting = mapInfo[vvNew[nUBucket][nUBucketPos]];
if (infoExisting.IsTerrible() || (infoExisting.nRefCount > 1 && pinfo->nRefCount == 0)) {
// Overwrite the existing new table entry.
fInsert = true;
@@ -673,17 +671,17 @@ bool CAddrMan::Add_(const CAddress& addr, const CNetAddr& source, int64_t nTimeP
return fNew;
}
-void CAddrMan::Attempt_(const CService& addr, bool fCountFailure, int64_t nTime)
+void AddrManImpl::Attempt_(const CService& addr, bool fCountFailure, int64_t nTime)
{
AssertLockHeld(cs);
- CAddrInfo* pinfo = Find(addr);
+ AddrInfo* pinfo = Find(addr);
// if not found, bail out
if (!pinfo)
return;
- CAddrInfo& info = *pinfo;
+ AddrInfo& info = *pinfo;
// check whether we are talking about the exact same CService (including same port)
if (info != addr)
@@ -697,15 +695,13 @@ void CAddrMan::Attempt_(const CService& addr, bool fCountFailure, int64_t nTime)
}
}
-CAddrInfo CAddrMan::Select_(bool newOnly) const
+std::pair<CAddress, int64_t> AddrManImpl::Select_(bool newOnly) const
{
AssertLockHeld(cs);
- if (vRandom.empty())
- return CAddrInfo();
+ if (vRandom.empty()) return {};
- if (newOnly && nNew == 0)
- return CAddrInfo();
+ if (newOnly && nNew == 0) return {};
// Use a 50% chance for choosing between tried and new table entries.
if (!newOnly &&
@@ -722,9 +718,10 @@ CAddrInfo CAddrMan::Select_(bool newOnly) const
int nId = vvTried[nKBucket][nKBucketPos];
const auto it_found{mapInfo.find(nId)};
assert(it_found != mapInfo.end());
- const CAddrInfo& info{it_found->second};
- if (insecure_rand.randbits(30) < fChanceFactor * info.GetChance() * (1 << 30))
- return info;
+ const AddrInfo& info{it_found->second};
+ if (insecure_rand.randbits(30) < fChanceFactor * info.GetChance() * (1 << 30)) {
+ return {info, info.nLastTry};
+ }
fChanceFactor *= 1.2;
}
} else {
@@ -740,118 +737,16 @@ CAddrInfo CAddrMan::Select_(bool newOnly) const
int nId = vvNew[nUBucket][nUBucketPos];
const auto it_found{mapInfo.find(nId)};
assert(it_found != mapInfo.end());
- const CAddrInfo& info{it_found->second};
- if (insecure_rand.randbits(30) < fChanceFactor * info.GetChance() * (1 << 30))
- return info;
- fChanceFactor *= 1.2;
- }
- }
-}
-
-void CAddrMan::Check() const
-{
- AssertLockHeld(cs);
-
- // Run consistency checks 1 in m_consistency_check_ratio times if enabled
- if (m_consistency_check_ratio == 0) return;
- if (insecure_rand.randrange(m_consistency_check_ratio) >= 1) return;
-
- const int err{ForceCheckAddrman()};
- if (err) {
- LogPrintf("ADDRMAN CONSISTENCY CHECK FAILED!!! err=%i\n", err);
- assert(false);
- }
-}
-
-int CAddrMan::ForceCheckAddrman() const
-{
- AssertLockHeld(cs);
-
- LogPrint(BCLog::ADDRMAN, "Addrman checks started: new %i, tried %i, total %u\n", nNew, nTried, vRandom.size());
-
- std::unordered_set<int> setTried;
- std::unordered_map<int, int> mapNew;
-
- if (vRandom.size() != (size_t)(nTried + nNew))
- return -7;
-
- for (const auto& entry : mapInfo) {
- int n = entry.first;
- const CAddrInfo& info = entry.second;
- if (info.fInTried) {
- if (!info.nLastSuccess)
- return -1;
- if (info.nRefCount)
- return -2;
- setTried.insert(n);
- } else {
- if (info.nRefCount < 0 || info.nRefCount > ADDRMAN_NEW_BUCKETS_PER_ADDRESS)
- return -3;
- if (!info.nRefCount)
- return -4;
- mapNew[n] = info.nRefCount;
- }
- const auto it{mapAddr.find(info)};
- if (it == mapAddr.end() || it->second != n) {
- return -5;
- }
- if (info.nRandomPos < 0 || (size_t)info.nRandomPos >= vRandom.size() || vRandom[info.nRandomPos] != n)
- return -14;
- if (info.nLastTry < 0)
- return -6;
- if (info.nLastSuccess < 0)
- return -8;
- }
-
- if (setTried.size() != (size_t)nTried)
- return -9;
- if (mapNew.size() != (size_t)nNew)
- return -10;
-
- for (int n = 0; n < ADDRMAN_TRIED_BUCKET_COUNT; n++) {
- for (int i = 0; i < ADDRMAN_BUCKET_SIZE; i++) {
- if (vvTried[n][i] != -1) {
- if (!setTried.count(vvTried[n][i]))
- return -11;
- const auto it{mapInfo.find(vvTried[n][i])};
- if (it == mapInfo.end() || it->second.GetTriedBucket(nKey, m_asmap) != n) {
- return -17;
- }
- if (it->second.GetBucketPosition(nKey, false, n) != i) {
- return -18;
- }
- setTried.erase(vvTried[n][i]);
- }
- }
- }
-
- for (int n = 0; n < ADDRMAN_NEW_BUCKET_COUNT; n++) {
- for (int i = 0; i < ADDRMAN_BUCKET_SIZE; i++) {
- if (vvNew[n][i] != -1) {
- if (!mapNew.count(vvNew[n][i]))
- return -12;
- const auto it{mapInfo.find(vvNew[n][i])};
- if (it == mapInfo.end() || it->second.GetBucketPosition(nKey, true, n) != i) {
- return -19;
- }
- if (--mapNew[vvNew[n][i]] == 0)
- mapNew.erase(vvNew[n][i]);
+ const AddrInfo& info{it_found->second};
+ if (insecure_rand.randbits(30) < fChanceFactor * info.GetChance() * (1 << 30)) {
+ return {info, info.nLastTry};
}
+ fChanceFactor *= 1.2;
}
}
-
- if (setTried.size())
- return -13;
- if (mapNew.size())
- return -15;
- if (nKey.IsNull())
- return -16;
-
- LogPrint(BCLog::ADDRMAN, "Addrman checks completed successfully\n");
- return 0;
}
-void CAddrMan::GetAddr_(std::vector<CAddress>& vAddr, size_t max_addresses, size_t max_pct, std::optional<Network> network) const
+std::vector<CAddress> AddrManImpl::GetAddr_(size_t max_addresses, size_t max_pct, std::optional<Network> network) const
{
AssertLockHeld(cs);
@@ -865,8 +760,9 @@ void CAddrMan::GetAddr_(std::vector<CAddress>& vAddr, size_t max_addresses, size
// gather a list of random nodes, skipping those of low quality
const int64_t now{GetAdjustedTime()};
+ std::vector<CAddress> addresses;
for (unsigned int n = 0; n < vRandom.size(); n++) {
- if (vAddr.size() >= nNodes)
+ if (addresses.size() >= nNodes)
break;
int nRndPos = insecure_rand.randrange(vRandom.size() - n) + n;
@@ -874,7 +770,7 @@ void CAddrMan::GetAddr_(std::vector<CAddress>& vAddr, size_t max_addresses, size
const auto it{mapInfo.find(vRandom[n])};
assert(it != mapInfo.end());
- const CAddrInfo& ai{it->second};
+ const AddrInfo& ai{it->second};
// Filter by network (optional)
if (network != std::nullopt && ai.GetNetClass() != network) continue;
@@ -882,21 +778,23 @@ void CAddrMan::GetAddr_(std::vector<CAddress>& vAddr, size_t max_addresses, size
// Filter for quality
if (ai.IsTerrible(now)) continue;
- vAddr.push_back(ai);
+ addresses.push_back(ai);
}
+
+ return addresses;
}
-void CAddrMan::Connected_(const CService& addr, int64_t nTime)
+void AddrManImpl::Connected_(const CService& addr, int64_t nTime)
{
AssertLockHeld(cs);
- CAddrInfo* pinfo = Find(addr);
+ AddrInfo* pinfo = Find(addr);
// if not found, bail out
if (!pinfo)
return;
- CAddrInfo& info = *pinfo;
+ AddrInfo& info = *pinfo;
// check whether we are talking about the exact same CService (including same port)
if (info != addr)
@@ -908,17 +806,17 @@ void CAddrMan::Connected_(const CService& addr, int64_t nTime)
info.nTime = nTime;
}
-void CAddrMan::SetServices_(const CService& addr, ServiceFlags nServices)
+void AddrManImpl::SetServices_(const CService& addr, ServiceFlags nServices)
{
AssertLockHeld(cs);
- CAddrInfo* pinfo = Find(addr);
+ AddrInfo* pinfo = Find(addr);
// if not found, bail out
if (!pinfo)
return;
- CAddrInfo& info = *pinfo;
+ AddrInfo& info = *pinfo;
// check whether we are talking about the exact same CService (including same port)
if (info != addr)
@@ -928,7 +826,7 @@ void CAddrMan::SetServices_(const CService& addr, ServiceFlags nServices)
info.nServices = nServices;
}
-void CAddrMan::ResolveCollisions_()
+void AddrManImpl::ResolveCollisions_()
{
AssertLockHeld(cs);
@@ -941,7 +839,7 @@ void CAddrMan::ResolveCollisions_()
if (mapInfo.count(id_new) != 1) {
erase_collision = true;
} else {
- CAddrInfo& info_new = mapInfo[id_new];
+ AddrInfo& info_new = mapInfo[id_new];
// Which tried bucket to move the entry to.
int tried_bucket = info_new.GetTriedBucket(nKey, m_asmap);
@@ -952,7 +850,7 @@ void CAddrMan::ResolveCollisions_()
// Get the to-be-evicted address that is being tested
int id_old = vvTried[tried_bucket][tried_bucket_pos];
- CAddrInfo& info_old = mapInfo[id_old];
+ AddrInfo& info_old = mapInfo[id_old];
// Has successfully connected in last X hours
if (GetAdjustedTime() - info_old.nLastSuccess < ADDRMAN_REPLACEMENT_HOURS*(60*60)) {
@@ -989,11 +887,11 @@ void CAddrMan::ResolveCollisions_()
}
}
-CAddrInfo CAddrMan::SelectTriedCollision_()
+std::pair<CAddress, int64_t> AddrManImpl::SelectTriedCollision_()
{
AssertLockHeld(cs);
- if (m_tried_collisions.size() == 0) return CAddrInfo();
+ if (m_tried_collisions.size() == 0) return {};
std::set<int>::iterator it = m_tried_collisions.begin();
@@ -1004,16 +902,291 @@ CAddrInfo CAddrMan::SelectTriedCollision_()
// If id_new not found in mapInfo remove it from m_tried_collisions
if (mapInfo.count(id_new) != 1) {
m_tried_collisions.erase(it);
- return CAddrInfo();
+ return {};
}
- const CAddrInfo& newInfo = mapInfo[id_new];
+ const AddrInfo& newInfo = mapInfo[id_new];
// which tried bucket to move the entry to
int tried_bucket = newInfo.GetTriedBucket(nKey, m_asmap);
int tried_bucket_pos = newInfo.GetBucketPosition(nKey, false, tried_bucket);
- int id_old = vvTried[tried_bucket][tried_bucket_pos];
+ const AddrInfo& info_old = mapInfo[vvTried[tried_bucket][tried_bucket_pos]];
+ return {info_old, info_old.nLastTry};
+}
+
+void AddrManImpl::Check() const
+{
+ AssertLockHeld(cs);
+
+ // Run consistency checks 1 in m_consistency_check_ratio times if enabled
+ if (m_consistency_check_ratio == 0) return;
+ if (insecure_rand.randrange(m_consistency_check_ratio) >= 1) return;
+
+ const int err{ForceCheckAddrman()};
+ if (err) {
+ LogPrintf("ADDRMAN CONSISTENCY CHECK FAILED!!! err=%i\n", err);
+ assert(false);
+ }
+}
+
+int AddrManImpl::ForceCheckAddrman() const
+{
+ AssertLockHeld(cs);
+
+ LogPrint(BCLog::ADDRMAN, "Addrman checks started: new %i, tried %i, total %u\n", nNew, nTried, vRandom.size());
+
+ std::unordered_set<int> setTried;
+ std::unordered_map<int, int> mapNew;
+
+ if (vRandom.size() != (size_t)(nTried + nNew))
+ return -7;
+
+ for (const auto& entry : mapInfo) {
+ int n = entry.first;
+ const AddrInfo& info = entry.second;
+ if (info.fInTried) {
+ if (!info.nLastSuccess)
+ return -1;
+ if (info.nRefCount)
+ return -2;
+ setTried.insert(n);
+ } else {
+ if (info.nRefCount < 0 || info.nRefCount > ADDRMAN_NEW_BUCKETS_PER_ADDRESS)
+ return -3;
+ if (!info.nRefCount)
+ return -4;
+ mapNew[n] = info.nRefCount;
+ }
+ const auto it{mapAddr.find(info)};
+ if (it == mapAddr.end() || it->second != n) {
+ return -5;
+ }
+ if (info.nRandomPos < 0 || (size_t)info.nRandomPos >= vRandom.size() || vRandom[info.nRandomPos] != n)
+ return -14;
+ if (info.nLastTry < 0)
+ return -6;
+ if (info.nLastSuccess < 0)
+ return -8;
+ }
+
+ if (setTried.size() != (size_t)nTried)
+ return -9;
+ if (mapNew.size() != (size_t)nNew)
+ return -10;
+
+ for (int n = 0; n < ADDRMAN_TRIED_BUCKET_COUNT; n++) {
+ for (int i = 0; i < ADDRMAN_BUCKET_SIZE; i++) {
+ if (vvTried[n][i] != -1) {
+ if (!setTried.count(vvTried[n][i]))
+ return -11;
+ const auto it{mapInfo.find(vvTried[n][i])};
+ if (it == mapInfo.end() || it->second.GetTriedBucket(nKey, m_asmap) != n) {
+ return -17;
+ }
+ if (it->second.GetBucketPosition(nKey, false, n) != i) {
+ return -18;
+ }
+ setTried.erase(vvTried[n][i]);
+ }
+ }
+ }
+
+ for (int n = 0; n < ADDRMAN_NEW_BUCKET_COUNT; n++) {
+ for (int i = 0; i < ADDRMAN_BUCKET_SIZE; i++) {
+ if (vvNew[n][i] != -1) {
+ if (!mapNew.count(vvNew[n][i]))
+ return -12;
+ const auto it{mapInfo.find(vvNew[n][i])};
+ if (it == mapInfo.end() || it->second.GetBucketPosition(nKey, true, n) != i) {
+ return -19;
+ }
+ if (--mapNew[vvNew[n][i]] == 0)
+ mapNew.erase(vvNew[n][i]);
+ }
+ }
+ }
+
+ if (setTried.size())
+ return -13;
+ if (mapNew.size())
+ return -15;
+ if (nKey.IsNull())
+ return -16;
+
+ LogPrint(BCLog::ADDRMAN, "Addrman checks completed successfully\n");
+ return 0;
+}
+
+size_t AddrManImpl::size() const
+{
+ LOCK(cs); // TODO: Cache this in an atomic to avoid this overhead
+ return vRandom.size();
+}
+
+bool AddrManImpl::Add(const std::vector<CAddress>& vAddr, const CNetAddr& source, int64_t nTimePenalty)
+{
+ LOCK(cs);
+ int nAdd = 0;
+ Check();
+ for (std::vector<CAddress>::const_iterator it = vAddr.begin(); it != vAddr.end(); it++)
+ nAdd += Add_(*it, source, nTimePenalty) ? 1 : 0;
+ Check();
+ if (nAdd) {
+ LogPrint(BCLog::ADDRMAN, "Added %i addresses from %s: %i tried, %i new\n", nAdd, source.ToString(), nTried, nNew);
+ }
+ return nAdd > 0;
+}
+
+void AddrManImpl::Good(const CService& addr, int64_t nTime)
+{
+ LOCK(cs);
+ Check();
+ Good_(addr, /* test_before_evict */ true, nTime);
+ Check();
+}
- return mapInfo[id_old];
+void AddrManImpl::Attempt(const CService& addr, bool fCountFailure, int64_t nTime)
+{
+ LOCK(cs);
+ Check();
+ Attempt_(addr, fCountFailure, nTime);
+ Check();
+}
+
+void AddrManImpl::ResolveCollisions()
+{
+ LOCK(cs);
+ Check();
+ ResolveCollisions_();
+ Check();
+}
+
+std::pair<CAddress, int64_t> AddrManImpl::SelectTriedCollision()
+{
+ LOCK(cs);
+ Check();
+ const auto ret = SelectTriedCollision_();
+ Check();
+ return ret;
+}
+
+std::pair<CAddress, int64_t> AddrManImpl::Select(bool newOnly) const
+{
+ LOCK(cs);
+ Check();
+ const auto addrRet = Select_(newOnly);
+ Check();
+ return addrRet;
+}
+
+std::vector<CAddress> AddrManImpl::GetAddr(size_t max_addresses, size_t max_pct, std::optional<Network> network) const
+{
+ LOCK(cs);
+ Check();
+ const auto addresses = GetAddr_(max_addresses, max_pct, network);
+ Check();
+ return addresses;
+}
+
+void AddrManImpl::Connected(const CService& addr, int64_t nTime)
+{
+ LOCK(cs);
+ Check();
+ Connected_(addr, nTime);
+ Check();
+}
+
+void AddrManImpl::SetServices(const CService& addr, ServiceFlags nServices)
+{
+ LOCK(cs);
+ Check();
+ SetServices_(addr, nServices);
+ Check();
+}
+
+const std::vector<bool>& AddrManImpl::GetAsmap() const
+{
+ return m_asmap;
+}
+
+AddrMan::AddrMan(std::vector<bool> asmap, bool deterministic, int32_t consistency_check_ratio)
+ : m_impl(std::make_unique<AddrManImpl>(std::move(asmap), deterministic, consistency_check_ratio)) {}
+
+AddrMan::~AddrMan() = default;
+
+template <typename Stream>
+void AddrMan::Serialize(Stream& s_) const
+{
+ m_impl->Serialize<Stream>(s_);
+}
+
+template <typename Stream>
+void AddrMan::Unserialize(Stream& s_)
+{
+ m_impl->Unserialize<Stream>(s_);
+}
+
+// explicit instantiation
+template void AddrMan::Serialize(CHashWriter& s) const;
+template void AddrMan::Serialize(CAutoFile& s) const;
+template void AddrMan::Serialize(CDataStream& s) const;
+template void AddrMan::Unserialize(CAutoFile& s);
+template void AddrMan::Unserialize(CHashVerifier<CAutoFile>& s);
+template void AddrMan::Unserialize(CDataStream& s);
+template void AddrMan::Unserialize(CHashVerifier<CDataStream>& s);
+
+size_t AddrMan::size() const
+{
+ return m_impl->size();
+}
+
+bool AddrMan::Add(const std::vector<CAddress>& vAddr, const CNetAddr& source, int64_t nTimePenalty)
+{
+ return m_impl->Add(vAddr, source, nTimePenalty);
+}
+
+void AddrMan::Good(const CService& addr, int64_t nTime)
+{
+ m_impl->Good(addr, nTime);
+}
+
+void AddrMan::Attempt(const CService& addr, bool fCountFailure, int64_t nTime)
+{
+ m_impl->Attempt(addr, fCountFailure, nTime);
+}
+
+void AddrMan::ResolveCollisions()
+{
+ m_impl->ResolveCollisions();
+}
+
+std::pair<CAddress, int64_t> AddrMan::SelectTriedCollision()
+{
+ return m_impl->SelectTriedCollision();
+}
+
+std::pair<CAddress, int64_t> AddrMan::Select(bool newOnly) const
+{
+ return m_impl->Select(newOnly);
+}
+
+std::vector<CAddress> AddrMan::GetAddr(size_t max_addresses, size_t max_pct, std::optional<Network> network) const
+{
+ return m_impl->GetAddr(max_addresses, max_pct, network);
+}
+
+void AddrMan::Connected(const CService& addr, int64_t nTime)
+{
+ m_impl->Connected(addr, nTime);
+}
+
+void AddrMan::SetServices(const CService& addr, ServiceFlags nServices)
+{
+ m_impl->SetServices(addr, nServices);
+}
+
+const std::vector<bool>& AddrMan::GetAsmap() const
+{
+ return m_impl->GetAsmap();
}