mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-02-04 10:07:27 -05:00
35fbc97208
c9d548c91f
net: remove CService::ToStringPort() (Vasil Dimov)fd4f0f41e9
gui: simplify OptionsDialog::updateDefaultProxyNets() (Vasil Dimov)96c791dd20
net: remove CService::ToString() use ToStringAddrPort() instead (Vasil Dimov)944a9de08a
net: remove CNetAddr::ToString() and use ToStringAddr() instead (Vasil Dimov)043b9de59a
scripted-diff: rename ToStringIP[Port]() to ToStringAddr[Port]() (Vasil Dimov) Pull request description: Before this PR we had the somewhat confusing combination of methods: `CNetAddr::ToStringIP()` `CNetAddr::ToString()` (duplicate of the above) `CService::ToStringIPPort()` `CService::ToString()` (duplicate of the above, overrides a non-virtual method from `CNetAddr`) `CService::ToStringPort()` Avoid [overriding non-virtual methods](https://github.com/bitcoin/bitcoin/pull/25349/#issuecomment-1185226396). "IP" stands for "Internet Protocol" and while sometimes "IP addresses" are called just "IPs", it is incorrect to call Tor or I2P addresses "IPs". Thus use "Addr" instead of "IP". Change the above to: `CNetAddr::ToStringAddr()` `CService::ToStringAddrPort()` The changes touch a lot of files, but are mostly mechanical. ACKs for top commit: sipa: utACKc9d548c91f
achow101: ACKc9d548c91f
jonatack: re-ACKc9d548c91f
only change since my previous reviews is rebase, but as a sanity check rebased to current master and at each commit quickly re-reviewed and re-verified clean build and green unit tests LarryRuane: ACKc9d548c91f
Tree-SHA512: 633fb044bdecf9f551b5e3314c385bf10e2b78e8027dc51ec324b66b018da35e5b01f3fbe6295bbc455ea1bcd1a3629de1918d28de510693afaf6a52693f2157
1029 lines
42 KiB
C++
1029 lines
42 KiB
C++
// Copyright (c) 2012-2022 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 <addrdb.h>
|
|
#include <addrman.h>
|
|
#include <addrman_impl.h>
|
|
#include <chainparams.h>
|
|
#include <clientversion.h>
|
|
#include <hash.h>
|
|
#include <netbase.h>
|
|
#include <random.h>
|
|
#include <test/data/asmap.raw.h>
|
|
#include <test/util/setup_common.h>
|
|
#include <util/asmap.h>
|
|
#include <util/string.h>
|
|
|
|
#include <boost/test/unit_test.hpp>
|
|
|
|
#include <optional>
|
|
#include <string>
|
|
|
|
using namespace std::literals;
|
|
using node::NodeContext;
|
|
|
|
static NetGroupManager EMPTY_NETGROUPMAN{std::vector<bool>()};
|
|
static const bool DETERMINISTIC{true};
|
|
|
|
static int32_t GetCheckRatio(const NodeContext& node_ctx)
|
|
{
|
|
return std::clamp<int32_t>(node_ctx.args->GetIntArg("-checkaddrman", 100), 0, 1000000);
|
|
}
|
|
|
|
static CNetAddr ResolveIP(const std::string& ip)
|
|
{
|
|
CNetAddr addr;
|
|
BOOST_CHECK_MESSAGE(LookupHost(ip, addr, false), strprintf("failed to resolve: %s", ip));
|
|
return addr;
|
|
}
|
|
|
|
static CService ResolveService(const std::string& ip, uint16_t port = 0)
|
|
{
|
|
CService serv;
|
|
BOOST_CHECK_MESSAGE(Lookup(ip, serv, port, false), strprintf("failed to resolve: %s:%i", ip, port));
|
|
return serv;
|
|
}
|
|
|
|
|
|
static std::vector<bool> FromBytes(const unsigned char* source, int vector_size)
|
|
{
|
|
std::vector<bool> result(vector_size);
|
|
for (int byte_i = 0; byte_i < vector_size / 8; ++byte_i) {
|
|
unsigned char cur_byte = source[byte_i];
|
|
for (int bit_i = 0; bit_i < 8; ++bit_i) {
|
|
result[byte_i * 8 + bit_i] = (cur_byte >> bit_i) & 1;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
BOOST_FIXTURE_TEST_SUITE(addrman_tests, BasicTestingSetup)
|
|
|
|
BOOST_AUTO_TEST_CASE(addrman_simple)
|
|
{
|
|
auto addrman = std::make_unique<AddrMan>(EMPTY_NETGROUPMAN, DETERMINISTIC, GetCheckRatio(m_node));
|
|
|
|
CNetAddr source = ResolveIP("252.2.2.2");
|
|
|
|
// Test: Does Addrman respond correctly when empty.
|
|
BOOST_CHECK_EQUAL(addrman->Size(), 0U);
|
|
auto addr_null = addrman->Select().first;
|
|
BOOST_CHECK_EQUAL(addr_null.ToStringAddrPort(), "[::]:0");
|
|
|
|
// Test: Does Addrman::Add work as expected.
|
|
CService addr1 = ResolveService("250.1.1.1", 8333);
|
|
BOOST_CHECK(addrman->Add({CAddress(addr1, NODE_NONE)}, source));
|
|
BOOST_CHECK_EQUAL(addrman->Size(), 1U);
|
|
auto addr_ret1 = addrman->Select().first;
|
|
BOOST_CHECK_EQUAL(addr_ret1.ToStringAddrPort(), "250.1.1.1:8333");
|
|
|
|
// Test: Does IP address deduplication work correctly.
|
|
// Expected dup IP should not be added.
|
|
CService addr1_dup = ResolveService("250.1.1.1", 8333);
|
|
BOOST_CHECK(!addrman->Add({CAddress(addr1_dup, NODE_NONE)}, source));
|
|
BOOST_CHECK_EQUAL(addrman->Size(), 1U);
|
|
|
|
|
|
// Test: New table has one addr and we add a diff addr we should
|
|
// have at least one addr.
|
|
// Note that addrman's size cannot be tested reliably after insertion, as
|
|
// hash collisions may occur. But we can always be sure of at least one
|
|
// success.
|
|
|
|
CService addr2 = ResolveService("250.1.1.2", 8333);
|
|
BOOST_CHECK(addrman->Add({CAddress(addr2, NODE_NONE)}, source));
|
|
BOOST_CHECK(addrman->Size() >= 1);
|
|
|
|
// Test: reset addrman and test AddrMan::Add multiple addresses works as expected
|
|
addrman = std::make_unique<AddrMan>(EMPTY_NETGROUPMAN, DETERMINISTIC, GetCheckRatio(m_node));
|
|
std::vector<CAddress> vAddr;
|
|
vAddr.push_back(CAddress(ResolveService("250.1.1.3", 8333), NODE_NONE));
|
|
vAddr.push_back(CAddress(ResolveService("250.1.1.4", 8333), NODE_NONE));
|
|
BOOST_CHECK(addrman->Add(vAddr, source));
|
|
BOOST_CHECK(addrman->Size() >= 1);
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(addrman_ports)
|
|
{
|
|
auto addrman = std::make_unique<AddrMan>(EMPTY_NETGROUPMAN, DETERMINISTIC, GetCheckRatio(m_node));
|
|
|
|
CNetAddr source = ResolveIP("252.2.2.2");
|
|
|
|
BOOST_CHECK_EQUAL(addrman->Size(), 0U);
|
|
|
|
// Test 7; Addr with same IP but diff port does not replace existing addr.
|
|
CService addr1 = ResolveService("250.1.1.1", 8333);
|
|
BOOST_CHECK(addrman->Add({CAddress(addr1, NODE_NONE)}, source));
|
|
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(), 2U);
|
|
auto addr_ret2 = addrman->Select().first;
|
|
BOOST_CHECK(addr_ret2.ToStringAddrPort() == "250.1.1.1:8333" || addr_ret2.ToStringAddrPort() == "250.1.1.1:8334");
|
|
|
|
// 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(), 2U);
|
|
bool newOnly = true;
|
|
auto addr_ret3 = addrman->Select(newOnly).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));
|
|
|
|
CNetAddr source = ResolveIP("252.2.2.2");
|
|
|
|
// Test: Select from new with 1 addr in new.
|
|
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");
|
|
|
|
// Test: move addr to tried, select from new expected nothing returned.
|
|
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);
|
|
|
|
|
|
// Add three addresses to new table.
|
|
CService addr2 = ResolveService("250.3.1.1", 8333);
|
|
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(addr4, NODE_NONE)}, ResolveService("250.4.1.1", 8333)));
|
|
|
|
// Add three addresses to tried table.
|
|
CService addr5 = ResolveService("250.4.4.4", 8333);
|
|
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->Good(CAddress(addr5, NODE_NONE)));
|
|
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->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.
|
|
BOOST_CHECK_EQUAL(addrman->Size(), 7U);
|
|
|
|
// Test: 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());
|
|
}
|
|
BOOST_CHECK_EQUAL(ports.size(), 3U);
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(addrman_new_collisions)
|
|
{
|
|
auto addrman = std::make_unique<AddrMan>(EMPTY_NETGROUPMAN, DETERMINISTIC, GetCheckRatio(m_node));
|
|
|
|
CNetAddr source = ResolveIP("252.2.2.2");
|
|
|
|
uint32_t num_addrs{0};
|
|
|
|
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
|
|
CService addr = ResolveService("250.1.1." + ToString(++num_addrs));
|
|
BOOST_CHECK(addrman->Add({CAddress(addr, NODE_NONE)}, source));
|
|
|
|
// Test: No collision in new table yet.
|
|
BOOST_CHECK_EQUAL(addrman->Size(), num_addrs);
|
|
}
|
|
|
|
// Test: new table collision!
|
|
CService addr1 = ResolveService("250.1.1." + ToString(++num_addrs));
|
|
uint32_t collisions{1};
|
|
BOOST_CHECK(addrman->Add({CAddress(addr1, NODE_NONE)}, source));
|
|
BOOST_CHECK_EQUAL(addrman->Size(), num_addrs - collisions);
|
|
|
|
CService addr2 = ResolveService("250.1.1." + ToString(++num_addrs));
|
|
BOOST_CHECK(addrman->Add({CAddress(addr2, NODE_NONE)}, source));
|
|
BOOST_CHECK_EQUAL(addrman->Size(), num_addrs - collisions);
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(addrman_new_multiplicity)
|
|
{
|
|
auto addrman = std::make_unique<AddrMan>(EMPTY_NETGROUPMAN, DETERMINISTIC, GetCheckRatio(m_node));
|
|
CAddress addr{CAddress(ResolveService("253.3.3.3", 8333), NODE_NONE)};
|
|
const auto start_time{Now<NodeSeconds>()};
|
|
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 + std::chrono::seconds{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)
|
|
{
|
|
auto addrman = std::make_unique<AddrMan>(EMPTY_NETGROUPMAN, DETERMINISTIC, GetCheckRatio(m_node));
|
|
|
|
CNetAddr source = ResolveIP("252.2.2.2");
|
|
|
|
uint32_t num_addrs{0};
|
|
|
|
BOOST_CHECK_EQUAL(addrman->Size(), num_addrs);
|
|
|
|
while (num_addrs < 35) { // Magic number! 250.1.1.1 - 250.1.1.35 do not collide in tried with deterministic key = 1
|
|
CService addr = ResolveService("250.1.1." + ToString(++num_addrs));
|
|
BOOST_CHECK(addrman->Add({CAddress(addr, NODE_NONE)}, source));
|
|
|
|
// Test: Add to tried without collision
|
|
BOOST_CHECK(addrman->Good(CAddress(addr, NODE_NONE)));
|
|
|
|
}
|
|
|
|
// Test: Unable to add to tried table due to collision!
|
|
CService addr1 = ResolveService("250.1.1." + ToString(++num_addrs));
|
|
BOOST_CHECK(addrman->Add({CAddress(addr1, NODE_NONE)}, source));
|
|
BOOST_CHECK(!addrman->Good(CAddress(addr1, NODE_NONE)));
|
|
|
|
// Test: Add the next address to tried without collision
|
|
CService addr2 = ResolveService("250.1.1." + ToString(++num_addrs));
|
|
BOOST_CHECK(addrman->Add({CAddress(addr2, NODE_NONE)}, source));
|
|
BOOST_CHECK(addrman->Good(CAddress(addr2, NODE_NONE)));
|
|
}
|
|
|
|
|
|
BOOST_AUTO_TEST_CASE(addrman_getaddr)
|
|
{
|
|
auto addrman = std::make_unique<AddrMan>(EMPTY_NETGROUPMAN, DETERMINISTIC, GetCheckRatio(m_node));
|
|
|
|
// Test: Sanity check, GetAddr should never return anything if addrman
|
|
// is empty.
|
|
BOOST_CHECK_EQUAL(addrman->Size(), 0U);
|
|
std::vector<CAddress> vAddr1 = addrman->GetAddr(/*max_addresses=*/0, /*max_pct=*/0, /*network=*/std::nullopt);
|
|
BOOST_CHECK_EQUAL(vAddr1.size(), 0U);
|
|
|
|
CAddress addr1 = CAddress(ResolveService("250.250.2.1", 8333), NODE_NONE);
|
|
addr1.nTime = Now<NodeSeconds>(); // Set time so isTerrible = false
|
|
CAddress addr2 = CAddress(ResolveService("250.251.2.2", 9999), NODE_NONE);
|
|
addr2.nTime = Now<NodeSeconds>();
|
|
CAddress addr3 = CAddress(ResolveService("251.252.2.3", 8333), NODE_NONE);
|
|
addr3.nTime = Now<NodeSeconds>();
|
|
CAddress addr4 = CAddress(ResolveService("252.253.3.4", 8333), NODE_NONE);
|
|
addr4.nTime = Now<NodeSeconds>();
|
|
CAddress addr5 = CAddress(ResolveService("252.254.4.5", 8333), NODE_NONE);
|
|
addr5.nTime = Now<NodeSeconds>();
|
|
CNetAddr source1 = ResolveIP("250.1.2.1");
|
|
CNetAddr source2 = ResolveIP("250.2.3.3");
|
|
|
|
// Test: Ensure GetAddr works with new addresses.
|
|
BOOST_CHECK(addrman->Add({addr1, addr3, addr5}, source1));
|
|
BOOST_CHECK(addrman->Add({addr2, addr4}, source2));
|
|
|
|
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.
|
|
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.
|
|
BOOST_CHECK(addrman->Good(CAddress(addr1, 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=*/2500, /*max_pct=*/23, /*network=*/std::nullopt).size(), 1U);
|
|
|
|
// Test: Ensure GetAddr still returns 23% when addrman has many addrs.
|
|
for (unsigned int i = 1; i < (8 * 256); i++) {
|
|
int octet1 = i % 256;
|
|
int octet2 = i >> 8 % 256;
|
|
std::string strAddr = ToString(octet1) + "." + ToString(octet2) + ".1.23";
|
|
CAddress addr = CAddress(ResolveService(strAddr), NODE_NONE);
|
|
|
|
// Ensure that for all addrs in addrman, isTerrible == false.
|
|
addr.nTime = Now<NodeSeconds>();
|
|
addrman->Add({addr}, ResolveIP(strAddr));
|
|
if (i % 8 == 0)
|
|
addrman->Good(addr);
|
|
}
|
|
std::vector<CAddress> vAddr = addrman->GetAddr(/*max_addresses=*/2500, /*max_pct=*/23, /*network=*/std::nullopt);
|
|
|
|
size_t percent23 = (addrman->Size() * 23) / 100;
|
|
BOOST_CHECK_EQUAL(vAddr.size(), percent23);
|
|
BOOST_CHECK_EQUAL(vAddr.size(), 461U);
|
|
// (addrman.Size() < number of addresses added) due to address collisions.
|
|
BOOST_CHECK_EQUAL(addrman->Size(), 2006U);
|
|
}
|
|
|
|
|
|
BOOST_AUTO_TEST_CASE(caddrinfo_get_tried_bucket_legacy)
|
|
{
|
|
CAddress addr1 = CAddress(ResolveService("250.1.1.1", 8333), NODE_NONE);
|
|
CAddress addr2 = CAddress(ResolveService("250.1.1.1", 9999), NODE_NONE);
|
|
|
|
CNetAddr source1 = ResolveIP("250.1.1.1");
|
|
|
|
|
|
AddrInfo info1 = AddrInfo(addr1, source1);
|
|
|
|
uint256 nKey1 = (uint256)(CHashWriter(SER_GETHASH, 0) << 1).GetHash();
|
|
uint256 nKey2 = (uint256)(CHashWriter(SER_GETHASH, 0) << 2).GetHash();
|
|
|
|
BOOST_CHECK_EQUAL(info1.GetTriedBucket(nKey1, EMPTY_NETGROUPMAN), 40);
|
|
|
|
// Test: Make sure key actually randomizes bucket placement. A fail on
|
|
// this test could be a security issue.
|
|
BOOST_CHECK(info1.GetTriedBucket(nKey1, EMPTY_NETGROUPMAN) != info1.GetTriedBucket(nKey2, EMPTY_NETGROUPMAN));
|
|
|
|
// Test: Two addresses with same IP but different ports can map to
|
|
// different buckets because they have different keys.
|
|
AddrInfo info2 = AddrInfo(addr2, source1);
|
|
|
|
BOOST_CHECK(info1.GetKey() != info2.GetKey());
|
|
BOOST_CHECK(info1.GetTriedBucket(nKey1, EMPTY_NETGROUPMAN) != info2.GetTriedBucket(nKey1, EMPTY_NETGROUPMAN));
|
|
|
|
std::set<int> buckets;
|
|
for (int i = 0; i < 255; i++) {
|
|
AddrInfo infoi = AddrInfo(
|
|
CAddress(ResolveService("250.1.1." + ToString(i)), NODE_NONE),
|
|
ResolveIP("250.1.1." + ToString(i)));
|
|
int bucket = infoi.GetTriedBucket(nKey1, EMPTY_NETGROUPMAN);
|
|
buckets.insert(bucket);
|
|
}
|
|
// Test: IP addresses in the same /16 prefix should
|
|
// never get more than 8 buckets with legacy grouping
|
|
BOOST_CHECK_EQUAL(buckets.size(), 8U);
|
|
|
|
buckets.clear();
|
|
for (int j = 0; j < 255; j++) {
|
|
AddrInfo infoj = AddrInfo(
|
|
CAddress(ResolveService("250." + ToString(j) + ".1.1"), NODE_NONE),
|
|
ResolveIP("250." + ToString(j) + ".1.1"));
|
|
int bucket = infoj.GetTriedBucket(nKey1, EMPTY_NETGROUPMAN);
|
|
buckets.insert(bucket);
|
|
}
|
|
// Test: IP addresses in the different /16 prefix should map to more than
|
|
// 8 buckets with legacy grouping
|
|
BOOST_CHECK_EQUAL(buckets.size(), 160U);
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(caddrinfo_get_new_bucket_legacy)
|
|
{
|
|
CAddress addr1 = CAddress(ResolveService("250.1.2.1", 8333), NODE_NONE);
|
|
CAddress addr2 = CAddress(ResolveService("250.1.2.1", 9999), NODE_NONE);
|
|
|
|
CNetAddr source1 = ResolveIP("250.1.2.1");
|
|
|
|
AddrInfo info1 = AddrInfo(addr1, source1);
|
|
|
|
uint256 nKey1 = (uint256)(CHashWriter(SER_GETHASH, 0) << 1).GetHash();
|
|
uint256 nKey2 = (uint256)(CHashWriter(SER_GETHASH, 0) << 2).GetHash();
|
|
|
|
// Test: Make sure the buckets are what we expect
|
|
BOOST_CHECK_EQUAL(info1.GetNewBucket(nKey1, EMPTY_NETGROUPMAN), 786);
|
|
BOOST_CHECK_EQUAL(info1.GetNewBucket(nKey1, source1, EMPTY_NETGROUPMAN), 786);
|
|
|
|
// Test: Make sure key actually randomizes bucket placement. A fail on
|
|
// this test could be a security issue.
|
|
BOOST_CHECK(info1.GetNewBucket(nKey1, EMPTY_NETGROUPMAN) != info1.GetNewBucket(nKey2, EMPTY_NETGROUPMAN));
|
|
|
|
// Test: Ports should not affect bucket placement in the addr
|
|
AddrInfo info2 = AddrInfo(addr2, source1);
|
|
BOOST_CHECK(info1.GetKey() != info2.GetKey());
|
|
BOOST_CHECK_EQUAL(info1.GetNewBucket(nKey1, EMPTY_NETGROUPMAN), info2.GetNewBucket(nKey1, EMPTY_NETGROUPMAN));
|
|
|
|
std::set<int> buckets;
|
|
for (int i = 0; i < 255; i++) {
|
|
AddrInfo infoi = AddrInfo(
|
|
CAddress(ResolveService("250.1.1." + ToString(i)), NODE_NONE),
|
|
ResolveIP("250.1.1." + ToString(i)));
|
|
int bucket = infoi.GetNewBucket(nKey1, EMPTY_NETGROUPMAN);
|
|
buckets.insert(bucket);
|
|
}
|
|
// Test: IP addresses in the same group (\16 prefix for IPv4) should
|
|
// always map to the same bucket.
|
|
BOOST_CHECK_EQUAL(buckets.size(), 1U);
|
|
|
|
buckets.clear();
|
|
for (int j = 0; j < 4 * 255; j++) {
|
|
AddrInfo infoj = AddrInfo(CAddress(
|
|
ResolveService(
|
|
ToString(250 + (j / 255)) + "." + ToString(j % 256) + ".1.1"), NODE_NONE),
|
|
ResolveIP("251.4.1.1"));
|
|
int bucket = infoj.GetNewBucket(nKey1, EMPTY_NETGROUPMAN);
|
|
buckets.insert(bucket);
|
|
}
|
|
// Test: IP addresses in the same source groups should map to NO MORE
|
|
// than 64 buckets.
|
|
BOOST_CHECK(buckets.size() <= 64);
|
|
|
|
buckets.clear();
|
|
for (int p = 0; p < 255; p++) {
|
|
AddrInfo infoj = AddrInfo(
|
|
CAddress(ResolveService("250.1.1.1"), NODE_NONE),
|
|
ResolveIP("250." + ToString(p) + ".1.1"));
|
|
int bucket = infoj.GetNewBucket(nKey1, EMPTY_NETGROUPMAN);
|
|
buckets.insert(bucket);
|
|
}
|
|
// Test: IP addresses in the different source groups should map to MORE
|
|
// than 64 buckets.
|
|
BOOST_CHECK(buckets.size() > 64);
|
|
}
|
|
|
|
// The following three test cases use asmap.raw
|
|
// We use an artificial minimal mock mapping
|
|
// 250.0.0.0/8 AS1000
|
|
// 101.1.0.0/16 AS1
|
|
// 101.2.0.0/16 AS2
|
|
// 101.3.0.0/16 AS3
|
|
// 101.4.0.0/16 AS4
|
|
// 101.5.0.0/16 AS5
|
|
// 101.6.0.0/16 AS6
|
|
// 101.7.0.0/16 AS7
|
|
// 101.8.0.0/16 AS8
|
|
BOOST_AUTO_TEST_CASE(caddrinfo_get_tried_bucket)
|
|
{
|
|
std::vector<bool> asmap = FromBytes(asmap_raw, sizeof(asmap_raw) * 8);
|
|
NetGroupManager ngm_asmap{asmap};
|
|
|
|
CAddress addr1 = CAddress(ResolveService("250.1.1.1", 8333), NODE_NONE);
|
|
CAddress addr2 = CAddress(ResolveService("250.1.1.1", 9999), NODE_NONE);
|
|
|
|
CNetAddr source1 = ResolveIP("250.1.1.1");
|
|
|
|
|
|
AddrInfo info1 = AddrInfo(addr1, source1);
|
|
|
|
uint256 nKey1 = (uint256)(CHashWriter(SER_GETHASH, 0) << 1).GetHash();
|
|
uint256 nKey2 = (uint256)(CHashWriter(SER_GETHASH, 0) << 2).GetHash();
|
|
|
|
BOOST_CHECK_EQUAL(info1.GetTriedBucket(nKey1, ngm_asmap), 236);
|
|
|
|
// Test: Make sure key actually randomizes bucket placement. A fail on
|
|
// this test could be a security issue.
|
|
BOOST_CHECK(info1.GetTriedBucket(nKey1, ngm_asmap) != info1.GetTriedBucket(nKey2, ngm_asmap));
|
|
|
|
// Test: Two addresses with same IP but different ports can map to
|
|
// different buckets because they have different keys.
|
|
AddrInfo info2 = AddrInfo(addr2, source1);
|
|
|
|
BOOST_CHECK(info1.GetKey() != info2.GetKey());
|
|
BOOST_CHECK(info1.GetTriedBucket(nKey1, ngm_asmap) != info2.GetTriedBucket(nKey1, ngm_asmap));
|
|
|
|
std::set<int> buckets;
|
|
for (int j = 0; j < 255; j++) {
|
|
AddrInfo infoj = AddrInfo(
|
|
CAddress(ResolveService("101." + ToString(j) + ".1.1"), NODE_NONE),
|
|
ResolveIP("101." + ToString(j) + ".1.1"));
|
|
int bucket = infoj.GetTriedBucket(nKey1, ngm_asmap);
|
|
buckets.insert(bucket);
|
|
}
|
|
// Test: IP addresses in the different /16 prefix MAY map to more than
|
|
// 8 buckets.
|
|
BOOST_CHECK(buckets.size() > 8);
|
|
|
|
buckets.clear();
|
|
for (int j = 0; j < 255; j++) {
|
|
AddrInfo infoj = AddrInfo(
|
|
CAddress(ResolveService("250." + ToString(j) + ".1.1"), NODE_NONE),
|
|
ResolveIP("250." + ToString(j) + ".1.1"));
|
|
int bucket = infoj.GetTriedBucket(nKey1, ngm_asmap);
|
|
buckets.insert(bucket);
|
|
}
|
|
// Test: IP addresses in the different /16 prefix MAY NOT map to more than
|
|
// 8 buckets.
|
|
BOOST_CHECK(buckets.size() == 8);
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(caddrinfo_get_new_bucket)
|
|
{
|
|
std::vector<bool> asmap = FromBytes(asmap_raw, sizeof(asmap_raw) * 8);
|
|
NetGroupManager ngm_asmap{asmap};
|
|
|
|
CAddress addr1 = CAddress(ResolveService("250.1.2.1", 8333), NODE_NONE);
|
|
CAddress addr2 = CAddress(ResolveService("250.1.2.1", 9999), NODE_NONE);
|
|
|
|
CNetAddr source1 = ResolveIP("250.1.2.1");
|
|
|
|
AddrInfo info1 = AddrInfo(addr1, source1);
|
|
|
|
uint256 nKey1 = (uint256)(CHashWriter(SER_GETHASH, 0) << 1).GetHash();
|
|
uint256 nKey2 = (uint256)(CHashWriter(SER_GETHASH, 0) << 2).GetHash();
|
|
|
|
// Test: Make sure the buckets are what we expect
|
|
BOOST_CHECK_EQUAL(info1.GetNewBucket(nKey1, ngm_asmap), 795);
|
|
BOOST_CHECK_EQUAL(info1.GetNewBucket(nKey1, source1, ngm_asmap), 795);
|
|
|
|
// Test: Make sure key actually randomizes bucket placement. A fail on
|
|
// this test could be a security issue.
|
|
BOOST_CHECK(info1.GetNewBucket(nKey1, ngm_asmap) != info1.GetNewBucket(nKey2, ngm_asmap));
|
|
|
|
// Test: Ports should not affect bucket placement in the addr
|
|
AddrInfo info2 = AddrInfo(addr2, source1);
|
|
BOOST_CHECK(info1.GetKey() != info2.GetKey());
|
|
BOOST_CHECK_EQUAL(info1.GetNewBucket(nKey1, ngm_asmap), info2.GetNewBucket(nKey1, ngm_asmap));
|
|
|
|
std::set<int> buckets;
|
|
for (int i = 0; i < 255; i++) {
|
|
AddrInfo infoi = AddrInfo(
|
|
CAddress(ResolveService("250.1.1." + ToString(i)), NODE_NONE),
|
|
ResolveIP("250.1.1." + ToString(i)));
|
|
int bucket = infoi.GetNewBucket(nKey1, ngm_asmap);
|
|
buckets.insert(bucket);
|
|
}
|
|
// Test: IP addresses in the same /16 prefix
|
|
// usually map to the same bucket.
|
|
BOOST_CHECK_EQUAL(buckets.size(), 1U);
|
|
|
|
buckets.clear();
|
|
for (int j = 0; j < 4 * 255; j++) {
|
|
AddrInfo infoj = AddrInfo(CAddress(
|
|
ResolveService(
|
|
ToString(250 + (j / 255)) + "." + ToString(j % 256) + ".1.1"), NODE_NONE),
|
|
ResolveIP("251.4.1.1"));
|
|
int bucket = infoj.GetNewBucket(nKey1, ngm_asmap);
|
|
buckets.insert(bucket);
|
|
}
|
|
// Test: IP addresses in the same source /16 prefix should not map to more
|
|
// than 64 buckets.
|
|
BOOST_CHECK(buckets.size() <= 64);
|
|
|
|
buckets.clear();
|
|
for (int p = 0; p < 255; p++) {
|
|
AddrInfo infoj = AddrInfo(
|
|
CAddress(ResolveService("250.1.1.1"), NODE_NONE),
|
|
ResolveIP("101." + ToString(p) + ".1.1"));
|
|
int bucket = infoj.GetNewBucket(nKey1, ngm_asmap);
|
|
buckets.insert(bucket);
|
|
}
|
|
// Test: IP addresses in the different source /16 prefixes usually map to MORE
|
|
// than 1 bucket.
|
|
BOOST_CHECK(buckets.size() > 1);
|
|
|
|
buckets.clear();
|
|
for (int p = 0; p < 255; p++) {
|
|
AddrInfo infoj = AddrInfo(
|
|
CAddress(ResolveService("250.1.1.1"), NODE_NONE),
|
|
ResolveIP("250." + ToString(p) + ".1.1"));
|
|
int bucket = infoj.GetNewBucket(nKey1, ngm_asmap);
|
|
buckets.insert(bucket);
|
|
}
|
|
// Test: IP addresses in the different source /16 prefixes sometimes map to NO MORE
|
|
// than 1 bucket.
|
|
BOOST_CHECK(buckets.size() == 1);
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(addrman_serialization)
|
|
{
|
|
std::vector<bool> asmap1 = FromBytes(asmap_raw, sizeof(asmap_raw) * 8);
|
|
NetGroupManager netgroupman{asmap1};
|
|
|
|
const auto ratio = GetCheckRatio(m_node);
|
|
auto addrman_asmap1 = std::make_unique<AddrMan>(netgroupman, DETERMINISTIC, ratio);
|
|
auto addrman_asmap1_dup = std::make_unique<AddrMan>(netgroupman, DETERMINISTIC, ratio);
|
|
auto addrman_noasmap = std::make_unique<AddrMan>(EMPTY_NETGROUPMAN, DETERMINISTIC, ratio);
|
|
|
|
CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
|
|
|
|
CAddress addr = CAddress(ResolveService("250.1.1.1"), NODE_NONE);
|
|
CNetAddr default_source;
|
|
|
|
addrman_asmap1->Add({addr}, default_source);
|
|
|
|
stream << *addrman_asmap1;
|
|
// serizalizing/deserializing addrman with the same asmap
|
|
stream >> *addrman_asmap1_dup;
|
|
|
|
AddressPosition addr_pos1 = addrman_asmap1->FindAddressEntry(addr).value();
|
|
AddressPosition addr_pos2 = addrman_asmap1_dup->FindAddressEntry(addr).value();
|
|
BOOST_CHECK(addr_pos1.multiplicity != 0);
|
|
BOOST_CHECK(addr_pos2.multiplicity != 0);
|
|
|
|
BOOST_CHECK(addr_pos1 == addr_pos2);
|
|
|
|
// deserializing asmaped peers.dat to non-asmaped addrman
|
|
stream << *addrman_asmap1;
|
|
stream >> *addrman_noasmap;
|
|
AddressPosition addr_pos3 = addrman_noasmap->FindAddressEntry(addr).value();
|
|
BOOST_CHECK(addr_pos3.multiplicity != 0);
|
|
BOOST_CHECK(addr_pos1.bucket != addr_pos3.bucket);
|
|
BOOST_CHECK(addr_pos1.position != addr_pos3.position);
|
|
|
|
// deserializing non-asmaped peers.dat to asmaped addrman
|
|
addrman_asmap1 = std::make_unique<AddrMan>(netgroupman, DETERMINISTIC, ratio);
|
|
addrman_noasmap = std::make_unique<AddrMan>(EMPTY_NETGROUPMAN, DETERMINISTIC, ratio);
|
|
addrman_noasmap->Add({addr}, default_source);
|
|
stream << *addrman_noasmap;
|
|
stream >> *addrman_asmap1;
|
|
|
|
AddressPosition addr_pos4 = addrman_asmap1->FindAddressEntry(addr).value();
|
|
BOOST_CHECK(addr_pos4.multiplicity != 0);
|
|
BOOST_CHECK(addr_pos4.bucket != addr_pos3.bucket);
|
|
BOOST_CHECK(addr_pos4 == addr_pos2);
|
|
|
|
// used to map to different buckets, now maps to the same bucket.
|
|
addrman_asmap1 = std::make_unique<AddrMan>(netgroupman, DETERMINISTIC, ratio);
|
|
addrman_noasmap = std::make_unique<AddrMan>(EMPTY_NETGROUPMAN, DETERMINISTIC, ratio);
|
|
CAddress addr1 = CAddress(ResolveService("250.1.1.1"), NODE_NONE);
|
|
CAddress addr2 = CAddress(ResolveService("250.2.1.1"), NODE_NONE);
|
|
addrman_noasmap->Add({addr, addr2}, default_source);
|
|
AddressPosition addr_pos5 = addrman_noasmap->FindAddressEntry(addr1).value();
|
|
AddressPosition addr_pos6 = addrman_noasmap->FindAddressEntry(addr2).value();
|
|
BOOST_CHECK(addr_pos5.bucket != addr_pos6.bucket);
|
|
stream << *addrman_noasmap;
|
|
stream >> *addrman_asmap1;
|
|
AddressPosition addr_pos7 = addrman_asmap1->FindAddressEntry(addr1).value();
|
|
AddressPosition addr_pos8 = addrman_asmap1->FindAddressEntry(addr2).value();
|
|
BOOST_CHECK(addr_pos7.bucket == addr_pos8.bucket);
|
|
BOOST_CHECK(addr_pos7.position != addr_pos8.position);
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(remove_invalid)
|
|
{
|
|
// Confirm that invalid addresses are ignored in unserialization.
|
|
|
|
auto addrman = std::make_unique<AddrMan>(EMPTY_NETGROUPMAN, DETERMINISTIC, GetCheckRatio(m_node));
|
|
CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
|
|
|
|
const CAddress new1{ResolveService("5.5.5.5"), NODE_NONE};
|
|
const CAddress new2{ResolveService("6.6.6.6"), NODE_NONE};
|
|
const CAddress tried1{ResolveService("7.7.7.7"), NODE_NONE};
|
|
const CAddress tried2{ResolveService("8.8.8.8"), NODE_NONE};
|
|
|
|
addrman->Add({new1, tried1, new2, tried2}, CNetAddr{});
|
|
addrman->Good(tried1);
|
|
addrman->Good(tried2);
|
|
BOOST_REQUIRE_EQUAL(addrman->Size(), 4);
|
|
|
|
stream << *addrman;
|
|
|
|
const std::string str{stream.str()};
|
|
size_t pos;
|
|
|
|
const char new2_raw[]{6, 6, 6, 6};
|
|
const uint8_t new2_raw_replacement[]{0, 0, 0, 0}; // 0.0.0.0 is !IsValid()
|
|
pos = str.find(new2_raw, 0, sizeof(new2_raw));
|
|
BOOST_REQUIRE(pos != std::string::npos);
|
|
BOOST_REQUIRE(pos + sizeof(new2_raw_replacement) <= stream.size());
|
|
memcpy(stream.data() + pos, new2_raw_replacement, sizeof(new2_raw_replacement));
|
|
|
|
const char tried2_raw[]{8, 8, 8, 8};
|
|
const uint8_t tried2_raw_replacement[]{255, 255, 255, 255}; // 255.255.255.255 is !IsValid()
|
|
pos = str.find(tried2_raw, 0, sizeof(tried2_raw));
|
|
BOOST_REQUIRE(pos != std::string::npos);
|
|
BOOST_REQUIRE(pos + sizeof(tried2_raw_replacement) <= stream.size());
|
|
memcpy(stream.data() + pos, tried2_raw_replacement, sizeof(tried2_raw_replacement));
|
|
|
|
addrman = std::make_unique<AddrMan>(EMPTY_NETGROUPMAN, DETERMINISTIC, GetCheckRatio(m_node));
|
|
stream >> *addrman;
|
|
BOOST_CHECK_EQUAL(addrman->Size(), 2);
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(addrman_selecttriedcollision)
|
|
{
|
|
auto addrman = std::make_unique<AddrMan>(EMPTY_NETGROUPMAN, DETERMINISTIC, GetCheckRatio(m_node));
|
|
|
|
BOOST_CHECK(addrman->Size() == 0);
|
|
|
|
// Empty addrman should return blank addrman info.
|
|
BOOST_CHECK(addrman->SelectTriedCollision().first.ToStringAddrPort() == "[::]:0");
|
|
|
|
// Add twenty two addresses.
|
|
CNetAddr source = ResolveIP("252.2.2.2");
|
|
for (unsigned int i = 1; i < 23; i++) {
|
|
CService addr = ResolveService("250.1.1." + ToString(i));
|
|
BOOST_CHECK(addrman->Add({CAddress(addr, NODE_NONE)}, source));
|
|
|
|
// No collisions in tried.
|
|
BOOST_CHECK(addrman->Good(addr));
|
|
BOOST_CHECK(addrman->SelectTriedCollision().first.ToStringAddrPort() == "[::]:0");
|
|
}
|
|
|
|
// Ensure Good handles duplicates well.
|
|
// If an address is a duplicate, Good will return false but will not count it as a collision.
|
|
for (unsigned int i = 1; i < 23; i++) {
|
|
CService addr = ResolveService("250.1.1." + ToString(i));
|
|
|
|
// Unable to add duplicate address to tried table.
|
|
BOOST_CHECK(!addrman->Good(addr));
|
|
|
|
// Verify duplicate address not marked as a collision.
|
|
BOOST_CHECK(addrman->SelectTriedCollision().first.ToStringAddrPort() == "[::]:0");
|
|
}
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(addrman_noevict)
|
|
{
|
|
auto addrman = std::make_unique<AddrMan>(EMPTY_NETGROUPMAN, DETERMINISTIC, GetCheckRatio(m_node));
|
|
|
|
// Add 35 addresses.
|
|
CNetAddr source = ResolveIP("252.2.2.2");
|
|
for (unsigned int i = 1; i < 36; i++) {
|
|
CService addr = ResolveService("250.1.1." + ToString(i));
|
|
BOOST_CHECK(addrman->Add({CAddress(addr, NODE_NONE)}, source));
|
|
|
|
// No collision yet.
|
|
BOOST_CHECK(addrman->Good(addr));
|
|
}
|
|
|
|
// Collision in tried table between 36 and 19.
|
|
CService addr36 = ResolveService("250.1.1.36");
|
|
BOOST_CHECK(addrman->Add({CAddress(addr36, NODE_NONE)}, source));
|
|
BOOST_CHECK(!addrman->Good(addr36));
|
|
BOOST_CHECK_EQUAL(addrman->SelectTriedCollision().first.ToStringAddrPort(), "250.1.1.19:0");
|
|
|
|
// 36 should be discarded and 19 not evicted.
|
|
// This means we keep 19 in the tried table and
|
|
// 36 stays in the new table.
|
|
addrman->ResolveCollisions();
|
|
BOOST_CHECK(addrman->SelectTriedCollision().first.ToStringAddrPort() == "[::]:0");
|
|
|
|
// Lets create two collisions.
|
|
for (unsigned int i = 37; i < 59; i++) {
|
|
CService addr = ResolveService("250.1.1." + ToString(i));
|
|
BOOST_CHECK(addrman->Add({CAddress(addr, NODE_NONE)}, source));
|
|
BOOST_CHECK(addrman->Good(addr));
|
|
}
|
|
|
|
// Cause a collision in the tried table.
|
|
CService addr59 = ResolveService("250.1.1.59");
|
|
BOOST_CHECK(addrman->Add({CAddress(addr59, NODE_NONE)}, source));
|
|
BOOST_CHECK(!addrman->Good(addr59));
|
|
|
|
BOOST_CHECK_EQUAL(addrman->SelectTriedCollision().first.ToStringAddrPort(), "250.1.1.10:0");
|
|
|
|
// Cause a second collision in the new table.
|
|
BOOST_CHECK(!addrman->Add({CAddress(addr36, NODE_NONE)}, source));
|
|
|
|
// 36 still cannot be moved from new to tried due to colliding with 19
|
|
BOOST_CHECK(!addrman->Good(addr36));
|
|
BOOST_CHECK(addrman->SelectTriedCollision().first.ToStringAddrPort() != "[::]:0");
|
|
|
|
// Resolve all collisions.
|
|
addrman->ResolveCollisions();
|
|
BOOST_CHECK(addrman->SelectTriedCollision().first.ToStringAddrPort() == "[::]:0");
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(addrman_evictionworks)
|
|
{
|
|
auto addrman = std::make_unique<AddrMan>(EMPTY_NETGROUPMAN, DETERMINISTIC, GetCheckRatio(m_node));
|
|
|
|
BOOST_CHECK(addrman->Size() == 0);
|
|
|
|
// Empty addrman should return blank addrman info.
|
|
BOOST_CHECK(addrman->SelectTriedCollision().first.ToStringAddrPort() == "[::]:0");
|
|
|
|
// Add 35 addresses
|
|
CNetAddr source = ResolveIP("252.2.2.2");
|
|
for (unsigned int i = 1; i < 36; i++) {
|
|
CService addr = ResolveService("250.1.1." + ToString(i));
|
|
BOOST_CHECK(addrman->Add({CAddress(addr, NODE_NONE)}, source));
|
|
|
|
// No collision yet.
|
|
BOOST_CHECK(addrman->Good(addr));
|
|
}
|
|
|
|
// Collision between 36 and 19.
|
|
CService addr = ResolveService("250.1.1.36");
|
|
BOOST_CHECK(addrman->Add({CAddress(addr, NODE_NONE)}, source));
|
|
BOOST_CHECK(!addrman->Good(addr));
|
|
|
|
auto info = addrman->SelectTriedCollision().first;
|
|
BOOST_CHECK_EQUAL(info.ToStringAddrPort(), "250.1.1.19:0");
|
|
|
|
// Ensure test of address fails, so that it is evicted.
|
|
// Update entry in tried by setting last good connection in the deep past.
|
|
BOOST_CHECK(!addrman->Good(info, NodeSeconds{1s}));
|
|
addrman->Attempt(info, /*fCountFailure=*/false, Now<NodeSeconds>() - 61s);
|
|
|
|
// Should swap 36 for 19.
|
|
addrman->ResolveCollisions();
|
|
BOOST_CHECK(addrman->SelectTriedCollision().first.ToStringAddrPort() == "[::]: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
|
|
// are attempting to add a duplicate.
|
|
// We check this by verifying Good() returns false and also verifying that
|
|
// we have no collisions.
|
|
BOOST_CHECK(!addrman->Good(addr));
|
|
BOOST_CHECK(addrman->SelectTriedCollision().first.ToStringAddrPort() == "[::]:0");
|
|
|
|
// 19 should fail as a collision (not a duplicate) if we now attempt to move
|
|
// it to the tried table.
|
|
CService addr19 = ResolveService("250.1.1.19");
|
|
BOOST_CHECK(!addrman->Good(addr19));
|
|
BOOST_CHECK_EQUAL(addrman->SelectTriedCollision().first.ToStringAddrPort(), "250.1.1.36:0");
|
|
|
|
// Eviction is also successful if too much time has passed since last try
|
|
SetMockTime(GetTime() + 4 * 60 *60);
|
|
addrman->ResolveCollisions();
|
|
BOOST_CHECK(addrman->SelectTriedCollision().first.ToStringAddrPort() == "[::]: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)
|
|
{
|
|
CDataStream ssPeersIn(SER_DISK, CLIENT_VERSION);
|
|
ssPeersIn << Params().MessageStart();
|
|
ssPeersIn << addrman;
|
|
return ssPeersIn;
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(load_addrman)
|
|
{
|
|
AddrMan addrman{EMPTY_NETGROUPMAN, DETERMINISTIC, GetCheckRatio(m_node)};
|
|
|
|
CService addr1, addr2, addr3;
|
|
BOOST_CHECK(Lookup("250.7.1.1", addr1, 8333, false));
|
|
BOOST_CHECK(Lookup("250.7.2.2", addr2, 9999, false));
|
|
BOOST_CHECK(Lookup("250.7.3.3", addr3, 9999, false));
|
|
BOOST_CHECK(Lookup("250.7.3.3"s, addr3, 9999, false));
|
|
BOOST_CHECK(!Lookup("250.7.3.3\0example.com"s, addr3, 9999, false));
|
|
|
|
// Add three addresses to new table.
|
|
CService source;
|
|
BOOST_CHECK(Lookup("252.5.1.1", source, 8333, false));
|
|
std::vector<CAddress> addresses{CAddress(addr1, NODE_NONE), CAddress(addr2, NODE_NONE), CAddress(addr3, NODE_NONE)};
|
|
BOOST_CHECK(addrman.Add(addresses, source));
|
|
BOOST_CHECK(addrman.Size() == 3);
|
|
|
|
// Test that the de-serialization does not throw an exception.
|
|
CDataStream ssPeers1 = AddrmanToStream(addrman);
|
|
bool exceptionThrown = false;
|
|
AddrMan addrman1{EMPTY_NETGROUPMAN, !DETERMINISTIC, GetCheckRatio(m_node)};
|
|
|
|
BOOST_CHECK(addrman1.Size() == 0);
|
|
try {
|
|
unsigned char pchMsgTmp[4];
|
|
ssPeers1 >> pchMsgTmp;
|
|
ssPeers1 >> addrman1;
|
|
} catch (const std::exception&) {
|
|
exceptionThrown = true;
|
|
}
|
|
|
|
BOOST_CHECK(addrman1.Size() == 3);
|
|
BOOST_CHECK(exceptionThrown == false);
|
|
|
|
// Test that ReadFromStream creates an addrman with the correct number of addrs.
|
|
CDataStream ssPeers2 = AddrmanToStream(addrman);
|
|
|
|
AddrMan addrman2{EMPTY_NETGROUPMAN, !DETERMINISTIC, GetCheckRatio(m_node)};
|
|
BOOST_CHECK(addrman2.Size() == 0);
|
|
ReadFromStream(addrman2, ssPeers2);
|
|
BOOST_CHECK(addrman2.Size() == 3);
|
|
}
|
|
|
|
// Produce a corrupt peers.dat that claims 20 addrs when it only has one addr.
|
|
static CDataStream MakeCorruptPeersDat()
|
|
{
|
|
CDataStream s(SER_DISK, CLIENT_VERSION);
|
|
s << ::Params().MessageStart();
|
|
|
|
unsigned char nVersion = 1;
|
|
s << nVersion;
|
|
s << ((unsigned char)32);
|
|
s << uint256::ONE;
|
|
s << 10; // nNew
|
|
s << 10; // nTried
|
|
|
|
int nUBuckets = ADDRMAN_NEW_BUCKET_COUNT ^ (1 << 30);
|
|
s << nUBuckets;
|
|
|
|
CService serv;
|
|
BOOST_CHECK(Lookup("252.1.1.1", serv, 7777, false));
|
|
CAddress addr = CAddress(serv, NODE_NONE);
|
|
CNetAddr resolved;
|
|
BOOST_CHECK(LookupHost("252.2.2.2", resolved, false));
|
|
AddrInfo info = AddrInfo(addr, resolved);
|
|
s << info;
|
|
|
|
return s;
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(load_addrman_corrupted)
|
|
{
|
|
// Test that the de-serialization of corrupted peers.dat throws an exception.
|
|
CDataStream ssPeers1 = MakeCorruptPeersDat();
|
|
bool exceptionThrown = false;
|
|
AddrMan addrman1{EMPTY_NETGROUPMAN, !DETERMINISTIC, GetCheckRatio(m_node)};
|
|
BOOST_CHECK(addrman1.Size() == 0);
|
|
try {
|
|
unsigned char pchMsgTmp[4];
|
|
ssPeers1 >> pchMsgTmp;
|
|
ssPeers1 >> addrman1;
|
|
} catch (const std::exception&) {
|
|
exceptionThrown = true;
|
|
}
|
|
BOOST_CHECK(exceptionThrown);
|
|
|
|
// Test that ReadFromStream fails if peers.dat is corrupt
|
|
CDataStream ssPeers2 = MakeCorruptPeersDat();
|
|
|
|
AddrMan addrman2{EMPTY_NETGROUPMAN, !DETERMINISTIC, GetCheckRatio(m_node)};
|
|
BOOST_CHECK(addrman2.Size() == 0);
|
|
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 = std::make_unique<AddrMan>(EMPTY_NETGROUPMAN, DETERMINISTIC, GetCheckRatio(m_node));
|
|
CNetAddr source{ResolveIP("252.2.2.2")};
|
|
CAddress addr{CAddress(ResolveService("250.1.1.1", 8333), NODE_NONE)};
|
|
|
|
const auto start_time{Now<NodeSeconds>() - 10000s};
|
|
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(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 + 10000s);
|
|
BOOST_CHECK_EQUAL(vAddr2.at(0).nServices, NODE_NETWORK_LIMITED);
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(addrman_size)
|
|
{
|
|
auto addrman = std::make_unique<AddrMan>(EMPTY_NETGROUPMAN, DETERMINISTIC, GetCheckRatio(m_node));
|
|
const CNetAddr source = ResolveIP("252.2.2.2");
|
|
|
|
// empty addrman
|
|
BOOST_CHECK_EQUAL(addrman->Size(/*net=*/std::nullopt, /*in_new=*/std::nullopt), 0U);
|
|
BOOST_CHECK_EQUAL(addrman->Size(/*net=*/NET_IPV4, /*in_new=*/std::nullopt), 0U);
|
|
BOOST_CHECK_EQUAL(addrman->Size(/*net=*/std::nullopt, /*in_new=*/true), 0U);
|
|
BOOST_CHECK_EQUAL(addrman->Size(/*net=*/NET_IPV4, /*in_new=*/false), 0U);
|
|
|
|
// add two ipv4 addresses, one to tried and new
|
|
const CAddress addr1{ResolveService("250.1.1.1", 8333), NODE_NONE};
|
|
BOOST_CHECK(addrman->Add({addr1}, source));
|
|
BOOST_CHECK(addrman->Good(addr1));
|
|
const CAddress addr2{ResolveService("250.1.1.2", 8333), NODE_NONE};
|
|
BOOST_CHECK(addrman->Add({addr2}, source));
|
|
|
|
BOOST_CHECK_EQUAL(addrman->Size(/*net=*/std::nullopt, /*in_new=*/std::nullopt), 2U);
|
|
BOOST_CHECK_EQUAL(addrman->Size(/*net=*/NET_IPV4, /*in_new=*/std::nullopt), 2U);
|
|
BOOST_CHECK_EQUAL(addrman->Size(/*net=*/std::nullopt, /*in_new=*/true), 1U);
|
|
BOOST_CHECK_EQUAL(addrman->Size(/*net=*/std::nullopt, /*in_new=*/false), 1U);
|
|
BOOST_CHECK_EQUAL(addrman->Size(/*net=*/NET_IPV4, /*in_new=*/true), 1U);
|
|
BOOST_CHECK_EQUAL(addrman->Size(/*net=*/NET_IPV4, /*in_new=*/false), 1U);
|
|
|
|
// add one i2p address to new
|
|
CService i2p_addr;
|
|
i2p_addr.SetSpecial("UDHDrtrcetjm5sxzskjyr5ztpeszydbh4dpl3pl4utgqqw2v4jna.b32.I2P");
|
|
const CAddress addr3{i2p_addr, NODE_NONE};
|
|
BOOST_CHECK(addrman->Add({addr3}, source));
|
|
BOOST_CHECK_EQUAL(addrman->Size(/*net=*/std::nullopt, /*in_new=*/std::nullopt), 3U);
|
|
BOOST_CHECK_EQUAL(addrman->Size(/*net=*/NET_IPV4, /*in_new=*/std::nullopt), 2U);
|
|
BOOST_CHECK_EQUAL(addrman->Size(/*net=*/NET_I2P, /*in_new=*/std::nullopt), 1U);
|
|
BOOST_CHECK_EQUAL(addrman->Size(/*net=*/NET_I2P, /*in_new=*/true), 1U);
|
|
BOOST_CHECK_EQUAL(addrman->Size(/*net=*/std::nullopt, /*in_new=*/true), 2U);
|
|
BOOST_CHECK_EQUAL(addrman->Size(/*net=*/std::nullopt, /*in_new=*/false), 1U);
|
|
}
|
|
|
|
BOOST_AUTO_TEST_SUITE_END()
|