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 <netaddress.h>
6 #include <net.h>
7 #include <test/util/net.h>
8 #include <test/util/setup_common.h>
9
10 #include <boost/test/unit_test.hpp>
11
12 #include <algorithm>
13 #include <functional>
14 #include <optional>
15 #include <unordered_set>
16 #include <vector>
17
18 BOOST_FIXTURE_TEST_SUITE(net_peer_eviction_tests, BasicTestingSetup)
19
20 // Create `num_peers` random nodes, apply setup function `candidate_setup_fn`,
21 // call ProtectEvictionCandidatesByRatio() to apply protection logic, and then
22 // return true if all of `protected_peer_ids` and none of `unprotected_peer_ids`
23 // are protected from eviction, i.e. removed from the eviction candidates.
24 bool IsProtected(int num_peers,
25 std::function<void(NodeEvictionCandidate&)> candidate_setup_fn,
26 const std::unordered_set<NodeId>& protected_peer_ids,
27 const std::unordered_set<NodeId>& unprotected_peer_ids,
28 FastRandomContext& random_context)
29 {
30 std::vector<NodeEvictionCandidate> candidates{GetRandomNodeEvictionCandidates(num_peers, random_context)};
31 for (NodeEvictionCandidate& candidate : candidates) {
32 candidate_setup_fn(candidate);
33 }
34 Shuffle(candidates.begin(), candidates.end(), random_context);
35
36 const size_t size{candidates.size()};
37 const size_t expected{size - size / 2}; // Expect half the candidates will be protected.
38 ProtectEvictionCandidatesByRatio(candidates);
39 BOOST_CHECK_EQUAL(candidates.size(), expected);
40
41 size_t unprotected_count{0};
42 for (const NodeEvictionCandidate& candidate : candidates) {
43 if (protected_peer_ids.count(candidate.id)) {
44 // this peer should have been removed from the eviction candidates
45 BOOST_TEST_MESSAGE(strprintf("expected candidate to be protected: %d", candidate.id));
check_success_fraction(const char * function,const RealType & p,RealType * result,const Policy &)46 return false;
47 }
48 if (unprotected_peer_ids.count(candidate.id)) {
49 // this peer remains in the eviction candidates, as expected
50 ++unprotected_count;
51 }
52 }
53
54 const bool is_protected{unprotected_count == unprotected_peer_ids.size()};
55 if (!is_protected) {
56 BOOST_TEST_MESSAGE(strprintf("unprotected: expected %d, actual %d",
57 unprotected_peer_ids.size(), unprotected_count));
check_dist(const char * function,const RealType & p,RealType * result,const Policy &,const boost::true_type &)58 }
59 return is_protected;
60 }
61
62 BOOST_AUTO_TEST_CASE(peer_protection_test)
check_dist(const char *,const RealType &,RealType *,const Policy &,const boost::false_type &)63 {
64 FastRandomContext random_context{true};
65 int num_peers{12};
66
67 // Expect half of the peers with greatest uptime (the lowest nTimeConnected)
68 // to be protected from eviction.
69 BOOST_CHECK(IsProtected(
70 num_peers, [](NodeEvictionCandidate& c) {
71 c.nTimeConnected = c.id;
72 c.m_is_local = false;
73 c.m_network = NET_IPV4;
74 },
75 /* protected_peer_ids */ {0, 1, 2, 3, 4, 5},
76 /* unprotected_peer_ids */ {6, 7, 8, 9, 10, 11},
77 random_context));
78
79 // Verify in the opposite direction.
80 BOOST_CHECK(IsProtected(
81 num_peers, [num_peers](NodeEvictionCandidate& c) {
82 c.nTimeConnected = num_peers - c.id;
83 c.m_is_local = false;
84 c.m_network = NET_IPV6;
85 },
86 /* protected_peer_ids */ {6, 7, 8, 9, 10, 11},
87 /* unprotected_peer_ids */ {0, 1, 2, 3, 4, 5},
88 random_context));
89
90 // Test protection of onion, localhost, and I2P peers...
91
92 // Expect 1/4 onion peers to be protected from eviction,
93 // if no localhost or I2P peers.
94 BOOST_CHECK(IsProtected(
95 num_peers, [](NodeEvictionCandidate& c) {
96 c.m_is_local = false;
97 c.m_network = (c.id == 3 || c.id == 8 || c.id == 9) ? NET_ONION : NET_IPV4;
98 },
99 /* protected_peer_ids */ {3, 8, 9},
100 /* unprotected_peer_ids */ {},
101 random_context));
102
103 // Expect 1/4 onion peers and 1/4 of the other peers to be protected,
104 // sorted by longest uptime (lowest nTimeConnected), if no localhost or I2P peers.
105 BOOST_CHECK(IsProtected(
106 num_peers, [](NodeEvictionCandidate& c) {
107 c.nTimeConnected = c.id;
108 c.m_is_local = false;
109 c.m_network = (c.id == 3 || c.id > 7) ? NET_ONION : NET_IPV6;
110 },
111 /* protected_peer_ids */ {0, 1, 2, 3, 8, 9},
112 /* unprotected_peer_ids */ {4, 5, 6, 7, 10, 11},
113 random_context));
114
115 // Expect 1/4 localhost peers to be protected from eviction,
116 // if no onion or I2P peers.
117 BOOST_CHECK(IsProtected(
118 num_peers, [](NodeEvictionCandidate& c) {
119 c.m_is_local = (c.id == 1 || c.id == 9 || c.id == 11);
120 c.m_network = NET_IPV4;
121 },
122 /* protected_peer_ids */ {1, 9, 11},
123 /* unprotected_peer_ids */ {},
124 random_context));
125
126 // Expect 1/4 localhost peers and 1/4 of the other peers to be protected,
127 // sorted by longest uptime (lowest nTimeConnected), if no onion or I2P peers.
128 BOOST_CHECK(IsProtected(
129 num_peers, [](NodeEvictionCandidate& c) {
130 c.nTimeConnected = c.id;
131 c.m_is_local = (c.id > 6);
132 c.m_network = NET_IPV6;
133 },
134 /* protected_peer_ids */ {0, 1, 2, 7, 8, 9},
135 /* unprotected_peer_ids */ {3, 4, 5, 6, 10, 11},
136 random_context));
137
138 // Expect 1/4 I2P peers to be protected from eviction,
139 // if no onion or localhost peers.
140 BOOST_CHECK(IsProtected(
141 num_peers, [](NodeEvictionCandidate& c) {
142 c.m_is_local = false;
143 c.m_network = (c.id == 2 || c.id == 7 || c.id == 10) ? NET_I2P : NET_IPV4;
144 },
145 /* protected_peer_ids */ {2, 7, 10},
146 /* unprotected_peer_ids */ {},
147 random_context));
148
149 // Expect 1/4 I2P peers and 1/4 of the other peers to be protected,
150 // sorted by longest uptime (lowest nTimeConnected), if no onion or localhost peers.
151 BOOST_CHECK(IsProtected(
152 num_peers, [](NodeEvictionCandidate& c) {
153 c.nTimeConnected = c.id;
154 c.m_is_local = false;
155 c.m_network = (c.id == 4 || c.id > 8) ? NET_I2P : NET_IPV6;
156 },
157 /* protected_peer_ids */ {0, 1, 2, 4, 9, 10},
158 /* unprotected_peer_ids */ {3, 5, 6, 7, 8, 11},
159 random_context));
160
161 // Tests with 2 networks...
162
163 // Combined test: expect having 1 localhost and 1 onion peer out of 4 to
164 // protect 1 localhost, 0 onion and 1 other peer, sorted by longest uptime;
165 // stable sort breaks tie with array order of localhost first.
166 BOOST_CHECK(IsProtected(
167 4, [](NodeEvictionCandidate& c) {
168 c.nTimeConnected = c.id;
169 c.m_is_local = (c.id == 4);
170 c.m_network = (c.id == 3) ? NET_ONION : NET_IPV4;
171 },
172 /* protected_peer_ids */ {0, 4},
173 /* unprotected_peer_ids */ {1, 2},
174 random_context));
175
176 // Combined test: expect having 1 localhost and 1 onion peer out of 7 to
177 // protect 1 localhost, 0 onion, and 2 other peers (3 total), sorted by
178 // uptime; stable sort breaks tie with array order of localhost first.
179 BOOST_CHECK(IsProtected(
180 7, [](NodeEvictionCandidate& c) {
181 c.nTimeConnected = c.id;
182 c.m_is_local = (c.id == 6);
183 c.m_network = (c.id == 5) ? NET_ONION : NET_IPV4;
184 },
185 /* protected_peer_ids */ {0, 1, 6},
186 /* unprotected_peer_ids */ {2, 3, 4, 5},
187 random_context));
188
189 // Combined test: expect having 1 localhost and 1 onion peer out of 8 to
190 // protect protect 1 localhost, 1 onion and 2 other peers (4 total), sorted
191 // by uptime; stable sort breaks tie with array order of localhost first.
192 BOOST_CHECK(IsProtected(
193 8, [](NodeEvictionCandidate& c) {
194 c.nTimeConnected = c.id;
195 c.m_is_local = (c.id == 6);
196 c.m_network = (c.id == 5) ? NET_ONION : NET_IPV4;
197 },
198 /* protected_peer_ids */ {0, 1, 5, 6},
199 /* unprotected_peer_ids */ {2, 3, 4, 7},
200 random_context));
201
202 // Combined test: expect having 3 localhost and 3 onion peers out of 12 to
203 // protect 2 localhost and 1 onion, plus 3 other peers, sorted by longest
204 // uptime; stable sort breaks ties with the array order of localhost first.
205 BOOST_CHECK(IsProtected(
206 num_peers, [](NodeEvictionCandidate& c) {
207 c.nTimeConnected = c.id;
208 c.m_is_local = (c.id == 6 || c.id == 9 || c.id == 11);
209 c.m_network = (c.id == 7 || c.id == 8 || c.id == 10) ? NET_ONION : NET_IPV6;
210 },
211 /* protected_peer_ids */ {0, 1, 2, 6, 7, 9},
212 /* unprotected_peer_ids */ {3, 4, 5, 8, 10, 11},
213 random_context));
214
215 // Combined test: expect having 4 localhost and 1 onion peer out of 12 to
216 // protect 2 localhost and 1 onion, plus 3 other peers, sorted by longest uptime.
217 BOOST_CHECK(IsProtected(
218 num_peers, [](NodeEvictionCandidate& c) {
219 c.nTimeConnected = c.id;
220 c.m_is_local = (c.id > 4 && c.id < 9);
221 c.m_network = (c.id == 10) ? NET_ONION : NET_IPV4;
222 },
223 /* protected_peer_ids */ {0, 1, 2, 5, 6, 10},
224 /* unprotected_peer_ids */ {3, 4, 7, 8, 9, 11},
225 random_context));
226
227 // Combined test: expect having 4 localhost and 2 onion peers out of 16 to
228 // protect 2 localhost and 2 onions, plus 4 other peers, sorted by longest uptime.
229 BOOST_CHECK(IsProtected(
230 16, [](NodeEvictionCandidate& c) {
231 c.nTimeConnected = c.id;
232 c.m_is_local = (c.id == 6 || c.id == 9 || c.id == 11 || c.id == 12);
233 c.m_network = (c.id == 8 || c.id == 10) ? NET_ONION : NET_IPV6;
234 },
235 /* protected_peer_ids */ {0, 1, 2, 3, 6, 8, 9, 10},
236 /* unprotected_peer_ids */ {4, 5, 7, 11, 12, 13, 14, 15},
237 random_context));
238
239 // Combined test: expect having 5 localhost and 1 onion peer out of 16 to
240 // protect 3 localhost (recovering the unused onion slot), 1 onion, and 4
241 // others, sorted by longest uptime.
242 BOOST_CHECK(IsProtected(
243 16, [](NodeEvictionCandidate& c) {
244 c.nTimeConnected = c.id;
245 c.m_is_local = (c.id > 10);
246 c.m_network = (c.id == 10) ? NET_ONION : NET_IPV4;
247 },
248 /* protected_peer_ids */ {0, 1, 2, 3, 10, 11, 12, 13},
249 /* unprotected_peer_ids */ {4, 5, 6, 7, 8, 9, 14, 15},
250 random_context));
251
252 // Combined test: expect having 1 localhost and 4 onion peers out of 16 to
253 // protect 1 localhost and 3 onions (recovering the unused localhost slot),
254 // plus 4 others, sorted by longest uptime.
255 BOOST_CHECK(IsProtected(
256 16, [](NodeEvictionCandidate& c) {
257 c.nTimeConnected = c.id;
258 c.m_is_local = (c.id == 15);
259 c.m_network = (c.id > 6 && c.id < 11) ? NET_ONION : NET_IPV6;
260 },
261 /* protected_peer_ids */ {0, 1, 2, 3, 7, 8, 9, 15},
262 /* unprotected_peer_ids */ {5, 6, 10, 11, 12, 13, 14},
263 random_context));
264
265 // Combined test: expect having 2 onion and 4 I2P out of 12 peers to protect
266 // 2 onion (prioritized for having fewer candidates) and 1 I2P, plus 3
267 // others, sorted by longest uptime.
268 BOOST_CHECK(IsProtected(
269 num_peers, [](NodeEvictionCandidate& c) {
270 c.nTimeConnected = c.id;
271 c.m_is_local = false;
272 if (c.id == 8 || c.id == 10) {
273 c.m_network = NET_ONION;
274 } else if (c.id == 6 || c.id == 9 || c.id == 11 || c.id == 12) {
275 c.m_network = NET_I2P;
276 } else {
277 c.m_network = NET_IPV4;
278 }
279 },
280 /* protected_peer_ids */ {0, 1, 2, 6, 8, 10},
281 /* unprotected_peer_ids */ {3, 4, 5, 7, 9, 11},
282 random_context));
283
284 // Tests with 3 networks...
285
286 // Combined test: expect having 1 localhost, 1 I2P and 1 onion peer out of 4
287 // to protect 1 I2P, 0 localhost, 0 onion and 1 other peer (2 total), sorted
288 // by longest uptime; stable sort breaks tie with array order of I2P first.
289 BOOST_CHECK(IsProtected(
290 4, [](NodeEvictionCandidate& c) {
291 c.nTimeConnected = c.id;
292 c.m_is_local = (c.id == 3);
293 if (c.id == 4) {
294 c.m_network = NET_I2P;
295 } else if (c.id == 2) {
296 c.m_network = NET_ONION;
297 } else {
298 c.m_network = NET_IPV6;
299 }
300 },
301 /* protected_peer_ids */ {0, 4},
302 /* unprotected_peer_ids */ {1, 2},
303 random_context));
304
305 // Combined test: expect having 1 localhost, 1 I2P and 1 onion peer out of 7
306 // to protect 1 I2P, 0 localhost, 0 onion and 2 other peers (3 total) sorted
307 // by longest uptime; stable sort breaks tie with array order of I2P first.
308 BOOST_CHECK(IsProtected(
309 7, [](NodeEvictionCandidate& c) {
310 c.nTimeConnected = c.id;
311 c.m_is_local = (c.id == 4);
312 if (c.id == 6) {
313 c.m_network = NET_I2P;
314 } else if (c.id == 5) {
315 c.m_network = NET_ONION;
316 } else {
317 c.m_network = NET_IPV6;
318 }
319 },
320 /* protected_peer_ids */ {0, 1, 6},
321 /* unprotected_peer_ids */ {2, 3, 4, 5},
322 random_context));
323
324 // Combined test: expect having 1 localhost, 1 I2P and 1 onion peer out of 8
325 // to protect 1 I2P, 1 localhost, 0 onion and 2 other peers (4 total) sorted
326 // by uptime; stable sort breaks tie with array order of I2P then localhost.
327 BOOST_CHECK(IsProtected(
328 8, [](NodeEvictionCandidate& c) {
329 c.nTimeConnected = c.id;
330 c.m_is_local = (c.id == 6);
331 if (c.id == 5) {
332 c.m_network = NET_I2P;
333 } else if (c.id == 4) {
334 c.m_network = NET_ONION;
335 } else {
336 c.m_network = NET_IPV6;
337 }
338 },
339 /* protected_peer_ids */ {0, 1, 5, 6},
340 /* unprotected_peer_ids */ {2, 3, 4, 7},
341 random_context));
342
343 // Combined test: expect having 4 localhost, 2 I2P, and 2 onion peers out of
344 // 16 to protect 1 localhost, 2 I2P, and 1 onion (4/16 total), plus 4 others
345 // for 8 total, sorted by longest uptime.
346 BOOST_CHECK(IsProtected(
347 16, [](NodeEvictionCandidate& c) {
348 c.nTimeConnected = c.id;
349 c.m_is_local = (c.id == 6 || c.id > 11);
350 if (c.id == 7 || c.id == 11) {
351 c.m_network = NET_I2P;
352 } else if (c.id == 9 || c.id == 10) {
353 c.m_network = NET_ONION;
354 } else {
355 c.m_network = NET_IPV4;
356 }
357 },
358 /* protected_peer_ids */ {0, 1, 2, 3, 6, 7, 9, 11},
359 /* unprotected_peer_ids */ {4, 5, 8, 10, 12, 13, 14, 15},
360 random_context));
361
362 // Combined test: expect having 1 localhost, 8 I2P and 1 onion peer out of
363 // 24 to protect 1, 4, and 1 (6 total), plus 6 others for 12/24 total,
364 // sorted by longest uptime.
365 BOOST_CHECK(IsProtected(
366 24, [](NodeEvictionCandidate& c) {
367 c.nTimeConnected = c.id;
368 c.m_is_local = (c.id == 12);
369 if (c.id > 14 && c.id < 23) { // 4 protected instead of usual 2
370 c.m_network = NET_I2P;
371 } else if (c.id == 23) {
372 c.m_network = NET_ONION;
373 } else {
374 c.m_network = NET_IPV6;
375 }
376 },
377 /* protected_peer_ids */ {0, 1, 2, 3, 4, 5, 12, 15, 16, 17, 18, 23},
378 /* unprotected_peer_ids */ {6, 7, 8, 9, 10, 11, 13, 14, 19, 20, 21, 22},
379 random_context));
380
381 // Combined test: expect having 1 localhost, 3 I2P and 6 onion peers out of
382 // 24 to protect 1, 3, and 2 (6 total, I2P has fewer candidates and so gets the
383 // unused localhost slot), plus 6 others for 12/24 total, sorted by longest uptime.
384 BOOST_CHECK(IsProtected(
385 24, [](NodeEvictionCandidate& c) {
386 c.nTimeConnected = c.id;
387 c.m_is_local = (c.id == 15);
388 if (c.id == 12 || c.id == 14 || c.id == 17) {
389 c.m_network = NET_I2P;
390 } else if (c.id > 17) { // 4 protected instead of usual 2
391 c.m_network = NET_ONION;
392 } else {
393 c.m_network = NET_IPV4;
394 }
395 },
396 /* protected_peer_ids */ {0, 1, 2, 3, 4, 5, 12, 14, 15, 17, 18, 19},
397 /* unprotected_peer_ids */ {6, 7, 8, 9, 10, 11, 13, 16, 20, 21, 22, 23},
398 random_context));
399
400 // Combined test: expect having 1 localhost, 7 I2P and 4 onion peers out of
401 // 24 to protect 1 localhost, 2 I2P, and 3 onions (6 total), plus 6 others
402 // for 12/24 total, sorted by longest uptime.
403 BOOST_CHECK(IsProtected(
404 24, [](NodeEvictionCandidate& c) {
405 c.nTimeConnected = c.id;
406 c.m_is_local = (c.id == 13);
407 if (c.id > 16) {
408 c.m_network = NET_I2P;
409 } else if (c.id == 12 || c.id == 14 || c.id == 15 || c.id == 16) {
410 c.m_network = NET_ONION;
411 } else {
412 c.m_network = NET_IPV6;
413 }
414 },
415 /* protected_peer_ids */ {0, 1, 2, 3, 4, 5, 12, 13, 14, 15, 17, 18},
416 /* unprotected_peer_ids */ {6, 7, 8, 9, 10, 11, 16, 19, 20, 21, 22, 23},
417 random_context));
418
419 // Combined test: expect having 8 localhost, 4 I2P, and 3 onion peers out of
420 // 24 to protect 2 of each (6 total), plus 6 others for 12/24 total, sorted
421 // by longest uptime.
422 BOOST_CHECK(IsProtected(
423 24, [](NodeEvictionCandidate& c) {
424 c.nTimeConnected = c.id;
425 c.m_is_local = (c.id > 15);
426 if (c.id > 10 && c.id < 15) {
427 c.m_network = NET_I2P;
428 } else if (c.id > 6 && c.id < 10) {
429 c.m_network = NET_ONION;
430 } else {
431 c.m_network = NET_IPV4;
432 }
433 },
434 /* protected_peer_ids */ {0, 1, 2, 3, 4, 5, 7, 8, 11, 12, 16, 17},
435 /* unprotected_peer_ids */ {6, 9, 10, 13, 14, 15, 18, 19, 20, 21, 22, 23},
436 random_context));
437 }
438
439 // Returns true if any of the node ids in node_ids are selected for eviction.
440 bool IsEvicted(std::vector<NodeEvictionCandidate> candidates, const std::unordered_set<NodeId>& node_ids, FastRandomContext& random_context)
441 {
442 Shuffle(candidates.begin(), candidates.end(), random_context);
443 const std::optional<NodeId> evicted_node_id = SelectNodeToEvict(std::move(candidates));
444 if (!evicted_node_id) {
445 return false;
446 }
447 return node_ids.count(*evicted_node_id);
448 }
449
450 // Create number_of_nodes random nodes, apply setup function candidate_setup_fn,
451 // apply eviction logic and then return true if any of the node ids in node_ids
452 // are selected for eviction.
453 bool IsEvicted(const int number_of_nodes, std::function<void(NodeEvictionCandidate&)> candidate_setup_fn, const std::unordered_set<NodeId>& node_ids, FastRandomContext& random_context)
454 {
455 std::vector<NodeEvictionCandidate> candidates = GetRandomNodeEvictionCandidates(number_of_nodes, random_context);
456 for (NodeEvictionCandidate& candidate : candidates) {
457 candidate_setup_fn(candidate);
458 }
459 return IsEvicted(candidates, node_ids, random_context);
460 }
461
462 BOOST_AUTO_TEST_CASE(peer_eviction_test)
463 {
464 FastRandomContext random_context{true};
465
466 for (int number_of_nodes = 0; number_of_nodes < 200; ++number_of_nodes) {
467 // Four nodes with the highest keyed netgroup values should be
468 // protected from eviction.
469 BOOST_CHECK(!IsEvicted(
470 number_of_nodes, [number_of_nodes](NodeEvictionCandidate& candidate) {
471 candidate.nKeyedNetGroup = number_of_nodes - candidate.id;
472 },
473 {0, 1, 2, 3}, random_context));
474
475 // Eight nodes with the lowest minimum ping time should be protected
476 // from eviction.
477 BOOST_CHECK(!IsEvicted(
478 number_of_nodes, [](NodeEvictionCandidate& candidate) {
479 candidate.m_min_ping_time = std::chrono::microseconds{candidate.id};
480 },
481 {0, 1, 2, 3, 4, 5, 6, 7}, random_context));
482
483 // Four nodes that most recently sent us novel transactions accepted
484 // into our mempool should be protected from eviction.
485 BOOST_CHECK(!IsEvicted(
486 number_of_nodes, [number_of_nodes](NodeEvictionCandidate& candidate) {
487 candidate.nLastTXTime = number_of_nodes - candidate.id;
488 },
489 {0, 1, 2, 3}, random_context));
490
491 // Up to eight non-tx-relay peers that most recently sent us novel
492 // blocks should be protected from eviction.
493 BOOST_CHECK(!IsEvicted(
494 number_of_nodes, [number_of_nodes](NodeEvictionCandidate& candidate) {
495 candidate.nLastBlockTime = number_of_nodes - candidate.id;
496 if (candidate.id <= 7) {
497 candidate.fRelayTxes = false;
498 candidate.fRelevantServices = true;
499 }
500 },
501 {0, 1, 2, 3, 4, 5, 6, 7}, random_context));
502
503 // Four peers that most recently sent us novel blocks should be
504 // protected from eviction.
505 BOOST_CHECK(!IsEvicted(
506 number_of_nodes, [number_of_nodes](NodeEvictionCandidate& candidate) {
507 candidate.nLastBlockTime = number_of_nodes - candidate.id;
508 },
509 {0, 1, 2, 3}, random_context));
510
511 // Combination of the previous two tests.
512 BOOST_CHECK(!IsEvicted(
513 number_of_nodes, [number_of_nodes](NodeEvictionCandidate& candidate) {
514 candidate.nLastBlockTime = number_of_nodes - candidate.id;
515 if (candidate.id <= 7) {
516 candidate.fRelayTxes = false;
517 candidate.fRelevantServices = true;
518 }
519 },
520 {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, random_context));
521
522 // Combination of all tests above.
523 BOOST_CHECK(!IsEvicted(
524 number_of_nodes, [number_of_nodes](NodeEvictionCandidate& candidate) {
525 candidate.nKeyedNetGroup = number_of_nodes - candidate.id; // 4 protected
526 candidate.m_min_ping_time = std::chrono::microseconds{candidate.id}; // 8 protected
527 candidate.nLastTXTime = number_of_nodes - candidate.id; // 4 protected
528 candidate.nLastBlockTime = number_of_nodes - candidate.id; // 4 protected
529 },
530 {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19}, random_context));
531
532 // An eviction is expected given >= 29 random eviction candidates. The eviction logic protects at most
533 // four peers by net group, eight by lowest ping time, four by last time of novel tx, up to eight non-tx-relay
534 // peers by last novel block time, and four more peers by last novel block time.
535 if (number_of_nodes >= 29) {
536 BOOST_CHECK(SelectNodeToEvict(GetRandomNodeEvictionCandidates(number_of_nodes, random_context)));
537 }
538
539 // No eviction is expected given <= 20 random eviction candidates. The eviction logic protects at least
540 // four peers by net group, eight by lowest ping time, four by last time of novel tx and four peers by last
541 // novel block time.
542 if (number_of_nodes <= 20) {
543 BOOST_CHECK(!SelectNodeToEvict(GetRandomNodeEvictionCandidates(number_of_nodes, random_context)));
544 }
545
546 // Cases left to test:
547 // * "If any remaining peers are preferred for eviction consider only them. [...]"
548 // * "Identify the network group with the most connections and youngest member. [...]"
549 }
550 }
551
552 BOOST_AUTO_TEST_SUITE_END()
553