merge bitcoin#22910: Encapsulate asmap in NetGroupManager

This commit is contained in:
Kittywhiskers Van Gogh 2024-09-01 17:07:46 +00:00
parent 8020bfa8c1
commit f032119456
No known key found for this signature in database
GPG Key ID: 30CD0C065E5C4AAD
25 changed files with 387 additions and 305 deletions

View File

@ -266,6 +266,7 @@ BITCOIN_CORE_H = \
netaddress.h \ netaddress.h \
netbase.h \ netbase.h \
netfulfilledman.h \ netfulfilledman.h \
netgroup.h \
netmessagemaker.h \ netmessagemaker.h \
node/blockstorage.h \ node/blockstorage.h \
node/coin.h \ node/coin.h \
@ -490,6 +491,7 @@ libbitcoin_server_a_SOURCES = \
miner.cpp \ miner.cpp \
net.cpp \ net.cpp \
netfulfilledman.cpp \ netfulfilledman.cpp \
netgroup.cpp \
net_processing.cpp \ net_processing.cpp \
node/blockstorage.cpp \ node/blockstorage.cpp \
node/coin.cpp \ node/coin.cpp \

View File

@ -12,6 +12,7 @@
#include <hash.h> #include <hash.h>
#include <logging/timer.h> #include <logging/timer.h>
#include <netbase.h> #include <netbase.h>
#include <netgroup.h>
#include <random.h> #include <random.h>
#include <streams.h> #include <streams.h>
#include <tinyformat.h> #include <tinyformat.h>
@ -182,10 +183,10 @@ void ReadFromStream(AddrMan& addr, CDataStream& ssPeers)
DeserializeDB(ssPeers, addr, false); DeserializeDB(ssPeers, addr, false);
} }
std::optional<bilingual_str> LoadAddrman(const std::vector<bool>& asmap, const ArgsManager& args, std::unique_ptr<AddrMan>& addrman) std::optional<bilingual_str> LoadAddrman(const NetGroupManager& netgroupman, const ArgsManager& args, std::unique_ptr<AddrMan>& addrman)
{ {
auto check_addrman = std::clamp<int32_t>(args.GetArg("-checkaddrman", DEFAULT_ADDRMAN_CONSISTENCY_CHECKS), 0, 1000000); auto check_addrman = std::clamp<int32_t>(args.GetArg("-checkaddrman", DEFAULT_ADDRMAN_CONSISTENCY_CHECKS), 0, 1000000);
addrman = std::make_unique<AddrMan>(asmap, /* deterministic */ false, /* consistency_check_ratio */ check_addrman); addrman = std::make_unique<AddrMan>(netgroupman, /*deterministic=*/false, /*consistency_check_ratio=*/check_addrman);
int64_t nStart = GetTimeMillis(); int64_t nStart = GetTimeMillis();
const auto path_addr{gArgs.GetDataDirNet() / "peers.dat"}; const auto path_addr{gArgs.GetDataDirNet() / "peers.dat"};
@ -194,7 +195,7 @@ std::optional<bilingual_str> LoadAddrman(const std::vector<bool>& asmap, const A
LogPrintf("Loaded %i addresses from peers.dat %dms\n", addrman->size(), GetTimeMillis() - nStart); LogPrintf("Loaded %i addresses from peers.dat %dms\n", addrman->size(), GetTimeMillis() - nStart);
} catch (const DbNotFoundError&) { } catch (const DbNotFoundError&) {
// Addrman can be in an inconsistent state after failure, reset it // Addrman can be in an inconsistent state after failure, reset it
addrman = std::make_unique<AddrMan>(asmap, /* deterministic */ false, /* consistency_check_ratio */ check_addrman); addrman = std::make_unique<AddrMan>(netgroupman, /*deterministic=*/false, /*consistency_check_ratio=*/check_addrman);
LogPrintf("Creating peers.dat because the file was not found (%s)\n", fs::quoted(fs::PathToString(path_addr))); LogPrintf("Creating peers.dat because the file was not found (%s)\n", fs::quoted(fs::PathToString(path_addr)));
DumpPeerAddresses(args, *addrman); DumpPeerAddresses(args, *addrman);
} catch (const DbInconsistentError& e) { } catch (const DbInconsistentError& e) {
@ -203,7 +204,7 @@ std::optional<bilingual_str> LoadAddrman(const std::vector<bool>& asmap, const A
// with frequent corruption, we are restoring old behaviour that does the same, silently. // with frequent corruption, we are restoring old behaviour that does the same, silently.
// //
// TODO: Evaluate cause and fix, revert this change at some point. // TODO: Evaluate cause and fix, revert this change at some point.
addrman = std::make_unique<AddrMan>(asmap, /* deterministic */ false, /* consistency_check_ratio */ check_addrman); addrman = std::make_unique<AddrMan>(netgroupman, /*deterministic=*/false, /*consistency_check_ratio=*/check_addrman);
LogPrintf("Creating peers.dat because of invalid or corrupt file (%s)\n", e.what()); LogPrintf("Creating peers.dat because of invalid or corrupt file (%s)\n", e.what());
DumpPeerAddresses(args, *addrman); DumpPeerAddresses(args, *addrman);
} catch (const InvalidAddrManVersionError&) { } catch (const InvalidAddrManVersionError&) {
@ -212,7 +213,7 @@ std::optional<bilingual_str> LoadAddrman(const std::vector<bool>& asmap, const A
return strprintf(_("Failed to rename invalid peers.dat file. Please move or delete it and try again.")); return strprintf(_("Failed to rename invalid peers.dat file. Please move or delete it and try again."));
} }
// Addrman can be in an inconsistent state after failure, reset it // Addrman can be in an inconsistent state after failure, reset it
addrman = std::make_unique<AddrMan>(asmap, /* deterministic */ false, /* consistency_check_ratio */ check_addrman); addrman = std::make_unique<AddrMan>(netgroupman, /*deterministic=*/false, /*consistency_check_ratio=*/check_addrman);
LogPrintf("Creating new peers.dat because the file version was not compatible (%s). Original backed up to peers.dat.bak\n", fs::quoted(fs::PathToString(path_addr))); LogPrintf("Creating new peers.dat because the file version was not compatible (%s). Original backed up to peers.dat.bak\n", fs::quoted(fs::PathToString(path_addr)));
DumpPeerAddresses(args, *addrman); DumpPeerAddresses(args, *addrman);
} catch (const std::exception& e) { } catch (const std::exception& e) {

View File

@ -18,6 +18,7 @@ class ArgsManager;
class AddrMan; class AddrMan;
class CAddress; class CAddress;
class CDataStream; class CDataStream;
class NetGroupManager;
struct bilingual_str; struct bilingual_str;
bool DumpPeerAddresses(const ArgsManager& args, const AddrMan& addr); bool DumpPeerAddresses(const ArgsManager& args, const AddrMan& addr);
@ -49,7 +50,7 @@ public:
}; };
/** Returns an error string on failure */ /** Returns an error string on failure */
std::optional<bilingual_str> LoadAddrman(const std::vector<bool>& asmap, const ArgsManager& args, std::unique_ptr<AddrMan>& addrman); std::optional<bilingual_str> LoadAddrman(const NetGroupManager& netgroupman, const ArgsManager& args, std::unique_ptr<AddrMan>& addrman);
/** /**
* Dump the anchor IP address database (anchors.dat) * Dump the anchor IP address database (anchors.dat)

View File

@ -43,17 +43,17 @@ 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 */ /** 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 static constexpr int64_t ADDRMAN_TEST_WINDOW{40*60}; // 40 minutes
int AddrInfo::GetTriedBucket(const uint256& nKey, const std::vector<bool>& asmap) const int AddrInfo::GetTriedBucket(const uint256& nKey, const NetGroupManager& netgroupman) const
{ {
uint64_t hash1 = (CHashWriter(SER_GETHASH, 0) << nKey << GetKey()).GetCheapHash(); 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(); uint64_t hash2 = (CHashWriter(SER_GETHASH, 0) << nKey << netgroupman.GetGroup(*this) << (hash1 % ADDRMAN_TRIED_BUCKETS_PER_GROUP)).GetCheapHash();
return hash2 % ADDRMAN_TRIED_BUCKET_COUNT; return hash2 % ADDRMAN_TRIED_BUCKET_COUNT;
} }
int AddrInfo::GetNewBucket(const uint256& nKey, const CNetAddr& src, const std::vector<bool>& asmap) const int AddrInfo::GetNewBucket(const uint256& nKey, const CNetAddr& src, const NetGroupManager& netgroupman) const
{ {
std::vector<unsigned char> vchSourceGroupKey = src.GetGroup(asmap); std::vector<unsigned char> vchSourceGroupKey = netgroupman.GetGroup(src);
uint64_t hash1 = (CHashWriter(SER_GETHASH, 0) << nKey << GetGroup(asmap) << vchSourceGroupKey).GetCheapHash(); uint64_t hash1 = (CHashWriter(SER_GETHASH, 0) << nKey << netgroupman.GetGroup(*this) << vchSourceGroupKey).GetCheapHash();
uint64_t hash2 = (CHashWriter(SER_GETHASH, 0) << nKey << vchSourceGroupKey << (hash1 % ADDRMAN_NEW_BUCKETS_PER_SOURCE_GROUP)).GetCheapHash(); uint64_t hash2 = (CHashWriter(SER_GETHASH, 0) << nKey << vchSourceGroupKey << (hash1 % ADDRMAN_NEW_BUCKETS_PER_SOURCE_GROUP)).GetCheapHash();
return hash2 % ADDRMAN_NEW_BUCKET_COUNT; return hash2 % ADDRMAN_NEW_BUCKET_COUNT;
} }
@ -101,11 +101,11 @@ double AddrInfo::GetChance(int64_t nNow) const
return fChance; return fChance;
} }
AddrManImpl::AddrManImpl(std::vector<bool>&& asmap, bool deterministic, int32_t consistency_check_ratio) AddrManImpl::AddrManImpl(const NetGroupManager& netgroupman, bool deterministic, int32_t consistency_check_ratio)
: insecure_rand{deterministic} : insecure_rand{deterministic}
, nKey{deterministic ? uint256{1} : insecure_rand.rand256()} , nKey{deterministic ? uint256{1} : insecure_rand.rand256()}
, m_consistency_check_ratio{consistency_check_ratio} , m_consistency_check_ratio{consistency_check_ratio}
, m_asmap{std::move(asmap)} , m_netgroupman{netgroupman}
{ {
for (auto& bucket : vvNew) { for (auto& bucket : vvNew) {
for (auto& entry : bucket) { for (auto& entry : bucket) {
@ -220,11 +220,7 @@ void AddrManImpl::Serialize(Stream& s_) const
} }
// Store asmap checksum after bucket entries so that it // Store asmap checksum after bucket entries so that it
// can be ignored by older clients for backward compatibility. // can be ignored by older clients for backward compatibility.
uint256 asmap_checksum; s << m_netgroupman.GetAsmapChecksum();
if (m_asmap.size() != 0) {
asmap_checksum = SerializeHash(m_asmap);
}
s << asmap_checksum;
} }
template <typename Stream> template <typename Stream>
@ -294,7 +290,7 @@ void AddrManImpl::Unserialize(Stream& s_)
for (int n = 0; n < nTried; n++) { for (int n = 0; n < nTried; n++) {
AddrInfo info; AddrInfo info;
s >> info; s >> info;
int nKBucket = info.GetTriedBucket(nKey, m_asmap); int nKBucket = info.GetTriedBucket(nKey, m_netgroupman);
int nKBucketPos = info.GetBucketPosition(nKey, false, nKBucket); int nKBucketPos = info.GetBucketPosition(nKey, false, nKBucket);
if (info.IsValid() if (info.IsValid()
&& vvTried[nKBucket][nKBucketPos] == -1) { && vvTried[nKBucket][nKBucketPos] == -1) {
@ -331,10 +327,7 @@ void AddrManImpl::Unserialize(Stream& s_)
// If the bucket count and asmap checksum haven't changed, then attempt // If the bucket count and asmap checksum haven't changed, then attempt
// to restore the entries to the buckets/positions they were in before // to restore the entries to the buckets/positions they were in before
// serialization. // serialization.
uint256 supplied_asmap_checksum; uint256 supplied_asmap_checksum{m_netgroupman.GetAsmapChecksum()};
if (m_asmap.size() != 0) {
supplied_asmap_checksum = SerializeHash(m_asmap);
}
uint256 serialized_asmap_checksum; uint256 serialized_asmap_checksum;
if (format >= Format::V2_ASMAP) { if (format >= Format::V2_ASMAP) {
s >> serialized_asmap_checksum; s >> serialized_asmap_checksum;
@ -367,7 +360,7 @@ void AddrManImpl::Unserialize(Stream& s_)
} else { } else {
// In case the new table data cannot be used (bucket count wrong or new asmap), // In case the new table data cannot be used (bucket count wrong or new asmap),
// try to give them a reference based on their primary source address. // try to give them a reference based on their primary source address.
bucket = info.GetNewBucket(nKey, m_asmap); bucket = info.GetNewBucket(nKey, m_netgroupman);
bucket_position = info.GetBucketPosition(nKey, true, bucket); bucket_position = info.GetBucketPosition(nKey, true, bucket);
if (vvNew[bucket][bucket_position] == -1) { if (vvNew[bucket][bucket_position] == -1) {
vvNew[bucket][bucket_position] = entry_index; vvNew[bucket][bucket_position] = entry_index;
@ -489,7 +482,7 @@ void AddrManImpl::MakeTried(AddrInfo& info, int nId)
AssertLockHeld(cs); AssertLockHeld(cs);
// remove the entry from all new buckets // remove the entry from all new buckets
const int start_bucket{info.GetNewBucket(nKey, m_asmap)}; const int start_bucket{info.GetNewBucket(nKey, m_netgroupman)};
for (int n = 0; n < ADDRMAN_NEW_BUCKET_COUNT; ++n) { for (int n = 0; n < ADDRMAN_NEW_BUCKET_COUNT; ++n) {
const int bucket{(start_bucket + n) % ADDRMAN_NEW_BUCKET_COUNT}; const int bucket{(start_bucket + n) % ADDRMAN_NEW_BUCKET_COUNT};
const int pos{info.GetBucketPosition(nKey, true, bucket)}; const int pos{info.GetBucketPosition(nKey, true, bucket)};
@ -504,7 +497,7 @@ void AddrManImpl::MakeTried(AddrInfo& info, int nId)
assert(info.nRefCount == 0); assert(info.nRefCount == 0);
// which tried bucket to move the entry to // which tried bucket to move the entry to
int nKBucket = info.GetTriedBucket(nKey, m_asmap); int nKBucket = info.GetTriedBucket(nKey, m_netgroupman);
int nKBucketPos = info.GetBucketPosition(nKey, false, nKBucket); int nKBucketPos = info.GetBucketPosition(nKey, false, nKBucket);
// first make space to add it (the existing tried entry there is moved to new, deleting whatever is there). // first make space to add it (the existing tried entry there is moved to new, deleting whatever is there).
@ -520,7 +513,7 @@ void AddrManImpl::MakeTried(AddrInfo& info, int nId)
nTried--; nTried--;
// find which new bucket it belongs to // find which new bucket it belongs to
int nUBucket = infoOld.GetNewBucket(nKey, m_asmap); int nUBucket = infoOld.GetNewBucket(nKey, m_netgroupman);
int nUBucketPos = infoOld.GetBucketPosition(nKey, true, nUBucket); int nUBucketPos = infoOld.GetBucketPosition(nKey, true, nUBucket);
ClearNew(nUBucket, nUBucketPos); ClearNew(nUBucket, nUBucketPos);
assert(vvNew[nUBucket][nUBucketPos] == -1); assert(vvNew[nUBucket][nUBucketPos] == -1);
@ -590,7 +583,7 @@ bool AddrManImpl::AddSingle(const CAddress& addr, const CNetAddr& source, int64_
nNew++; nNew++;
} }
int nUBucket = pinfo->GetNewBucket(nKey, source, m_asmap); int nUBucket = pinfo->GetNewBucket(nKey, source, m_netgroupman);
int nUBucketPos = pinfo->GetBucketPosition(nKey, true, nUBucket); int nUBucketPos = pinfo->GetBucketPosition(nKey, true, nUBucket);
bool fInsert = vvNew[nUBucket][nUBucketPos] == -1; bool fInsert = vvNew[nUBucket][nUBucketPos] == -1;
if (vvNew[nUBucket][nUBucketPos] != nId) { if (vvNew[nUBucket][nUBucketPos] != nId) {
@ -606,7 +599,7 @@ bool AddrManImpl::AddSingle(const CAddress& addr, const CNetAddr& source, int64_
pinfo->nRefCount++; pinfo->nRefCount++;
vvNew[nUBucket][nUBucketPos] = nId; vvNew[nUBucket][nUBucketPos] = nId;
LogPrint(BCLog::ADDRMAN, "Added %s mapped to AS%i to new[%i][%i]\n", LogPrint(BCLog::ADDRMAN, "Added %s mapped to AS%i to new[%i][%i]\n",
addr.ToString(), addr.GetMappedAS(m_asmap), nUBucket, nUBucketPos); addr.ToString(), m_netgroupman.GetMappedAS(addr), nUBucket, nUBucketPos);
} else { } else {
if (pinfo->nRefCount == 0) { if (pinfo->nRefCount == 0) {
Delete(nId); Delete(nId);
@ -646,7 +639,7 @@ bool AddrManImpl::Good_(const CService& addr, bool test_before_evict, int64_t nT
// which tried bucket to move the entry to // which tried bucket to move the entry to
int tried_bucket = info.GetTriedBucket(nKey, m_asmap); int tried_bucket = info.GetTriedBucket(nKey, m_netgroupman);
int tried_bucket_pos = info.GetBucketPosition(nKey, false, tried_bucket); int tried_bucket_pos = info.GetBucketPosition(nKey, false, tried_bucket);
// Will moving this address into tried evict another entry? // Will moving this address into tried evict another entry?
@ -668,7 +661,7 @@ bool AddrManImpl::Good_(const CService& addr, bool test_before_evict, int64_t nT
MakeTried(info, nId); MakeTried(info, nId);
if (fLogIPs) { if (fLogIPs) {
LogPrint(BCLog::ADDRMAN, "Moved %s mapped to AS%i to tried[%i][%i]\n", LogPrint(BCLog::ADDRMAN, "Moved %s mapped to AS%i to tried[%i][%i]\n",
addr.ToString(), addr.GetMappedAS(m_asmap), tried_bucket, tried_bucket_pos); addr.ToString(), m_netgroupman.GetMappedAS(addr), tried_bucket, tried_bucket_pos);
} }
return true; return true;
} }
@ -874,7 +867,7 @@ void AddrManImpl::ResolveCollisions_()
AddrInfo& info_new = mapInfo[id_new]; AddrInfo& info_new = mapInfo[id_new];
// Which tried bucket to move the entry to. // Which tried bucket to move the entry to.
int tried_bucket = info_new.GetTriedBucket(nKey, m_asmap); int tried_bucket = info_new.GetTriedBucket(nKey, m_netgroupman);
int tried_bucket_pos = info_new.GetBucketPosition(nKey, false, tried_bucket); int tried_bucket_pos = info_new.GetBucketPosition(nKey, false, tried_bucket);
if (!info_new.IsValid()) { // id_new may no longer map to a valid address if (!info_new.IsValid()) { // id_new may no longer map to a valid address
erase_collision = true; erase_collision = true;
@ -942,7 +935,7 @@ std::pair<CAddress, int64_t> AddrManImpl::SelectTriedCollision_()
const AddrInfo& newInfo = mapInfo[id_new]; const AddrInfo& newInfo = mapInfo[id_new];
// which tried bucket to move the entry to // which tried bucket to move the entry to
int tried_bucket = newInfo.GetTriedBucket(nKey, m_asmap); int tried_bucket = newInfo.GetTriedBucket(nKey, m_netgroupman);
int tried_bucket_pos = newInfo.GetBucketPosition(nKey, false, tried_bucket); int tried_bucket_pos = newInfo.GetBucketPosition(nKey, false, tried_bucket);
const AddrInfo& info_old = mapInfo[vvTried[tried_bucket][tried_bucket_pos]]; const AddrInfo& info_old = mapInfo[vvTried[tried_bucket][tried_bucket_pos]];
@ -958,13 +951,13 @@ std::optional<AddressPosition> AddrManImpl::FindAddressEntry_(const CAddress& ad
if (!addr_info) return std::nullopt; if (!addr_info) return std::nullopt;
if(addr_info->fInTried) { if(addr_info->fInTried) {
int bucket{addr_info->GetTriedBucket(nKey, m_asmap)}; int bucket{addr_info->GetTriedBucket(nKey, m_netgroupman)};
return AddressPosition(/*tried_in=*/true, return AddressPosition(/*tried_in=*/true,
/*multiplicity_in=*/1, /*multiplicity_in=*/1,
/*bucket_in=*/bucket, /*bucket_in=*/bucket,
/*position_in=*/addr_info->GetBucketPosition(nKey, false, bucket)); /*position_in=*/addr_info->GetBucketPosition(nKey, false, bucket));
} else { } else {
int bucket{addr_info->GetNewBucket(nKey, m_asmap)}; int bucket{addr_info->GetNewBucket(nKey, m_netgroupman)};
return AddressPosition(/*tried_in=*/false, return AddressPosition(/*tried_in=*/false,
/*multiplicity_in=*/addr_info->nRefCount, /*multiplicity_in=*/addr_info->nRefCount,
/*bucket_in=*/bucket, /*bucket_in=*/bucket,
@ -1039,7 +1032,7 @@ int AddrManImpl::CheckAddrman() const
if (!setTried.count(vvTried[n][i])) if (!setTried.count(vvTried[n][i]))
return -11; return -11;
const auto it{mapInfo.find(vvTried[n][i])}; const auto it{mapInfo.find(vvTried[n][i])};
if (it == mapInfo.end() || it->second.GetTriedBucket(nKey, m_asmap) != n) { if (it == mapInfo.end() || it->second.GetTriedBucket(nKey, m_netgroupman) != n) {
return -17; return -17;
} }
if (it->second.GetBucketPosition(nKey, false, n) != i) { if (it->second.GetBucketPosition(nKey, false, n) != i) {
@ -1179,13 +1172,8 @@ AddrInfo AddrManImpl::GetAddressInfo(const CService& addr)
return addrRet; return addrRet;
} }
const std::vector<bool>& AddrManImpl::GetAsmap() const AddrMan::AddrMan(const NetGroupManager& netgroupman, bool deterministic, int32_t consistency_check_ratio)
{ : m_impl(std::make_unique<AddrManImpl>(netgroupman, deterministic, consistency_check_ratio)) {}
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; AddrMan::~AddrMan() = default;
@ -1264,11 +1252,6 @@ AddrInfo AddrMan::GetAddressInfo(const CService& addr)
return m_impl->GetAddressInfo(addr); return m_impl->GetAddressInfo(addr);
} }
const std::vector<bool>& AddrMan::GetAsmap() const
{
return m_impl->GetAsmap();
}
std::optional<AddressPosition> AddrMan::FindAddressEntry(const CAddress& addr) std::optional<AddressPosition> AddrMan::FindAddressEntry(const CAddress& addr)
{ {
return m_impl->FindAddressEntry(addr); return m_impl->FindAddressEntry(addr);

View File

@ -7,6 +7,7 @@
#define BITCOIN_ADDRMAN_H #define BITCOIN_ADDRMAN_H
#include <netaddress.h> #include <netaddress.h>
#include <netgroup.h>
#include <protocol.h> #include <protocol.h>
#include <streams.h> #include <streams.h>
#include <timedata.h> #include <timedata.h>
@ -99,7 +100,7 @@ protected:
const std::unique_ptr<AddrManImpl> m_impl; const std::unique_ptr<AddrManImpl> m_impl;
public: public:
explicit AddrMan(std::vector<bool> asmap, bool deterministic, int32_t consistency_check_ratio); explicit AddrMan(const NetGroupManager& netgroupman, bool deterministic, int32_t consistency_check_ratio);
~AddrMan(); ~AddrMan();
@ -186,8 +187,6 @@ public:
//! See if any to-be-evicted tried table entries have been tested and if so resolve the collisions. //! See if any to-be-evicted tried table entries have been tested and if so resolve the collisions.
AddrInfo GetAddressInfo(const CService& addr); AddrInfo GetAddressInfo(const CService& addr);
const std::vector<bool>& GetAsmap() const;
/** Test-only function /** Test-only function
* Find the address record in AddrMan and return information about its * Find the address record in AddrMan and return information about its
* position. * position.

View File

@ -76,15 +76,15 @@ public:
} }
//! Calculate in which "tried" bucket this entry belongs //! Calculate in which "tried" bucket this entry belongs
int GetTriedBucket(const uint256 &nKey, const std::vector<bool> &asmap) const; int GetTriedBucket(const uint256& nKey, const NetGroupManager& netgroupman) const;
//! Calculate in which "new" bucket this entry belongs, given a certain source //! Calculate in which "new" bucket this entry belongs, given a certain source
int GetNewBucket(const uint256 &nKey, const CNetAddr& src, const std::vector<bool> &asmap) const; int GetNewBucket(const uint256& nKey, const CNetAddr& src, const NetGroupManager& netgroupman) const;
//! Calculate in which "new" bucket this entry belongs, using its default source //! Calculate in which "new" bucket this entry belongs, using its default source
int GetNewBucket(const uint256 &nKey, const std::vector<bool> &asmap) const int GetNewBucket(const uint256& nKey, const NetGroupManager& netgroupman) const
{ {
return GetNewBucket(nKey, source, asmap); return GetNewBucket(nKey, source, netgroupman);
} }
//! Calculate in which position of a bucket to store this entry. //! Calculate in which position of a bucket to store this entry.
@ -100,7 +100,7 @@ public:
class AddrManImpl class AddrManImpl
{ {
public: public:
AddrManImpl(std::vector<bool>&& asmap, bool deterministic, int32_t consistency_check_ratio); AddrManImpl(const NetGroupManager& netgroupman, bool deterministic, int32_t consistency_check_ratio);
~AddrManImpl(); ~AddrManImpl();
@ -143,8 +143,6 @@ public:
AddrInfo GetAddressInfo(const CService& addr) AddrInfo GetAddressInfo(const CService& addr)
EXCLUSIVE_LOCKS_REQUIRED(!cs); EXCLUSIVE_LOCKS_REQUIRED(!cs);
const std::vector<bool>& GetAsmap() const;
friend class AddrManDeterministic; friend class AddrManDeterministic;
private: private:
@ -215,21 +213,8 @@ private:
/** Perform consistency checks every m_consistency_check_ratio operations (if non-zero). */ /** Perform consistency checks every m_consistency_check_ratio operations (if non-zero). */
const int32_t m_consistency_check_ratio; const int32_t m_consistency_check_ratio;
// Compressed IP->ASN mapping, loaded from a file when a node starts. /** Reference to the netgroup manager. netgroupman must be constructed before addrman and destructed after. */
// Should be always empty if no file was provided. const NetGroupManager& m_netgroupman;
// 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. //! Find an entry.
AddrInfo* Find(const CService& addr, int* pnId = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs); AddrInfo* Find(const CService& addr, int* pnId = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs);

View File

@ -4,6 +4,7 @@
#include <addrman.h> #include <addrman.h>
#include <bench/bench.h> #include <bench/bench.h>
#include <netgroup.h>
#include <random.h> #include <random.h>
#include <util/time.h> #include <util/time.h>
@ -14,7 +15,7 @@
static constexpr size_t NUM_SOURCES = 64; static constexpr size_t NUM_SOURCES = 64;
static constexpr size_t NUM_ADDRESSES_PER_SOURCE = 256; static constexpr size_t NUM_ADDRESSES_PER_SOURCE = 256;
static const std::vector<bool> EMPTY_ASMAP; static NetGroupManager EMPTY_NETGROUPMAN{std::vector<bool>()};
static constexpr uint32_t ADDRMAN_CONSISTENCY_CHECK_RATIO{0}; static constexpr uint32_t ADDRMAN_CONSISTENCY_CHECK_RATIO{0};
static std::vector<CAddress> g_sources; static std::vector<CAddress> g_sources;
@ -75,14 +76,14 @@ static void AddrManAdd(benchmark::Bench& bench)
CreateAddresses(); CreateAddresses();
bench.run([&] { bench.run([&] {
AddrMan addrman{EMPTY_ASMAP, /*deterministic=*/false, ADDRMAN_CONSISTENCY_CHECK_RATIO}; AddrMan addrman{EMPTY_NETGROUPMAN, /*deterministic=*/false, ADDRMAN_CONSISTENCY_CHECK_RATIO};
AddAddressesToAddrMan(addrman); AddAddressesToAddrMan(addrman);
}); });
} }
static void AddrManSelect(benchmark::Bench& bench) static void AddrManSelect(benchmark::Bench& bench)
{ {
AddrMan addrman{EMPTY_ASMAP, /*deterministic=*/false, ADDRMAN_CONSISTENCY_CHECK_RATIO}; AddrMan addrman{EMPTY_NETGROUPMAN, /*deterministic=*/false, ADDRMAN_CONSISTENCY_CHECK_RATIO};
FillAddrMan(addrman); FillAddrMan(addrman);
@ -94,7 +95,7 @@ static void AddrManSelect(benchmark::Bench& bench)
static void AddrManGetAddr(benchmark::Bench& bench) static void AddrManGetAddr(benchmark::Bench& bench)
{ {
AddrMan addrman{EMPTY_ASMAP, /*deterministic=*/false, ADDRMAN_CONSISTENCY_CHECK_RATIO}; AddrMan addrman{EMPTY_NETGROUPMAN, /*deterministic=*/false, ADDRMAN_CONSISTENCY_CHECK_RATIO};
FillAddrMan(addrman); FillAddrMan(addrman);
@ -123,7 +124,7 @@ static void AddrManAddThenGood(benchmark::Bench& bench)
// //
// This has some overhead (exactly the result of AddrManAdd benchmark), but that overhead is constant so improvements in // This has some overhead (exactly the result of AddrManAdd benchmark), but that overhead is constant so improvements in
// AddrMan::Good() will still be noticeable. // AddrMan::Good() will still be noticeable.
AddrMan addrman{EMPTY_ASMAP, /*deterministic=*/false, ADDRMAN_CONSISTENCY_CHECK_RATIO}; AddrMan addrman{EMPTY_NETGROUPMAN, /*deterministic=*/false, ADDRMAN_CONSISTENCY_CHECK_RATIO};
AddAddressesToAddrMan(addrman); AddAddressesToAddrMan(addrman);
markSomeAsGood(addrman); markSomeAsGood(addrman);

View File

@ -36,6 +36,7 @@
#include <net_permissions.h> #include <net_permissions.h>
#include <net_processing.h> #include <net_processing.h>
#include <netbase.h> #include <netbase.h>
#include <netgroup.h>
#include <node/blockstorage.h> #include <node/blockstorage.h>
#include <node/context.h> #include <node/context.h>
#include <node/ui_interface.h> #include <node/ui_interface.h>
@ -286,6 +287,7 @@ void PrepareShutdown(NodeContext& node)
node.connman.reset(); node.connman.reset();
node.banman.reset(); node.banman.reset();
node.addrman.reset(); node.addrman.reset();
node.netgroupman.reset();
if (node.mempool && node.mempool->IsLoaded() && node.args->GetBoolArg("-persistmempool", DEFAULT_PERSIST_MEMPOOL)) { if (node.mempool && node.mempool->IsLoaded() && node.args->GetBoolArg("-persistmempool", DEFAULT_PERSIST_MEMPOOL)) {
DumpMempool(*node.mempool); DumpMempool(*node.mempool);
@ -1522,8 +1524,6 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
const bool ignores_incoming_txs{args.GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY)}; const bool ignores_incoming_txs{args.GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY)};
{ {
// Initialize addrman
assert(!node.addrman);
// Read asmap file if configured // Read asmap file if configured
std::vector<bool> asmap; std::vector<bool> asmap;
@ -1550,8 +1550,14 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
LogPrintf("Using /16 prefix for IP bucketing\n"); LogPrintf("Using /16 prefix for IP bucketing\n");
} }
// Initialize netgroup manager
assert(!node.netgroupman);
node.netgroupman = std::make_unique<NetGroupManager>(std::move(asmap));
// Initialize addrman
assert(!node.addrman);
uiInterface.InitMessage(_("Loading P2P addresses…").translated); uiInterface.InitMessage(_("Loading P2P addresses…").translated);
if (const auto error{LoadAddrman(asmap, args, node.addrman)}) { if (const auto error{LoadAddrman(*node.netgroupman, args, node.addrman)}) {
return InitError(*error); return InitError(*error);
} }
} }
@ -1559,7 +1565,9 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
assert(!node.banman); assert(!node.banman);
node.banman = std::make_unique<BanMan>(gArgs.GetDataDirNet() / "banlist", &uiInterface, args.GetArg("-bantime", DEFAULT_MISBEHAVING_BANTIME)); node.banman = std::make_unique<BanMan>(gArgs.GetDataDirNet() / "banlist", &uiInterface, args.GetArg("-bantime", DEFAULT_MISBEHAVING_BANTIME));
assert(!node.connman); assert(!node.connman);
node.connman = std::make_unique<CConnman>(GetRand(std::numeric_limits<uint64_t>::max()), GetRand(std::numeric_limits<uint64_t>::max()), *node.addrman, args.GetBoolArg("-networkactive", true)); node.connman = std::make_unique<CConnman>(GetRand(std::numeric_limits<uint64_t>::max()),
GetRand(std::numeric_limits<uint64_t>::max()),
*node.addrman, *node.netgroupman, args.GetBoolArg("-networkactive", true));
assert(!node.fee_estimator); assert(!node.fee_estimator);
// Don't initialize fee estimation with old data if we don't relay transactions, // Don't initialize fee estimation with old data if we don't relay transactions,

View File

@ -2577,7 +2577,7 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect, CDe
case ConnectionType::BLOCK_RELAY: case ConnectionType::BLOCK_RELAY:
case ConnectionType::ADDR_FETCH: case ConnectionType::ADDR_FETCH:
case ConnectionType::FEELER: case ConnectionType::FEELER:
setConnected.insert(pnode->addr.GetGroup(addrman.GetAsmap())); setConnected.insert(m_netgroupman.GetGroup(pnode->addr));
} // no default case, so the compiler can warn about missing cases } // no default case, so the compiler can warn about missing cases
} }
} }
@ -2667,7 +2667,7 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect, CDe
m_anchors.pop_back(); m_anchors.pop_back();
if (!addr.IsValid() || IsLocal(addr) || !IsReachable(addr) || if (!addr.IsValid() || IsLocal(addr) || !IsReachable(addr) ||
!HasAllDesirableServiceFlags(addr.nServices) || !HasAllDesirableServiceFlags(addr.nServices) ||
setConnected.count(addr.GetGroup(addrman.GetAsmap()))) continue; setConnected.count(m_netgroupman.GetGroup(addr))) continue;
addrConnect = addr; addrConnect = addr;
LogPrint(BCLog::NET, "Trying to make an anchor connection to %s\n", addrConnect.ToString()); LogPrint(BCLog::NET, "Trying to make an anchor connection to %s\n", addrConnect.ToString());
break; break;
@ -2711,12 +2711,12 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect, CDe
bool isMasternode = dmn != nullptr; bool isMasternode = dmn != nullptr;
// Require outbound connections, other than feelers, to be to distinct network groups // Require outbound connections, other than feelers, to be to distinct network groups
if (!fFeeler && setConnected.count(addr.GetGroup(addrman.GetAsmap()))) { if (!fFeeler && setConnected.count(m_netgroupman.GetGroup(addr))) {
break; break;
} }
// if we selected an invalid address, restart // if we selected an invalid address, restart
if (!addr.IsValid() || setConnected.count(addr.GetGroup(addrman.GetAsmap()))) if (!addr.IsValid() || setConnected.count(m_netgroupman.GetGroup(addr)))
break; break;
// don't try to connect to masternodes that we already have a connection to (most likely inbound) // don't try to connect to masternodes that we already have a connection to (most likely inbound)
@ -3374,8 +3374,12 @@ void CConnman::SetNetworkActive(bool active, CMasternodeSync* const mn_sync)
uiInterface.NotifyNetworkActiveChanged(fNetworkActive); uiInterface.NotifyNetworkActiveChanged(fNetworkActive);
} }
CConnman::CConnman(uint64_t nSeed0In, uint64_t nSeed1In, AddrMan& addrman_in, bool network_active) : CConnman::CConnman(uint64_t nSeed0In, uint64_t nSeed1In, AddrMan& addrman_in,
addrman(addrman_in), nSeed0(nSeed0In), nSeed1(nSeed1In) const NetGroupManager& netgroupman, bool network_active)
: addrman(addrman_in)
, m_netgroupman{netgroupman}
, nSeed0(nSeed0In)
, nSeed1(nSeed1In)
{ {
SetTryNewOutboundPeer(false); SetTryNewOutboundPeer(false);
@ -3955,7 +3959,7 @@ void CConnman::GetNodeStats(std::vector<CNodeStats>& vstats) const
} }
vstats.emplace_back(); vstats.emplace_back();
pnode->CopyStats(vstats.back()); pnode->CopyStats(vstats.back());
vstats.back().m_mapped_as = pnode->addr.GetMappedAS(addrman.GetAsmap()); vstats.back().m_mapped_as = m_netgroupman.GetMappedAS(pnode->addr);
} }
} }
@ -4281,9 +4285,9 @@ CSipHasher CConnman::GetDeterministicRandomizer(uint64_t id) const
return CSipHasher(nSeed0, nSeed1).Write(id); return CSipHasher(nSeed0, nSeed1).Write(id);
} }
uint64_t CConnman::CalculateKeyedNetGroup(const CAddress& ad) const uint64_t CConnman::CalculateKeyedNetGroup(const CAddress& address) const
{ {
std::vector<unsigned char> vchNetGroup(ad.GetGroup(addrman.GetAsmap())); std::vector<unsigned char> vchNetGroup(m_netgroupman.GetGroup(address));
return GetDeterministicRandomizer(RANDOMIZER_ID_NETGROUP).Write(vchNetGroup.data(), vchNetGroup.size()).Finalize(); return GetDeterministicRandomizer(RANDOMIZER_ID_NETGROUP).Write(vchNetGroup.data(), vchNetGroup.size()).Finalize();
} }

