mirror of
https://github.com/bitcoin/bitcoin.git
synced 2024-11-20 02:25:40 +01:00
Merge bitcoin/bitcoin#27214: addrman: Enable selecting addresses by network
17e705428d
doc: clarify new_only param for Select function (Amiti Uttarwar)b0010c83a1
bench: test select for a new table with only one address (Amiti Uttarwar)9b91aae085
bench: add coverage for addrman select with network parameter (Amiti Uttarwar)22a4d1489c
test: increase coverage of addrman select (without network) (Amiti Uttarwar)a98e542e0c
test: add addrman test for special case (Amiti Uttarwar)5c8b4baff2
tests: add addrman_select_by_network test (Amiti Uttarwar)6b229284fd
addrman: add functionality to select by network (Amiti Uttarwar)26c3bf11e2
scripted-diff: rename local variables to match modern conventions (Amiti Uttarwar)48806412e2
refactor: consolidate select logic for new and tried tables (Amiti Uttarwar)ca2a9c5f8f
refactor: generalize select logic (Amiti Uttarwar)052fbcd5a7
addrman: Introduce helper to generalize looking up an addrman entry (Amiti Uttarwar)9bf078f66c
refactor: update Select_ function (Amiti Uttarwar) Pull request description: For the full context & motivation of this patch, see #27213 This is joint work with mzumsande. This PR adds functionality to `AddrMan::Select` to enable callers to specify a network they are interested in. Along the way, it refactors the function to deduplicate the logic, updates the local variables to match modern conventions, adds test coverage for both the new and existing `Select` logic, and adds bench tests for the worst case performance of both the new and existing `Select` logic. This functionality is used in the parent PR. ACKs for top commit: vasild: ACK17e705428d
brunoerg: re-ACK17e705428d
ajtowns: ACK17e705428d
mzumsande: Code Review ACK17e705428d
Tree-SHA512: e99d1ce0c44a15601a3daa37deeadfc9d26208a92969ecffbea358d57ca951102d759734ccf77eacd38db368da0bf5b6fede3cd900d8a77b3061f4adc54e52d8
This commit is contained in:
commit
3a93957a5d
152
src/addrman.cpp
152
src/addrman.cpp
@ -58,9 +58,9 @@ int AddrInfo::GetNewBucket(const uint256& nKey, const CNetAddr& src, const NetGr
|
||||
return hash2 % ADDRMAN_NEW_BUCKET_COUNT;
|
||||
}
|
||||
|
||||
int AddrInfo::GetBucketPosition(const uint256& nKey, bool fNew, int nBucket) const
|
||||
int AddrInfo::GetBucketPosition(const uint256& nKey, bool fNew, int bucket) const
|
||||
{
|
||||
uint64_t hash1 = (CHashWriter(SER_GETHASH, 0) << nKey << (fNew ? uint8_t{'N'} : uint8_t{'K'}) << nBucket << GetKey()).GetCheapHash();
|
||||
uint64_t hash1 = (CHashWriter(SER_GETHASH, 0) << nKey << (fNew ? uint8_t{'N'} : uint8_t{'K'}) << bucket << GetKey()).GetCheapHash();
|
||||
return hash1 % ADDRMAN_BUCKET_SIZE;
|
||||
}
|
||||
|
||||
@ -714,72 +714,98 @@ void AddrManImpl::Attempt_(const CService& addr, bool fCountFailure, NodeSeconds
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<CAddress, NodeSeconds> AddrManImpl::Select_(bool newOnly) const
|
||||
std::pair<CAddress, NodeSeconds> AddrManImpl::Select_(bool new_only, std::optional<Network> network) const
|
||||
{
|
||||
AssertLockHeld(cs);
|
||||
|
||||
if (vRandom.empty()) return {};
|
||||
|
||||
if (newOnly && nNew == 0) return {};
|
||||
size_t new_count = nNew;
|
||||
size_t tried_count = nTried;
|
||||
|
||||
// Use a 50% chance for choosing between tried and new table entries.
|
||||
if (!newOnly &&
|
||||
(nTried > 0 && (nNew == 0 || insecure_rand.randbool() == 0))) {
|
||||
// use a tried node
|
||||
double fChanceFactor = 1.0;
|
||||
while (1) {
|
||||
// Pick a tried bucket, and an initial position in that bucket.
|
||||
int nKBucket = insecure_rand.randrange(ADDRMAN_TRIED_BUCKET_COUNT);
|
||||
int nKBucketPos = insecure_rand.randrange(ADDRMAN_BUCKET_SIZE);
|
||||
// Iterate over the positions of that bucket, starting at the initial one,
|
||||
// and looping around.
|
||||
int i;
|
||||
for (i = 0; i < ADDRMAN_BUCKET_SIZE; ++i) {
|
||||
if (vvTried[nKBucket][(nKBucketPos + i) % ADDRMAN_BUCKET_SIZE] != -1) break;
|
||||
}
|
||||
// If the bucket is entirely empty, start over with a (likely) different one.
|
||||
if (i == ADDRMAN_BUCKET_SIZE) continue;
|
||||
// Find the entry to return.
|
||||
int nId = vvTried[nKBucket][(nKBucketPos + i) % ADDRMAN_BUCKET_SIZE];
|
||||
const auto it_found{mapInfo.find(nId)};
|
||||
assert(it_found != mapInfo.end());
|
||||
const AddrInfo& info{it_found->second};
|
||||
// With probability GetChance() * fChanceFactor, return the entry.
|
||||
if (insecure_rand.randbits(30) < fChanceFactor * info.GetChance() * (1 << 30)) {
|
||||
LogPrint(BCLog::ADDRMAN, "Selected %s from tried\n", info.ToStringAddrPort());
|
||||
return {info, info.m_last_try};
|
||||
}
|
||||
// Otherwise start over with a (likely) different bucket, and increased chance factor.
|
||||
fChanceFactor *= 1.2;
|
||||
}
|
||||
if (network.has_value()) {
|
||||
auto it = m_network_counts.find(*network);
|
||||
if (it == m_network_counts.end()) return {};
|
||||
|
||||
auto counts = it->second;
|
||||
new_count = counts.n_new;
|
||||
tried_count = counts.n_tried;
|
||||
}
|
||||
|
||||
if (new_only && new_count == 0) return {};
|
||||
if (new_count + tried_count == 0) return {};
|
||||
|
||||
// Decide if we are going to search the new or tried table
|
||||
// If either option is viable, use a 50% chance to choose
|
||||
bool search_tried;
|
||||
if (new_only || tried_count == 0) {
|
||||
search_tried = false;
|
||||
} else if (new_count == 0) {
|
||||
search_tried = true;
|
||||
} else {
|
||||
// use a new node
|
||||
double fChanceFactor = 1.0;
|
||||
while (1) {
|
||||
// Pick a new bucket, and an initial position in that bucket.
|
||||
int nUBucket = insecure_rand.randrange(ADDRMAN_NEW_BUCKET_COUNT);
|
||||
int nUBucketPos = insecure_rand.randrange(ADDRMAN_BUCKET_SIZE);
|
||||
// Iterate over the positions of that bucket, starting at the initial one,
|
||||
// and looping around.
|
||||
int i;
|
||||
for (i = 0; i < ADDRMAN_BUCKET_SIZE; ++i) {
|
||||
if (vvNew[nUBucket][(nUBucketPos + i) % ADDRMAN_BUCKET_SIZE] != -1) break;
|
||||
search_tried = insecure_rand.randbool();
|
||||
}
|
||||
|
||||
const int bucket_count{search_tried ? ADDRMAN_TRIED_BUCKET_COUNT : ADDRMAN_NEW_BUCKET_COUNT};
|
||||
|
||||
// Loop through the addrman table until we find an appropriate entry
|
||||
double chance_factor = 1.0;
|
||||
while (1) {
|
||||
// Pick a bucket, and an initial position in that bucket.
|
||||
int bucket = insecure_rand.randrange(bucket_count);
|
||||
int initial_position = insecure_rand.randrange(ADDRMAN_BUCKET_SIZE);
|
||||
|
||||
// Iterate over the positions of that bucket, starting at the initial one,
|
||||
// and looping around.
|
||||
int i;
|
||||
for (i = 0; i < ADDRMAN_BUCKET_SIZE; ++i) {
|
||||
int position = (initial_position + i) % ADDRMAN_BUCKET_SIZE;
|
||||
int node_id = GetEntry(search_tried, bucket, position);
|
||||
if (node_id != -1) {
|
||||
if (network.has_value()) {
|
||||
const auto it{mapInfo.find(node_id)};
|
||||
assert(it != mapInfo.end());
|
||||
const auto info{it->second};
|
||||
if (info.GetNetwork() == *network) break;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// If the bucket is entirely empty, start over with a (likely) different one.
|
||||
if (i == ADDRMAN_BUCKET_SIZE) continue;
|
||||
// Find the entry to return.
|
||||
int nId = vvNew[nUBucket][(nUBucketPos + i) % ADDRMAN_BUCKET_SIZE];
|
||||
const auto it_found{mapInfo.find(nId)};
|
||||
assert(it_found != mapInfo.end());
|
||||
const AddrInfo& info{it_found->second};
|
||||
// With probability GetChance() * fChanceFactor, return the entry.
|
||||
if (insecure_rand.randbits(30) < fChanceFactor * info.GetChance() * (1 << 30)) {
|
||||
LogPrint(BCLog::ADDRMAN, "Selected %s from new\n", info.ToStringAddrPort());
|
||||
return {info, info.m_last_try};
|
||||
}
|
||||
// Otherwise start over with a (likely) different bucket, and increased chance factor.
|
||||
fChanceFactor *= 1.2;
|
||||
}
|
||||
|
||||
// If the bucket is entirely empty, start over with a (likely) different one.
|
||||
if (i == ADDRMAN_BUCKET_SIZE) continue;
|
||||
|
||||
// Find the entry to return.
|
||||
int position = (initial_position + i) % ADDRMAN_BUCKET_SIZE;
|
||||
int nId = GetEntry(search_tried, bucket, position);
|
||||
const auto it_found{mapInfo.find(nId)};
|
||||
assert(it_found != mapInfo.end());
|
||||
const AddrInfo& info{it_found->second};
|
||||
|
||||
// With probability GetChance() * chance_factor, return the entry.
|
||||
if (insecure_rand.randbits(30) < chance_factor * info.GetChance() * (1 << 30)) {
|
||||
LogPrint(BCLog::ADDRMAN, "Selected %s from %s\n", info.ToStringAddrPort(), search_tried ? "tried" : "new");
|
||||
return {info, info.m_last_try};
|
||||
}
|
||||
|
||||
// Otherwise start over with a (likely) different bucket, and increased chance factor.
|
||||
chance_factor *= 1.2;
|
||||
}
|
||||
}
|
||||
|
||||
int AddrManImpl::GetEntry(bool use_tried, size_t bucket, size_t position) const
|
||||
{
|
||||
AssertLockHeld(cs);
|
||||
|
||||
assert(position < ADDRMAN_BUCKET_SIZE);
|
||||
|
||||
if (use_tried) {
|
||||
assert(bucket < ADDRMAN_TRIED_BUCKET_COUNT);
|
||||
return vvTried[bucket][position];
|
||||
} else {
|
||||
assert(bucket < ADDRMAN_NEW_BUCKET_COUNT);
|
||||
return vvNew[bucket][position];
|
||||
}
|
||||
}
|
||||
|
||||
@ -1164,11 +1190,11 @@ std::pair<CAddress, NodeSeconds> AddrManImpl::SelectTriedCollision()
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::pair<CAddress, NodeSeconds> AddrManImpl::Select(bool newOnly) const
|
||||
std::pair<CAddress, NodeSeconds> AddrManImpl::Select(bool new_only, std::optional<Network> network) const
|
||||
{
|
||||
LOCK(cs);
|
||||
Check();
|
||||
auto addrRet = Select_(newOnly);
|
||||
auto addrRet = Select_(new_only, network);
|
||||
Check();
|
||||
return addrRet;
|
||||
}
|
||||
@ -1262,9 +1288,9 @@ std::pair<CAddress, NodeSeconds> AddrMan::SelectTriedCollision()
|
||||
return m_impl->SelectTriedCollision();
|
||||
}
|
||||
|
||||
std::pair<CAddress, NodeSeconds> AddrMan::Select(bool newOnly) const
|
||||
std::pair<CAddress, NodeSeconds> AddrMan::Select(bool new_only, std::optional<Network> network) const
|
||||
{
|
||||
return m_impl->Select(newOnly);
|
||||
return m_impl->Select(new_only, network);
|
||||
}
|
||||
|
||||
std::vector<CAddress> AddrMan::GetAddr(size_t max_addresses, size_t max_pct, std::optional<Network> network) const
|
||||
|
@ -146,11 +146,14 @@ public:
|
||||
/**
|
||||
* Choose an address to connect to.
|
||||
*
|
||||
* @param[in] newOnly Whether to only select addresses from the new table.
|
||||
* @param[in] new_only Whether to only select addresses from the new table. Passing `true` returns
|
||||
* an address from the new table or an empty pair. Passing `false` will return an
|
||||
* address from either the new or tried table (it does not guarantee a tried entry).
|
||||
* @param[in] network Select only addresses of this network (nullopt = all)
|
||||
* @return CAddress The record for the selected peer.
|
||||
* seconds The last time we attempted to connect to that peer.
|
||||
*/
|
||||
std::pair<CAddress, NodeSeconds> Select(bool newOnly = false) const;
|
||||
std::pair<CAddress, NodeSeconds> Select(bool new_only = false, std::optional<Network> network = std::nullopt) const;
|
||||
|
||||
/**
|
||||
* Return all or many randomly selected addresses, optionally by network.
|
||||
|
@ -90,7 +90,7 @@ public:
|
||||
}
|
||||
|
||||
//! Calculate in which position of a bucket to store this entry.
|
||||
int GetBucketPosition(const uint256 &nKey, bool fNew, int nBucket) const;
|
||||
int GetBucketPosition(const uint256 &nKey, bool fNew, int bucket) const;
|
||||
|
||||
//! Determine whether the statistics about this entry are bad enough so that it can just be deleted
|
||||
bool IsTerrible(NodeSeconds now = Now<NodeSeconds>()) const;
|
||||
@ -127,7 +127,7 @@ public:
|
||||
|
||||
std::pair<CAddress, NodeSeconds> SelectTriedCollision() EXCLUSIVE_LOCKS_REQUIRED(!cs);
|
||||
|
||||
std::pair<CAddress, NodeSeconds> Select(bool newOnly) const
|
||||
std::pair<CAddress, NodeSeconds> Select(bool new_only, std::optional<Network> network) const
|
||||
EXCLUSIVE_LOCKS_REQUIRED(!cs);
|
||||
|
||||
std::vector<CAddress> GetAddr(size_t max_addresses, size_t max_pct, std::optional<Network> network) const
|
||||
@ -251,7 +251,13 @@ private:
|
||||
|
||||
void Attempt_(const CService& addr, bool fCountFailure, NodeSeconds time) EXCLUSIVE_LOCKS_REQUIRED(cs);
|
||||
|
||||
std::pair<CAddress, NodeSeconds> Select_(bool newOnly) const EXCLUSIVE_LOCKS_REQUIRED(cs);
|
||||
std::pair<CAddress, NodeSeconds> Select_(bool new_only, std::optional<Network> network) const EXCLUSIVE_LOCKS_REQUIRED(cs);
|
||||
|
||||
/** Helper to generalize looking up an addrman entry from either table.
|
||||
*
|
||||
* @return int The nid of the entry or -1 if the addrman position is empty.
|
||||
* */
|
||||
int GetEntry(bool use_tried, size_t bucket, size_t position) const EXCLUSIVE_LOCKS_REQUIRED(cs);
|
||||
|
||||
std::vector<CAddress> GetAddr_(size_t max_addresses, size_t max_pct, std::optional<Network> network) const EXCLUSIVE_LOCKS_REQUIRED(cs);
|
||||
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
#include <addrman.h>
|
||||
#include <bench/bench.h>
|
||||
#include <netbase.h>
|
||||
#include <netgroup.h>
|
||||
#include <random.h>
|
||||
#include <util/check.h>
|
||||
@ -71,6 +72,20 @@ static void FillAddrMan(AddrMan& addrman)
|
||||
AddAddressesToAddrMan(addrman);
|
||||
}
|
||||
|
||||
static CNetAddr ResolveIP(const std::string& ip)
|
||||
{
|
||||
CNetAddr addr;
|
||||
LookupHost(ip, addr, false);
|
||||
return addr;
|
||||
}
|
||||
|
||||
static CService ResolveService(const std::string& ip, uint16_t port = 0)
|
||||
{
|
||||
CService serv;
|
||||
Lookup(ip, serv, port, false);
|
||||
return serv;
|
||||
}
|
||||
|
||||
/* Benchmarks */
|
||||
|
||||
static void AddrManAdd(benchmark::Bench& bench)
|
||||
@ -95,6 +110,41 @@ static void AddrManSelect(benchmark::Bench& bench)
|
||||
});
|
||||
}
|
||||
|
||||
// The worst case performance of the Select() function is when there is only
|
||||
// one address on the table, because it linearly searches every position of
|
||||
// several buckets before identifying the correct bucket
|
||||
static void AddrManSelectFromAlmostEmpty(benchmark::Bench& bench)
|
||||
{
|
||||
AddrMan addrman{EMPTY_NETGROUPMAN, /*deterministic=*/false, ADDRMAN_CONSISTENCY_CHECK_RATIO};
|
||||
|
||||
// Add one address to the new table
|
||||
CService addr = ResolveService("250.3.1.1", 8333);
|
||||
addrman.Add({CAddress(addr, NODE_NONE)}, ResolveService("250.3.1.1", 8333));
|
||||
|
||||
bench.run([&] {
|
||||
(void)addrman.Select();
|
||||
});
|
||||
}
|
||||
|
||||
static void AddrManSelectByNetwork(benchmark::Bench& bench)
|
||||
{
|
||||
AddrMan addrman{EMPTY_NETGROUPMAN, /*deterministic=*/false, ADDRMAN_CONSISTENCY_CHECK_RATIO};
|
||||
|
||||
// add single I2P address to new table
|
||||
CService i2p_service;
|
||||
i2p_service.SetSpecial("udhdrtrcetjm5sxzskjyr5ztpeszydbh4dpl3pl4utgqqw2v4jna.b32.i2p");
|
||||
CAddress i2p_address(i2p_service, NODE_NONE);
|
||||
i2p_address.nTime = Now<NodeSeconds>();
|
||||
CNetAddr source = ResolveIP("252.2.2.2");
|
||||
addrman.Add({i2p_address}, source);
|
||||
|
||||
FillAddrMan(addrman);
|
||||
|
||||
bench.run([&] {
|
||||
(void)addrman.Select(/*new_only=*/false, NET_I2P);
|
||||
});
|
||||
}
|
||||
|
||||
static void AddrManGetAddr(benchmark::Bench& bench)
|
||||
{
|
||||
AddrMan addrman{EMPTY_NETGROUPMAN, /*deterministic=*/false, ADDRMAN_CONSISTENCY_CHECK_RATIO};
|
||||
@ -135,5 +185,7 @@ static void AddrManAddThenGood(benchmark::Bench& bench)
|
||||
|
||||
BENCHMARK(AddrManAdd, benchmark::PriorityLevel::HIGH);
|
||||
BENCHMARK(AddrManSelect, benchmark::PriorityLevel::HIGH);
|
||||
BENCHMARK(AddrManSelectFromAlmostEmpty, benchmark::PriorityLevel::HIGH);
|
||||
BENCHMARK(AddrManSelectByNetwork, benchmark::PriorityLevel::HIGH);
|
||||
BENCHMARK(AddrManGetAddr, benchmark::PriorityLevel::HIGH);
|
||||
BENCHMARK(AddrManAddThenGood, benchmark::PriorityLevel::HIGH);
|
||||
|
@ -127,46 +127,45 @@ BOOST_AUTO_TEST_CASE(addrman_ports)
|
||||
// the specified port to tried, but not the other.
|
||||
addrman->Good(CAddress(addr1_port, NODE_NONE));
|
||||
BOOST_CHECK_EQUAL(addrman->Size(), 2U);
|
||||
bool newOnly = true;
|
||||
auto addr_ret3 = addrman->Select(newOnly).first;
|
||||
bool new_only = true;
|
||||
auto addr_ret3 = addrman->Select(new_only).first;
|
||||
BOOST_CHECK_EQUAL(addr_ret3.ToStringAddrPort(), "250.1.1.1:8333");
|
||||
}
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE(addrman_select)
|
||||
{
|
||||
auto addrman = std::make_unique<AddrMan>(EMPTY_NETGROUPMAN, DETERMINISTIC, GetCheckRatio(m_node));
|
||||
BOOST_CHECK(!addrman->Select(false).first.IsValid());
|
||||
BOOST_CHECK(!addrman->Select(true).first.IsValid());
|
||||
|
||||
CNetAddr source = ResolveIP("252.2.2.2");
|
||||
|
||||
// Test: Select from new with 1 addr in new.
|
||||
// Add 1 address to the new table
|
||||
CService addr1 = ResolveService("250.1.1.1", 8333);
|
||||
BOOST_CHECK(addrman->Add({CAddress(addr1, NODE_NONE)}, source));
|
||||
BOOST_CHECK_EQUAL(addrman->Size(), 1U);
|
||||
|
||||
bool newOnly = true;
|
||||
auto addr_ret1 = addrman->Select(newOnly).first;
|
||||
BOOST_CHECK_EQUAL(addr_ret1.ToStringAddrPort(), "250.1.1.1:8333");
|
||||
BOOST_CHECK(addrman->Select(/*new_only=*/true).first == addr1);
|
||||
BOOST_CHECK(addrman->Select(/*new_only=*/false).first == addr1);
|
||||
|
||||
// Test: move addr to tried, select from new expected nothing returned.
|
||||
// Move address to the tried table
|
||||
BOOST_CHECK(addrman->Good(CAddress(addr1, NODE_NONE)));
|
||||
BOOST_CHECK_EQUAL(addrman->Size(), 1U);
|
||||
auto addr_ret2 = addrman->Select(newOnly).first;
|
||||
BOOST_CHECK_EQUAL(addr_ret2.ToStringAddrPort(), "[::]:0");
|
||||
|
||||
auto addr_ret3 = addrman->Select().first;
|
||||
BOOST_CHECK_EQUAL(addr_ret3.ToStringAddrPort(), "250.1.1.1:8333");
|
||||
|
||||
BOOST_CHECK_EQUAL(addrman->Size(), 1U);
|
||||
BOOST_CHECK(!addrman->Select(/*new_only=*/true).first.IsValid());
|
||||
BOOST_CHECK(addrman->Select().first == addr1);
|
||||
BOOST_CHECK_EQUAL(addrman->Size(), 1U);
|
||||
|
||||
|
||||
// Add three addresses to new table.
|
||||
// Add one address to the new table
|
||||
CService addr2 = ResolveService("250.3.1.1", 8333);
|
||||
BOOST_CHECK(addrman->Add({CAddress(addr2, NODE_NONE)}, addr2));
|
||||
BOOST_CHECK(addrman->Select(/*new_only=*/true).first == addr2);
|
||||
|
||||
// Add two more addresses to the new table
|
||||
CService addr3 = ResolveService("250.3.2.2", 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(addr3, NODE_NONE)}, ResolveService("250.3.1.1", 8333)));
|
||||
BOOST_CHECK(addrman->Add({CAddress(addr3, NODE_NONE)}, addr2));
|
||||
BOOST_CHECK(addrman->Add({CAddress(addr4, NODE_NONE)}, ResolveService("250.4.1.1", 8333)));
|
||||
|
||||
// Add three addresses to tried table.
|
||||
@ -174,17 +173,17 @@ BOOST_AUTO_TEST_CASE(addrman_select)
|
||||
CService addr6 = ResolveService("250.4.5.5", 7777);
|
||||
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)}, addr3));
|
||||
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)}, addr3));
|
||||
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->Good(CAddress(addr7, NODE_NONE)));
|
||||
|
||||
// Test: 6 addrs + 1 addr from last test = 7.
|
||||
// 6 addrs + 1 addr from last test = 7.
|
||||
BOOST_CHECK_EQUAL(addrman->Size(), 7U);
|
||||
|
||||
// Test: Select pulls from new and tried regardless of port number.
|
||||
// Select pulls from new and tried regardless of port number.
|
||||
std::set<uint16_t> ports;
|
||||
for (int i = 0; i < 20; ++i) {
|
||||
ports.insert(addrman->Select().first.GetPort());
|
||||
@ -192,6 +191,88 @@ BOOST_AUTO_TEST_CASE(addrman_select)
|
||||
BOOST_CHECK_EQUAL(ports.size(), 3U);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(addrman_select_by_network)
|
||||
{
|
||||
auto addrman = std::make_unique<AddrMan>(EMPTY_NETGROUPMAN, DETERMINISTIC, GetCheckRatio(m_node));
|
||||
BOOST_CHECK(!addrman->Select(/*new_only=*/true, NET_IPV4).first.IsValid());
|
||||
BOOST_CHECK(!addrman->Select(/*new_only=*/false, NET_IPV4).first.IsValid());
|
||||
|
||||
// add ipv4 address to the new table
|
||||
CNetAddr source = ResolveIP("252.2.2.2");
|
||||
CService addr1 = ResolveService("250.1.1.1", 8333);
|
||||
BOOST_CHECK(addrman->Add({CAddress(addr1, NODE_NONE)}, source));
|
||||
|
||||
BOOST_CHECK(addrman->Select(/*new_only=*/true, NET_IPV4).first == addr1);
|
||||
BOOST_CHECK(addrman->Select(/*new_only=*/false, NET_IPV4).first == addr1);
|
||||
BOOST_CHECK(!addrman->Select(/*new_only=*/false, NET_IPV6).first.IsValid());
|
||||
BOOST_CHECK(!addrman->Select(/*new_only=*/false, NET_ONION).first.IsValid());
|
||||
BOOST_CHECK(!addrman->Select(/*new_only=*/false, NET_I2P).first.IsValid());
|
||||
BOOST_CHECK(!addrman->Select(/*new_only=*/false, NET_CJDNS).first.IsValid());
|
||||
BOOST_CHECK(!addrman->Select(/*new_only=*/true, NET_CJDNS).first.IsValid());
|
||||
BOOST_CHECK(addrman->Select(/*new_only=*/false).first == addr1);
|
||||
|
||||
// add I2P address to the new table
|
||||
CAddress i2p_addr;
|
||||
i2p_addr.SetSpecial("udhdrtrcetjm5sxzskjyr5ztpeszydbh4dpl3pl4utgqqw2v4jna.b32.i2p");
|
||||
BOOST_CHECK(addrman->Add({i2p_addr}, source));
|
||||
|
||||
BOOST_CHECK(addrman->Select(/*new_only=*/true, NET_I2P).first == i2p_addr);
|
||||
BOOST_CHECK(addrman->Select(/*new_only=*/false, NET_I2P).first == i2p_addr);
|
||||
BOOST_CHECK(addrman->Select(/*new_only=*/false, NET_IPV4).first == addr1);
|
||||
BOOST_CHECK(!addrman->Select(/*new_only=*/false, NET_IPV6).first.IsValid());
|
||||
BOOST_CHECK(!addrman->Select(/*new_only=*/false, NET_ONION).first.IsValid());
|
||||
BOOST_CHECK(!addrman->Select(/*new_only=*/false, NET_CJDNS).first.IsValid());
|
||||
|
||||
// bump I2P address to tried table
|
||||
BOOST_CHECK(addrman->Good(i2p_addr));
|
||||
|
||||
BOOST_CHECK(!addrman->Select(/*new_only=*/true, NET_I2P).first.IsValid());
|
||||
BOOST_CHECK(addrman->Select(/*new_only=*/false, NET_I2P).first == i2p_addr);
|
||||
|
||||
// add another I2P address to the new table
|
||||
CAddress i2p_addr2;
|
||||
i2p_addr2.SetSpecial("c4gfnttsuwqomiygupdqqqyy5y5emnk5c73hrfvatri67prd7vyq.b32.i2p");
|
||||
BOOST_CHECK(addrman->Add({i2p_addr2}, source));
|
||||
|
||||
BOOST_CHECK(addrman->Select(/*new_only=*/true, NET_I2P).first == i2p_addr2);
|
||||
|
||||
// ensure that both new and tried table are selected from
|
||||
bool new_selected{false};
|
||||
bool tried_selected{false};
|
||||
|
||||
while (!new_selected || !tried_selected) {
|
||||
const CAddress selected{addrman->Select(/*new_only=*/false, NET_I2P).first};
|
||||
BOOST_REQUIRE(selected == i2p_addr || selected == i2p_addr2);
|
||||
if (selected == i2p_addr) {
|
||||
tried_selected = true;
|
||||
} else {
|
||||
new_selected = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(addrman_select_special)
|
||||
{
|
||||
// use a non-deterministic addrman to ensure a passing test isn't due to setup
|
||||
auto addrman = std::make_unique<AddrMan>(EMPTY_NETGROUPMAN, /*deterministic=*/false, GetCheckRatio(m_node));
|
||||
|
||||
// add ipv4 address to the new table
|
||||
CNetAddr source = ResolveIP("252.2.2.2");
|
||||
CService addr1 = ResolveService("250.1.1.3", 8333);
|
||||
BOOST_CHECK(addrman->Add({CAddress(addr1, NODE_NONE)}, source));
|
||||
|
||||
// add I2P address to the tried table
|
||||
CAddress i2p_addr;
|
||||
i2p_addr.SetSpecial("udhdrtrcetjm5sxzskjyr5ztpeszydbh4dpl3pl4utgqqw2v4jna.b32.i2p");
|
||||
BOOST_CHECK(addrman->Add({i2p_addr}, source));
|
||||
BOOST_CHECK(addrman->Good(i2p_addr));
|
||||
|
||||
// since the only ipv4 address is on the new table, ensure that the new
|
||||
// table gets selected even if new_only is false. if the table was being
|
||||
// selected at random, this test will sporadically fail
|
||||
BOOST_CHECK(addrman->Select(/*new_only=*/false, NET_IPV4).first == addr1);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(addrman_new_collisions)
|
||||
{
|
||||
auto addrman = std::make_unique<AddrMan>(EMPTY_NETGROUPMAN, DETERMINISTIC, GetCheckRatio(m_node));
|
||||
|
Loading…
Reference in New Issue
Block a user