Skip to content

Commit 41f84d5

Browse files
committed
Move peer eviction tests to a separate test file
out of net_tests, because the eviction tests: - are a different domain of test coverage, with different dependencies - run more slowly than the net tests - will be growing in size, in this PR branch and in the future, as eviction test coverage is improved
1 parent f126cbd commit 41f84d5

File tree

3 files changed

+161
-143
lines changed

3 files changed

+161
-143
lines changed

src/Makefile.test.include

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ BITCOIN_TESTS =\
101101
test/merkleblock_tests.cpp \
102102
test/miner_tests.cpp \
103103
test/multisig_tests.cpp \
104+
test/net_peer_eviction_tests.cpp \
104105
test/net_tests.cpp \
105106
test/netbase_tests.cpp \
106107
test/pmt_tests.cpp \

src/test/net_peer_eviction_tests.cpp

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
// Copyright (c) 2021 The Bitcoin Core developers
2+
// Distributed under the MIT software license, see the accompanying
3+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
5+
#include <net.h>
6+
#include <test/util/setup_common.h>
7+
8+
#include <boost/test/unit_test.hpp>
9+
10+
#include <algorithm>
11+
#include <functional>
12+
#include <optional>
13+
#include <vector>
14+
15+
BOOST_FIXTURE_TEST_SUITE(net_peer_eviction_tests, BasicTestingSetup)
16+
17+
std::vector<NodeEvictionCandidate> GetRandomNodeEvictionCandidates(const int n_candidates, FastRandomContext& random_context)
18+
{
19+
std::vector<NodeEvictionCandidate> candidates;
20+
for (int id = 0; id < n_candidates; ++id) {
21+
candidates.push_back({
22+
/* id */ id,
23+
/* nTimeConnected */ static_cast<int64_t>(random_context.randrange(100)),
24+
/* m_min_ping_time */ std::chrono::microseconds{random_context.randrange(100)},
25+
/* nLastBlockTime */ static_cast<int64_t>(random_context.randrange(100)),
26+
/* nLastTXTime */ static_cast<int64_t>(random_context.randrange(100)),
27+
/* fRelevantServices */ random_context.randbool(),
28+
/* fRelayTxes */ random_context.randbool(),
29+
/* fBloomFilter */ random_context.randbool(),
30+
/* nKeyedNetGroup */ random_context.randrange(100),
31+
/* prefer_evict */ random_context.randbool(),
32+
/* m_is_local */ random_context.randbool(),
33+
});
34+
}
35+
return candidates;
36+
}
37+
38+
// Returns true if any of the node ids in node_ids are selected for eviction.
39+
bool IsEvicted(std::vector<NodeEvictionCandidate> candidates, const std::vector<NodeId>& node_ids, FastRandomContext& random_context)
40+
{
41+
Shuffle(candidates.begin(), candidates.end(), random_context);
42+
const std::optional<NodeId> evicted_node_id = SelectNodeToEvict(std::move(candidates));
43+
if (!evicted_node_id) {
44+
return false;
45+
}
46+
return std::find(node_ids.begin(), node_ids.end(), *evicted_node_id) != node_ids.end();
47+
}
48+
49+
// Create number_of_nodes random nodes, apply setup function candidate_setup_fn,
50+
// apply eviction logic and then return true if any of the node ids in node_ids
51+
// are selected for eviction.
52+
bool IsEvicted(const int number_of_nodes, std::function<void(NodeEvictionCandidate&)> candidate_setup_fn, const std::vector<NodeId>& node_ids, FastRandomContext& random_context)
53+
{
54+
std::vector<NodeEvictionCandidate> candidates = GetRandomNodeEvictionCandidates(number_of_nodes, random_context);
55+
for (NodeEvictionCandidate& candidate : candidates) {
56+
candidate_setup_fn(candidate);
57+
}
58+
return IsEvicted(candidates, node_ids, random_context);
59+
}
60+
61+
namespace {
62+
constexpr int NODE_EVICTION_TEST_ROUNDS{10};
63+
constexpr int NODE_EVICTION_TEST_UP_TO_N_NODES{200};
64+
} // namespace
65+
66+
BOOST_AUTO_TEST_CASE(peer_eviction_test)
67+
{
68+
FastRandomContext random_context{true};
69+
70+
for (int i = 0; i < NODE_EVICTION_TEST_ROUNDS; ++i) {
71+
for (int number_of_nodes = 0; number_of_nodes < NODE_EVICTION_TEST_UP_TO_N_NODES; ++number_of_nodes) {
72+
// Four nodes with the highest keyed netgroup values should be
73+
// protected from eviction.
74+
BOOST_CHECK(!IsEvicted(
75+
number_of_nodes, [number_of_nodes](NodeEvictionCandidate& candidate) {
76+
candidate.nKeyedNetGroup = number_of_nodes - candidate.id;
77+
},
78+
{0, 1, 2, 3}, random_context));
79+
80+
// Eight nodes with the lowest minimum ping time should be protected
81+
// from eviction.
82+
BOOST_CHECK(!IsEvicted(
83+
number_of_nodes, [](NodeEvictionCandidate& candidate) {
84+
candidate.m_min_ping_time = std::chrono::microseconds{candidate.id};
85+
},
86+
{0, 1, 2, 3, 4, 5, 6, 7}, random_context));
87+
88+
// Four nodes that most recently sent us novel transactions accepted
89+
// into our mempool should be protected from eviction.
90+
BOOST_CHECK(!IsEvicted(
91+
number_of_nodes, [number_of_nodes](NodeEvictionCandidate& candidate) {
92+
candidate.nLastTXTime = number_of_nodes - candidate.id;
93+
},
94+
{0, 1, 2, 3}, random_context));
95+
96+
// Up to eight non-tx-relay peers that most recently sent us novel
97+
// blocks should be protected from eviction.
98+
BOOST_CHECK(!IsEvicted(
99+
number_of_nodes, [number_of_nodes](NodeEvictionCandidate& candidate) {
100+
candidate.nLastBlockTime = number_of_nodes - candidate.id;
101+
if (candidate.id <= 7) {
102+
candidate.fRelayTxes = false;
103+
candidate.fRelevantServices = true;
104+
}
105+
},
106+
{0, 1, 2, 3, 4, 5, 6, 7}, random_context));
107+
108+
// Four peers that most recently sent us novel blocks should be
109+
// protected from eviction.
110+
BOOST_CHECK(!IsEvicted(
111+
number_of_nodes, [number_of_nodes](NodeEvictionCandidate& candidate) {
112+
candidate.nLastBlockTime = number_of_nodes - candidate.id;
113+
},
114+
{0, 1, 2, 3}, random_context));
115+
116+
// Combination of the previous two tests.
117+
BOOST_CHECK(!IsEvicted(
118+
number_of_nodes, [number_of_nodes](NodeEvictionCandidate& candidate) {
119+
candidate.nLastBlockTime = number_of_nodes - candidate.id;
120+
if (candidate.id <= 7) {
121+
candidate.fRelayTxes = false;
122+
candidate.fRelevantServices = true;
123+
}
124+
},
125+
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, random_context));
126+
127+
// Combination of all tests above.
128+
BOOST_CHECK(!IsEvicted(
129+
number_of_nodes, [number_of_nodes](NodeEvictionCandidate& candidate) {
130+
candidate.nKeyedNetGroup = number_of_nodes - candidate.id; // 4 protected
131+
candidate.m_min_ping_time = std::chrono::microseconds{candidate.id}; // 8 protected
132+
candidate.nLastTXTime = number_of_nodes - candidate.id; // 4 protected
133+
candidate.nLastBlockTime = number_of_nodes - candidate.id; // 4 protected
134+
},
135+
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19}, random_context));
136+
137+
// An eviction is expected given >= 29 random eviction candidates. The eviction logic protects at most
138+
// four peers by net group, eight by lowest ping time, four by last time of novel tx, up to eight non-tx-relay
139+
// peers by last novel block time, and four more peers by last novel block time.
140+
if (number_of_nodes >= 29) {
141+
BOOST_CHECK(SelectNodeToEvict(GetRandomNodeEvictionCandidates(number_of_nodes, random_context)));
142+
}
143+
144+
// No eviction is expected given <= 20 random eviction candidates. The eviction logic protects at least
145+
// four peers by net group, eight by lowest ping time, four by last time of novel tx and four peers by last
146+
// novel block time.
147+
if (number_of_nodes <= 20) {
148+
BOOST_CHECK(!SelectNodeToEvict(GetRandomNodeEvictionCandidates(number_of_nodes, random_context)));
149+
}
150+
151+
// Cases left to test:
152+
// * "Protect the half of the remaining nodes which have been connected the longest. [...]"
153+
// * "Pick out up to 1/4 peers that are localhost, sorted by longest uptime. [...]"
154+
// * "If any remaining peers are preferred for eviction consider only them. [...]"
155+
// * "Identify the network group with the most connections and youngest member. [...]"
156+
}
157+
}
158+
}
159+
160+
BOOST_AUTO_TEST_SUITE_END()