View File

@ -18,6 +18,7 @@
#include <net_permissions.h> #include <net_permissions.h>
#include <netaddress.h> #include <netaddress.h>
#include <netbase.h> #include <netbase.h>
#include <netgroup.h>
#include <policy/feerate.h> #include <policy/feerate.h>
#include <protocol.h> #include <protocol.h>
#include <random.h> #include <random.h>
@ -915,7 +916,9 @@ public:
m_onion_binds = connOptions.onion_binds; m_onion_binds = connOptions.onion_binds;
} }
CConnman(uint64_t seed0, uint64_t seed1, AddrMan& addrman, bool network_active = true); CConnman(uint64_t seed0, uint64_t seed1, AddrMan& addrman, const NetGroupManager& netgroupman,
bool network_active = true);
~CConnman(); ~CConnman();
bool Start(CDeterministicMNManager& dmnman, CMasternodeMetaMan& mn_metaman, CMasternodeSync& mn_sync, bool Start(CDeterministicMNManager& dmnman, CMasternodeMetaMan& mn_metaman, CMasternodeSync& mn_sync,
CScheduler& scheduler, const Options& options) CScheduler& scheduler, const Options& options)
@ -1432,6 +1435,7 @@ private:
std::atomic<bool> fNetworkActive{true}; std::atomic<bool> fNetworkActive{true};
bool fAddressesInitialized{false}; bool fAddressesInitialized{false};
AddrMan& addrman; AddrMan& addrman;
const NetGroupManager& m_netgroupman;
std::deque<std::string> m_addr_fetches GUARDED_BY(m_addr_fetches_mutex); std::deque<std::string> m_addr_fetches GUARDED_BY(m_addr_fetches_mutex);
Mutex m_addr_fetches_mutex; Mutex m_addr_fetches_mutex;
std::vector<std::string> m_added_nodes GUARDED_BY(m_added_nodes_mutex); std::vector<std::string> m_added_nodes GUARDED_BY(m_added_nodes_mutex);

