aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAmiti Uttarwar <amiti@uttarwar.org>2021-09-01 11:21:29 -0700
committerAmiti Uttarwar <amiti@uttarwar.org>2021-09-28 19:02:34 -0400
commit8af5b54f973e11c847345418d8631bc301b96130 (patch)
tree29080d2442f453fc2483fb42b7302b3bfc7fd026 /src
parentf2e5f38f09ee40933f752680fe7d75ee8e529fae (diff)
downloadbitcoin-8af5b54f973e11c847345418d8631bc301b96130.tar.xz
[addrman] Introduce CAddrMan::Impl to encapsulate addrman implementation.
Introduce the pimpl pattern for CAddrMan to separate the implementation details from the externally used object representation. This reduces compile-time dependencies and conceptually clarifies AddrMan's interface from the implementation specifics. Since the unit & fuzz tests currently rely on accessing CAddrMan internals, this commit introduces addrman_impl.h, which is exclusively imported by addrman.cpp and test files. Review hint: git diff --color-moved=dimmed-zebra --color-moved-ws=ignore-all-space
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am1
-rw-r--r--src/addrman.cpp157
-rw-r--r--src/addrman.h188
-rw-r--r--src/addrman_impl.h206
-rw-r--r--src/test/addrman_tests.cpp19
-rw-r--r--src/test/fuzz/addrman.cpp45
6 files changed, 371 insertions, 245 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 52c8b85357..72aa3829f4 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -117,6 +117,7 @@ endif
BITCOIN_CORE_H = \
addrdb.h \
addrman.h \
+ addrman_impl.h \
attributes.h \
banman.h \
base58.h \
diff --git a/src/addrman.cpp b/src/addrman.cpp
index 99c4f00d6d..2fc6ca29a1 100644
--- a/src/addrman.cpp
+++ b/src/addrman.cpp
@@ -4,6 +4,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <addrman.h>
+#include <addrman_impl.h>
#include <clientversion.h>
#include <hash.h>
@@ -101,7 +102,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,13 +120,13 @@ CAddrMan::CAddrMan(std::vector<bool> asmap, bool deterministic, int32_t consiste
}
}
-CAddrMan::~CAddrMan()
+AddrManImpl::~AddrManImpl()
{
nKey.SetNull();
}
template <typename Stream>
-void CAddrMan::Serialize(Stream& s_) const
+void AddrManImpl::Serialize(Stream& s_) const
{
LOCK(cs);
@@ -228,7 +229,7 @@ void CAddrMan::Serialize(Stream& s_) const
}
template <typename Stream>
-void CAddrMan::Unserialize(Stream& s_)
+void AddrManImpl::Unserialize(Stream& s_)
{
LOCK(cs);
@@ -399,16 +400,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)
+CAddrInfo* AddrManImpl::Find(const CNetAddr& addr, int* pnId)
{
AssertLockHeld(cs);
@@ -423,7 +415,7 @@ CAddrInfo* CAddrMan::Find(const CNetAddr& addr, int* pnId)
return nullptr;
}
-CAddrInfo* CAddrMan::Create(const CAddress& addr, const CNetAddr& addrSource, int* pnId)
+CAddrInfo* AddrManImpl::Create(const CAddress& addr, const CNetAddr& addrSource, int* pnId)
{
AssertLockHeld(cs);
@@ -437,7 +429,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);
@@ -461,7 +453,7 @@ 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);
@@ -477,7 +469,7 @@ void CAddrMan::Delete(int nId)
nNew--;
}
-void CAddrMan::ClearNew(int nUBucket, int nUBucketPos)
+void AddrManImpl::ClearNew(int nUBucket, int nUBucketPos)
{
AssertLockHeld(cs);
@@ -494,7 +486,7 @@ void CAddrMan::ClearNew(int nUBucket, int nUBucketPos)
}
}
-void CAddrMan::MakeTried(CAddrInfo& info, int nId)
+void AddrManImpl::MakeTried(CAddrInfo& info, int nId)
{
AssertLockHeld(cs);
@@ -547,7 +539,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);
@@ -603,7 +595,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);
@@ -678,7 +670,7 @@ 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);
@@ -702,7 +694,7 @@ void CAddrMan::Attempt_(const CService& addr, bool fCountFailure, int64_t nTime)
}
}
-CAddrInfo CAddrMan::Select_(bool newOnly) const
+CAddrInfo AddrManImpl::Select_(bool newOnly) const
{
AssertLockHeld(cs);
@@ -753,8 +745,7 @@ CAddrInfo CAddrMan::Select_(bool newOnly) const
}
}
-
-void CAddrMan::GetAddr_(std::vector<CAddress>& vAddr, size_t max_addresses, size_t max_pct, std::optional<Network> network) const
+void AddrManImpl::GetAddr_(std::vector<CAddress>& vAddr, size_t max_addresses, size_t max_pct, std::optional<Network> network) const
{
AssertLockHeld(cs);
@@ -789,7 +780,7 @@ void CAddrMan::GetAddr_(std::vector<CAddress>& vAddr, size_t max_addresses, size
}
}
-void CAddrMan::Connected_(const CService& addr, int64_t nTime)
+void AddrManImpl::Connected_(const CService& addr, int64_t nTime)
{
AssertLockHeld(cs);
@@ -811,7 +802,7 @@ 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);
@@ -831,7 +822,7 @@ void CAddrMan::SetServices_(const CService& addr, ServiceFlags nServices)
info.nServices = nServices;
}
-void CAddrMan::ResolveCollisions_()
+void AddrManImpl::ResolveCollisions_()
{
AssertLockHeld(cs);
@@ -892,7 +883,7 @@ void CAddrMan::ResolveCollisions_()
}
}
-CAddrInfo CAddrMan::SelectTriedCollision_()
+CAddrInfo AddrManImpl::SelectTriedCollision_()
{
AssertLockHeld(cs);
@@ -921,7 +912,7 @@ CAddrInfo CAddrMan::SelectTriedCollision_()
return mapInfo[id_old];
}
-void CAddrMan::Check() const
+void AddrManImpl::Check() const
{
AssertLockHeld(cs);
@@ -936,7 +927,7 @@ void CAddrMan::Check() const
}
}
-int CAddrMan::ForceCheckAddrman() const
+int AddrManImpl::ForceCheckAddrman() const
{
AssertLockHeld(cs);
@@ -1024,13 +1015,13 @@ int CAddrMan::ForceCheckAddrman() const
return 0;
}
-size_t CAddrMan::size() const
+size_t AddrManImpl::size() const
{
LOCK(cs); // TODO: Cache this in an atomic to avoid this overhead
return vRandom.size();
}
-bool CAddrMan::Add(const std::vector<CAddress> &vAddr, const CNetAddr& source, int64_t nTimePenalty)
+bool AddrManImpl::Add(const std::vector<CAddress> &vAddr, const CNetAddr& source, int64_t nTimePenalty)
{
LOCK(cs);
int nAdd = 0;
@@ -1044,7 +1035,7 @@ bool CAddrMan::Add(const std::vector<CAddress> &vAddr, const CNetAddr& source, i
return nAdd > 0;
}
-void CAddrMan::Good(const CService &addr, int64_t nTime)
+void AddrManImpl::Good(const CService &addr, int64_t nTime)
{
LOCK(cs);
Check();
@@ -1052,7 +1043,7 @@ void CAddrMan::Good(const CService &addr, int64_t nTime)
Check();
}
-void CAddrMan::Attempt(const CService &addr, bool fCountFailure, int64_t nTime)
+void AddrManImpl::Attempt(const CService &addr, bool fCountFailure, int64_t nTime)
{
LOCK(cs);
Check();
@@ -1060,8 +1051,7 @@ void CAddrMan::Attempt(const CService &addr, bool fCountFailure, int64_t nTime)
Check();
}
-
-void CAddrMan::ResolveCollisions()
+void AddrManImpl::ResolveCollisions()
{
LOCK(cs);
Check();
@@ -1069,7 +1059,7 @@ void CAddrMan::ResolveCollisions()
Check();
}
-CAddrInfo CAddrMan::SelectTriedCollision()
+CAddrInfo AddrManImpl::SelectTriedCollision()
{
LOCK(cs);
Check();
@@ -1078,7 +1068,7 @@ CAddrInfo CAddrMan::SelectTriedCollision()
return ret;
}
-CAddrInfo CAddrMan::Select(bool newOnly) const
+CAddrInfo AddrManImpl::Select(bool newOnly) const
{
LOCK(cs);
Check();
@@ -1087,7 +1077,7 @@ CAddrInfo CAddrMan::Select(bool newOnly) const
return addrRet;
}
-std::vector<CAddress> CAddrMan::GetAddr(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
{
LOCK(cs);
Check();
@@ -1097,7 +1087,7 @@ std::vector<CAddress> CAddrMan::GetAddr(size_t max_addresses, size_t max_pct, st
return vAddr;
}
-void CAddrMan::Connected(const CService &addr, int64_t nTime)
+void AddrManImpl::Connected(const CService &addr, int64_t nTime)
{
LOCK(cs);
Check();
@@ -1105,7 +1095,7 @@ void CAddrMan::Connected(const CService &addr, int64_t nTime)
Check();
}
-void CAddrMan::SetServices(const CService &addr, ServiceFlags nServices)
+void AddrManImpl::SetServices(const CService &addr, ServiceFlags nServices)
{
LOCK(cs);
Check();
@@ -1113,7 +1103,88 @@ void CAddrMan::SetServices(const CService &addr, ServiceFlags nServices)
Check();
}
-const std::vector<bool>& CAddrMan::GetAsmap() const
+const std::vector<bool>& AddrManImpl::GetAsmap() const
{
return m_asmap;
}
+
+CAddrMan::CAddrMan(std::vector<bool> asmap, bool deterministic, int32_t consistency_check_ratio)
+ : m_impl(std::make_unique<AddrManImpl>(std::move(asmap), deterministic, consistency_check_ratio)) {}
+
+CAddrMan::~CAddrMan() = default;
+
+template <typename Stream>
+void CAddrMan::Serialize(Stream& s_) const
+{
+ m_impl->Serialize<Stream>(s_);
+}
+
+template <typename Stream>
+void CAddrMan::Unserialize(Stream& s_)
+{
+ m_impl->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);
+
+size_t CAddrMan::size() const
+{
+ return m_impl->size();
+}
+
+bool CAddrMan::Add(const std::vector<CAddress> &vAddr, const CNetAddr& source, int64_t nTimePenalty)
+{
+ return m_impl->Add(vAddr, source, nTimePenalty);
+}
+
+void CAddrMan::Good(const CService &addr, int64_t nTime)
+{
+ m_impl->Good(addr, nTime);
+}
+
+void CAddrMan::Attempt(const CService &addr, bool fCountFailure, int64_t nTime)
+{
+ m_impl->Attempt(addr, fCountFailure, nTime);
+}
+
+void CAddrMan::ResolveCollisions()
+{
+ m_impl->ResolveCollisions();
+}
+
+CAddrInfo CAddrMan::SelectTriedCollision()
+{
+ return m_impl->SelectTriedCollision();
+}
+
+CAddrInfo CAddrMan::Select(bool newOnly) const
+{
+ return m_impl->Select(newOnly);
+}
+
+std::vector<CAddress> CAddrMan::GetAddr(size_t max_addresses, size_t max_pct, std::optional<Network> network) const
+{
+ return m_impl->GetAddr(max_addresses, max_pct, network);
+}
+
+void CAddrMan::Connected(const CService &addr, int64_t nTime)
+{
+ m_impl->Connected(addr, nTime);
+}
+
+void CAddrMan::SetServices(const CService &addr, ServiceFlags nServices)
+{
+ m_impl->SetServices(addr, nServices);
+}
+
+const std::vector<bool>& CAddrMan::GetAsmap() const
+{
+ return m_impl->GetAsmap();
+}
diff --git a/src/addrman.h b/src/addrman.h
index d3d764c9c6..2135295b89 100644
--- a/src/addrman.h
+++ b/src/addrman.h
@@ -19,6 +19,8 @@
#include <unordered_map>
#include <vector>
+class AddrManImpl;
+
/** Default for -checkaddrman */
static constexpr int32_t DEFAULT_ADDRMAN_CONSISTENCY_CHECKS{0};
@@ -53,7 +55,7 @@ private:
//! position in vRandom
mutable int nRandomPos{-1};
- friend class CAddrMan;
+ friend class AddrManImpl;
friend class CAddrManDeterministic;
public:
@@ -141,42 +143,41 @@ static constexpr int ADDRMAN_BUCKET_SIZE{1 << ADDRMAN_BUCKET_SIZE_LOG2};
*/
class CAddrMan
{
+ const std::unique_ptr<AddrManImpl> m_impl;
+
public:
explicit CAddrMan(std::vector<bool> asmap, bool deterministic, int32_t consistency_check_ratio);
~CAddrMan();
template <typename Stream>
- void Serialize(Stream& s_) const EXCLUSIVE_LOCKS_REQUIRED(!cs);
+ void Serialize(Stream& s_) const;
template <typename Stream>
- void Unserialize(Stream& s_) EXCLUSIVE_LOCKS_REQUIRED(!cs);
+ void Unserialize(Stream& s_);
//! Return the number of (unique) addresses in all tables.
- size_t size() const EXCLUSIVE_LOCKS_REQUIRED(!cs);
+ size_t size() const;
//! Add addresses to addrman's new table.
- bool Add(const std::vector<CAddress> &vAddr, const CNetAddr& source, int64_t nTimePenalty = 0)
- EXCLUSIVE_LOCKS_REQUIRED(!cs);
+ bool Add(const std::vector<CAddress> &vAddr, const CNetAddr& source, int64_t nTimePenalty = 0);
//! Mark an entry as accessible.
- void Good(const CService &addr, int64_t nTime = GetAdjustedTime())
- EXCLUSIVE_LOCKS_REQUIRED(!cs);
+ void Good(const CService &addr, int64_t nTime = GetAdjustedTime());
//! Mark an entry as connection attempted to.
- void Attempt(const CService &addr, bool fCountFailure, int64_t nTime = GetAdjustedTime())
- EXCLUSIVE_LOCKS_REQUIRED(!cs);
+ void Attempt(const CService &addr, bool fCountFailure, int64_t nTime = GetAdjustedTime());
//! See if any to-be-evicted tried table entries have been tested and if so resolve the collisions.
- void ResolveCollisions() EXCLUSIVE_LOCKS_REQUIRED(!cs);
+ void ResolveCollisions();
//! Randomly select an address in tried that another address is attempting to evict.
- CAddrInfo SelectTriedCollision() EXCLUSIVE_LOCKS_REQUIRED(!cs);
+ CAddrInfo SelectTriedCollision();
/**
* Choose an address to connect to.
*/
- CAddrInfo Select(bool newOnly = false) const EXCLUSIVE_LOCKS_REQUIRED(!cs);
+ CAddrInfo Select(bool newOnly = false) const;
/**
* Return all or many randomly selected addresses, optionally by network.
@@ -185,170 +186,15 @@ public:
* @param[in] max_pct Maximum percentage of addresses to return (0 = all).
* @param[in] network Select only addresses of this network (nullopt = all).
*/
- std::vector<CAddress> GetAddr(size_t max_addresses, size_t max_pct, std::optional<Network> network) const
- EXCLUSIVE_LOCKS_REQUIRED(!cs);
+ std::vector<CAddress> GetAddr(size_t max_addresses, size_t max_pct, std::optional<Network> network) const;
//! Outer function for Connected_()
- void Connected(const CService &addr, int64_t nTime = GetAdjustedTime())
- EXCLUSIVE_LOCKS_REQUIRED(!cs);
+ void Connected(const CService &addr, int64_t nTime = GetAdjustedTime());
- void SetServices(const CService &addr, ServiceFlags nServices)
- EXCLUSIVE_LOCKS_REQUIRED(!cs);
+ void SetServices(const CService &addr, ServiceFlags nServices);
const std::vector<bool>& GetAsmap() const;
-private:
- //! A mutex to protect the inner data structures.
- mutable Mutex cs;
-
- //! Source of random numbers for randomization in inner loops
- mutable FastRandomContext insecure_rand GUARDED_BY(cs);
-
- //! secret key to randomize bucket select with
- uint256 nKey;
-
- //! Serialization versions.
- enum Format : uint8_t {
- V0_HISTORICAL = 0, //!< historic format, before commit e6b343d88
- V1_DETERMINISTIC = 1, //!< for pre-asmap files
- V2_ASMAP = 2, //!< for files including asmap version
- V3_BIP155 = 3, //!< same as V2_ASMAP plus addresses are in BIP155 format
- };
-
- //! The maximum format this software knows it can unserialize. Also, we always serialize
- //! in this format.
- //! The format (first byte in the serialized stream) can be higher than this and
- //! still this software may be able to unserialize the file - if the second byte
- //! (see `lowest_compatible` in `Unserialize()`) is less or equal to this.
- static constexpr Format FILE_FORMAT = Format::V3_BIP155;
-
- //! The initial value of a field that is incremented every time an incompatible format
- //! change is made (such that old software versions would not be able to parse and
- //! understand the new file format). This is 32 because we overtook the "key size"
- //! field which was 32 historically.
- //! @note Don't increment this. Increment `lowest_compatible` in `Serialize()` instead.
- static constexpr uint8_t INCOMPATIBILITY_BASE = 32;
-
- //! last used nId
- int nIdCount GUARDED_BY(cs){0};
-
- //! table with information about all nIds
- std::unordered_map<int, CAddrInfo> mapInfo GUARDED_BY(cs);
-
- //! find an nId based on its network address
- std::unordered_map<CNetAddr, int, CNetAddrHash> mapAddr GUARDED_BY(cs);
-
- //! randomly-ordered vector of all nIds
- //! This is mutable because it is unobservable outside the class, so any
- //! changes to it (even in const methods) are also unobservable.
- mutable std::vector<int> vRandom GUARDED_BY(cs);
-
- // number of "tried" entries
- int nTried GUARDED_BY(cs){0};
-
- //! list of "tried" buckets
- int vvTried[ADDRMAN_TRIED_BUCKET_COUNT][ADDRMAN_BUCKET_SIZE] GUARDED_BY(cs);
-
- //! number of (unique) "new" entries
- int nNew GUARDED_BY(cs){0};
-
- //! list of "new" buckets
- int vvNew[ADDRMAN_NEW_BUCKET_COUNT][ADDRMAN_BUCKET_SIZE] GUARDED_BY(cs);
-
- //! last time Good was called (memory only). Initially set to 1 so that "never" is strictly worse.
- int64_t nLastGood GUARDED_BY(cs){1};
-
- //! Holds addrs inserted into tried table that collide with existing entries. Test-before-evict discipline used to resolve these collisions.
- std::set<int> m_tried_collisions;
-
- /** Perform consistency checks every m_consistency_check_ratio operations (if non-zero). */
- const int32_t m_consistency_check_ratio;
-
- // Compressed IP->ASN mapping, loaded from a file when a node starts.
- // Should be always empty if no file was provided.
- // This mapping is then used for bucketing nodes in Addrman.
- //
- // If asmap is provided, nodes will be bucketed by
- // AS they belong to, in order to make impossible for a node
- // to connect to several nodes hosted in a single AS.
- // This is done in response to Erebus attack, but also to generally
- // diversify the connections every node creates,
- // especially useful when a large fraction of nodes
- // operate under a couple of cloud providers.
- //
- // If a new asmap was provided, the existing records
- // would be re-bucketed accordingly.
- const std::vector<bool> m_asmap;
-
- //! Find an entry.
- CAddrInfo* Find(const CNetAddr& addr, int *pnId = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs);
-
- //! Create a new entry and add it to the internal data structures mapInfo, mapAddr and vRandom.
- CAddrInfo* Create(const CAddress &addr, const CNetAddr &addrSource, int *pnId = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs);
-
- //! Swap two elements in vRandom.
- void SwapRandom(unsigned int nRandomPos1, unsigned int nRandomPos2) const EXCLUSIVE_LOCKS_REQUIRED(cs);
-
- //! Delete an entry. It must not be in tried, and have refcount 0.
- void Delete(int nId) EXCLUSIVE_LOCKS_REQUIRED(cs);
-
- //! Clear a position in a "new" table. This is the only place where entries are actually deleted.
- void ClearNew(int nUBucket, int nUBucketPos) EXCLUSIVE_LOCKS_REQUIRED(cs);
-
- //! Move an entry from the "new" table(s) to the "tried" table
- void MakeTried(CAddrInfo& info, int nId) EXCLUSIVE_LOCKS_REQUIRED(cs);
-
- //! Mark an entry "good", possibly moving it from "new" to "tried".
- void Good_(const CService &addr, bool test_before_evict, int64_t time) EXCLUSIVE_LOCKS_REQUIRED(cs);
-
- //! Add an entry to the "new" table.
- bool Add_(const CAddress &addr, const CNetAddr& source, int64_t nTimePenalty) EXCLUSIVE_LOCKS_REQUIRED(cs);
-
- //! Mark an entry as attempted to connect.
- void Attempt_(const CService &addr, bool fCountFailure, int64_t nTime) EXCLUSIVE_LOCKS_REQUIRED(cs);
-
- //! Select an address to connect to, if newOnly is set to true, only the new table is selected from.
- CAddrInfo Select_(bool newOnly) const EXCLUSIVE_LOCKS_REQUIRED(cs);
-
- /**
- * Return all or many randomly selected addresses, optionally by network.
- *
- * @param[out] vAddr Vector of randomly selected addresses from vRandom.
- * @param[in] max_addresses Maximum number of addresses to return (0 = all).
- * @param[in] max_pct Maximum percentage of addresses to return (0 = all).
- * @param[in] network Select only addresses of this network (nullopt = all).
- */
- void GetAddr_(std::vector<CAddress>& vAddr, size_t max_addresses, size_t max_pct, std::optional<Network> network) const EXCLUSIVE_LOCKS_REQUIRED(cs);
-
- /** We have successfully connected to this peer. Calling this function
- * updates the CAddress's nTime, which is used in our IsTerrible()
- * decisions and gossiped to peers. Callers should be careful that updating
- * this information doesn't leak topology information to network spies.
- *
- * net_processing calls this function when it *disconnects* from a peer to
- * not leak information about currently connected peers.
- *
- * @param[in] addr The address of the peer we were connected to
- * @param[in] nTime The time that we were last connected to this peer
- */
- void Connected_(const CService& addr, int64_t nTime) EXCLUSIVE_LOCKS_REQUIRED(cs);
-
- //! Update an entry's service bits.
- void SetServices_(const CService &addr, ServiceFlags nServices) EXCLUSIVE_LOCKS_REQUIRED(cs);
-
- //! See if any to-be-evicted tried table entries have been tested and if so resolve the collisions.
- void ResolveCollisions_() EXCLUSIVE_LOCKS_REQUIRED(cs);
-
- //! Return a random to-be-evicted tried table address.
- CAddrInfo SelectTriedCollision_() EXCLUSIVE_LOCKS_REQUIRED(cs);
-
- //! Consistency check, taking into account m_consistency_check_ratio. Will std::abort if an inconsistency is detected.
- void Check() const EXCLUSIVE_LOCKS_REQUIRED(cs);
-
- //! Perform consistency check, regardless of m_consistency_check_ratio.
- //! @returns an error code or zero.
- int ForceCheckAddrman() const EXCLUSIVE_LOCKS_REQUIRED(cs);
-
friend class CAddrManTest;
friend class CAddrManDeterministic;
};
diff --git a/src/addrman_impl.h b/src/addrman_impl.h
new file mode 100644
index 0000000000..ee4b55e5c4
--- /dev/null
+++ b/src/addrman_impl.h
@@ -0,0 +1,206 @@
+// Copyright (c) 2021 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_ADDRMAN_IMPL_H
+#define BITCOIN_ADDRMAN_IMPL_H
+
+class AddrManImpl
+{
+public:
+ AddrManImpl(std::vector<bool>&& asmap, bool deterministic, int32_t consistency_check_ratio);
+
+ ~AddrManImpl();
+
+ template <typename Stream>
+ void Serialize(Stream& s_) const EXCLUSIVE_LOCKS_REQUIRED(!cs);
+
+ template <typename Stream>
+ void Unserialize(Stream& s_) EXCLUSIVE_LOCKS_REQUIRED(!cs);
+
+ size_t size() const EXCLUSIVE_LOCKS_REQUIRED(!cs);
+
+ bool Add(const std::vector<CAddress> &vAddr, const CNetAddr& source, int64_t nTimePenalty)
+ EXCLUSIVE_LOCKS_REQUIRED(!cs);
+
+ void Good(const CService &addr, int64_t nTime)
+ EXCLUSIVE_LOCKS_REQUIRED(!cs);
+
+ void Attempt(const CService &addr, bool fCountFailure, int64_t nTime)
+ EXCLUSIVE_LOCKS_REQUIRED(!cs);
+
+ void ResolveCollisions() EXCLUSIVE_LOCKS_REQUIRED(!cs);
+
+ CAddrInfo SelectTriedCollision() EXCLUSIVE_LOCKS_REQUIRED(!cs);
+
+ CAddrInfo Select(bool newOnly) 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);
+
+ void Connected(const CService &addr, int64_t nTime)
+ EXCLUSIVE_LOCKS_REQUIRED(!cs);
+
+ void SetServices(const CService &addr, ServiceFlags nServices)
+ EXCLUSIVE_LOCKS_REQUIRED(!cs);
+
+ const std::vector<bool>& GetAsmap() const;
+
+ friend class CAddrManTest;
+ friend class CAddrManDeterministic;
+
+private:
+ //! A mutex to protect the inner data structures.
+ mutable Mutex cs;
+
+ //! Source of random numbers for randomization in inner loops
+ mutable FastRandomContext insecure_rand GUARDED_BY(cs);
+
+ //! secret key to randomize bucket select with
+ uint256 nKey;
+
+ //! Serialization versions.
+ enum Format : uint8_t {
+ V0_HISTORICAL = 0, //!< historic format, before commit e6b343d88
+ V1_DETERMINISTIC = 1, //!< for pre-asmap files
+ V2_ASMAP = 2, //!< for files including asmap version
+ V3_BIP155 = 3, //!< same as V2_ASMAP plus addresses are in BIP155 format
+ };
+
+ //! The maximum format this software knows it can unserialize. Also, we always serialize
+ //! in this format.
+ //! The format (first byte in the serialized stream) can be higher than this and
+ //! still this software may be able to unserialize the file - if the second byte
+ //! (see `lowest_compatible` in `Unserialize()`) is less or equal to this.
+ static constexpr Format FILE_FORMAT = Format::V3_BIP155;
+
+ //! The initial value of a field that is incremented every time an incompatible format
+ //! change is made (such that old software versions would not be able to parse and
+ //! understand the new file format). This is 32 because we overtook the "key size"
+ //! field which was 32 historically.
+ //! @note Don't increment this. Increment `lowest_compatible` in `Serialize()` instead.
+ static constexpr uint8_t INCOMPATIBILITY_BASE = 32;
+
+ //! last used nId
+ int nIdCount GUARDED_BY(cs){0};
+
+ //! table with information about all nIds
+ std::unordered_map<int, CAddrInfo> mapInfo GUARDED_BY(cs);
+
+ //! find an nId based on its network address
+ std::unordered_map<CNetAddr, int, CNetAddrHash> mapAddr GUARDED_BY(cs);
+
+ //! randomly-ordered vector of all nIds
+ //! This is mutable because it is unobservable outside the class, so any
+ //! changes to it (even in const methods) are also unobservable.
+ mutable std::vector<int> vRandom GUARDED_BY(cs);
+
+ // number of "tried" entries
+ int nTried GUARDED_BY(cs){0};
+
+ //! list of "tried" buckets
+ int vvTried[ADDRMAN_TRIED_BUCKET_COUNT][ADDRMAN_BUCKET_SIZE] GUARDED_BY(cs);
+
+ //! number of (unique) "new" entries
+ int nNew GUARDED_BY(cs){0};
+
+ //! list of "new" buckets
+ int vvNew[ADDRMAN_NEW_BUCKET_COUNT][ADDRMAN_BUCKET_SIZE] GUARDED_BY(cs);
+
+ //! last time Good was called (memory only). Initially set to 1 so that "never" is strictly worse.
+ int64_t nLastGood GUARDED_BY(cs){1};
+
+ //! Holds addrs inserted into tried table that collide with existing entries. Test-before-evict discipline used to resolve these collisions.
+ std::set<int> m_tried_collisions;
+
+ /** Perform consistency checks every m_consistency_check_ratio operations (if non-zero). */
+ const int32_t m_consistency_check_ratio;
+
+ // Compressed IP->ASN mapping, loaded from a file when a node starts.
+ // Should be always empty if no file was provided.
+ // This mapping is then used for bucketing nodes in Addrman.
+ //
+ // If asmap is provided, nodes will be bucketed by
+ // AS they belong to, in order to make impossible for a node
+ // to connect to several nodes hosted in a single AS.
+ // This is done in response to Erebus attack, but also to generally
+ // diversify the connections every node creates,
+ // especially useful when a large fraction of nodes
+ // operate under a couple of cloud providers.
+ //
+ // If a new asmap was provided, the existing records
+ // would be re-bucketed accordingly.
+ const std::vector<bool> m_asmap;
+
+ //! Find an entry.
+ CAddrInfo* Find(const CNetAddr& addr, int *pnId = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs);
+
+ //! Create a new entry and add it to the internal data structures mapInfo, mapAddr and vRandom.
+ CAddrInfo* Create(const CAddress &addr, const CNetAddr &addrSource, int *pnId = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs);
+
+ //! Swap two elements in vRandom.
+ void SwapRandom(unsigned int nRandomPos1, unsigned int nRandomPos2) const EXCLUSIVE_LOCKS_REQUIRED(cs);
+
+ //! Delete an entry. It must not be in tried, and have refcount 0.
+ void Delete(int nId) EXCLUSIVE_LOCKS_REQUIRED(cs);
+
+ //! Clear a position in a "new" table. This is the only place where entries are actually deleted.
+ void ClearNew(int nUBucket, int nUBucketPos) EXCLUSIVE_LOCKS_REQUIRED(cs);
+
+ //! Move an entry from the "new" table(s) to the "tried" table
+ void MakeTried(CAddrInfo& info, int nId) EXCLUSIVE_LOCKS_REQUIRED(cs);
+
+ //! Mark an entry "good", possibly moving it from "new" to "tried".
+ void Good_(const CService &addr, bool test_before_evict, int64_t time) EXCLUSIVE_LOCKS_REQUIRED(cs);
+
+ //! Add an entry to the "new" table.
+ bool Add_(const CAddress &addr, const CNetAddr& source, int64_t nTimePenalty) EXCLUSIVE_LOCKS_REQUIRED(cs);
+
+ //! Mark an entry as attempted to connect.
+ void Attempt_(const CService &addr, bool fCountFailure, int64_t nTime) EXCLUSIVE_LOCKS_REQUIRED(cs);
+
+ //! Select an address to connect to, if newOnly is set to true, only the new table is selected from.
+ CAddrInfo Select_(bool newOnly) const EXCLUSIVE_LOCKS_REQUIRED(cs);
+
+ /**
+ * Return all or many randomly selected addresses, optionally by network.
+ *
+ * @param[out] vAddr Vector of randomly selected addresses from vRandom.
+ * @param[in] max_addresses Maximum number of addresses to return (0 = all).
+ * @param[in] max_pct Maximum percentage of addresses to return (0 = all).
+ * @param[in] network Select only addresses of this network (nullopt = all).
+ */
+ void GetAddr_(std::vector<CAddress>& vAddr, size_t max_addresses, size_t max_pct, std::optional<Network> network) const EXCLUSIVE_LOCKS_REQUIRED(cs);
+
+ /** We have successfully connected to this peer. Calling this function
+ * updates the CAddress's nTime, which is used in our IsTerrible()
+ * decisions and gossiped to peers. Callers should be careful that updating
+ * this information doesn't leak topology information to network spies.
+ *
+ * net_processing calls this function when it *disconnects* from a peer to
+ * not leak information about currently connected peers.
+ *
+ * @param[in] addr The address of the peer we were connected to
+ * @param[in] nTime The time that we were last connected to this peer
+ */
+ void Connected_(const CService& addr, int64_t nTime) EXCLUSIVE_LOCKS_REQUIRED(cs);
+
+ //! Update an entry's service bits.
+ void SetServices_(const CService &addr, ServiceFlags nServices) EXCLUSIVE_LOCKS_REQUIRED(cs);
+
+ //! See if any to-be-evicted tried table entries have been tested and if so resolve the collisions.
+ void ResolveCollisions_() EXCLUSIVE_LOCKS_REQUIRED(cs);
+
+ //! Return a random to-be-evicted tried table address.
+ CAddrInfo SelectTriedCollision_() EXCLUSIVE_LOCKS_REQUIRED(cs);
+
+ //! Consistency check, taking into account m_consistency_check_ratio. Will std::abort if an inconsistency is detected.
+ void Check() const EXCLUSIVE_LOCKS_REQUIRED(cs);
+
+ //! Perform consistency check, regardless of m_consistency_check_ratio.
+ //! @returns an error code or zero.
+ int ForceCheckAddrman() const EXCLUSIVE_LOCKS_REQUIRED(cs);
+};
+
+#endif // BITCOIN_ADDRMAN_IMPL_H
diff --git a/src/test/addrman_tests.cpp b/src/test/addrman_tests.cpp
index 01a492a20b..41c298e036 100644
--- a/src/test/addrman_tests.cpp
+++ b/src/test/addrman_tests.cpp
@@ -4,6 +4,7 @@
#include <addrdb.h>
#include <addrman.h>
+#include <addrman_impl.h>
#include <chainparams.h>
#include <clientversion.h>
#include <hash.h>
@@ -90,30 +91,30 @@ public:
CAddrInfo* Find(const CNetAddr& addr, int* pnId = nullptr)
{
- LOCK(cs);
- return CAddrMan::Find(addr, pnId);
+ LOCK(m_impl->cs);
+ return m_impl->Find(addr, pnId);
}
CAddrInfo* Create(const CAddress& addr, const CNetAddr& addrSource, int* pnId = nullptr)
{
- LOCK(cs);
- return CAddrMan::Create(addr, addrSource, pnId);
+ LOCK(m_impl->cs);
+ return m_impl->Create(addr, addrSource, pnId);
}
void Delete(int nId)
{
- LOCK(cs);
- CAddrMan::Delete(nId);
+ LOCK(m_impl->cs);
+ m_impl->Delete(nId);
}
// Used to test deserialization
std::pair<int, int> GetBucketAndEntry(const CAddress& addr)
{
- LOCK(cs);
- int nId = mapAddr[addr];
+ LOCK(m_impl->cs);
+ int nId = m_impl->mapAddr[addr];
for (int bucket = 0; bucket < ADDRMAN_NEW_BUCKET_COUNT; ++bucket) {
for (int entry = 0; entry < ADDRMAN_BUCKET_SIZE; ++entry) {
- if (nId == vvNew[bucket][entry]) {
+ if (nId == m_impl->vvNew[bucket][entry]) {
return std::pair<int, int>(bucket, entry);
}
}
diff --git a/src/test/fuzz/addrman.cpp b/src/test/fuzz/addrman.cpp
index 45ee778b87..6625b69b14 100644
--- a/src/test/fuzz/addrman.cpp
+++ b/src/test/fuzz/addrman.cpp
@@ -4,6 +4,7 @@
#include <addrdb.h>
#include <addrman.h>
+#include <addrman_impl.h>
#include <chainparams.h>
#include <merkleblock.h>
#include <test/fuzz/FuzzedDataProvider.h>
@@ -43,13 +44,13 @@ public:
: CAddrMan(std::move(asmap), /* deterministic */ true, /* consistency_check_ratio */ 0)
, m_fuzzed_data_provider(fuzzed_data_provider)
{
- WITH_LOCK(cs, insecure_rand = FastRandomContext{ConsumeUInt256(fuzzed_data_provider)});
+ WITH_LOCK(m_impl->cs, m_impl->insecure_rand = FastRandomContext{ConsumeUInt256(fuzzed_data_provider)});
}
/**
* Generate a random address. Always returns a valid address.
*/
- CNetAddr RandAddr() EXCLUSIVE_LOCKS_REQUIRED(cs)
+ CNetAddr RandAddr() EXCLUSIVE_LOCKS_REQUIRED(m_impl->cs)
{
CNetAddr addr;
if (m_fuzzed_data_provider.remaining_bytes() > 1 && m_fuzzed_data_provider.ConsumeBool()) {
@@ -61,7 +62,7 @@ public:
{4, ADDR_TORV3_SIZE},
{5, ADDR_I2P_SIZE},
{6, ADDR_CJDNS_SIZE}};
- uint8_t net = insecure_rand.randrange(5) + 1; // [1..5]
+ uint8_t net = m_impl->insecure_rand.randrange(5) + 1; // [1..5]
if (net == 3) {
net = 6;
}
@@ -69,7 +70,7 @@ public:
CDataStream s(SER_NETWORK, PROTOCOL_VERSION | ADDRV2_FORMAT);
s << net;
- s << insecure_rand.randbytes(net_len_map.at(net));
+ s << m_impl->insecure_rand.randbytes(net_len_map.at(net));
s >> addr;
}
@@ -89,7 +90,7 @@ public:
*/
void Fill()
{
- LOCK(cs);
+ LOCK(m_impl->cs);
// Add some of the addresses directly to the "tried" table.
@@ -102,20 +103,20 @@ public:
// the latter is exhausted it just returns 0.
for (size_t i = 0; i < num_sources; ++i) {
const auto source = RandAddr();
- const size_t num_addresses = insecure_rand.randrange(500) + 1; // [1..500]
+ const size_t num_addresses = m_impl->insecure_rand.randrange(500) + 1; // [1..500]
for (size_t j = 0; j < num_addresses; ++j) {
const auto addr = CAddress{CService{RandAddr(), 8333}, NODE_NETWORK};
- const auto time_penalty = insecure_rand.randrange(100000001);
- Add_(addr, source, time_penalty);
+ const auto time_penalty = m_impl->insecure_rand.randrange(100000001);
+ m_impl->Add_(addr, source, time_penalty);
- if (n > 0 && mapInfo.size() % n == 0) {
- Good_(addr, false, GetTime());
+ if (n > 0 && m_impl->mapInfo.size() % n == 0) {
+ m_impl->Good_(addr, false, GetTime());
}
// Add 10% of the addresses from more than one source.
- if (insecure_rand.randrange(10) == 0 && prev_source.IsValid()) {
- Add_(addr, prev_source, time_penalty);
+ if (m_impl->insecure_rand.randrange(10) == 0 && prev_source.IsValid()) {
+ m_impl->Add_({addr}, prev_source, time_penalty);
}
}
prev_source = source;
@@ -131,10 +132,10 @@ public:
*/
bool operator==(const CAddrManDeterministic& other)
{
- LOCK2(cs, other.cs);
+ LOCK2(m_impl->cs, other.m_impl->cs);
- if (mapInfo.size() != other.mapInfo.size() || nNew != other.nNew ||
- nTried != other.nTried) {
+ if (m_impl->mapInfo.size() != other.m_impl->mapInfo.size() || m_impl->nNew != other.m_impl->nNew ||
+ m_impl->nTried != other.m_impl->nTried) {
return false;
}
@@ -160,15 +161,15 @@ public:
using Addresses = std::unordered_set<CAddrInfo, CAddrInfoHasher, CAddrInfoEq>;
- const size_t num_addresses{mapInfo.size()};
+ const size_t num_addresses{m_impl->mapInfo.size()};
Addresses addresses{num_addresses, addrinfo_hasher, addrinfo_eq};
- for (const auto& [id, addr] : mapInfo) {
+ for (const auto& [id, addr] : m_impl->mapInfo) {
addresses.insert(addr);
}
Addresses other_addresses{num_addresses, addrinfo_hasher, addrinfo_eq};
- for (const auto& [id, addr] : other.mapInfo) {
+ for (const auto& [id, addr] : other.m_impl->mapInfo) {
other_addresses.insert(addr);
}
@@ -176,14 +177,14 @@ public:
return false;
}
- auto IdsReferToSameAddress = [&](int id, int other_id) EXCLUSIVE_LOCKS_REQUIRED(cs, other.cs) {
+ auto IdsReferToSameAddress = [&](int id, int other_id) EXCLUSIVE_LOCKS_REQUIRED(m_impl->cs, other.m_impl->cs) {
if (id == -1 && other_id == -1) {
return true;
}
if ((id == -1 && other_id != -1) || (id != -1 && other_id == -1)) {
return false;
}
- return mapInfo.at(id) == other.mapInfo.at(other_id);
+ return m_impl->mapInfo.at(id) == other.m_impl->mapInfo.at(other_id);
};
// Check that `vvNew` contains the same addresses as `other.vvNew`. Notice - `vvNew[i][j]`
@@ -191,7 +192,7 @@ public:
// themselves may differ between `vvNew` and `other.vvNew`.
for (size_t i = 0; i < ADDRMAN_NEW_BUCKET_COUNT; ++i) {
for (size_t j = 0; j < ADDRMAN_BUCKET_SIZE; ++j) {
- if (!IdsReferToSameAddress(vvNew[i][j], other.vvNew[i][j])) {
+ if (!IdsReferToSameAddress(m_impl->vvNew[i][j], other.m_impl->vvNew[i][j])) {
return false;
}
}
@@ -200,7 +201,7 @@ public:
// Same for `vvTried`.
for (size_t i = 0; i < ADDRMAN_TRIED_BUCKET_COUNT; ++i) {
for (size_t j = 0; j < ADDRMAN_BUCKET_SIZE; ++j) {
- if (!IdsReferToSameAddress(vvTried[i][j], other.vvTried[i][j])) {
+ if (!IdsReferToSameAddress(m_impl->vvTried[i][j], other.m_impl->vvTried[i][j])) {
return false;
}
}