src/test/net_tests.cpp

Lines changed: 0 additions & 143 deletions
Original file line numberDiff line numberDiff line change
@@ -802,147 +802,4 @@ BOOST_AUTO_TEST_CASE(LocalAddress_BasicLifecycle)
802802
BOOST_CHECK_EQUAL(IsLocal(addr), false);
803803
}
804804

805-
std::vector<NodeEvictionCandidate> GetRandomNodeEvictionCandidates(const int n_candidates, FastRandomContext& random_context)
806-
{
807-
std::vector<NodeEvictionCandidate> candidates;
808-
for (int id = 0; id < n_candidates; ++id) {
809-
candidates.push_back({
810-
/* id */ id,
811-
/* nTimeConnected */ static_cast<int64_t>(random_context.randrange(100)),
812-
/* m_min_ping_time */ std::chrono::microseconds{random_context.randrange(100)},
813-
/* nLastBlockTime */ static_cast<int64_t>(random_context.randrange(100)),
814-
/* nLastTXTime */ static_cast<int64_t>(random_context.randrange(100)),
815-
/* fRelevantServices */ random_context.randbool(),
816-
/* fRelayTxes */ random_context.randbool(),
817-
/* fBloomFilter */ random_context.randbool(),
818-
/* nKeyedNetGroup */ random_context.randrange(100),
819-
/* prefer_evict */ random_context.randbool(),
820-
/* m_is_local */ random_context.randbool(),
821-
});
822-
}
823-
return candidates;
824-
}
825-
826-
// Returns true if any of the node ids in node_ids are selected for eviction.
827-
bool IsEvicted(std::vector<NodeEvictionCandidate> candidates, const std::vector<NodeId>& node_ids, FastRandomContext& random_context)
828-
{
829-
Shuffle(candidates.begin(), candidates.end(), random_context);
830-
const std::optional<NodeId> evicted_node_id = SelectNodeToEvict(std::move(candidates));
831-
if (!evicted_node_id) {
832-
return false;
833-
}
834-
return std::find(node_ids.begin(), node_ids.end(), *evicted_node_id) != node_ids.end();
835-
}
836-
837-
// Create number_of_nodes random nodes, apply setup function candidate_setup_fn,
838-
// apply eviction logic and then return true if any of the node ids in node_ids
839-
// are selected for eviction.
840-
bool IsEvicted(const int number_of_nodes, std::function<void(NodeEvictionCandidate&)> candidate_setup_fn, const std::vector<NodeId>& node_ids, FastRandomContext& random_context)
841-
{
842-
std::vector<NodeEvictionCandidate> candidates = GetRandomNodeEvictionCandidates(number_of_nodes, random_context);
843-
for (NodeEvictionCandidate& candidate : candidates) {
844-
candidate_setup_fn(candidate);
845-
}
846-
return IsEvicted(candidates, node_ids, random_context);
847-
}
848-
849-
namespace {
850-
constexpr int NODE_EVICTION_TEST_ROUNDS{10};
851-
constexpr int NODE_EVICTION_TEST_UP_TO_N_NODES{200};
852-
} // namespace
853-
854-
BOOST_AUTO_TEST_CASE(node_eviction_test)
855-
{
856-
FastRandomContext random_context{true};
857-
858-
for (int i = 0; i < NODE_EVICTION_TEST_ROUNDS; ++i) {
859-
for (int number_of_nodes = 0; number_of_nodes < NODE_EVICTION_TEST_UP_TO_N_NODES; ++number_of_nodes) {
860-
// Four nodes with the highest keyed netgroup values should be
861-
// protected from eviction.
862-
BOOST_CHECK(!IsEvicted(
863-
number_of_nodes, [number_of_nodes](NodeEvictionCandidate& candidate) {
864-
candidate.nKeyedNetGroup = number_of_nodes - candidate.id;
865-
},
866-
{0, 1, 2, 3}, random_context));
867-
868-
// Eight nodes with the lowest minimum ping time should be protected
869-
// from eviction.
870-
BOOST_CHECK(!IsEvicted(
871-
number_of_nodes, [](NodeEvictionCandidate& candidate) {
872-
candidate.m_min_ping_time = std::chrono::microseconds{candidate.id};
873-
},
874-
{0, 1, 2, 3, 4, 5, 6, 7}, random_context));
875-
876-
// Four nodes that most recently sent us novel transactions accepted
877-
// into our mempool should be protected from eviction.
878-
BOOST_CHECK(!IsEvicted(
879-
number_of_nodes, [number_of_nodes](NodeEvictionCandidate& candidate) {
880-
candidate.nLastTXTime = number_of_nodes - candidate.id;
881-
},
882-
{0, 1, 2, 3}, random_context));
883-
884-
// Up to eight non-tx-relay peers that most recently sent us novel
885-
// blocks should be protected from eviction.
886-
BOOST_CHECK(!IsEvicted(
887-
number_of_nodes, [number_of_nodes](NodeEvictionCandidate& candidate) {
888-
candidate.nLastBlockTime = number_of_nodes - candidate.id;
889-
if (candidate.id <= 7) {
890-
candidate.fRelayTxes = false;
891-
candidate.fRelevantServices = true;
892-
}
893-
},
894-
{0, 1, 2, 3, 4, 5, 6, 7}, random_context));
895-
896-
// Four peers that most recently sent us novel blocks should be
897-
// protected from eviction.
898-
BOOST_CHECK(!IsEvicted(
899-
number_of_nodes, [number_of_nodes](NodeEvictionCandidate& candidate) {
900-
candidate.nLastBlockTime = number_of_nodes - candidate.id;
901-
},
902-
{0, 1, 2, 3}, random_context));
903-
904-
// Combination of the previous two tests.
905-
BOOST_CHECK(!IsEvicted(
906-
number_of_nodes, [number_of_nodes](NodeEvictionCandidate& candidate) {
907-
candidate.nLastBlockTime = number_of_nodes - candidate.id;
908-
if (candidate.id <= 7) {
909-
candidate.fRelayTxes = false;
910-
candidate.fRelevantServices = true;
911-
}
912-
},
913-
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, random_context));
914-
915-
// Combination of all tests above.
916-
BOOST_CHECK(!IsEvicted(
917-
number_of_nodes, [number_of_nodes](NodeEvictionCandidate& candidate) {
918-
candidate.nKeyedNetGroup = number_of_nodes - candidate.id; // 4 protected
919-
candidate.m_min_ping_time = std::chrono::microseconds{candidate.id}; // 8 protected
920-
candidate.nLastTXTime = number_of_nodes - candidate.id; // 4 protected
921-
candidate.nLastBlockTime = number_of_nodes - candidate.id; // 4 protected
922-
},
923-
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19}, random_context));
924-
925-
// An eviction is expected given >= 29 random eviction candidates. The eviction logic protects at most
926-
// four peers by net group, eight by lowest ping time, four by last time of novel tx, up to eight non-tx-relay
927-
// peers by last novel block time, and four more peers by last novel block time.
928-
if (number_of_nodes >= 29) {
929-
BOOST_CHECK(SelectNodeToEvict(GetRandomNodeEvictionCandidates(number_of_nodes, random_context)));
930-
}
931-
932-
// No eviction is expected given <= 20 random eviction candidates. The eviction logic protects at least
933-
// four peers by net group, eight by lowest ping time, four by last time of novel tx and four peers by last
934-
// novel block time.
935-
if (number_of_nodes <= 20) {
936-
BOOST_CHECK(!SelectNodeToEvict(GetRandomNodeEvictionCandidates(number_of_nodes, random_context)));
937-
}
938-
939-
// Cases left to test:
940-
// * "Protect the half of the remaining nodes which have been connected the longest. [...]"
941-
// * "Pick out up to 1/4 peers that are localhost, sorted by longest uptime. [...]"
942-
// * "If any remaining peers are preferred for eviction consider only them. [...]"
943-
// * "Identify the network group with the most connections and youngest member. [...]"
944-
}
945-
}
946-
}
947-
948805
BOOST_AUTO_TEST_SUITE_END()

0 commit comments

Comments
 (0)