View File

@ -10,7 +10,6 @@
#include <hash.h> #include <hash.h>
#include <prevector.h> #include <prevector.h>
#include <tinyformat.h> #include <tinyformat.h>
#include <util/asmap.h>
#include <util/strencodings.h> #include <util/strencodings.h>
#include <util/string.h> #include <util/string.h>
@ -728,107 +727,6 @@ Network CNetAddr::GetNetClass() const
return m_net; return m_net;
} }
uint32_t CNetAddr::GetMappedAS(const std::vector<bool> &asmap) const {
uint32_t net_class = GetNetClass();
if (asmap.size() == 0 || (net_class != NET_IPV4 && net_class != NET_IPV6)) {
return 0; // Indicates not found, safe because AS0 is reserved per RFC7607.
}
std::vector<bool> ip_bits(128);
if (HasLinkedIPv4()) {
// For lookup, treat as if it was just an IPv4 address (IPV4_IN_IPV6_PREFIX + IPv4 bits)
for (int8_t byte_i = 0; byte_i < 12; ++byte_i) {
for (uint8_t bit_i = 0; bit_i < 8; ++bit_i) {
ip_bits[byte_i * 8 + bit_i] = (IPV4_IN_IPV6_PREFIX[byte_i] >> (7 - bit_i)) & 1;
}
}
uint32_t ipv4 = GetLinkedIPv4();
for (int i = 0; i < 32; ++i) {
ip_bits[96 + i] = (ipv4 >> (31 - i)) & 1;
}
} else {
// Use all 128 bits of the IPv6 address otherwise
assert(IsIPv6());
for (int8_t byte_i = 0; byte_i < 16; ++byte_i) {
uint8_t cur_byte = m_addr[byte_i];
for (uint8_t bit_i = 0; bit_i < 8; ++bit_i) {
ip_bits[byte_i * 8 + bit_i] = (cur_byte >> (7 - bit_i)) & 1;
}
}
}
uint32_t mapped_as = Interpret(asmap, ip_bits);
return mapped_as;
}
/**
* Get the canonical identifier of our network group
*
* The groups are assigned in a way where it should be costly for an attacker to
* obtain addresses with many different group identifiers, even if it is cheap
* to obtain addresses with the same identifier.
*
* @note No two connections will be attempted to addresses with the same network
* group.
*/
std::vector<unsigned char> CNetAddr::GetGroup(const std::vector<bool> &asmap) const
{
std::vector<unsigned char> vchRet;
uint32_t net_class = GetNetClass();
// If non-empty asmap is supplied and the address is IPv4/IPv6,
// return ASN to be used for bucketing.
uint32_t asn = GetMappedAS(asmap);
if (asn != 0) { // Either asmap was empty, or address has non-asmappable net class (e.g. TOR).
vchRet.push_back(NET_IPV6); // IPv4 and IPv6 with same ASN should be in the same bucket
for (int i = 0; i < 4; i++) {
vchRet.push_back((asn >> (8 * i)) & 0xFF);
}
return vchRet;
}
vchRet.push_back(net_class);
int nBits{0};
if (IsLocal()) {
// all local addresses belong to the same group
} else if (IsInternal()) {
// all internal-usage addresses get their own group
nBits = ADDR_INTERNAL_SIZE * 8;
} else if (!IsRoutable()) {
// all other unroutable addresses belong to the same group
} else if (HasLinkedIPv4()) {
// IPv4 addresses (and mapped IPv4 addresses) use /16 groups
uint32_t ipv4 = GetLinkedIPv4();
vchRet.push_back((ipv4 >> 24) & 0xFF);
vchRet.push_back((ipv4 >> 16) & 0xFF);
return vchRet;
} else if (IsTor() || IsI2P()) {
nBits = 4;
} else if (IsCJDNS()) {
// Treat in the same way as Tor and I2P because the address in all of
// them is "random" bytes (derived from a public key). However in CJDNS
// the first byte is a constant 0xfc, so the random bytes come after it.
// Thus skip the constant 8 bits at the start.
nBits = 12;
} else if (IsHeNet()) {
// for he.net, use /36 groups
nBits = 36;
} else {
// for the rest of the IPv6 network, use /32 groups
nBits = 32;
}
// Push our address onto vchRet.
const size_t num_bytes = nBits / 8;
vchRet.insert(vchRet.end(), m_addr.begin(), m_addr.begin() + num_bytes);
nBits %= 8;
// ...for the last byte, push nBits and for the rest of the byte push 1's
if (nBits > 0) {
assert(num_bytes < m_addr.size());
vchRet.push_back(m_addr[num_bytes] | ((1 << (8 - nBits)) - 1));
}
return vchRet;
}
std::vector<unsigned char> CNetAddr::GetAddrBytes() const std::vector<unsigned char> CNetAddr::GetAddrBytes() const
{ {
if (IsAddrV1Compatible()) { if (IsAddrV1Compatible()) {

View File

@ -211,12 +211,6 @@ public:
//! Whether this address has a linked IPv4 address (see GetLinkedIPv4()). //! Whether this address has a linked IPv4 address (see GetLinkedIPv4()).
bool HasLinkedIPv4() const; bool HasLinkedIPv4() const;
// The AS on the BGP path to the node we use to diversify
// peers in AddrMan bucketing based on the AS infrastructure.
// The ip->AS mapping depends on how asmap is constructed.
uint32_t GetMappedAS(const std::vector<bool> &asmap) const;
std::vector<unsigned char> GetGroup(const std::vector<bool> &asmap) const;
std::vector<unsigned char> GetAddrBytes() const; std::vector<unsigned char> GetAddrBytes() const;
int GetReachabilityFrom(const CNetAddr *paddrPartner = nullptr) const; int GetReachabilityFrom(const CNetAddr *paddrPartner = nullptr) const;

111
src/netgroup.cpp Normal file
View File

@ -0,0 +1,111 @@
// 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.
#include <netgroup.h>
#include <hash.h>
#include <util/asmap.h>
uint256 NetGroupManager::GetAsmapChecksum() const
{
if (!m_asmap.size()) return {};
return SerializeHash(m_asmap);
}
std::vector<unsigned char> NetGroupManager::GetGroup(const CNetAddr& address) const
{
std::vector<unsigned char> vchRet;
// If non-empty asmap is supplied and the address is IPv4/IPv6,
// return ASN to be used for bucketing.
uint32_t asn = GetMappedAS(address);
if (asn != 0) { // Either asmap was empty, or address has non-asmappable net class (e.g. TOR).
vchRet.push_back(NET_IPV6); // IPv4 and IPv6 with same ASN should be in the same bucket
for (int i = 0; i < 4; i++) {
vchRet.push_back((asn >> (8 * i)) & 0xFF);
}
return vchRet;
}
vchRet.push_back(address.GetNetClass());
int nStartByte{0};
int nBits{0};
if (address.IsLocal()) {
// all local addresses belong to the same group
} else if (address.IsInternal()) {
// All internal-usage addresses get their own group.
// Skip over the INTERNAL_IN_IPV6_PREFIX returned by CAddress::GetAddrBytes().
nStartByte = INTERNAL_IN_IPV6_PREFIX.size();
nBits = ADDR_INTERNAL_SIZE * 8;
} else if (!address.IsRoutable()) {
// all other unroutable addresses belong to the same group
} else if (address.HasLinkedIPv4()) {
// IPv4 addresses (and mapped IPv4 addresses) use /16 groups
uint32_t ipv4 = address.GetLinkedIPv4();
vchRet.push_back((ipv4 >> 24) & 0xFF);
vchRet.push_back((ipv4 >> 16) & 0xFF);
return vchRet;
} else if (address.IsTor() || address.IsI2P()) {
nBits = 4;
} else if (address.IsCJDNS()) {
// Treat in the same way as Tor and I2P because the address in all of
// them is "random" bytes (derived from a public key). However in CJDNS
// the first byte is a constant 0xfc, so the random bytes come after it.
// Thus skip the constant 8 bits at the start.
nBits = 12;
} else if (address.IsHeNet()) {
// for he.net, use /36 groups
nBits = 36;
} else {
// for the rest of the IPv6 network, use /32 groups
nBits = 32;
}
// Push our address onto vchRet.
auto addr_bytes = address.GetAddrBytes();
const size_t num_bytes = nBits / 8;
vchRet.insert(vchRet.end(), addr_bytes.begin() + nStartByte, addr_bytes.begin() + nStartByte + num_bytes);
nBits %= 8;
// ...for the last byte, push nBits and for the rest of the byte push 1's
if (nBits > 0) {
assert(num_bytes < addr_bytes.size());
vchRet.push_back(addr_bytes[num_bytes] | ((1 << (8 - nBits)) - 1));
}
return vchRet;
}
uint32_t NetGroupManager::GetMappedAS(const CNetAddr& address) const
{
uint32_t net_class = address.GetNetClass();
if (m_asmap.size() == 0 || (net_class != NET_IPV4 && net_class != NET_IPV6)) {
return 0; // Indicates not found, safe because AS0 is reserved per RFC7607.
}
std::vector<bool> ip_bits(128);
if (address.HasLinkedIPv4()) {
// For lookup, treat as if it was just an IPv4 address (IPV4_IN_IPV6_PREFIX + IPv4 bits)
for (int8_t byte_i = 0; byte_i < 12; ++byte_i) {
for (uint8_t bit_i = 0; bit_i < 8; ++bit_i) {
ip_bits[byte_i * 8 + bit_i] = (IPV4_IN_IPV6_PREFIX[byte_i] >> (7 - bit_i)) & 1;
}
}
uint32_t ipv4 = address.GetLinkedIPv4();
for (int i = 0; i < 32; ++i) {
ip_bits[96 + i] = (ipv4 >> (31 - i)) & 1;
}
} else {
// Use all 128 bits of the IPv6 address otherwise
assert(address.IsIPv6());
auto addr_bytes = address.GetAddrBytes();
for (int8_t byte_i = 0; byte_i < 16; ++byte_i) {
uint8_t cur_byte = addr_bytes[byte_i];
for (uint8_t bit_i = 0; bit_i < 8; ++bit_i) {
ip_bits[byte_i * 8 + bit_i] = (cur_byte >> (7 - bit_i)) & 1;
}
}
}
uint32_t mapped_as = Interpret(m_asmap, ip_bits);
return mapped_as;
}

66
src/netgroup.h Normal file
View File

@ -0,0 +1,66 @@
// 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_NETGROUP_H
#define BITCOIN_NETGROUP_H
#include <netaddress.h>
#include <uint256.h>
#include <vector>
/**
* Netgroup manager
*/
class NetGroupManager {
public:
explicit NetGroupManager(std::vector<bool> asmap)
: m_asmap{std::move(asmap)}
{}
/** Get a checksum identifying the asmap being used. */
uint256 GetAsmapChecksum() const;
/**
* Get the canonical identifier of the network group for address.
*
* The groups are assigned in a way where it should be costly for an attacker to
* obtain addresses with many different group identifiers, even if it is cheap
* to obtain addresses with the same identifier.
*
* @note No two connections will be attempted to addresses with the same network
* group.
*/
std::vector<unsigned char> GetGroup(const CNetAddr& address) const;
/**
* Get the autonomous system on the BGP path to address.
*
* The ip->AS mapping depends on how asmap is constructed.
*/
uint32_t GetMappedAS(const CNetAddr& address) const;
private:
/** Compressed IP->ASN mapping, loaded from a file when a node starts.
*
* This mapping is then used for bucketing nodes in Addrman and for
* ensuring we connect to a diverse set of peers in Connman. The map is
* empty if no file was provided.
*
* 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 is provided, the existing addrman records are
* re-bucketed.
*
* This is initialized in the constructor, const, and therefore is
* thread-safe. */
const std::vector<bool> m_asmap;
};
#endif // BITCOIN_NETGROUP_H

View File

@ -22,6 +22,7 @@
#include <net.h> #include <net.h>
#include <netfulfilledman.h> #include <netfulfilledman.h>
#include <net_processing.h> #include <net_processing.h>
#include <netgroup.h>
#include <policy/fees.h> #include <policy/fees.h>
#include <scheduler.h> #include <scheduler.h>
#include <spork.h> #include <spork.h>

View File

@ -29,6 +29,7 @@ class CScheduler;
class CSporkManager; class CSporkManager;
class CTxMemPool; class CTxMemPool;
class CMNHFManager; class CMNHFManager;
class NetGroupManager;
class PeerManager; class PeerManager;
struct CJContext; struct CJContext;
struct LLMQContext; struct LLMQContext;
@ -59,6 +60,7 @@ struct NodeContext {
std::unique_ptr<AddrMan> addrman; std::unique_ptr<AddrMan> addrman;
std::unique_ptr<CConnman> connman; std::unique_ptr<CConnman> connman;
std::unique_ptr<CTxMemPool> mempool; std::unique_ptr<CTxMemPool> mempool;
std::unique_ptr<const NetGroupManager> netgroupman;
std::unique_ptr<CBlockPolicyEstimator> fee_estimator; std::unique_ptr<CBlockPolicyEstimator> fee_estimator;
std::unique_ptr<PeerManager> peerman; std::unique_ptr<PeerManager> peerman;
std::unique_ptr<ChainstateManager> chainman; std::unique_ptr<ChainstateManager> chainman;

View File

@ -21,7 +21,7 @@
using namespace std::literals; using namespace std::literals;
static const std::vector<bool> EMPTY_ASMAP; static NetGroupManager EMPTY_NETGROUPMAN{std::vector<bool>()};
static const bool DETERMINISTIC{true}; static const bool DETERMINISTIC{true};
static int32_t GetCheckRatio(const NodeContext& node_ctx) static int32_t GetCheckRatio(const NodeContext& node_ctx)
@ -60,7 +60,7 @@ BOOST_FIXTURE_TEST_SUITE(addrman_tests, BasicTestingSetup)
BOOST_AUTO_TEST_CASE(addrman_simple) BOOST_AUTO_TEST_CASE(addrman_simple)
{ {
auto addrman = std::make_unique<AddrMan>(EMPTY_ASMAP, DETERMINISTIC, GetCheckRatio(m_node)); auto addrman = std::make_unique<AddrMan>(EMPTY_NETGROUPMAN, DETERMINISTIC, GetCheckRatio(m_node));
CNetAddr source = ResolveIP("252.2.2.2"); CNetAddr source = ResolveIP("252.2.2.2");
@ -94,7 +94,7 @@ BOOST_AUTO_TEST_CASE(addrman_simple)
BOOST_CHECK(addrman->size() >= 1); BOOST_CHECK(addrman->size() >= 1);
// Test: reset addrman and test AddrMan::Add multiple addresses works as expected // Test: reset addrman and test AddrMan::Add multiple addresses works as expected
addrman = std::make_unique<AddrMan>(EMPTY_ASMAP, DETERMINISTIC, GetCheckRatio(m_node)); addrman = std::make_unique<AddrMan>(EMPTY_NETGROUPMAN, DETERMINISTIC, GetCheckRatio(m_node));
std::vector<CAddress> vAddr; std::vector<CAddress> vAddr;
vAddr.push_back(CAddress(ResolveService("250.1.1.3", 8333), NODE_NONE)); vAddr.push_back(CAddress(ResolveService("250.1.1.3", 8333), NODE_NONE));
vAddr.push_back(CAddress(ResolveService("250.1.1.4", 8333), NODE_NONE)); vAddr.push_back(CAddress(ResolveService("250.1.1.4", 8333), NODE_NONE));
@ -104,7 +104,7 @@ BOOST_AUTO_TEST_CASE(addrman_simple)
BOOST_AUTO_TEST_CASE(addrman_ports) BOOST_AUTO_TEST_CASE(addrman_ports)
{ {
auto addrman = std::make_unique<AddrMan>(EMPTY_ASMAP, DETERMINISTIC, GetCheckRatio(m_node)); auto addrman = std::make_unique<AddrMan>(EMPTY_NETGROUPMAN, DETERMINISTIC, GetCheckRatio(m_node));
CNetAddr source = ResolveIP("252.2.2.2"); CNetAddr source = ResolveIP("252.2.2.2");
@ -132,7 +132,7 @@ BOOST_AUTO_TEST_CASE(addrman_ports)
BOOST_AUTO_TEST_CASE(addrman_select) BOOST_AUTO_TEST_CASE(addrman_select)
{ {
auto addrman = std::make_unique<AddrMan>(EMPTY_ASMAP, DETERMINISTIC, GetCheckRatio(m_node)); auto addrman = std::make_unique<AddrMan>(EMPTY_NETGROUPMAN, DETERMINISTIC, GetCheckRatio(m_node));
CNetAddr source = ResolveIP("252.2.2.2"); CNetAddr source = ResolveIP("252.2.2.2");
@ -191,7 +191,7 @@ BOOST_AUTO_TEST_CASE(addrman_select)
BOOST_AUTO_TEST_CASE(addrman_new_collisions) BOOST_AUTO_TEST_CASE(addrman_new_collisions)
{ {
auto addrman = std::make_unique<AddrMan>(EMPTY_ASMAP, DETERMINISTIC, GetCheckRatio(m_node)); auto addrman = std::make_unique<AddrMan>(EMPTY_NETGROUPMAN, DETERMINISTIC, GetCheckRatio(m_node));
CNetAddr source = ResolveIP("252.2.2.2"); CNetAddr source = ResolveIP("252.2.2.2");
@ -220,7 +220,7 @@ BOOST_AUTO_TEST_CASE(addrman_new_collisions)
BOOST_AUTO_TEST_CASE(addrman_new_multiplicity) BOOST_AUTO_TEST_CASE(addrman_new_multiplicity)
{ {
auto addrman = std::make_unique<AddrMan>(EMPTY_ASMAP, DETERMINISTIC, GetCheckRatio(m_node)); auto addrman = std::make_unique<AddrMan>(EMPTY_NETGROUPMAN, DETERMINISTIC, GetCheckRatio(m_node));
CAddress addr{CAddress(ResolveService("253.3.3.3", 8333), NODE_NONE)}; CAddress addr{CAddress(ResolveService("253.3.3.3", 8333), NODE_NONE)};
int64_t start_time{GetAdjustedTime()}; int64_t start_time{GetAdjustedTime()};
addr.nTime = start_time; addr.nTime = start_time;
@ -252,7 +252,7 @@ BOOST_AUTO_TEST_CASE(addrman_new_multiplicity)
BOOST_AUTO_TEST_CASE(addrman_tried_collisions) BOOST_AUTO_TEST_CASE(addrman_tried_collisions)
{ {
auto addrman = std::make_unique<AddrMan>(EMPTY_ASMAP, DETERMINISTIC, GetCheckRatio(m_node)); auto addrman = std::make_unique<AddrMan>(EMPTY_NETGROUPMAN, DETERMINISTIC, GetCheckRatio(m_node));
CNetAddr source = ResolveIP("252.2.2.2"); CNetAddr source = ResolveIP("252.2.2.2");
@ -283,7 +283,7 @@ BOOST_AUTO_TEST_CASE(addrman_tried_collisions)
BOOST_AUTO_TEST_CASE(addrman_getaddr) BOOST_AUTO_TEST_CASE(addrman_getaddr)
{ {
auto addrman = std::make_unique<AddrMan>(EMPTY_ASMAP, DETERMINISTIC, GetCheckRatio(m_node)); auto addrman = std::make_unique<AddrMan>(EMPTY_NETGROUPMAN, DETERMINISTIC, GetCheckRatio(m_node));
// Test: Sanity check, GetAddr should never return anything if addrman // Test: Sanity check, GetAddr should never return anything if addrman
// is empty. // is empty.
@ -354,27 +354,25 @@ BOOST_AUTO_TEST_CASE(caddrinfo_get_tried_bucket_legacy)
uint256 nKey1 = (uint256)(CHashWriter(SER_GETHASH, 0) << 1).GetHash(); uint256 nKey1 = (uint256)(CHashWriter(SER_GETHASH, 0) << 1).GetHash();
uint256 nKey2 = (uint256)(CHashWriter(SER_GETHASH, 0) << 2).GetHash(); uint256 nKey2 = (uint256)(CHashWriter(SER_GETHASH, 0) << 2).GetHash();
std::vector<bool> asmap; // use /16 BOOST_CHECK_EQUAL(info1.GetTriedBucket(nKey1, EMPTY_NETGROUPMAN), 40);
BOOST_CHECK_EQUAL(info1.GetTriedBucket(nKey1, asmap), 40);
// Test: Make sure key actually randomizes bucket placement. A fail on // Test: Make sure key actually randomizes bucket placement. A fail on
// this test could be a security issue. // this test could be a security issue.
BOOST_CHECK(info1.GetTriedBucket(nKey1, asmap) != info1.GetTriedBucket(nKey2, asmap)); BOOST_CHECK(info1.GetTriedBucket(nKey1, EMPTY_NETGROUPMAN) != info1.GetTriedBucket(nKey2, EMPTY_NETGROUPMAN));
// Test: Two addresses with same IP but different ports can map to // Test: Two addresses with same IP but different ports can map to
// different buckets because they have different keys. // different buckets because they have different keys.
AddrInfo info2 = AddrInfo(addr2, source1); AddrInfo info2 = AddrInfo(addr2, source1);
BOOST_CHECK(info1.GetKey() != info2.GetKey()); BOOST_CHECK(info1.GetKey() != info2.GetKey());
BOOST_CHECK(info1.GetTriedBucket(nKey1, asmap) != info2.GetTriedBucket(nKey1, asmap)); BOOST_CHECK(info1.GetTriedBucket(nKey1, EMPTY_NETGROUPMAN) != info2.GetTriedBucket(nKey1, EMPTY_NETGROUPMAN));
std::set<int> buckets; std::set<int> buckets;
for (int i = 0; i < 255; i++) { for (int i = 0; i < 255; i++) {
AddrInfo infoi = AddrInfo( AddrInfo infoi = AddrInfo(
CAddress(ResolveService("250.1.1." + ToString(i)), NODE_NONE), CAddress(ResolveService("250.1.1." + ToString(i)), NODE_NONE),
ResolveIP("250.1.1." + ToString(i))); ResolveIP("250.1.1." + ToString(i)));
int bucket = infoi.GetTriedBucket(nKey1, asmap); int bucket = infoi.GetTriedBucket(nKey1, EMPTY_NETGROUPMAN);
buckets.insert(bucket); buckets.insert(bucket);
} }
// Test: IP addresses in the same /16 prefix should // Test: IP addresses in the same /16 prefix should
@ -386,7 +384,7 @@ BOOST_AUTO_TEST_CASE(caddrinfo_get_tried_bucket_legacy)
AddrInfo infoj = AddrInfo( AddrInfo infoj = AddrInfo(
CAddress(ResolveService("250." + ToString(j) + ".1.1"), NODE_NONE), CAddress(ResolveService("250." + ToString(j) + ".1.1"), NODE_NONE),
ResolveIP("250." + ToString(j) + ".1.1")); ResolveIP("250." + ToString(j) + ".1.1"));
int bucket = infoj.GetTriedBucket(nKey1, asmap); int bucket = infoj.GetTriedBucket(nKey1, EMPTY_NETGROUPMAN);
buckets.insert(bucket); buckets.insert(bucket);
} }
// Test: IP addresses in the different /16 prefix should map to more than // Test: IP addresses in the different /16 prefix should map to more than
@ -406,27 +404,25 @@ BOOST_AUTO_TEST_CASE(caddrinfo_get_new_bucket_legacy)
uint256 nKey1 = (uint256)(CHashWriter(SER_GETHASH, 0) << 1).GetHash(); uint256 nKey1 = (uint256)(CHashWriter(SER_GETHASH, 0) << 1).GetHash();
uint256 nKey2 = (uint256)(CHashWriter(SER_GETHASH, 0) << 2).GetHash(); uint256 nKey2 = (uint256)(CHashWriter(SER_GETHASH, 0) << 2).GetHash();
std::vector<bool> asmap; // use /16
// Test: Make sure the buckets are what we expect // Test: Make sure the buckets are what we expect
BOOST_CHECK_EQUAL(info1.GetNewBucket(nKey1, asmap), 786); BOOST_CHECK_EQUAL(info1.GetNewBucket(nKey1, EMPTY_NETGROUPMAN), 786);
BOOST_CHECK_EQUAL(info1.GetNewBucket(nKey1, source1, asmap), 786); BOOST_CHECK_EQUAL(info1.GetNewBucket(nKey1, source1, EMPTY_NETGROUPMAN), 786);
// Test: Make sure key actually randomizes bucket placement. A fail on // Test: Make sure key actually randomizes bucket placement. A fail on
// this test could be a security issue. // this test could be a security issue.
BOOST_CHECK(info1.GetNewBucket(nKey1, asmap) != info1.GetNewBucket(nKey2, asmap)); BOOST_CHECK(info1.GetNewBucket(nKey1, EMPTY_NETGROUPMAN) != info1.GetNewBucket(nKey2, EMPTY_NETGROUPMAN));
// Test: Ports should not affect bucket placement in the addr // Test: Ports should not affect bucket placement in the addr
AddrInfo info2 = AddrInfo(addr2, source1); AddrInfo info2 = AddrInfo(addr2, source1);
BOOST_CHECK(info1.GetKey() != info2.GetKey()); BOOST_CHECK(info1.GetKey() != info2.GetKey());
BOOST_CHECK_EQUAL(info1.GetNewBucket(nKey1, asmap), info2.GetNewBucket(nKey1, asmap)); BOOST_CHECK_EQUAL(info1.GetNewBucket(nKey1, EMPTY_NETGROUPMAN), info2.GetNewBucket(nKey1, EMPTY_NETGROUPMAN));
std::set<int> buckets; std::set<int> buckets;
for (int i = 0; i < 255; i++) { for (int i = 0; i < 255; i++) {
AddrInfo infoi = AddrInfo( AddrInfo infoi = AddrInfo(
CAddress(ResolveService("250.1.1." + ToString(i)), NODE_NONE), CAddress(ResolveService("250.1.1." + ToString(i)), NODE_NONE),
ResolveIP("250.1.1." + ToString(i))); ResolveIP("250.1.1." + ToString(i)));
int bucket = infoi.GetNewBucket(nKey1, asmap); int bucket = infoi.GetNewBucket(nKey1, EMPTY_NETGROUPMAN);
buckets.insert(bucket); buckets.insert(bucket);
} }
// Test: IP addresses in the same group (\16 prefix for IPv4) should // Test: IP addresses in the same group (\16 prefix for IPv4) should
@ -439,7 +435,7 @@ BOOST_AUTO_TEST_CASE(caddrinfo_get_new_bucket_legacy)
ResolveService( ResolveService(
ToString(250 + (j / 255)) + "." + ToString(j % 256) + ".1.1"), NODE_NONE), ToString(250 + (j / 255)) + "." + ToString(j % 256) + ".1.1"), NODE_NONE),
ResolveIP("251.4.1.1")); ResolveIP("251.4.1.1"));
int bucket = infoj.GetNewBucket(nKey1, asmap); int bucket = infoj.GetNewBucket(nKey1, EMPTY_NETGROUPMAN);
buckets.insert(bucket); buckets.insert(bucket);
} }
// Test: IP addresses in the same source groups should map to NO MORE // Test: IP addresses in the same source groups should map to NO MORE
@ -451,7 +447,7 @@ BOOST_AUTO_TEST_CASE(caddrinfo_get_new_bucket_legacy)
AddrInfo infoj = AddrInfo( AddrInfo infoj = AddrInfo(
CAddress(ResolveService("250.1.1.1"), NODE_NONE), CAddress(ResolveService("250.1.1.1"), NODE_NONE),
ResolveIP("250." + ToString(p) + ".1.1")); ResolveIP("250." + ToString(p) + ".1.1"));
int bucket = infoj.GetNewBucket(nKey1, asmap); int bucket = infoj.GetNewBucket(nKey1, EMPTY_NETGROUPMAN);
buckets.insert(bucket); buckets.insert(bucket);
} }
// Test: IP addresses in the different source groups should map to MORE // Test: IP addresses in the different source groups should map to MORE
@ -472,6 +468,9 @@ BOOST_AUTO_TEST_CASE(caddrinfo_get_new_bucket_legacy)
// 101.8.0.0/16 AS8 // 101.8.0.0/16 AS8
BOOST_AUTO_TEST_CASE(caddrinfo_get_tried_bucket) BOOST_AUTO_TEST_CASE(caddrinfo_get_tried_bucket)
{ {
std::vector<bool> asmap = FromBytes(raw_tests::asmap, sizeof(raw_tests::asmap) * 8);
NetGroupManager ngm_asmap{asmap};
CAddress addr1 = CAddress(ResolveService("250.1.1.1", 8333), NODE_NONE); CAddress addr1 = CAddress(ResolveService("250.1.1.1", 8333), NODE_NONE);
CAddress addr2 = CAddress(ResolveService("250.1.1.1", 9999), NODE_NONE); CAddress addr2 = CAddress(ResolveService("250.1.1.1", 9999), NODE_NONE);
@ -483,27 +482,25 @@ BOOST_AUTO_TEST_CASE(caddrinfo_get_tried_bucket)
uint256 nKey1 = (uint256)(CHashWriter(SER_GETHASH, 0) << 1).GetHash(); uint256 nKey1 = (uint256)(CHashWriter(SER_GETHASH, 0) << 1).GetHash();
uint256 nKey2 = (uint256)(CHashWriter(SER_GETHASH, 0) << 2).GetHash(); uint256 nKey2 = (uint256)(CHashWriter(SER_GETHASH, 0) << 2).GetHash();
std::vector<bool> asmap = FromBytes(raw_tests::asmap, sizeof(raw_tests::asmap) * 8); BOOST_CHECK_EQUAL(info1.GetTriedBucket(nKey1, ngm_asmap), 236);
BOOST_CHECK_EQUAL(info1.GetTriedBucket(nKey1, asmap), 236);
// Test: Make sure key actually randomizes bucket placement. A fail on // Test: Make sure key actually randomizes bucket placement. A fail on
// this test could be a security issue. // this test could be a security issue.
BOOST_CHECK(info1.GetTriedBucket(nKey1, asmap) != info1.GetTriedBucket(nKey2, asmap)); BOOST_CHECK(info1.GetTriedBucket(nKey1, ngm_asmap) != info1.GetTriedBucket(nKey2, ngm_asmap));
// Test: Two addresses with same IP but different ports can map to // Test: Two addresses with same IP but different ports can map to
// different buckets because they have different keys. // different buckets because they have different keys.
AddrInfo info2 = AddrInfo(addr2, source1); AddrInfo info2 = AddrInfo(addr2, source1);
BOOST_CHECK(info1.GetKey() != info2.GetKey()); BOOST_CHECK(info1.GetKey() != info2.GetKey());
BOOST_CHECK(info1.GetTriedBucket(nKey1, asmap) != info2.GetTriedBucket(nKey1, asmap)); BOOST_CHECK(info1.GetTriedBucket(nKey1, ngm_asmap) != info2.GetTriedBucket(nKey1, ngm_asmap));
std::set<int> buckets; std::set<int> buckets;
for (int j = 0; j < 255; j++) { for (int j = 0; j < 255; j++) {
AddrInfo infoj = AddrInfo( AddrInfo infoj = AddrInfo(
CAddress(ResolveService("101." + ToString(j) + ".1.1"), NODE_NONE), CAddress(ResolveService("101." + ToString(j) + ".1.1"), NODE_NONE),
ResolveIP("101." + ToString(j) + ".1.1")); ResolveIP("101." + ToString(j) + ".1.1"));
int bucket = infoj.GetTriedBucket(nKey1, asmap); int bucket = infoj.GetTriedBucket(nKey1, ngm_asmap);
buckets.insert(bucket); buckets.insert(bucket);
} }
// Test: IP addresses in the different /16 prefix MAY map to more than // Test: IP addresses in the different /16 prefix MAY map to more than
@ -515,7 +512,7 @@ BOOST_AUTO_TEST_CASE(caddrinfo_get_tried_bucket)
AddrInfo infoj = AddrInfo( AddrInfo infoj = AddrInfo(
CAddress(ResolveService("250." + ToString(j) + ".1.1"), NODE_NONE), CAddress(ResolveService("250." + ToString(j) + ".1.1"), NODE_NONE),
ResolveIP("250." + ToString(j) + ".1.1")); ResolveIP("250." + ToString(j) + ".1.1"));
int bucket = infoj.GetTriedBucket(nKey1, asmap); int bucket = infoj.GetTriedBucket(nKey1, ngm_asmap);
buckets.insert(bucket); buckets.insert(bucket);
} }
// Test: IP addresses in the different /16 prefix MAY NOT map to more than // Test: IP addresses in the different /16 prefix MAY NOT map to more than
@ -525,6 +522,9 @@ BOOST_AUTO_TEST_CASE(caddrinfo_get_tried_bucket)
BOOST_AUTO_TEST_CASE(caddrinfo_get_new_bucket) BOOST_AUTO_TEST_CASE(caddrinfo_get_new_bucket)
{ {
std::vector<bool> asmap = FromBytes(raw_tests::asmap, sizeof(raw_tests::asmap) * 8);
NetGroupManager ngm_asmap{asmap};
CAddress addr1 = CAddress(ResolveService("250.1.2.1", 8333), NODE_NONE); CAddress addr1 = CAddress(ResolveService("250.1.2.1", 8333), NODE_NONE);
CAddress addr2 = CAddress(ResolveService("250.1.2.1", 9999), NODE_NONE); CAddress addr2 = CAddress(ResolveService("250.1.2.1", 9999), NODE_NONE);
@ -535,27 +535,25 @@ BOOST_AUTO_TEST_CASE(caddrinfo_get_new_bucket)
uint256 nKey1 = (uint256)(CHashWriter(SER_GETHASH, 0) << 1).GetHash(); uint256 nKey1 = (uint256)(CHashWriter(SER_GETHASH, 0) << 1).GetHash();
uint256 nKey2 = (uint256)(CHashWriter(SER_GETHASH, 0) << 2).GetHash(); uint256 nKey2 = (uint256)(CHashWriter(SER_GETHASH, 0) << 2).GetHash();
std::vector<bool> asmap = FromBytes(raw_tests::asmap, sizeof(raw_tests::asmap) * 8);
// Test: Make sure the buckets are what we expect // Test: Make sure the buckets are what we expect
BOOST_CHECK_EQUAL(info1.GetNewBucket(nKey1, asmap), 795); BOOST_CHECK_EQUAL(info1.GetNewBucket(nKey1, ngm_asmap), 795);
BOOST_CHECK_EQUAL(info1.GetNewBucket(nKey1, source1, asmap), 795); BOOST_CHECK_EQUAL(info1.GetNewBucket(nKey1, source1, ngm_asmap), 795);
// Test: Make sure key actually randomizes bucket placement. A fail on // Test: Make sure key actually randomizes bucket placement. A fail on
// this test could be a security issue. // this test could be a security issue.
BOOST_CHECK(info1.GetNewBucket(nKey1, asmap) != info1.GetNewBucket(nKey2, asmap)); BOOST_CHECK(info1.GetNewBucket(nKey1, ngm_asmap) != info1.GetNewBucket(nKey2, ngm_asmap));
// Test: Ports should not affect bucket placement in the addr // Test: Ports should not affect bucket placement in the addr
AddrInfo info2 = AddrInfo(addr2, source1); AddrInfo info2 = AddrInfo(addr2, source1);
BOOST_CHECK(info1.GetKey() != info2.GetKey()); BOOST_CHECK(info1.GetKey() != info2.GetKey());
BOOST_CHECK_EQUAL(info1.GetNewBucket(nKey1, asmap), info2.GetNewBucket(nKey1, asmap)); BOOST_CHECK_EQUAL(info1.GetNewBucket(nKey1, ngm_asmap), info2.GetNewBucket(nKey1, ngm_asmap));
std::set<int> buckets; std::set<int> buckets;
for (int i = 0; i < 255; i++) { for (int i = 0; i < 255; i++) {
AddrInfo infoi = AddrInfo( AddrInfo infoi = AddrInfo(
CAddress(ResolveService("250.1.1." + ToString(i)), NODE_NONE), CAddress(ResolveService("250.1.1." + ToString(i)), NODE_NONE),
ResolveIP("250.1.1." + ToString(i))); ResolveIP("250.1.1." + ToString(i)));
int bucket = infoi.GetNewBucket(nKey1, asmap); int bucket = infoi.GetNewBucket(nKey1, ngm_asmap);
buckets.insert(bucket); buckets.insert(bucket);
} }
// Test: IP addresses in the same /16 prefix // Test: IP addresses in the same /16 prefix
@ -568,7 +566,7 @@ BOOST_AUTO_TEST_CASE(caddrinfo_get_new_bucket)
ResolveService( ResolveService(
ToString(250 + (j / 255)) + "." + ToString(j % 256) + ".1.1"), NODE_NONE), ToString(250 + (j / 255)) + "." + ToString(j % 256) + ".1.1"), NODE_NONE),
ResolveIP("251.4.1.1")); ResolveIP("251.4.1.1"));
int bucket = infoj.GetNewBucket(nKey1, asmap); int bucket = infoj.GetNewBucket(nKey1, ngm_asmap);
buckets.insert(bucket); buckets.insert(bucket);
} }
// Test: IP addresses in the same source /16 prefix should not map to more // Test: IP addresses in the same source /16 prefix should not map to more
@ -580,7 +578,7 @@ BOOST_AUTO_TEST_CASE(caddrinfo_get_new_bucket)
AddrInfo infoj = AddrInfo( AddrInfo infoj = AddrInfo(
CAddress(ResolveService("250.1.1.1"), NODE_NONE), CAddress(ResolveService("250.1.1.1"), NODE_NONE),
ResolveIP("101." + ToString(p) + ".1.1")); ResolveIP("101." + ToString(p) + ".1.1"));
int bucket = infoj.GetNewBucket(nKey1, asmap); int bucket = infoj.GetNewBucket(nKey1, ngm_asmap);
buckets.insert(bucket); buckets.insert(bucket);
} }
// Test: IP addresses in the different source /16 prefixes usually map to MORE // Test: IP addresses in the different source /16 prefixes usually map to MORE
@ -592,7 +590,7 @@ BOOST_AUTO_TEST_CASE(caddrinfo_get_new_bucket)
AddrInfo infoj = AddrInfo( AddrInfo infoj = AddrInfo(
CAddress(ResolveService("250.1.1.1"), NODE_NONE), CAddress(ResolveService("250.1.1.1"), NODE_NONE),
ResolveIP("250." + ToString(p) + ".1.1")); ResolveIP("250." + ToString(p) + ".1.1"));
int bucket = infoj.GetNewBucket(nKey1, asmap); int bucket = infoj.GetNewBucket(nKey1, ngm_asmap);
buckets.insert(bucket); buckets.insert(bucket);
} }
// Test: IP addresses in the different source /16 prefixes sometimes map to NO MORE // Test: IP addresses in the different source /16 prefixes sometimes map to NO MORE
@ -603,11 +601,12 @@ BOOST_AUTO_TEST_CASE(caddrinfo_get_new_bucket)
BOOST_AUTO_TEST_CASE(addrman_serialization) BOOST_AUTO_TEST_CASE(addrman_serialization)
{ {
std::vector<bool> asmap1 = FromBytes(raw_tests::asmap, sizeof(raw_tests::asmap) * 8); std::vector<bool> asmap1 = FromBytes(raw_tests::asmap, sizeof(raw_tests::asmap) * 8);
NetGroupManager netgroupman{asmap1};
const auto ratio = GetCheckRatio(m_node); const auto ratio = GetCheckRatio(m_node);
auto addrman_asmap1 = std::make_unique<AddrMan>(asmap1, DETERMINISTIC, ratio); auto addrman_asmap1 = std::make_unique<AddrMan>(netgroupman, DETERMINISTIC, ratio);
auto addrman_asmap1_dup = std::make_unique<AddrMan>(asmap1, DETERMINISTIC, ratio); auto addrman_asmap1_dup = std::make_unique<AddrMan>(netgroupman, DETERMINISTIC, ratio);
auto addrman_noasmap = std::make_unique<AddrMan>(EMPTY_ASMAP, DETERMINISTIC, ratio); auto addrman_noasmap = std::make_unique<AddrMan>(EMPTY_NETGROUPMAN, DETERMINISTIC, ratio);
CDataStream stream(SER_NETWORK, PROTOCOL_VERSION); CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
@ -636,8 +635,8 @@ BOOST_AUTO_TEST_CASE(addrman_serialization)
BOOST_CHECK(addr_pos1.position != addr_pos3.position); BOOST_CHECK(addr_pos1.position != addr_pos3.position);
// deserializing non-asmaped peers.dat to asmaped addrman // deserializing non-asmaped peers.dat to asmaped addrman
addrman_asmap1 = std::make_unique<AddrMan>(asmap1, DETERMINISTIC, ratio); addrman_asmap1 = std::make_unique<AddrMan>(netgroupman, DETERMINISTIC, ratio);
addrman_noasmap = std::make_unique<AddrMan>(EMPTY_ASMAP, DETERMINISTIC, ratio); addrman_noasmap = std::make_unique<AddrMan>(EMPTY_NETGROUPMAN, DETERMINISTIC, ratio);
addrman_noasmap->Add({addr}, default_source); addrman_noasmap->Add({addr}, default_source);
stream << *addrman_noasmap; stream << *addrman_noasmap;
stream >> *addrman_asmap1; stream >> *addrman_asmap1;
@ -648,8 +647,8 @@ BOOST_AUTO_TEST_CASE(addrman_serialization)
BOOST_CHECK(addr_pos4 == addr_pos2); BOOST_CHECK(addr_pos4 == addr_pos2);
// used to map to different buckets, now maps to the same bucket. // used to map to different buckets, now maps to the same bucket.
addrman_asmap1 = std::make_unique<AddrMan>(asmap1, DETERMINISTIC, ratio); addrman_asmap1 = std::make_unique<AddrMan>(netgroupman, DETERMINISTIC, ratio);
addrman_noasmap = std::make_unique<AddrMan>(EMPTY_ASMAP, DETERMINISTIC, ratio); addrman_noasmap = std::make_unique<AddrMan>(EMPTY_NETGROUPMAN, DETERMINISTIC, ratio);
CAddress addr1 = CAddress(ResolveService("250.1.1.1"), NODE_NONE); CAddress addr1 = CAddress(ResolveService("250.1.1.1"), NODE_NONE);
CAddress addr2 = CAddress(ResolveService("250.2.1.1"), NODE_NONE); CAddress addr2 = CAddress(ResolveService("250.2.1.1"), NODE_NONE);
addrman_noasmap->Add({addr, addr2}, default_source); addrman_noasmap->Add({addr, addr2}, default_source);
@ -668,7 +667,7 @@ BOOST_AUTO_TEST_CASE(remove_invalid)
{ {
// Confirm that invalid addresses are ignored in unserialization. // Confirm that invalid addresses are ignored in unserialization.
auto addrman = std::make_unique<AddrMan>(EMPTY_ASMAP, DETERMINISTIC, GetCheckRatio(m_node)); auto addrman = std::make_unique<AddrMan>(EMPTY_NETGROUPMAN, DETERMINISTIC, GetCheckRatio(m_node));
CDataStream stream(SER_NETWORK, PROTOCOL_VERSION); CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
const CAddress new1{ResolveService("5.5.5.5"), NODE_NONE}; const CAddress new1{ResolveService("5.5.5.5"), NODE_NONE};
@ -700,14 +699,14 @@ BOOST_AUTO_TEST_CASE(remove_invalid)
BOOST_REQUIRE(pos + sizeof(tried2_raw_replacement) <= stream.size()); BOOST_REQUIRE(pos + sizeof(tried2_raw_replacement) <= stream.size());
memcpy(stream.data() + pos, tried2_raw_replacement, sizeof(tried2_raw_replacement)); memcpy(stream.data() + pos, tried2_raw_replacement, sizeof(tried2_raw_replacement));
addrman = std::make_unique<AddrMan>(EMPTY_ASMAP, DETERMINISTIC, GetCheckRatio(m_node)); addrman = std::make_unique<AddrMan>(EMPTY_NETGROUPMAN, DETERMINISTIC, GetCheckRatio(m_node));
stream >> *addrman; stream >> *addrman;
BOOST_CHECK_EQUAL(addrman->size(), 2); BOOST_CHECK_EQUAL(addrman->size(), 2);
} }
BOOST_AUTO_TEST_CASE(addrman_selecttriedcollision) BOOST_AUTO_TEST_CASE(addrman_selecttriedcollision)
{ {
auto addrman = std::make_unique<AddrMan>(EMPTY_ASMAP, DETERMINISTIC, GetCheckRatio(m_node)); auto addrman = std::make_unique<AddrMan>(EMPTY_NETGROUPMAN, DETERMINISTIC, GetCheckRatio(m_node));
BOOST_CHECK(addrman->size() == 0); BOOST_CHECK(addrman->size() == 0);
@ -740,7 +739,7 @@ BOOST_AUTO_TEST_CASE(addrman_selecttriedcollision)
BOOST_AUTO_TEST_CASE(addrman_noevict) BOOST_AUTO_TEST_CASE(addrman_noevict)
{ {
auto addrman = std::make_unique<AddrMan>(EMPTY_ASMAP, DETERMINISTIC, GetCheckRatio(m_node)); auto addrman = std::make_unique<AddrMan>(EMPTY_NETGROUPMAN, DETERMINISTIC, GetCheckRatio(m_node));
// Add 35 addresses. // Add 35 addresses.
CNetAddr source = ResolveIP("252.2.2.2"); CNetAddr source = ResolveIP("252.2.2.2");
@ -792,7 +791,7 @@ BOOST_AUTO_TEST_CASE(addrman_noevict)
BOOST_AUTO_TEST_CASE(addrman_evictionworks) BOOST_AUTO_TEST_CASE(addrman_evictionworks)
{ {
auto addrman = std::make_unique<AddrMan>(EMPTY_ASMAP, DETERMINISTIC, GetCheckRatio(m_node)); auto addrman = std::make_unique<AddrMan>(EMPTY_NETGROUPMAN, DETERMINISTIC, GetCheckRatio(m_node));
BOOST_CHECK(addrman->size() == 0); BOOST_CHECK(addrman->size() == 0);
@ -862,7 +861,7 @@ static CDataStream AddrmanToStream(const AddrMan& addrman)
BOOST_AUTO_TEST_CASE(load_addrman) BOOST_AUTO_TEST_CASE(load_addrman)
{ {
AddrMan addrman{EMPTY_ASMAP, DETERMINISTIC, GetCheckRatio(m_node)}; AddrMan addrman{EMPTY_NETGROUPMAN, DETERMINISTIC, GetCheckRatio(m_node)};
CService addr1, addr2, addr3; CService addr1, addr2, addr3;
BOOST_CHECK(Lookup("250.7.1.1", addr1, 8333, false)); BOOST_CHECK(Lookup("250.7.1.1", addr1, 8333, false));
@ -881,7 +880,7 @@ BOOST_AUTO_TEST_CASE(load_addrman)
// Test that the de-serialization does not throw an exception. // Test that the de-serialization does not throw an exception.
CDataStream ssPeers1 = AddrmanToStream(addrman); CDataStream ssPeers1 = AddrmanToStream(addrman);
bool exceptionThrown = false; bool exceptionThrown = false;
AddrMan addrman1{EMPTY_ASMAP, !DETERMINISTIC, GetCheckRatio(m_node)}; AddrMan addrman1{EMPTY_NETGROUPMAN, !DETERMINISTIC, GetCheckRatio(m_node)};
BOOST_CHECK(addrman1.size() == 0); BOOST_CHECK(addrman1.size() == 0);
try { try {
@ -898,7 +897,7 @@ BOOST_AUTO_TEST_CASE(load_addrman)
// Test that ReadFromStream creates an addrman with the correct number of addrs. // Test that ReadFromStream creates an addrman with the correct number of addrs.
CDataStream ssPeers2 = AddrmanToStream(addrman); CDataStream ssPeers2 = AddrmanToStream(addrman);
AddrMan addrman2{EMPTY_ASMAP, !DETERMINISTIC, GetCheckRatio(m_node)}; AddrMan addrman2{EMPTY_NETGROUPMAN, !DETERMINISTIC, GetCheckRatio(m_node)};
BOOST_CHECK(addrman2.size() == 0); BOOST_CHECK(addrman2.size() == 0);
ReadFromStream(addrman2, ssPeers2); ReadFromStream(addrman2, ssPeers2);
BOOST_CHECK(addrman2.size() == 3); BOOST_CHECK(addrman2.size() == 3);
@ -936,7 +935,7 @@ BOOST_AUTO_TEST_CASE(load_addrman_corrupted)
// Test that the de-serialization of corrupted peers.dat throws an exception. // Test that the de-serialization of corrupted peers.dat throws an exception.
CDataStream ssPeers1 = MakeCorruptPeersDat(); CDataStream ssPeers1 = MakeCorruptPeersDat();
bool exceptionThrown = false; bool exceptionThrown = false;
AddrMan addrman1{EMPTY_ASMAP, !DETERMINISTIC, GetCheckRatio(m_node)}; AddrMan addrman1{EMPTY_NETGROUPMAN, !DETERMINISTIC, GetCheckRatio(m_node)};
BOOST_CHECK(addrman1.size() == 0); BOOST_CHECK(addrman1.size() == 0);
try { try {
unsigned char pchMsgTmp[4]; unsigned char pchMsgTmp[4];
@ -952,7 +951,7 @@ BOOST_AUTO_TEST_CASE(load_addrman_corrupted)
// Test that ReadFromStream fails if peers.dat is corrupt // Test that ReadFromStream fails if peers.dat is corrupt
CDataStream ssPeers2 = MakeCorruptPeersDat(); CDataStream ssPeers2 = MakeCorruptPeersDat();
AddrMan addrman2{EMPTY_ASMAP, !DETERMINISTIC, GetCheckRatio(m_node)}; AddrMan addrman2{EMPTY_NETGROUPMAN, !DETERMINISTIC, GetCheckRatio(m_node)};
BOOST_CHECK(addrman2.size() == 0); BOOST_CHECK(addrman2.size() == 0);
BOOST_CHECK_THROW(ReadFromStream(addrman2, ssPeers2), std::ios_base::failure); BOOST_CHECK_THROW(ReadFromStream(addrman2, ssPeers2), std::ios_base::failure);
} }
@ -960,7 +959,7 @@ BOOST_AUTO_TEST_CASE(load_addrman_corrupted)
BOOST_AUTO_TEST_CASE(addrman_update_address) BOOST_AUTO_TEST_CASE(addrman_update_address)
{ {
// Tests updating nTime via Connected() and nServices via SetServices() // Tests updating nTime via Connected() and nServices via SetServices()
auto addrman = std::make_unique<AddrMan>(EMPTY_ASMAP, DETERMINISTIC, GetCheckRatio(m_node)); auto addrman = std::make_unique<AddrMan>(EMPTY_NETGROUPMAN, DETERMINISTIC, GetCheckRatio(m_node));
CNetAddr source{ResolveIP("252.2.2.2")}; CNetAddr source{ResolveIP("252.2.2.2")};
CAddress addr{CAddress(ResolveService("250.1.1.1", 8333), NODE_NONE)}; CAddress addr{CAddress(ResolveService("250.1.1.1", 8333), NODE_NONE)};

View File

@ -153,7 +153,7 @@ BOOST_AUTO_TEST_CASE(stale_tip_peer_management)
{ {
NodeId id{0}; NodeId id{0};
const CChainParams& chainparams = Params(); const CChainParams& chainparams = Params();
auto connman = std::make_unique<ConnmanTestMsg>(0x1337, 0x1337, *m_node.addrman); auto connman = std::make_unique<ConnmanTestMsg>(0x1337, 0x1337, *m_node.addrman, *m_node.netgroupman);
auto peerLogic = PeerManager::make(chainparams, *connman, *m_node.addrman, nullptr, auto peerLogic = PeerManager::make(chainparams, *connman, *m_node.addrman, nullptr,
*m_node.chainman, *m_node.mempool, *m_node.mn_metaman, *m_node.mn_sync, *m_node.chainman, *m_node.mempool, *m_node.mn_metaman, *m_node.mn_sync,
*m_node.govman, *m_node.sporkman, /* mn_activeman = */ nullptr, m_node.dmnman, *m_node.govman, *m_node.sporkman, /* mn_activeman = */ nullptr, m_node.dmnman,
@ -233,7 +233,7 @@ BOOST_AUTO_TEST_CASE(block_relay_only_eviction)
{ {
NodeId id{0}; NodeId id{0};
const CChainParams& chainparams = Params(); const CChainParams& chainparams = Params();
auto connman = std::make_unique<ConnmanTestMsg>(0x1337, 0x1337, *m_node.addrman); auto connman = std::make_unique<ConnmanTestMsg>(0x1337, 0x1337, *m_node.addrman, *m_node.netgroupman);
auto peerLogic = PeerManager::make(chainparams, *connman, *m_node.addrman, nullptr, auto peerLogic = PeerManager::make(chainparams, *connman, *m_node.addrman, nullptr,
*m_node.chainman, *m_node.mempool, *m_node.mn_metaman, *m_node.mn_sync, *m_node.chainman, *m_node.mempool, *m_node.mn_metaman, *m_node.mn_sync,
*m_node.govman, *m_node.sporkman, /* mn_activeman = */ nullptr, m_node.dmnman, *m_node.govman, *m_node.sporkman, /* mn_activeman = */ nullptr, m_node.dmnman,
@ -298,7 +298,7 @@ BOOST_AUTO_TEST_CASE(peer_discouragement)
{ {
const CChainParams& chainparams = Params(); const CChainParams& chainparams = Params();
auto banman = std::make_unique<BanMan>(m_args.GetDataDirBase() / "banlist", nullptr, DEFAULT_MISBEHAVING_BANTIME); auto banman = std::make_unique<BanMan>(m_args.GetDataDirBase() / "banlist", nullptr, DEFAULT_MISBEHAVING_BANTIME);
auto connman = std::make_unique<ConnmanTestMsg>(0x1337, 0x1337, *m_node.addrman); auto connman = std::make_unique<ConnmanTestMsg>(0x1337, 0x1337, *m_node.addrman, *m_node.netgroupman);
auto peerLogic = PeerManager::make(chainparams, *connman, *m_node.addrman, banman.get(), auto peerLogic = PeerManager::make(chainparams, *connman, *m_node.addrman, banman.get(),
*m_node.chainman, *m_node.mempool, *m_node.mn_metaman, *m_node.mn_sync, *m_node.chainman, *m_node.mempool, *m_node.mn_metaman, *m_node.mn_sync,
*m_node.govman, *m_node.sporkman, /* mn_activeman = */ nullptr, m_node.dmnman, *m_node.govman, *m_node.sporkman, /* mn_activeman = */ nullptr, m_node.dmnman,
@ -413,7 +413,7 @@ BOOST_AUTO_TEST_CASE(DoS_bantime)
{ {
const CChainParams& chainparams = Params(); const CChainParams& chainparams = Params();
auto banman = std::make_unique<BanMan>(m_args.GetDataDirBase() / "banlist", nullptr, DEFAULT_MISBEHAVING_BANTIME); auto banman = std::make_unique<BanMan>(m_args.GetDataDirBase() / "banlist", nullptr, DEFAULT_MISBEHAVING_BANTIME);
auto connman = std::make_unique<CConnman>(0x1337, 0x1337, *m_node.addrman); auto connman = std::make_unique<CConnman>(0x1337, 0x1337, *m_node.addrman, *m_node.netgroupman);
auto peerLogic = PeerManager::make(chainparams, *connman, *m_node.addrman, banman.get(), auto peerLogic = PeerManager::make(chainparams, *connman, *m_node.addrman, banman.get(),
*m_node.chainman, *m_node.mempool, *m_node.mn_metaman, *m_node.mn_sync, *m_node.chainman, *m_node.mempool, *m_node.mn_metaman, *m_node.mn_sync,
*m_node.govman, *m_node.sporkman, /* mn_activeman = */ nullptr, m_node.dmnman, *m_node.govman, *m_node.sporkman, /* mn_activeman = */ nullptr, m_node.dmnman,

View File

@ -37,11 +37,19 @@ void initialize_addrman()
g_setup = testing_setup.get(); g_setup = testing_setup.get();
} }
[[nodiscard]] NetGroupManager ConsumeNetGroupManager(FuzzedDataProvider& fuzzed_data_provider) noexcept
{
std::vector<bool> asmap = ConsumeRandomLengthBitVector(fuzzed_data_provider);
if (!SanityCheckASMap(asmap, 128)) asmap.clear();
return NetGroupManager(asmap);
}
FUZZ_TARGET_INIT(data_stream_addr_man, initialize_addrman) FUZZ_TARGET_INIT(data_stream_addr_man, initialize_addrman)
{ {
FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
CDataStream data_stream = ConsumeDataStream(fuzzed_data_provider); CDataStream data_stream = ConsumeDataStream(fuzzed_data_provider);
AddrMan addr_man{/*asmap=*/std::vector<bool>(), /*deterministic=*/false, GetCheckRatio()}; NetGroupManager netgroupman{ConsumeNetGroupManager(fuzzed_data_provider)};
AddrMan addr_man(netgroupman, /*deterministic=*/false, GetCheckRatio());
try { try {
ReadFromStream(addr_man, data_stream); ReadFromStream(addr_man, data_stream);
} catch (const std::exception&) { } catch (const std::exception&) {
@ -124,8 +132,8 @@ void FillAddrman(AddrMan& addrman, FuzzedDataProvider& fuzzed_data_provider)
class AddrManDeterministic : public AddrMan class AddrManDeterministic : public AddrMan
{ {
public: public:
explicit AddrManDeterministic(std::vector<bool> asmap, FuzzedDataProvider& fuzzed_data_provider) explicit AddrManDeterministic(const NetGroupManager& netgroupman, FuzzedDataProvider& fuzzed_data_provider)
: AddrMan{std::move(asmap), /*deterministic=*/true, GetCheckRatio()} : AddrMan(netgroupman, /*deterministic=*/true, GetCheckRatio())
{ {
WITH_LOCK(m_impl->cs, m_impl->insecure_rand = FastRandomContext{ConsumeUInt256(fuzzed_data_provider)}); WITH_LOCK(m_impl->cs, m_impl->insecure_rand = FastRandomContext{ConsumeUInt256(fuzzed_data_provider)});
} }
@ -223,19 +231,12 @@ public:
} }
}; };
[[nodiscard]] inline std::vector<bool> ConsumeAsmap(FuzzedDataProvider& fuzzed_data_provider) noexcept
{
std::vector<bool> asmap = ConsumeRandomLengthBitVector(fuzzed_data_provider);
if (!SanityCheckASMap(asmap, 128)) asmap.clear();
return asmap;
}
FUZZ_TARGET_INIT(addrman, initialize_addrman) FUZZ_TARGET_INIT(addrman, initialize_addrman)
{ {
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
SetMockTime(ConsumeTime(fuzzed_data_provider)); SetMockTime(ConsumeTime(fuzzed_data_provider));
std::vector<bool> asmap = ConsumeAsmap(fuzzed_data_provider); NetGroupManager netgroupman{ConsumeNetGroupManager(fuzzed_data_provider)};
auto addr_man_ptr = std::make_unique<AddrManDeterministic>(asmap, fuzzed_data_provider); auto addr_man_ptr = std::make_unique<AddrManDeterministic>(netgroupman, fuzzed_data_provider);
if (fuzzed_data_provider.ConsumeBool()) { if (fuzzed_data_provider.ConsumeBool()) {
const std::vector<uint8_t> serialized_data{ConsumeRandomLengthByteVector(fuzzed_data_provider)}; const std::vector<uint8_t> serialized_data{ConsumeRandomLengthByteVector(fuzzed_data_provider)};
CDataStream ds(serialized_data, SER_DISK, INIT_PROTO_VERSION); CDataStream ds(serialized_data, SER_DISK, INIT_PROTO_VERSION);
@ -244,7 +245,7 @@ FUZZ_TARGET_INIT(addrman, initialize_addrman)
try { try {
ds >> *addr_man_ptr; ds >> *addr_man_ptr;
} catch (const std::ios_base::failure&) { } catch (const std::ios_base::failure&) {
addr_man_ptr = std::make_unique<AddrManDeterministic>(asmap, fuzzed_data_provider); addr_man_ptr = std::make_unique<AddrManDeterministic>(netgroupman, fuzzed_data_provider);
} }
} }
AddrManDeterministic& addr_man = *addr_man_ptr; AddrManDeterministic& addr_man = *addr_man_ptr;
@ -316,9 +317,9 @@ FUZZ_TARGET_INIT(addrman_serdeser, initialize_addrman)
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
SetMockTime(ConsumeTime(fuzzed_data_provider)); SetMockTime(ConsumeTime(fuzzed_data_provider));
std::vector<bool> asmap = ConsumeAsmap(fuzzed_data_provider); NetGroupManager netgroupman{ConsumeNetGroupManager(fuzzed_data_provider)};
AddrManDeterministic addr_man1{asmap, fuzzed_data_provider}; AddrManDeterministic addr_man1{netgroupman, fuzzed_data_provider};
AddrManDeterministic addr_man2{asmap, fuzzed_data_provider}; AddrManDeterministic addr_man2{netgroupman, fuzzed_data_provider};
CDataStream data_stream(SER_NETWORK, PROTOCOL_VERSION); CDataStream data_stream(SER_NETWORK, PROTOCOL_VERSION);

View File

@ -3,6 +3,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php. // file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <netaddress.h> #include <netaddress.h>
#include <netgroup.h>
#include <test/fuzz/fuzz.h> #include <test/fuzz/fuzz.h>
#include <util/asmap.h> #include <util/asmap.h>
@ -56,5 +57,6 @@ FUZZ_TARGET(asmap)
memcpy(&ipv4, addr_data, addr_size); memcpy(&ipv4, addr_data, addr_size);
net_addr.SetIP(CNetAddr{ipv4}); net_addr.SetIP(CNetAddr{ipv4});
} }
(void)net_addr.GetMappedAS(asmap); NetGroupManager netgroupman{asmap};
(void)netgroupman.GetMappedAS(net_addr);
} }

View File

@ -18,12 +18,12 @@
#include <vector> #include <vector>
namespace { namespace {
const BasicTestingSetup* g_setup; const TestingSetup* g_setup;
} // namespace } // namespace
void initialize_connman() void initialize_connman()
{ {
static const auto testing_setup = MakeNoLogFileContext<>(); static const auto testing_setup = MakeNoLogFileContext<const TestingSetup>();
g_setup = testing_setup.get(); g_setup = testing_setup.get();
} }
@ -31,10 +31,11 @@ FUZZ_TARGET_INIT(connman, initialize_connman)
{ {
FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
SetMockTime(ConsumeTime(fuzzed_data_provider)); SetMockTime(ConsumeTime(fuzzed_data_provider));
AddrMan addrman(/*asmap=*/std::vector<bool>(), CConnman connman{fuzzed_data_provider.ConsumeIntegral<uint64_t>(),
/*deterministic=*/false, fuzzed_data_provider.ConsumeIntegral<uint64_t>(),
g_setup->m_node.args->GetArg("-checkaddrman", 0)); *g_setup->m_node.addrman,
CConnman connman{fuzzed_data_provider.ConsumeIntegral<uint64_t>(), fuzzed_data_provider.ConsumeIntegral<uint64_t>(), addrman}; *g_setup->m_node.netgroupman,
fuzzed_data_provider.ConsumeBool()};
CNetAddr random_netaddr; CNetAddr random_netaddr;
CNode random_node = ConsumeNode(fuzzed_data_provider); CNode random_node = ConsumeNode(fuzzed_data_provider);
CSubNet random_subnet; CSubNet random_subnet;

View File

@ -15,6 +15,7 @@
#include <merkleblock.h> #include <merkleblock.h>
#include <net.h> #include <net.h>
#include <netbase.h> #include <netbase.h>
#include <netgroup.h>
#include <node/utxo_snapshot.h> #include <node/utxo_snapshot.h>
#include <primitives/block.h> #include <primitives/block.h>
#include <protocol.h> #include <protocol.h>
@ -198,7 +199,8 @@ FUZZ_TARGET_DESERIALIZE(blockmerkleroot, {
BlockMerkleRoot(block, &mutated); BlockMerkleRoot(block, &mutated);
}) })
FUZZ_TARGET_DESERIALIZE(addrman_deserialize, { FUZZ_TARGET_DESERIALIZE(addrman_deserialize, {
AddrMan am(/*asmap=*/std::vector<bool>(), NetGroupManager netgroupman{std::vector<bool>()};
AddrMan am(netgroupman,
/*deterministic=*/false, /*deterministic=*/false,
g_setup->m_node.args->GetArg("-checkaddrman", 0)); g_setup->m_node.args->GetArg("-checkaddrman", 0));
DeserializeFromFuzzingInput(buffer, am); DeserializeFromFuzzingInput(buffer, am);

View File

@ -21,11 +21,24 @@
#include <string> #include <string>
#include <vector> #include <vector>
namespace {
const BasicTestingSetup* g_setup;
int32_t GetCheckRatio()
{
return std::clamp<int32_t>(g_setup->m_node.args->GetArg("-checkaddrman", 0), 0, 1000000);
}
} // namespace
void initialize_net() void initialize_net()
{ {
static const auto testing_setup = MakeNoLogFileContext<>(CBaseChainParams::MAIN); static const auto testing_setup = MakeNoLogFileContext<>(CBaseChainParams::MAIN);
g_setup = testing_setup.get();
} }
// From src/test/fuzz/addrman.cpp
extern NetGroupManager ConsumeNetGroupManager(FuzzedDataProvider& fuzzed_data_provider) noexcept;
FUZZ_TARGET_INIT(net, initialize_net) FUZZ_TARGET_INIT(net, initialize_net)
{ {
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
@ -37,8 +50,9 @@ FUZZ_TARGET_INIT(net, initialize_net)
CallOneOf( CallOneOf(
fuzzed_data_provider, fuzzed_data_provider,
[&] { [&] {
AddrMan addrman(/* asmap */ std::vector<bool>(), /* deterministic */ false, /* consistency_check_ratio */ 0); NetGroupManager netgroupman{ConsumeNetGroupManager(fuzzed_data_provider)};
CConnman connman{fuzzed_data_provider.ConsumeIntegral<uint64_t>(), fuzzed_data_provider.ConsumeIntegral<uint64_t>(), addrman}; AddrMan addrman(netgroupman, /*deterministic=*/false, GetCheckRatio());
CConnman connman{fuzzed_data_provider.ConsumeIntegral<uint64_t>(), fuzzed_data_provider.ConsumeIntegral<uint64_t>(), addrman, netgroupman};
node.CloseSocketDisconnect(&connman); node.CloseSocketDisconnect(&connman);
}, },
[&] { [&] {

View File

@ -6,6 +6,7 @@
#include <net_permissions.h> #include <net_permissions.h>
#include <netaddress.h> #include <netaddress.h>
#include <netbase.h> #include <netbase.h>
#include <netgroup.h>
#include <protocol.h> #include <protocol.h>
#include <serialize.h> #include <serialize.h>
#include <streams.h> #include <streams.h>
@ -316,22 +317,22 @@ BOOST_AUTO_TEST_CASE(subnet_test)
BOOST_AUTO_TEST_CASE(netbase_getgroup) BOOST_AUTO_TEST_CASE(netbase_getgroup)
{ {
std::vector<bool> asmap; // use /16 NetGroupManager netgroupman{std::vector<bool>()}; // use /16
BOOST_CHECK(ResolveIP("127.0.0.1").GetGroup(asmap) == std::vector<unsigned char>({0})); // Local -> !Routable() BOOST_CHECK(netgroupman.GetGroup(ResolveIP("127.0.0.1")) == std::vector<unsigned char>({0})); // Local -> !Routable()
BOOST_CHECK(ResolveIP("257.0.0.1").GetGroup(asmap) == std::vector<unsigned char>({0})); // !Valid -> !Routable() BOOST_CHECK(netgroupman.GetGroup(ResolveIP("257.0.0.1")) == std::vector<unsigned char>({0})); // !Valid -> !Routable()
BOOST_CHECK(ResolveIP("10.0.0.1").GetGroup(asmap) == std::vector<unsigned char>({0})); // RFC1918 -> !Routable() BOOST_CHECK(netgroupman.GetGroup(ResolveIP("10.0.0.1")) == std::vector<unsigned char>({0})); // RFC1918 -> !Routable()
BOOST_CHECK(ResolveIP("169.254.1.1").GetGroup(asmap) == std::vector<unsigned char>({0})); // RFC3927 -> !Routable() BOOST_CHECK(netgroupman.GetGroup(ResolveIP("169.254.1.1")) == std::vector<unsigned char>({0})); // RFC3927 -> !Routable()
BOOST_CHECK(ResolveIP("1.2.3.4").GetGroup(asmap) == std::vector<unsigned char>({(unsigned char)NET_IPV4, 1, 2})); // IPv4 BOOST_CHECK(netgroupman.GetGroup(ResolveIP("1.2.3.4")) == std::vector<unsigned char>({(unsigned char)NET_IPV4, 1, 2})); // IPv4
BOOST_CHECK(ResolveIP("::FFFF:0:102:304").GetGroup(asmap) == std::vector<unsigned char>({(unsigned char)NET_IPV4, 1, 2})); // RFC6145 BOOST_CHECK(netgroupman.GetGroup(ResolveIP("::FFFF:0:102:304")) == std::vector<unsigned char>({(unsigned char)NET_IPV4, 1, 2})); // RFC6145
BOOST_CHECK(ResolveIP("64:FF9B::102:304").GetGroup(asmap) == std::vector<unsigned char>({(unsigned char)NET_IPV4, 1, 2})); // RFC6052 BOOST_CHECK(netgroupman.GetGroup(ResolveIP("64:FF9B::102:304")) == std::vector<unsigned char>({(unsigned char)NET_IPV4, 1, 2})); // RFC6052
BOOST_CHECK(ResolveIP("2002:102:304:9999:9999:9999:9999:9999").GetGroup(asmap) == std::vector<unsigned char>({(unsigned char)NET_IPV4, 1, 2})); // RFC3964 BOOST_CHECK(netgroupman.GetGroup(ResolveIP("2002:102:304:9999:9999:9999:9999:9999")) == std::vector<unsigned char>({(unsigned char)NET_IPV4, 1, 2})); // RFC3964
BOOST_CHECK(ResolveIP("2001:0:9999:9999:9999:9999:FEFD:FCFB").GetGroup(asmap) == std::vector<unsigned char>({(unsigned char)NET_IPV4, 1, 2})); // RFC4380 BOOST_CHECK(netgroupman.GetGroup(ResolveIP("2001:0:9999:9999:9999:9999:FEFD:FCFB")) == std::vector<unsigned char>({(unsigned char)NET_IPV4, 1, 2})); // RFC4380
BOOST_CHECK(ResolveIP("2001:470:abcd:9999:9999:9999:9999:9999").GetGroup(asmap) == std::vector<unsigned char>({(unsigned char)NET_IPV6, 32, 1, 4, 112, 175})); //he.net BOOST_CHECK(netgroupman.GetGroup(ResolveIP("2001:470:abcd:9999:9999:9999:9999:9999")) == std::vector<unsigned char>({(unsigned char)NET_IPV6, 32, 1, 4, 112, 175})); //he.net
BOOST_CHECK(ResolveIP("2001:2001:9999:9999:9999:9999:9999:9999").GetGroup(asmap) == std::vector<unsigned char>({(unsigned char)NET_IPV6, 32, 1, 32, 1})); //IPv6 BOOST_CHECK(netgroupman.GetGroup(ResolveIP("2001:2001:9999:9999:9999:9999:9999:9999")) == std::vector<unsigned char>({(unsigned char)NET_IPV6, 32, 1, 32, 1})); //IPv6
// baz.net sha256 hash: 12929400eb4607c4ac075f087167e75286b179c693eb059a01774b864e8fe505 // baz.net sha256 hash: 12929400eb4607c4ac075f087167e75286b179c693eb059a01774b864e8fe505
std::vector<unsigned char> internal_group = {NET_INTERNAL, 0x12, 0x92, 0x94, 0x00, 0xeb, 0x46, 0x07, 0xc4, 0xac, 0x07}; std::vector<unsigned char> internal_group = {NET_INTERNAL, 0x12, 0x92, 0x94, 0x00, 0xeb, 0x46, 0x07, 0xc4, 0xac, 0x07};
BOOST_CHECK(CreateInternal("baz.net").GetGroup(asmap) == internal_group); BOOST_CHECK(netgroupman.GetGroup(CreateInternal("baz.net")) == internal_group);
} }
// Since CNetAddr (un)ser is tested separately in net_tests.cpp here we only // Since CNetAddr (un)ser is tested separately in net_tests.cpp here we only

View File

@ -184,10 +184,11 @@ BasicTestingSetup::BasicTestingSetup(const std::string& chainName, const std::ve
InitScriptExecutionCache(); InitScriptExecutionCache();
m_node.chain = interfaces::MakeChain(m_node); m_node.chain = interfaces::MakeChain(m_node);
m_node.addrman = std::make_unique<AddrMan>(/*asmap=*/std::vector<bool>(), m_node.netgroupman = std::make_unique<NetGroupManager>(/*asmap=*/std::vector<bool>());
m_node.addrman = std::make_unique<AddrMan>(*m_node.netgroupman,
/*deterministic=*/false, /*deterministic=*/false,
m_node.args->GetArg("-checkaddrman", 0)); m_node.args->GetArg("-checkaddrman", 0));
m_node.connman = std::make_unique<CConnman>(0x1337, 0x1337, *m_node.addrman); // Deterministic randomness for tests. m_node.connman = std::make_unique<CConnman>(0x1337, 0x1337, *m_node.addrman, *m_node.netgroupman); // Deterministic randomness for tests.
// while g_wallet_init_interface is init here at very early stage // while g_wallet_init_interface is init here at very early stage
// we can't get rid of unique_ptr from wallet/contex.h // we can't get rid of unique_ptr from wallet/contex.h
@ -215,6 +216,7 @@ BasicTestingSetup::~BasicTestingSetup()
m_node.evodb.reset(); m_node.evodb.reset();
m_node.connman.reset(); m_node.connman.reset();
m_node.addrman.reset(); m_node.addrman.reset();
m_node.netgroupman.reset();
LogInstance().DisconnectTestLogger(); LogInstance().DisconnectTestLogger();
fs::remove_all(m_path_root); fs::remove_all(m_path_root);