merge bitcoin#23306: Make AddrMan support multiple ports per IP

This commit is contained in:
Kittywhiskers Van Gogh 2024-06-03 14:17:24 +00:00
parent d56702aa0c
commit 1a050d6cb4
No known key found for this signature in database
GPG Key ID: 30CD0C065E5C4AAD
5 changed files with 58 additions and 51 deletions

View File

@ -569,7 +569,7 @@ void AddrManImpl::Good_(const CService& addr, bool test_before_evict, int64_t nT
AddrInfo& info = *pinfo;
// check whether we are talking about the exact same CService (including same port)
if (info != addr)
if (!m_discriminate_ports && info != addr)
return;
// update info
@ -707,7 +707,7 @@ void AddrManImpl::Attempt_(const CService& addr, bool fCountFailure, int64_t nTi
AddrInfo& info = *pinfo;
// check whether we are talking about the exact same CService (including same port)
if (info != addr)
if (!m_discriminate_ports && info != addr)
return;
// update info
@ -838,7 +838,7 @@ void AddrManImpl::Connected_(const CService& addr, int64_t nTime)
AddrInfo& info = *pinfo;
// check whether we are talking about the exact same CService (including same port)
if (info != addr)
if (!m_discriminate_ports && info != addr)
return;
// update info
@ -860,7 +860,7 @@ void AddrManImpl::SetServices_(const CService& addr, ServiceFlags nServices)
AddrInfo& info = *pinfo;
// check whether we are talking about the exact same CService (including same port)
if (info != addr)
if (!m_discriminate_ports && info != addr)
return;
// update info
@ -878,7 +878,7 @@ AddrInfo AddrManImpl::GetAddressInfo_(const CService& addr)
AddrInfo& info = *pinfo;
// check whether we are talking about the exact same CService (including same port)
if (info != addr)
if (!m_discriminate_ports && info != addr)
return AddrInfo();
return *pinfo;

View File

@ -181,8 +181,8 @@ private:
//! table with information about all nIds
std::unordered_map<int, AddrInfo> mapInfo GUARDED_BY(cs);
//! find an nId based on its network address
std::unordered_map<CNetAddr, int, CNetAddrHash> mapAddr GUARDED_BY(cs);
//! find an nId based on its network address and port.
std::unordered_map<CService, int, CServiceHash> mapAddr GUARDED_BY(cs);
//! randomly-ordered vector of all nIds
//! This is mutable because it is unobservable outside the class, so any

View File

@ -261,7 +261,6 @@ public:
}
}
friend class CNetAddrHash;
friend class CSubNet;
private:
@ -482,22 +481,6 @@ public:
}
};
class CNetAddrHash
{
public:
size_t operator()(const CNetAddr& a) const noexcept
{
CSipHasher hasher(m_salt_k0, m_salt_k1);
hasher.Write(a.m_net);
hasher.Write(a.m_addr.data(), a.m_addr.size());
return static_cast<size_t>(hasher.Finalize());
}
private:
const uint64_t m_salt_k0 = GetRand(std::numeric_limits<uint64_t>::max());
const uint64_t m_salt_k1 = GetRand(std::numeric_limits<uint64_t>::max());
};
class CSubNet
{
protected:
@ -582,7 +565,25 @@ public:
READWRITE(Using<BigEndianFormatter<2>>(obj.port));
}
friend class CServiceHash;
friend CService MaybeFlipIPv6toCJDNS(const CService& service);
};
class CServiceHash
{
public:
size_t operator()(const CService& a) const noexcept
{
CSipHasher hasher(m_salt_k0, m_salt_k1);
hasher.Write(a.m_net);
hasher.Write(a.port);
hasher.Write(a.m_addr.data(), a.m_addr.size());
return static_cast<size_t>(hasher.Finalize());
}
private:
const uint64_t m_salt_k0 = GetRand(std::numeric_limits<uint64_t>::max());
const uint64_t m_salt_k1 = GetRand(std::numeric_limits<uint64_t>::max());
};
#endif // BITCOIN_NETADDRESS_H

View File

