merge bitcoin#23826: Make AddrMan unit tests use public interface, extend coverage

This commit is contained in:
Kittywhiskers Van Gogh 2024-09-01 15:13:05 +00:00
parent c14a54089f
commit df43565464
No known key found for this signature in database
GPG Key ID: 30CD0C065E5C4AAD
4 changed files with 287 additions and 268 deletions

View File

@ -949,6 +949,29 @@ std::pair<CAddress, int64_t> AddrManImpl::SelectTriedCollision_()
return {info_old, info_old.nLastTry}; return {info_old, info_old.nLastTry};
} }
std::optional<AddressPosition> AddrManImpl::FindAddressEntry_(const CAddress& addr)
{
AssertLockHeld(cs);
AddrInfo* addr_info = Find(addr);
if (!addr_info) return std::nullopt;
if(addr_info->fInTried) {
int bucket{addr_info->GetTriedBucket(nKey, m_asmap)};
return AddressPosition(/*tried=*/true,
/*multiplicity=*/1,
/*bucket=*/bucket,
/*position=*/addr_info->GetBucketPosition(nKey, false, bucket));
} else {
int bucket{addr_info->GetNewBucket(nKey, m_asmap)};
return AddressPosition(/*tried=*/false,
/*multiplicity=*/addr_info->nRefCount,
/*bucket=*/bucket,
/*position=*/addr_info->GetBucketPosition(nKey, true, bucket));
}
}
void AddrManImpl::Check() const void AddrManImpl::Check() const
{ {
AssertLockHeld(cs); AssertLockHeld(cs);
@ -1135,6 +1158,15 @@ void AddrManImpl::SetServices(const CService& addr, ServiceFlags nServices)
Check(); Check();
} }
std::optional<AddressPosition> AddrManImpl::FindAddressEntry(const CAddress& addr)
{
LOCK(cs);
Check();
auto entry = FindAddressEntry_(addr);
Check();
return entry;
}
AddrInfo AddrManImpl::GetAddressInfo(const CService& addr) AddrInfo AddrManImpl::GetAddressInfo(const CService& addr)
{ {
AddrInfo addrRet; AddrInfo addrRet;
@ -1236,3 +1268,8 @@ const std::vector<bool>& AddrMan::GetAsmap() const
{ {
return m_impl->GetAsmap(); return m_impl->GetAsmap();
} }
std::optional<AddressPosition> AddrMan::FindAddressEntry(const CAddress& addr)
{
return m_impl->FindAddressEntry(addr);
}

View File

@ -23,6 +23,31 @@ class AddrManImpl;
/** Default for -checkaddrman */ /** Default for -checkaddrman */
static constexpr int32_t DEFAULT_ADDRMAN_CONSISTENCY_CHECKS{0}; static constexpr int32_t DEFAULT_ADDRMAN_CONSISTENCY_CHECKS{0};
/** Test-only struct, capturing info about an address in AddrMan */
struct AddressPosition {
// Whether the address is in the new or tried table
const bool tried;
// Addresses in the tried table should always have a multiplicity of 1.
// Addresses in the new table can have multiplicity between 1 and
// ADDRMAN_NEW_BUCKETS_PER_ADDRESS
const int multiplicity;
// If the address is in the new table, the bucket and position are
// populated based on the first source who sent the address.
// In certain edge cases, this may not be where the address is currently
// located.
const int bucket;
const int position;
bool operator==(AddressPosition other) {
return std::tie(tried, multiplicity, bucket, position) ==
std::tie(other.tried, other.multiplicity, other.bucket, other.position);
}
explicit AddressPosition(bool tried_in, int multiplicity_in, int bucket_in, int position_in)
: tried{tried_in}, multiplicity{multiplicity_in}, bucket{bucket_in}, position{position_in} {}
};
class DbInconsistentError : public std::exception class DbInconsistentError : public std::exception
{ {
using std::exception::exception; using std::exception::exception;
@ -156,6 +181,15 @@ public:
AddrInfo GetAddressInfo(const CService& addr); AddrInfo GetAddressInfo(const CService& addr);
const std::vector<bool>& GetAsmap() const; const std::vector<bool>& GetAsmap() const;
/** Test-only function
* Find the address record in AddrMan and return information about its
* position.
* @param[in] addr The address record to look up.
* @return Information about the address record in AddrMan
* or nullopt if address is not found.
*/
std::optional<AddressPosition> FindAddressEntry(const CAddress& addr);
}; };
#endif // BITCOIN_ADDRMAN_H #endif // BITCOIN_ADDRMAN_H

View File

@ -137,11 +137,14 @@ public:
void SetServices(const CService& addr, ServiceFlags nServices) void SetServices(const CService& addr, ServiceFlags nServices)
EXCLUSIVE_LOCKS_REQUIRED(!cs); EXCLUSIVE_LOCKS_REQUIRED(!cs);
AddrInfo GetAddressInfo(const CService& addr); std::optional<AddressPosition> FindAddressEntry(const CAddress& addr)
EXCLUSIVE_LOCKS_REQUIRED(!cs);
AddrInfo GetAddressInfo(const CService& addr)
EXCLUSIVE_LOCKS_REQUIRED(!cs);
const std::vector<bool>& GetAsmap() const; const std::vector<bool>& GetAsmap() const;
friend class AddrManTest;
friend class AddrManDeterministic; friend class AddrManDeterministic;
private: private:
@ -270,6 +273,8 @@ private:
std::pair<CAddress, int64_t> SelectTriedCollision_() EXCLUSIVE_LOCKS_REQUIRED(cs); std::pair<CAddress, int64_t> SelectTriedCollision_() EXCLUSIVE_LOCKS_REQUIRED(cs);
std::optional<AddressPosition> FindAddressEntry_(const CAddress& addr) EXCLUSIVE_LOCKS_REQUIRED(cs);
//! Consistency check, taking into account m_consistency_check_ratio. //! Consistency check, taking into account m_consistency_check_ratio.
//! Will std::abort if an inconsistency is detected. //! Will std::abort if an inconsistency is detected.
void Check() const EXCLUSIVE_LOCKS_REQUIRED(cs); void Check() const EXCLUSIVE_LOCKS_REQUIRED(cs);

View File

@ -20,59 +20,6 @@
using namespace std::literals; using namespace std::literals;
class AddrManTest : public AddrMan
{
public:
explicit AddrManTest(std::vector<bool> asmap = std::vector<bool>())
: AddrMan(asmap, /*deterministic=*/true, /* consistency_check_ratio */ 100)
{}
AddrInfo* Find(const CService& addr)
{
LOCK(m_impl->cs);
return m_impl->Find(addr);
}
AddrInfo* Create(const CAddress& addr, const CNetAddr& addrSource, int* pnId)
{
LOCK(m_impl->cs);
return m_impl->Create(addr, addrSource, pnId);
}
void Delete(int nId)
{
LOCK(m_impl->cs);
m_impl->Delete(nId);
}
// Used to test deserialization
std::pair<int, int> GetBucketAndEntry(const CAddress& addr)
{
LOCK(m_impl->cs);
int nId = m_impl->mapAddr[addr];
for (int bucket = 0; bucket < ADDRMAN_NEW_BUCKET_COUNT; ++bucket) {
for (int entry = 0; entry < ADDRMAN_BUCKET_SIZE; ++entry) {
if (nId == m_impl->vvNew[bucket][entry]) {
return std::pair<int, int>(bucket, entry);
}
}
}
return std::pair<int, int>(-1, -1);
}
// Simulates connection failure so that we can test eviction of offline nodes
void SimConnFail(const CService& addr)
{
int64_t nLastSuccess = 1;
// Set last good connection in the deep past.
Good(addr, nLastSuccess);
bool count_failure = false;
int64_t nLastTry = GetAdjustedTime() - 61;
Attempt(addr, count_failure, nLastTry);
}
};
static CNetAddr ResolveIP(const std::string& ip) static CNetAddr ResolveIP(const std::string& ip)
{ {
CNetAddr addr; CNetAddr addr;
@ -100,12 +47,17 @@ static std::vector<bool> FromBytes(const unsigned char* source, int vector_size)
return result; return result;
} }
/* Utility function to create a deterministic addrman, as used in most tests */
static std::unique_ptr<AddrMan> TestAddrMan(std::vector<bool> asmap = std::vector<bool>())
{
return std::make_unique<AddrMan>(asmap, /*deterministic=*/true, /*consistency_check_ratio=*/100);
}
BOOST_FIXTURE_TEST_SUITE(addrman_tests, BasicTestingSetup) BOOST_FIXTURE_TEST_SUITE(addrman_tests, BasicTestingSetup)
BOOST_AUTO_TEST_CASE(addrman_simple) BOOST_AUTO_TEST_CASE(addrman_simple)
{ {
auto addrman = std::make_unique<AddrManTest>(); auto addrman = TestAddrMan();
CNetAddr source = ResolveIP("252.2.2.2"); CNetAddr source = ResolveIP("252.2.2.2");
@ -139,7 +91,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<AddrManTest>(); addrman = TestAddrMan();
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));
@ -149,57 +101,57 @@ BOOST_AUTO_TEST_CASE(addrman_simple)
BOOST_AUTO_TEST_CASE(addrman_ports) BOOST_AUTO_TEST_CASE(addrman_ports)
{ {
AddrManTest addrman; auto addrman = TestAddrMan();
CNetAddr source = ResolveIP("252.2.2.2"); CNetAddr source = ResolveIP("252.2.2.2");
BOOST_CHECK_EQUAL(addrman.size(), 0U); BOOST_CHECK_EQUAL(addrman->size(), 0U);
// Test 7; Addr with same IP but diff port does not replace existing addr. // Test 7; Addr with same IP but diff port does not replace existing addr.
CService addr1 = ResolveService("250.1.1.1", 8333); CService addr1 = ResolveService("250.1.1.1", 8333);
BOOST_CHECK(addrman.Add({CAddress(addr1, NODE_NONE)}, source)); BOOST_CHECK(addrman->Add({CAddress(addr1, NODE_NONE)}, source));
BOOST_CHECK_EQUAL(addrman.size(), 1U); BOOST_CHECK_EQUAL(addrman->size(), 1U);
CService addr1_port = ResolveService("250.1.1.1", 8334); CService addr1_port = ResolveService("250.1.1.1", 8334);
BOOST_CHECK(addrman.Add({CAddress(addr1_port, NODE_NONE)}, source)); BOOST_CHECK(addrman->Add({CAddress(addr1_port, NODE_NONE)}, source));
BOOST_CHECK_EQUAL(addrman.size(), 2U); BOOST_CHECK_EQUAL(addrman->size(), 2U);
auto addr_ret2 = addrman.Select().first; auto addr_ret2 = addrman->Select().first;
BOOST_CHECK(addr_ret2.ToString() == "250.1.1.1:8333" || addr_ret2.ToString() == "250.1.1.1:8334"); 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; this converts the entry with // Test: Add same IP but diff port to tried table; this converts the entry with
// the specified port to tried, but not the other. // the specified port to tried, but not the other.
addrman.Good(CAddress(addr1_port, NODE_NONE)); addrman->Good(CAddress(addr1_port, NODE_NONE));
BOOST_CHECK_EQUAL(addrman.size(), 2U); BOOST_CHECK_EQUAL(addrman->size(), 2U);
bool newOnly = true; bool newOnly = true;
auto addr_ret3 = addrman.Select(newOnly).first; auto addr_ret3 = addrman->Select(newOnly).first;
BOOST_CHECK_EQUAL(addr_ret3.ToString(), "250.1.1.1:8333"); BOOST_CHECK_EQUAL(addr_ret3.ToString(), "250.1.1.1:8333");
} }
BOOST_AUTO_TEST_CASE(addrman_select) BOOST_AUTO_TEST_CASE(addrman_select)
{ {
AddrManTest addrman; auto addrman = TestAddrMan();
CNetAddr source = ResolveIP("252.2.2.2"); CNetAddr source = ResolveIP("252.2.2.2");
// Test: Select from new with 1 addr in new. // Test: Select from new with 1 addr in new.
CService addr1 = ResolveService("250.1.1.1", 8333); CService addr1 = ResolveService("250.1.1.1", 8333);
BOOST_CHECK(addrman.Add({CAddress(addr1, NODE_NONE)}, source)); BOOST_CHECK(addrman->Add({CAddress(addr1, NODE_NONE)}, source));
BOOST_CHECK_EQUAL(addrman.size(), 1U); BOOST_CHECK_EQUAL(addrman->size(), 1U);
bool newOnly = true; bool newOnly = true;
auto addr_ret1 = addrman.Select(newOnly).first; auto addr_ret1 = addrman->Select(newOnly).first;
BOOST_CHECK_EQUAL(addr_ret1.ToString(), "250.1.1.1:8333"); BOOST_CHECK_EQUAL(addr_ret1.ToString(), "250.1.1.1:8333");
// Test: move addr to tried, select from new expected nothing returned. // Test: move addr to tried, select from new expected nothing returned.
BOOST_CHECK(addrman.Good(CAddress(addr1, NODE_NONE))); BOOST_CHECK(addrman->Good(CAddress(addr1, NODE_NONE)));
BOOST_CHECK_EQUAL(addrman.size(), 1U); BOOST_CHECK_EQUAL(addrman->size(), 1U);
auto addr_ret2 = addrman.Select(newOnly).first; auto addr_ret2 = addrman->Select(newOnly).first;
BOOST_CHECK_EQUAL(addr_ret2.ToString(), "[::]:0"); BOOST_CHECK_EQUAL(addr_ret2.ToString(), "[::]:0");
auto addr_ret3 = addrman.Select().first; auto addr_ret3 = addrman->Select().first;
BOOST_CHECK_EQUAL(addr_ret3.ToString(), "250.1.1.1:8333"); BOOST_CHECK_EQUAL(addr_ret3.ToString(), "250.1.1.1:8333");
BOOST_CHECK_EQUAL(addrman.size(), 1U); BOOST_CHECK_EQUAL(addrman->size(), 1U);
// Add three addresses to new table. // Add three addresses to new table.
@ -207,65 +159,97 @@ BOOST_AUTO_TEST_CASE(addrman_select)
CService addr3 = ResolveService("250.3.2.2", 9999); CService addr3 = ResolveService("250.3.2.2", 9999);
CService addr4 = ResolveService("250.3.3.3", 9999); CService addr4 = ResolveService("250.3.3.3", 9999);
BOOST_CHECK(addrman.Add({CAddress(addr2, NODE_NONE)}, ResolveService("250.3.1.1", 8333))); BOOST_CHECK(addrman->Add({CAddress(addr2, NODE_NONE)}, ResolveService("250.3.1.1", 8333)));
BOOST_CHECK(addrman.Add({CAddress(addr3, NODE_NONE)}, ResolveService("250.3.1.1", 8333))); BOOST_CHECK(addrman->Add({CAddress(addr3, NODE_NONE)}, ResolveService("250.3.1.1", 8333)));
BOOST_CHECK(addrman.Add({CAddress(addr4, NODE_NONE)}, ResolveService("250.4.1.1", 8333))); BOOST_CHECK(addrman->Add({CAddress(addr4, NODE_NONE)}, ResolveService("250.4.1.1", 8333)));
// Add three addresses to tried table. // Add three addresses to tried table.
CService addr5 = ResolveService("250.4.4.4", 8333); CService addr5 = ResolveService("250.4.4.4", 8333);
CService addr6 = ResolveService("250.4.5.5", 7777); CService addr6 = ResolveService("250.4.5.5", 7777);
CService addr7 = ResolveService("250.4.6.6", 8333); CService addr7 = ResolveService("250.4.6.6", 8333);
BOOST_CHECK(addrman.Add({CAddress(addr5, NODE_NONE)}, ResolveService("250.3.1.1", 8333))); BOOST_CHECK(addrman->Add({CAddress(addr5, NODE_NONE)}, ResolveService("250.3.1.1", 8333)));
BOOST_CHECK(addrman.Good(CAddress(addr5, NODE_NONE))); BOOST_CHECK(addrman->Good(CAddress(addr5, NODE_NONE)));
BOOST_CHECK(addrman.Add({CAddress(addr6, NODE_NONE)}, ResolveService("250.3.1.1", 8333))); BOOST_CHECK(addrman->Add({CAddress(addr6, NODE_NONE)}, ResolveService("250.3.1.1", 8333)));
BOOST_CHECK(addrman.Good(CAddress(addr6, NODE_NONE))); BOOST_CHECK(addrman->Good(CAddress(addr6, NODE_NONE)));
BOOST_CHECK(addrman.Add({CAddress(addr7, NODE_NONE)}, ResolveService("250.1.1.3", 8333))); BOOST_CHECK(addrman->Add({CAddress(addr7, NODE_NONE)}, ResolveService("250.1.1.3", 8333)));
BOOST_CHECK(addrman.Good(CAddress(addr7, NODE_NONE))); BOOST_CHECK(addrman->Good(CAddress(addr7, NODE_NONE)));
// Test: 6 addrs + 1 addr from last test = 7. // Test: 6 addrs + 1 addr from last test = 7.
BOOST_CHECK_EQUAL(addrman.size(), 7U); BOOST_CHECK_EQUAL(addrman->size(), 7U);
// Test: Select pulls from new and tried regardless of port number. // Test: Select pulls from new and tried regardless of port number.
std::set<uint16_t> ports; std::set<uint16_t> ports;
for (int i = 0; i < 20; ++i) { for (int i = 0; i < 20; ++i) {
ports.insert(addrman.Select().first.GetPort()); ports.insert(addrman->Select().first.GetPort());
} }
BOOST_CHECK_EQUAL(ports.size(), 3U); BOOST_CHECK_EQUAL(ports.size(), 3U);
} }
BOOST_AUTO_TEST_CASE(addrman_new_collisions) BOOST_AUTO_TEST_CASE(addrman_new_collisions)
{ {
AddrManTest addrman; auto addrman = TestAddrMan();
CNetAddr source = ResolveIP("252.2.2.2"); CNetAddr source = ResolveIP("252.2.2.2");
uint32_t num_addrs{0}; uint32_t num_addrs{0};
BOOST_CHECK_EQUAL(addrman.size(), num_addrs); BOOST_CHECK_EQUAL(addrman->size(), num_addrs);
while (num_addrs < 22) { // Magic number! 250.1.1.1 - 250.1.1.22 do not collide with deterministic key = 1 while (num_addrs < 22) { // Magic number! 250.1.1.1 - 250.1.1.22 do not collide with deterministic key = 1
CService addr = ResolveService("250.1.1." + ToString(++num_addrs)); CService addr = ResolveService("250.1.1." + ToString(++num_addrs));
BOOST_CHECK(addrman.Add({CAddress(addr, NODE_NONE)}, source)); BOOST_CHECK(addrman->Add({CAddress(addr, NODE_NONE)}, source));
// Test: No collision in new table yet. // Test: No collision in new table yet.
BOOST_CHECK_EQUAL(addrman.size(), num_addrs); BOOST_CHECK_EQUAL(addrman->size(), num_addrs);
} }
// Test: new table collision! // Test: new table collision!
CService addr1 = ResolveService("250.1.1." + ToString(++num_addrs)); CService addr1 = ResolveService("250.1.1." + ToString(++num_addrs));
uint32_t collisions{1}; uint32_t collisions{1};
BOOST_CHECK(addrman.Add({CAddress(addr1, NODE_NONE)}, source)); BOOST_CHECK(addrman->Add({CAddress(addr1, NODE_NONE)}, source));
BOOST_CHECK_EQUAL(addrman.size(), num_addrs - collisions); BOOST_CHECK_EQUAL(addrman->size(), num_addrs - collisions);
CService addr2 = ResolveService("250.1.1." + ToString(++num_addrs)); CService addr2 = ResolveService("250.1.1." + ToString(++num_addrs));
BOOST_CHECK(addrman.Add({CAddress(addr2, NODE_NONE)}, source)); BOOST_CHECK(addrman->Add({CAddress(addr2, NODE_NONE)}, source));
BOOST_CHECK_EQUAL(addrman.size(), num_addrs - collisions); BOOST_CHECK_EQUAL(addrman->size(), num_addrs - collisions);
}
BOOST_AUTO_TEST_CASE(addrman_new_multiplicity)
{
auto addrman = TestAddrMan();
CAddress addr{CAddress(ResolveService("253.3.3.3", 8333), NODE_NONE)};
int64_t start_time{GetAdjustedTime()};
addr.nTime = start_time;
// test that multiplicity stays at 1 if nTime doesn't increase
for (unsigned int i = 1; i < 20; ++i) {
std::string addr_ip{ToString(i % 256) + "." + ToString(i >> 8 % 256) + ".1.1"};
CNetAddr source{ResolveIP(addr_ip)};
addrman->Add({addr}, source);
}
AddressPosition addr_pos = addrman->FindAddressEntry(addr).value();
BOOST_CHECK_EQUAL(addr_pos.multiplicity, 1U);
BOOST_CHECK_EQUAL(addrman->size(), 1U);
// if nTime increases, an addr can occur in up to 8 buckets
// The acceptance probability decreases exponentially with existing multiplicity -
// choose number of iterations such that it gets to 8 with deterministic addrman.
for (unsigned int i = 1; i < 400; ++i) {
std::string addr_ip{ToString(i % 256) + "." + ToString(i >> 8 % 256) + ".1.1"};
CNetAddr source{ResolveIP(addr_ip)};
addr.nTime = start_time + i;
addrman->Add({addr}, source);
}
AddressPosition addr_pos_multi = addrman->FindAddressEntry(addr).value();
BOOST_CHECK_EQUAL(addr_pos_multi.multiplicity, 8U);
// multiplicity doesn't affect size
BOOST_CHECK_EQUAL(addrman->size(), 1U);
} }
BOOST_AUTO_TEST_CASE(addrman_tried_collisions) BOOST_AUTO_TEST_CASE(addrman_tried_collisions)
{ {
auto addrman = std::make_unique<AddrMan>(std::vector<bool>(), /*deterministic=*/true, /*consistency_check_ratio=*/100); auto addrman = TestAddrMan();
CNetAddr source = ResolveIP("252.2.2.2"); CNetAddr source = ResolveIP("252.2.2.2");
@ -293,87 +277,15 @@ BOOST_AUTO_TEST_CASE(addrman_tried_collisions)
BOOST_CHECK(addrman->Good(CAddress(addr2, NODE_NONE))); BOOST_CHECK(addrman->Good(CAddress(addr2, NODE_NONE)));
} }
BOOST_AUTO_TEST_CASE(addrman_find)
{
AddrManTest addrman;
BOOST_CHECK_EQUAL(addrman.size(), 0U);
CAddress addr1 = CAddress(ResolveService("250.1.2.1", 8333), NODE_NONE);
CAddress addr2 = CAddress(ResolveService("250.1.2.1", 9999), NODE_NONE);
CAddress addr3 = CAddress(ResolveService("251.255.2.1", 8333), NODE_NONE);
CNetAddr source1 = ResolveIP("250.1.2.1");
CNetAddr source2 = ResolveIP("250.1.2.2");
BOOST_CHECK(addrman.Add({addr1}, source1));
BOOST_CHECK(addrman.Add({addr2}, source2));
BOOST_CHECK(addrman.Add({addr3}, source1));
// 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; Find discriminates by port number.
AddrInfo* info2 = addrman.Find(addr2);
BOOST_REQUIRE(info2);
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);
BOOST_REQUIRE(info3);
BOOST_CHECK_EQUAL(info3->ToString(), "251.255.2.1:8333");
}
BOOST_AUTO_TEST_CASE(addrman_create)
{
AddrManTest addrman;
BOOST_CHECK_EQUAL(addrman.size(), 0U);
CAddress addr1 = CAddress(ResolveService("250.1.2.1", 8333), NODE_NONE);
CNetAddr source1 = ResolveIP("250.1.2.1");
int nId;
AddrInfo* pinfo = addrman.Create(addr1, source1, &nId);
// Test: The result should be the same as the input addr.
BOOST_CHECK_EQUAL(pinfo->ToString(), "250.1.2.1:8333");
AddrInfo* info2 = addrman.Find(addr1);
BOOST_CHECK_EQUAL(info2->ToString(), "250.1.2.1:8333");
}
BOOST_AUTO_TEST_CASE(addrman_delete)
{
AddrManTest addrman;
BOOST_CHECK_EQUAL(addrman.size(), 0U);
CAddress addr1 = CAddress(ResolveService("250.1.2.1", 8333), NODE_NONE);
CNetAddr source1 = ResolveIP("250.1.2.1");
int nId;
addrman.Create(addr1, source1, &nId);
// Test: Delete should actually delete the addr.
BOOST_CHECK_EQUAL(addrman.size(), 1U);
addrman.Delete(nId);
BOOST_CHECK_EQUAL(addrman.size(), 0U);
AddrInfo* info2 = addrman.Find(addr1);
BOOST_CHECK(info2 == nullptr);
}
BOOST_AUTO_TEST_CASE(addrman_getaddr) BOOST_AUTO_TEST_CASE(addrman_getaddr)
{ {
AddrManTest addrman; auto addrman = TestAddrMan();
// Test: Sanity check, GetAddr should never return anything if addrman // Test: Sanity check, GetAddr should never return anything if addrman
// is empty. // is empty.
BOOST_CHECK_EQUAL(addrman.size(), 0U); BOOST_CHECK_EQUAL(addrman->size(), 0U);
std::vector<CAddress> vAddr1 = addrman.GetAddr(/* max_addresses */ 0, /* max_pct */ 0, /* network */ std::nullopt); std::vector<CAddress> vAddr1 = addrman->GetAddr(/*max_addresses=*/0, /*max_pct=*/0, /*network=*/std::nullopt);
BOOST_CHECK_EQUAL(vAddr1.size(), 0U); BOOST_CHECK_EQUAL(vAddr1.size(), 0U);
CAddress addr1 = CAddress(ResolveService("250.250.2.1", 8333), NODE_NONE); CAddress addr1 = CAddress(ResolveService("250.250.2.1", 8333), NODE_NONE);
@ -390,18 +302,18 @@ BOOST_AUTO_TEST_CASE(addrman_getaddr)
CNetAddr source2 = ResolveIP("250.2.3.3"); CNetAddr source2 = ResolveIP("250.2.3.3");
// Test: Ensure GetAddr works with new addresses. // Test: Ensure GetAddr works with new addresses.
BOOST_CHECK(addrman.Add({addr1, addr3, addr5}, source1)); BOOST_CHECK(addrman->Add({addr1, addr3, addr5}, source1));
BOOST_CHECK(addrman.Add({addr2, addr4}, source2)); BOOST_CHECK(addrman->Add({addr2, addr4}, source2));
BOOST_CHECK_EQUAL(addrman.GetAddr(/* max_addresses */ 0, /* max_pct */ 0, /* network */ std::nullopt).size(), 5U); BOOST_CHECK_EQUAL(addrman->GetAddr(/*max_addresses=*/0, /*max_pct=*/0, /*network=*/std::nullopt).size(), 5U);
// Net processing asks for 23% of addresses. 23% of 5 is 1 rounded down. // Net processing asks for 23% of addresses. 23% of 5 is 1 rounded down.
BOOST_CHECK_EQUAL(addrman.GetAddr(/* max_addresses */ 2500, /* max_pct */ 23, /* network */ std::nullopt).size(), 1U); BOOST_CHECK_EQUAL(addrman->GetAddr(/*max_addresses=*/2500, /*max_pct=*/23, /*network=*/std::nullopt).size(), 1U);
// Test: Ensure GetAddr works with new and tried addresses. // Test: Ensure GetAddr works with new and tried addresses.
addrman.Good(CAddress(addr1, NODE_NONE)); BOOST_CHECK(addrman->Good(CAddress(addr1, NODE_NONE)));
addrman.Good(CAddress(addr2, NODE_NONE)); BOOST_CHECK(addrman->Good(CAddress(addr2, NODE_NONE)));
BOOST_CHECK_EQUAL(addrman.GetAddr(/* max_addresses */ 0, /* max_pct */ 0, /* network */ std::nullopt).size(), 5U); BOOST_CHECK_EQUAL(addrman->GetAddr(/*max_addresses=*/0, /*max_pct=*/0, /*network=*/std::nullopt).size(), 5U);
BOOST_CHECK_EQUAL(addrman.GetAddr(/* max_addresses */ 2500, /* max_pct */ 23, /* network */ std::nullopt).size(), 1U); BOOST_CHECK_EQUAL(addrman->GetAddr(/*max_addresses=*/2500, /*max_pct=*/23, /*network=*/std::nullopt).size(), 1U);
// Test: Ensure GetAddr still returns 23% when addrman has many addrs. // Test: Ensure GetAddr still returns 23% when addrman has many addrs.
for (unsigned int i = 1; i < (8 * 256); i++) { for (unsigned int i = 1; i < (8 * 256); i++) {
@ -412,24 +324,22 @@ BOOST_AUTO_TEST_CASE(addrman_getaddr)
// Ensure that for all addrs in addrman, isTerrible == false. // Ensure that for all addrs in addrman, isTerrible == false.
addr.nTime = GetAdjustedTime(); addr.nTime = GetAdjustedTime();
addrman.Add({addr}, ResolveIP(strAddr)); addrman->Add({addr}, ResolveIP(strAddr));
if (i % 8 == 0) if (i % 8 == 0)
addrman.Good(addr); addrman->Good(addr);
} }
std::vector<CAddress> vAddr = addrman.GetAddr(/* max_addresses */ 2500, /* max_pct */ 23, /* network */ std::nullopt); std::vector<CAddress> vAddr = addrman->GetAddr(/*max_addresses=*/2500, /*max_pct=*/23, /*network=*/std::nullopt);
size_t percent23 = (addrman.size() * 23) / 100; size_t percent23 = (addrman->size() * 23) / 100;
BOOST_CHECK_EQUAL(vAddr.size(), percent23); BOOST_CHECK_EQUAL(vAddr.size(), percent23);
BOOST_CHECK_EQUAL(vAddr.size(), 461U); BOOST_CHECK_EQUAL(vAddr.size(), 461U);
// (Addrman.size() < number of addresses added) due to address collisions. // (Addrman.size() < number of addresses added) due to address collisions.
BOOST_CHECK_EQUAL(addrman.size(), 2006U); BOOST_CHECK_EQUAL(addrman->size(), 2006U);
} }
BOOST_AUTO_TEST_CASE(caddrinfo_get_tried_bucket_legacy) BOOST_AUTO_TEST_CASE(caddrinfo_get_tried_bucket_legacy)
{ {
AddrManTest addrman;
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,8 +393,6 @@ BOOST_AUTO_TEST_CASE(caddrinfo_get_tried_bucket_legacy)
BOOST_AUTO_TEST_CASE(caddrinfo_get_new_bucket_legacy) BOOST_AUTO_TEST_CASE(caddrinfo_get_new_bucket_legacy)
{ {
AddrManTest addrman;
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);
@ -561,8 +469,6 @@ 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)
{ {
AddrManTest addrman;
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);
@ -616,8 +522,6 @@ BOOST_AUTO_TEST_CASE(caddrinfo_get_tried_bucket)
BOOST_AUTO_TEST_CASE(caddrinfo_get_new_bucket) BOOST_AUTO_TEST_CASE(caddrinfo_get_new_bucket)
{ {
AddrManTest addrman;
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);
@ -697,72 +601,69 @@ 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);
auto addrman_asmap1 = std::make_unique<AddrManTest>(asmap1); auto addrman_asmap1 = TestAddrMan(asmap1);
auto addrman_asmap1_dup = std::make_unique<AddrManTest>(asmap1); auto addrman_asmap1_dup = TestAddrMan(asmap1);
auto addrman_noasmap = std::make_unique<AddrManTest>(); auto addrman_noasmap = TestAddrMan();
CDataStream stream(SER_NETWORK, PROTOCOL_VERSION); CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
CAddress addr = CAddress(ResolveService("250.1.1.1"), NODE_NONE); CAddress addr = CAddress(ResolveService("250.1.1.1"), NODE_NONE);
CNetAddr default_source; CNetAddr default_source;
addrman_asmap1->Add({addr}, default_source); addrman_asmap1->Add({addr}, default_source);
stream << *addrman_asmap1; stream << *addrman_asmap1;
// serizalizing/deserializing addrman with the same asmap // serizalizing/deserializing addrman with the same asmap
stream >> *addrman_asmap1_dup; stream >> *addrman_asmap1_dup;
std::pair<int, int> bucketAndEntry_asmap1 = addrman_asmap1->GetBucketAndEntry(addr); AddressPosition addr_pos1 = addrman_asmap1->FindAddressEntry(addr).value();
std::pair<int, int> bucketAndEntry_asmap1_dup = addrman_asmap1_dup->GetBucketAndEntry(addr); AddressPosition addr_pos2 = addrman_asmap1_dup->FindAddressEntry(addr).value();
BOOST_CHECK(bucketAndEntry_asmap1.second != -1); BOOST_CHECK(addr_pos1.multiplicity != 0);
BOOST_CHECK(bucketAndEntry_asmap1_dup.second != -1); BOOST_CHECK(addr_pos2.multiplicity != 0);
BOOST_CHECK(bucketAndEntry_asmap1.first == bucketAndEntry_asmap1_dup.first); BOOST_CHECK(addr_pos1 == addr_pos2);
BOOST_CHECK(bucketAndEntry_asmap1.second == bucketAndEntry_asmap1_dup.second);
// deserializing asmaped peers.dat to non-asmaped addrman // deserializing asmaped peers.dat to non-asmaped addrman
stream << *addrman_asmap1; stream << *addrman_asmap1;
stream >> *addrman_noasmap; stream >> *addrman_noasmap;
std::pair<int, int> bucketAndEntry_noasmap = addrman_noasmap->GetBucketAndEntry(addr); AddressPosition addr_pos3 = addrman_noasmap->FindAddressEntry(addr).value();
BOOST_CHECK(bucketAndEntry_noasmap.second != -1); BOOST_CHECK(addr_pos3.multiplicity != 0);
BOOST_CHECK(bucketAndEntry_asmap1.first != bucketAndEntry_noasmap.first); BOOST_CHECK(addr_pos1.bucket != addr_pos3.bucket);
BOOST_CHECK(bucketAndEntry_asmap1.second != bucketAndEntry_noasmap.second); 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<AddrManTest>(asmap1); addrman_asmap1 = TestAddrMan(asmap1);
addrman_noasmap = std::make_unique<AddrManTest>(); addrman_noasmap = TestAddrMan();
addrman_noasmap->Add({addr}, default_source); addrman_noasmap->Add({addr}, default_source);
stream << *addrman_noasmap; stream << *addrman_noasmap;
stream >> *addrman_asmap1; stream >> *addrman_asmap1;
std::pair<int, int> bucketAndEntry_asmap1_deser = addrman_asmap1->GetBucketAndEntry(addr);
BOOST_CHECK(bucketAndEntry_asmap1_deser.second != -1); AddressPosition addr_pos4 = addrman_asmap1->FindAddressEntry(addr).value();
BOOST_CHECK(bucketAndEntry_asmap1_deser.first != bucketAndEntry_noasmap.first); BOOST_CHECK(addr_pos4.multiplicity != 0);
BOOST_CHECK(bucketAndEntry_asmap1_deser.first == bucketAndEntry_asmap1_dup.first); BOOST_CHECK(addr_pos4.bucket != addr_pos3.bucket);
BOOST_CHECK(bucketAndEntry_asmap1_deser.second == bucketAndEntry_asmap1_dup.second); 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<AddrManTest>(asmap1); addrman_asmap1 = TestAddrMan(asmap1);
addrman_noasmap = std::make_unique<AddrManTest>(); addrman_noasmap = TestAddrMan();
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);
std::pair<int, int> bucketAndEntry_noasmap_addr1 = addrman_noasmap->GetBucketAndEntry(addr1); AddressPosition addr_pos5 = addrman_noasmap->FindAddressEntry(addr1).value();
std::pair<int, int> bucketAndEntry_noasmap_addr2 = addrman_noasmap->GetBucketAndEntry(addr2); AddressPosition addr_pos6 = addrman_noasmap->FindAddressEntry(addr2).value();
BOOST_CHECK(bucketAndEntry_noasmap_addr1.first != bucketAndEntry_noasmap_addr2.first); BOOST_CHECK(addr_pos5.bucket != addr_pos6.bucket);
BOOST_CHECK(bucketAndEntry_noasmap_addr1.second != bucketAndEntry_noasmap_addr2.second);
stream << *addrman_noasmap; stream << *addrman_noasmap;
stream >> *addrman_asmap1; stream >> *addrman_asmap1;
std::pair<int, int> bucketAndEntry_asmap1_deser_addr1 = addrman_asmap1->GetBucketAndEntry(addr1); AddressPosition addr_pos7 = addrman_asmap1->FindAddressEntry(addr1).value();
std::pair<int, int> bucketAndEntry_asmap1_deser_addr2 = addrman_asmap1->GetBucketAndEntry(addr2); AddressPosition addr_pos8 = addrman_asmap1->FindAddressEntry(addr2).value();
BOOST_CHECK(bucketAndEntry_asmap1_deser_addr1.first == bucketAndEntry_asmap1_deser_addr2.first); BOOST_CHECK(addr_pos7.bucket == addr_pos8.bucket);
BOOST_CHECK(bucketAndEntry_asmap1_deser_addr1.second != bucketAndEntry_asmap1_deser_addr2.second); BOOST_CHECK(addr_pos7.position != addr_pos8.position);
} }
BOOST_AUTO_TEST_CASE(remove_invalid) 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<AddrManTest>(); auto addrman = TestAddrMan();
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};
@ -794,29 +695,29 @@ 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<AddrManTest>(); addrman = TestAddrMan();
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)
{ {
AddrManTest addrman; auto addrman = TestAddrMan();
BOOST_CHECK(addrman.size() == 0); BOOST_CHECK(addrman->size() == 0);
// Empty addrman should return blank addrman info. // Empty addrman should return blank addrman info.
BOOST_CHECK(addrman.SelectTriedCollision().first.ToString() == "[::]:0"); BOOST_CHECK(addrman->SelectTriedCollision().first.ToString() == "[::]:0");
// Add twenty two addresses. // Add twenty two addresses.
CNetAddr source = ResolveIP("252.2.2.2"); CNetAddr source = ResolveIP("252.2.2.2");
for (unsigned int i = 1; i < 23; i++) { for (unsigned int i = 1; i < 23; i++) {
CService addr = ResolveService("250.1.1." + ToString(i)); CService addr = ResolveService("250.1.1." + ToString(i));
BOOST_CHECK(addrman.Add({CAddress(addr, NODE_NONE)}, source)); BOOST_CHECK(addrman->Add({CAddress(addr, NODE_NONE)}, source));
// No collisions in tried. // No collisions in tried.
BOOST_CHECK(addrman.Good(addr)); BOOST_CHECK(addrman->Good(addr));
BOOST_CHECK(addrman.SelectTriedCollision().first.ToString() == "[::]:0"); BOOST_CHECK(addrman->SelectTriedCollision().first.ToString() == "[::]:0");
} }
// Ensure Good handles duplicates well. // Ensure Good handles duplicates well.
@ -825,114 +726,125 @@ BOOST_AUTO_TEST_CASE(addrman_selecttriedcollision)
CService addr = ResolveService("250.1.1." + ToString(i)); CService addr = ResolveService("250.1.1." + ToString(i));
// Unable to add duplicate address to tried table. // Unable to add duplicate address to tried table.
BOOST_CHECK(!addrman.Good(addr)); BOOST_CHECK(!addrman->Good(addr));
// Verify duplicate address not marked as a collision. // Verify duplicate address not marked as a collision.
BOOST_CHECK(addrman.SelectTriedCollision().first.ToString() == "[::]:0"); BOOST_CHECK(addrman->SelectTriedCollision().first.ToString() == "[::]:0");
} }
} }
BOOST_AUTO_TEST_CASE(addrman_noevict) BOOST_AUTO_TEST_CASE(addrman_noevict)
{ {
AddrManTest addrman; auto addrman = TestAddrMan();
// Add 35 addresses. // Add 35 addresses.
CNetAddr source = ResolveIP("252.2.2.2"); CNetAddr source = ResolveIP("252.2.2.2");
for (unsigned int i = 1; i < 36; i++) { for (unsigned int i = 1; i < 36; i++) {
CService addr = ResolveService("250.1.1." + ToString(i)); CService addr = ResolveService("250.1.1." + ToString(i));
BOOST_CHECK(addrman.Add({CAddress(addr, NODE_NONE)}, source)); BOOST_CHECK(addrman->Add({CAddress(addr, NODE_NONE)}, source));
// No collision yet. // No collision yet.
BOOST_CHECK(addrman.Good(addr)); BOOST_CHECK(addrman->Good(addr));
} }
// Collision in tried table between 36 and 19. // Collision in tried table between 36 and 19.
CService addr36 = ResolveService("250.1.1.36"); CService addr36 = ResolveService("250.1.1.36");
BOOST_CHECK(addrman.Add({CAddress(addr36, NODE_NONE)}, source)); BOOST_CHECK(addrman->Add({CAddress(addr36, NODE_NONE)}, source));
BOOST_CHECK(!addrman.Good(addr36)); BOOST_CHECK(!addrman->Good(addr36));
BOOST_CHECK_EQUAL(addrman.SelectTriedCollision().first.ToString(), "250.1.1.19:0"); BOOST_CHECK_EQUAL(addrman->SelectTriedCollision().first.ToString(), "250.1.1.19:0");
// 36 should be discarded and 19 not evicted. // 36 should be discarded and 19 not evicted.
// This means we keep 19 in the tried table and // This means we keep 19 in the tried table and
// 36 stays in the new table. // 36 stays in the new table.
addrman.ResolveCollisions(); addrman->ResolveCollisions();
BOOST_CHECK(addrman.SelectTriedCollision().first.ToString() == "[::]:0"); BOOST_CHECK(addrman->SelectTriedCollision().first.ToString() == "[::]:0");
// Lets create two collisions. // Lets create two collisions.
for (unsigned int i = 37; i < 59; i++) { for (unsigned int i = 37; i < 59; i++) {
CService addr = ResolveService("250.1.1." + ToString(i)); CService addr = ResolveService("250.1.1." + ToString(i));
BOOST_CHECK(addrman.Add({CAddress(addr, NODE_NONE)}, source)); BOOST_CHECK(addrman->Add({CAddress(addr, NODE_NONE)}, source));
BOOST_CHECK(addrman.Good(addr)); BOOST_CHECK(addrman->Good(addr));
} }
// Cause a collision in the tried table. // Cause a collision in the tried table.
CService addr59 = ResolveService("250.1.1.59"); CService addr59 = ResolveService("250.1.1.59");
BOOST_CHECK(addrman.Add({CAddress(addr59, NODE_NONE)}, source)); BOOST_CHECK(addrman->Add({CAddress(addr59, NODE_NONE)}, source));
BOOST_CHECK(!addrman.Good(addr59)); BOOST_CHECK(!addrman->Good(addr59));
BOOST_CHECK_EQUAL(addrman.SelectTriedCollision().first.ToString(), "250.1.1.10:0"); BOOST_CHECK_EQUAL(addrman->SelectTriedCollision().first.ToString(), "250.1.1.10:0");
// Cause a second collision in the new table. // Cause a second collision in the new table.
BOOST_CHECK(!addrman.Add({CAddress(addr36, NODE_NONE)}, source)); BOOST_CHECK(!addrman->Add({CAddress(addr36, NODE_NONE)}, source));
// 36 still cannot be moved from new to tried due to colliding with 19 // 36 still cannot be moved from new to tried due to colliding with 19
BOOST_CHECK(!addrman.Good(addr36)); BOOST_CHECK(!addrman->Good(addr36));
BOOST_CHECK(addrman.SelectTriedCollision().first.ToString() != "[::]:0"); BOOST_CHECK(addrman->SelectTriedCollision().first.ToString() != "[::]:0");
// Resolve all collisions. // Resolve all collisions.
addrman.ResolveCollisions(); addrman->ResolveCollisions();
BOOST_CHECK(addrman.SelectTriedCollision().first.ToString() == "[::]:0"); BOOST_CHECK(addrman->SelectTriedCollision().first.ToString() == "[::]:0");
} }
BOOST_AUTO_TEST_CASE(addrman_evictionworks) BOOST_AUTO_TEST_CASE(addrman_evictionworks)
{ {
AddrManTest addrman; auto addrman = TestAddrMan();
BOOST_CHECK(addrman.size() == 0); BOOST_CHECK(addrman->size() == 0);
// Empty addrman should return blank addrman info. // Empty addrman should return blank addrman info.
BOOST_CHECK(addrman.SelectTriedCollision().first.ToString() == "[::]:0"); BOOST_CHECK(addrman->SelectTriedCollision().first.ToString() == "[::]:0");
// Add 35 addresses // Add 35 addresses
CNetAddr source = ResolveIP("252.2.2.2"); CNetAddr source = ResolveIP("252.2.2.2");
for (unsigned int i = 1; i < 36; i++) { for (unsigned int i = 1; i < 36; i++) {
CService addr = ResolveService("250.1.1." + ToString(i)); CService addr = ResolveService("250.1.1." + ToString(i));
BOOST_CHECK(addrman.Add({CAddress(addr, NODE_NONE)}, source)); BOOST_CHECK(addrman->Add({CAddress(addr, NODE_NONE)}, source));
// No collision yet. // No collision yet.
BOOST_CHECK(addrman.Good(addr)); BOOST_CHECK(addrman->Good(addr));
} }
// Collision between 36 and 19. // Collision between 36 and 19.
CService addr = ResolveService("250.1.1.36"); CService addr = ResolveService("250.1.1.36");
BOOST_CHECK(addrman.Add({CAddress(addr, NODE_NONE)}, source)); BOOST_CHECK(addrman->Add({CAddress(addr, NODE_NONE)}, source));
BOOST_CHECK(!addrman.Good(addr)); BOOST_CHECK(!addrman->Good(addr));
auto info = addrman.SelectTriedCollision().first; auto info = addrman->SelectTriedCollision().first;
BOOST_CHECK_EQUAL(info.ToString(), "250.1.1.19:0"); BOOST_CHECK_EQUAL(info.ToString(), "250.1.1.19:0");
// Ensure test of address fails, so that it is evicted. // Ensure test of address fails, so that it is evicted.
addrman.SimConnFail(info); // Update entry in tried by setting last good connection in the deep past.
BOOST_CHECK(!addrman->Good(info, /*nTime=*/1));
addrman->Attempt(info, /*fCountFailure=*/false, /*nTime=*/GetAdjustedTime() - 61);
// Should swap 36 for 19. // Should swap 36 for 19.
addrman.ResolveCollisions(); addrman->ResolveCollisions();
BOOST_CHECK(addrman.SelectTriedCollision().first.ToString() == "[::]:0"); BOOST_CHECK(addrman->SelectTriedCollision().first.ToString() == "[::]:0");
AddressPosition addr_pos{addrman->FindAddressEntry(CAddress(addr, NODE_NONE)).value()};
BOOST_CHECK(addr_pos.tried);
// If 36 was swapped for 19, then adding 36 to tried should fail because we // If 36 was swapped for 19, then adding 36 to tried should fail because we
// are attempting to add a duplicate. // are attempting to add a duplicate.
// We check this by verifying Good() returns false and also verifying that // We check this by verifying Good() returns false and also verifying that
// we have no collisions. // we have no collisions.
BOOST_CHECK(!addrman.Good(addr)); BOOST_CHECK(!addrman->Good(addr));
BOOST_CHECK(addrman.SelectTriedCollision().first.ToString() == "[::]:0"); BOOST_CHECK(addrman->SelectTriedCollision().first.ToString() == "[::]:0");
// 19 should fail as a collision (not a duplicate) if we now attempt to move // 19 should fail as a collision (not a duplicate) if we now attempt to move
// it to the tried table. // it to the tried table.
CService addr19 = ResolveService("250.1.1.19"); CService addr19 = ResolveService("250.1.1.19");
BOOST_CHECK(!addrman.Good(addr19)); BOOST_CHECK(!addrman->Good(addr19));
BOOST_CHECK_EQUAL(addrman.SelectTriedCollision().first.ToString(), "250.1.1.36:0"); BOOST_CHECK_EQUAL(addrman->SelectTriedCollision().first.ToString(), "250.1.1.36:0");
addrman.ResolveCollisions(); // Eviction is also successful if too much time has passed since last try
BOOST_CHECK(addrman.SelectTriedCollision().first.ToString() == "[::]:0"); SetMockTime(GetTime() + 4 * 60 *60);
addrman->ResolveCollisions();
BOOST_CHECK(addrman->SelectTriedCollision().first.ToString() == "[::]:0");
//Now 19 is in tried again, and 36 back to new
AddressPosition addr_pos19{addrman->FindAddressEntry(CAddress(addr19, NODE_NONE)).value()};
BOOST_CHECK(addr_pos19.tried);
AddressPosition addr_pos36{addrman->FindAddressEntry(CAddress(addr, NODE_NONE)).value()};
BOOST_CHECK(!addr_pos36.tried);
} }
static CDataStream AddrmanToStream(const AddrMan& addrman) static CDataStream AddrmanToStream(const AddrMan& addrman)
@ -1041,4 +953,35 @@ BOOST_AUTO_TEST_CASE(load_addrman_corrupted)
BOOST_CHECK_THROW(ReadFromStream(addrman2, ssPeers2), std::ios_base::failure); BOOST_CHECK_THROW(ReadFromStream(addrman2, ssPeers2), std::ios_base::failure);
} }
BOOST_AUTO_TEST_CASE(addrman_update_address)
{
// Tests updating nTime via Connected() and nServices via SetServices()
auto addrman = TestAddrMan();
CNetAddr source{ResolveIP("252.2.2.2")};
CAddress addr{CAddress(ResolveService("250.1.1.1", 8333), NODE_NONE)};
int64_t start_time{GetAdjustedTime() - 10000};
addr.nTime = start_time;
BOOST_CHECK(addrman->Add({addr}, source));
BOOST_CHECK_EQUAL(addrman->size(), 1U);
// Updating an addrman entry with a different port doesn't change it
CAddress addr_diff_port{CAddress(ResolveService("250.1.1.1", 8334), NODE_NONE)};
addr_diff_port.nTime = start_time;
addrman->Connected(addr_diff_port);
addrman->SetServices(addr_diff_port, NODE_NETWORK_LIMITED);
std::vector<CAddress> vAddr1{addrman->GetAddr(/*max_addresses=*/0, /*max_pct=*/0, /*network=*/std::nullopt)};
BOOST_CHECK_EQUAL(vAddr1.size(), 1U);
BOOST_CHECK_EQUAL(vAddr1.at(0).nTime, start_time);
BOOST_CHECK_EQUAL(vAddr1.at(0).nServices, NODE_NONE);
// Updating an addrman entry with the correct port is successful
addrman->Connected(addr);
addrman->SetServices(addr, NODE_NETWORK_LIMITED);
std::vector<CAddress> vAddr2 = addrman->GetAddr(/*max_addresses=*/0, /*max_pct=*/0, /*network=*/std::nullopt);
BOOST_CHECK_EQUAL(vAddr2.size(), 1U);
BOOST_CHECK(vAddr2.at(0).nTime >= start_time + 10000);
BOOST_CHECK_EQUAL(vAddr2.at(0).nServices, NODE_NETWORK_LIMITED);
}
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()