15
15
16
16
BOOST_FIXTURE_TEST_SUITE (net_peer_eviction_tests, BasicTestingSetup)
17
17
18
+ namespace {
19
+ constexpr int NODE_EVICTION_TEST_ROUNDS{10 };
20
+ constexpr int NODE_EVICTION_TEST_UP_TO_N_NODES{200 };
21
+ } // namespace
22
+
18
23
std::vector<NodeEvictionCandidate> GetRandomNodeEvictionCandidates (const int n_candidates, FastRandomContext& random_context)
19
24
{
20
25
std::vector<NodeEvictionCandidate> candidates;
@@ -36,6 +41,98 @@ std::vector<NodeEvictionCandidate> GetRandomNodeEvictionCandidates(const int n_c
36
41
return candidates;
37
42
}
38
43
44
+ // Create `num_peers` random nodes, apply setup function `candidate_setup_fn`,
45
+ // call ProtectEvictionCandidatesByRatio() to apply protection logic, and then
46
+ // return true if all of `protected_peer_ids` and none of `unprotected_peer_ids`
47
+ // are protected from eviction, i.e. removed from the eviction candidates.
48
+ bool IsProtected (int num_peers,
49
+ std::function<void (NodeEvictionCandidate&)> candidate_setup_fn,
50
+ const std::unordered_set<NodeId>& protected_peer_ids,
51
+ const std::unordered_set<NodeId>& unprotected_peer_ids,
52
+ FastRandomContext& random_context)
53
+ {
54
+ std::vector<NodeEvictionCandidate> candidates{GetRandomNodeEvictionCandidates (num_peers, random_context)};
55
+ for (NodeEvictionCandidate& candidate : candidates) {
56
+ candidate_setup_fn (candidate);
57
+ }
58
+ Shuffle (candidates.begin (), candidates.end (), random_context);
59
+
60
+ const size_t size{candidates.size ()};
61
+ const size_t expected{size - size / 2 }; // Expect half the candidates will be protected.
62
+ ProtectEvictionCandidatesByRatio (candidates);
63
+ BOOST_CHECK_EQUAL (candidates.size (), expected);
64
+
65
+ size_t unprotected_count{0 };
66
+ for (const NodeEvictionCandidate& candidate : candidates) {
67
+ if (protected_peer_ids.count (candidate.id )) {
68
+ // this peer should have been removed from the eviction candidates
69
+ BOOST_TEST_MESSAGE (strprintf (" expected candidate to be protected: %d" , candidate.id ));
70
+ return false ;
71
+ }
72
+ if (unprotected_peer_ids.count (candidate.id )) {
73
+ // this peer remains in the eviction candidates, as expected
74
+ ++unprotected_count;
75
+ }
76
+ }
77
+
78
+ const bool is_protected{unprotected_count == unprotected_peer_ids.size ()};
79
+ if (!is_protected) {
80
+ BOOST_TEST_MESSAGE (strprintf (" unprotected: expected %d, actual %d" ,
81
+ unprotected_peer_ids.size (), unprotected_count));
82
+ }
83
+ return is_protected;
84
+ }
85
+
86
+ BOOST_AUTO_TEST_CASE (peer_protection_test)
87
+ {
88
+ FastRandomContext random_context{true };
89
+ int num_peers{12 };
90
+
91
+ // Expect half of the peers with greatest uptime (the lowest nTimeConnected)
92
+ // to be protected from eviction.
93
+ BOOST_CHECK (IsProtected (
94
+ num_peers, [](NodeEvictionCandidate& c) {
95
+ c.nTimeConnected = c.id ;
96
+ c.m_is_local = false ;
97
+ },
98
+ /* protected_peer_ids */ {0 , 1 , 2 , 3 , 4 , 5 },
99
+ /* unprotected_peer_ids */ {6 , 7 , 8 , 9 , 10 , 11 },
100
+ random_context));
101
+
102
+ // Verify in the opposite direction.
103
+ BOOST_CHECK (IsProtected (
104
+ num_peers, [num_peers](NodeEvictionCandidate& c) {
105
+ c.nTimeConnected = num_peers - c.id ;
106
+ c.m_is_local = false ;
107
+ },
108
+ /* protected_peer_ids */ {6 , 7 , 8 , 9 , 10 , 11 },
109
+ /* unprotected_peer_ids */ {0 , 1 , 2 , 3 , 4 , 5 },
110
+ random_context));
111
+
112
+ // Test protection of localhost peers...
113
+
114
+ // Expect 1/4 localhost peers to be protected from eviction,
115
+ // independently of other characteristics.
116
+ BOOST_CHECK (IsProtected (
117
+ num_peers, [](NodeEvictionCandidate& c) {
118
+ c.m_is_local = (c.id == 1 || c.id == 9 || c.id == 11 );
119
+ },
120
+ /* protected_peer_ids */ {1 , 9 , 11 },
121
+ /* unprotected_peer_ids */ {},
122
+ random_context));
123
+
124
+ // Expect 1/4 localhost peers and 1/4 of the others to be protected
125
+ // from eviction, sorted by longest uptime (lowest nTimeConnected).
126
+ BOOST_CHECK (IsProtected (
127
+ num_peers, [](NodeEvictionCandidate& c) {
128
+ c.nTimeConnected = c.id ;
129
+ c.m_is_local = (c.id > 6 );
130
+ },
131
+ /* protected_peer_ids */ {0 , 1 , 2 , 7 , 8 , 9 },
132
+ /* unprotected_peer_ids */ {3 , 4 , 5 , 6 , 10 , 11 },
133
+ random_context));
134
+ }
135
+
39
136
// Returns true if any of the node ids in node_ids are selected for eviction.
40
137
bool IsEvicted (std::vector<NodeEvictionCandidate> candidates, const std::unordered_set<NodeId>& node_ids, FastRandomContext& random_context)
41
138
{
@@ -59,11 +156,6 @@ bool IsEvicted(const int number_of_nodes, std::function<void(NodeEvictionCandida
59
156
return IsEvicted (candidates, node_ids, random_context);
60
157
}
61
158
62
- namespace {
63
- constexpr int NODE_EVICTION_TEST_ROUNDS{10 };
64
- constexpr int NODE_EVICTION_TEST_UP_TO_N_NODES{200 };
65
- } // namespace
66
-
67
159
BOOST_AUTO_TEST_CASE (peer_eviction_test)
68
160
{
69
161
FastRandomContext random_context{true };
@@ -150,8 +242,6 @@ BOOST_AUTO_TEST_CASE(peer_eviction_test)
150
242
}
151
243
152
244
// Cases left to test:
153
- // * "Protect the half of the remaining nodes which have been connected the longest. [...]"
154
- // * "Pick out up to 1/4 peers that are localhost, sorted by longest uptime. [...]"
155
245
// * "If any remaining peers are preferred for eviction consider only them. [...]"
156
246
// * "Identify the network group with the most connections and youngest member. [...]"
157
247
}
0 commit comments