@ -26,7 +26,7 @@ public:
virtual void Serialize(CDataStream& s) const = 0;
AddrManSerializationMock()
: AddrMan(/* asmap */ std::vector<bool>(), /* deterministic */ true, /* consistency_check_ratio */ 100)
: AddrMan(/* asmap */ std::vector<bool>(), /* deterministic */ true, /* consistency_check_ratio */ 100, /* discriminate_ports */ true)
{}
};
@ -82,8 +82,9 @@ private:
public:
explicit AddrManTest(bool makeDeterministic = true,
std::vector<bool> asmap = std::vector<bool>())
: AddrMan(asmap, makeDeterministic, /* consistency_check_ratio */ 100)
std::vector<bool> asmap = std::vector<bool>(),
bool discriminatePorts = true)
: AddrMan(asmap, makeDeterministic, /* consistency_check_ratio */ 100, discriminatePorts)
{
deterministic = makeDeterministic;
}
@ -221,15 +222,15 @@ BOOST_AUTO_TEST_CASE(addrman_ports)
BOOST_CHECK_EQUAL(addrman.size(), 1U);
CService addr1_port = ResolveService("250.1.1.1", 8334);
BOOST_CHECK(!addrman.Add({CAddress(addr1_port, NODE_NONE)}, source));
BOOST_CHECK_EQUAL(addrman.size(), 1U);
BOOST_CHECK(addrman.Add({CAddress(addr1_port, NODE_NONE)}, source));
BOOST_CHECK_EQUAL(addrman.size(), 2U);
auto addr_ret2 = addrman.Select().first;
BOOST_CHECK_EQUAL(addr_ret2.ToString(), "250.1.1.1:8333");
BOOST_CHECK(addr_ret2.ToString() == "250.1.1.1:8333" || addr_ret2.ToString() == "250.1.1.1:8334");
// Test: Add same IP but diff port to tried table, it doesn't get added.
// Perhaps this is not ideal behavior but it is the current behavior.
// Test: Add same IP but diff port to tried table; this converts the entry with
// the specified port to tried, but not the other.
addrman.Good(CAddress(addr1_port, NODE_NONE));
BOOST_CHECK_EQUAL(addrman.size(), 1U);
BOOST_CHECK_EQUAL(addrman.size(), 2U);
bool newOnly = true;
auto addr_ret3 = addrman.Select(newOnly).first;
BOOST_CHECK_EQUAL(addr_ret3.ToString(), "250.1.1.1:8333");
@ -368,18 +369,18 @@ BOOST_AUTO_TEST_CASE(addrman_find)
CNetAddr source2 = ResolveIP("250.1.2.2");
BOOST_CHECK(addrman.Add({addr1}, source1));
BOOST_CHECK(!addrman.Add({addr2}, source2));
BOOST_CHECK(addrman.Add({addr2}, source2));
BOOST_CHECK(addrman.Add({addr3}, source1));
// Test: ensure Find returns an IP matching what we searched on.
// Test: ensure Find returns an IP/port matching what we searched on.
AddrInfo* info1 = addrman.Find(addr1);
BOOST_REQUIRE(info1);
BOOST_CHECK_EQUAL(info1->ToString(), "250.1.2.1:8333");
// Test 18; Find does not discriminate by port number.
// Test; Find discriminates by port number.
AddrInfo* info2 = addrman.Find(addr2);
BOOST_REQUIRE(info2);
BOOST_CHECK_EQUAL(info2->ToString(), info1->ToString());
BOOST_CHECK_EQUAL(info2->ToString(), "250.1.2.1:9999");
// Test: Find returns another IP matching what we searched on.
AddrInfo* info3 = addrman.Find(addr3);

View File

@ -137,24 +137,29 @@ public:
// Check that all values in `mapInfo` are equal to all values in `other.mapInfo`.
// Keys may be different.
using AddrInfoHasher = std::function<size_t(const AddrInfo&)>;
using AddrInfoEq = std::function<bool(const AddrInfo&, const AddrInfo&)>;
CNetAddrHash netaddr_hasher;
AddrInfoHasher addrinfo_hasher = [&netaddr_hasher](const AddrInfo& a) {
return netaddr_hasher(static_cast<CNetAddr>(a)) ^ netaddr_hasher(a.source) ^
a.nLastSuccess ^ a.nAttempts ^ a.nRefCount ^ a.fInTried;
auto addrinfo_hasher = [](const AddrInfo& a) {
CSipHasher hasher(0, 0);
auto addr_key = a.GetKey();
auto source_key = a.source.GetAddrBytes();
hasher.Write(a.nLastSuccess);
hasher.Write(a.nAttempts);
hasher.Write(a.nRefCount);
hasher.Write(a.fInTried);
hasher.Write(a.GetNetwork());
hasher.Write(a.source.GetNetwork());
hasher.Write(addr_key.size());
hasher.Write(source_key.size());
hasher.Write(addr_key.data(), addr_key.size());
hasher.Write(source_key.data(), source_key.size());
return (size_t)hasher.Finalize();
};
AddrInfoEq addrinfo_eq = [](const AddrInfo& lhs, const AddrInfo& rhs) {
return static_cast<CNetAddr>(lhs) == static_cast<CNetAddr>(rhs) &&
lhs.source == rhs.source && lhs.nLastSuccess == rhs.nLastSuccess &&
lhs.nAttempts == rhs.nAttempts && lhs.nRefCount == rhs.nRefCount &&
lhs.fInTried == rhs.fInTried;
auto addrinfo_eq = [](const AddrInfo& lhs, const AddrInfo& rhs) {
return std::tie(static_cast<const CService&>(lhs), lhs.source, lhs.nLastSuccess, lhs.nAttempts, lhs.nRefCount, lhs.fInTried) ==
std::tie(static_cast<const CService&>(rhs), rhs.source, rhs.nLastSuccess, rhs.nAttempts, rhs.nRefCount, rhs.fInTried);
};
using Addresses = std::unordered_set<AddrInfo, AddrInfoHasher, AddrInfoEq>;
using Addresses = std::unordered_set<AddrInfo, decltype(addrinfo_hasher), decltype(addrinfo_eq)>;
const size_t num_addresses{m_impl->mapInfo.size()};