rippled
Loading...
Searching...
No Matches
HashRouter_test.cpp
1#include <xrpld/app/misc/HashRouter.h>
2#include <xrpld/core/Config.h>
3
4#include <xrpl/basics/chrono.h>
5#include <xrpl/beast/unit_test.h>
6
7namespace ripple {
8namespace test {
9
11{
14 {
16 setup.holdTime = hold;
17 setup.relayTime = relay;
18 return setup;
19 }
20
21 void
23 {
24 testcase("Non-expiration");
25 using namespace std::chrono_literals;
27 HashRouter router(getSetup(2s, 1s), stopwatch);
28
32
33 auto const ukey1 = uint256{static_cast<std::uint64_t>(key1)};
34 auto const ukey2 = uint256{static_cast<std::uint64_t>(key2)};
35 auto const ukey3 = uint256{static_cast<std::uint64_t>(key3)};
36
37 // t=0
39 BEAST_EXPECT(router.getFlags(ukey1) == HashRouterFlags::PRIVATE1);
41 BEAST_EXPECT(router.getFlags(ukey2) == HashRouterFlags::PRIVATE2);
42 // key1 : 0
43 // key2 : 0
44 // key3: null
45
46 ++stopwatch;
47
48 // Because we are accessing key1 here, it
49 // will NOT be expired for another two ticks
50 BEAST_EXPECT(router.getFlags(ukey1) == HashRouterFlags::PRIVATE1);
51 // key1 : 1
52 // key2 : 0
53 // key3 null
54
55 ++stopwatch;
56
57 // t=3
58 router.setFlags(ukey3, HashRouterFlags::PRIVATE3); // force expiration
59 BEAST_EXPECT(router.getFlags(ukey1) == HashRouterFlags::PRIVATE1);
60 BEAST_EXPECT(router.getFlags(ukey2) == HashRouterFlags::UNDEFINED);
61 }
62
63 void
65 {
66 testcase("Expiration");
67 using namespace std::chrono_literals;
69 HashRouter router(getSetup(2s, 1s), stopwatch);
70
75
76 auto const ukey1 = uint256{static_cast<std::uint64_t>(key1)};
77 auto const ukey2 = uint256{static_cast<std::uint64_t>(key2)};
78 auto const ukey3 = uint256{static_cast<std::uint64_t>(key3)};
79 auto const ukey4 = uint256{static_cast<std::uint64_t>(key4)};
80
81 BEAST_EXPECT(key1 != key2 && key2 != key3 && key3 != key4);
82
83 // t=0
84 router.setFlags(ukey1, HashRouterFlags::BAD);
85 BEAST_EXPECT(router.getFlags(ukey1) == HashRouterFlags::BAD);
86 // key1 : 0
87 // key2 : null
88 // key3 : null
89
90 ++stopwatch;
91
92 // Expiration is triggered by insertion,
93 // and timestamps are updated on access,
94 // so key1 will be expired after the second
95 // call to setFlags.
96 // t=1
97
99 BEAST_EXPECT(router.getFlags(ukey1) == HashRouterFlags::BAD);
100 BEAST_EXPECT(router.getFlags(ukey2) == HashRouterFlags::PRIVATE5);
101 // key1 : 1
102 // key2 : 1
103 // key3 : null
104
105 ++stopwatch;
106 // t=2
107 BEAST_EXPECT(router.getFlags(ukey2) == HashRouterFlags::PRIVATE5);
108 // key1 : 1
109 // key2 : 2
110 // key3 : null
111
112 ++stopwatch;
113 // t=3
114 router.setFlags(ukey3, HashRouterFlags::BAD);
115 BEAST_EXPECT(router.getFlags(ukey1) == HashRouterFlags::UNDEFINED);
116 BEAST_EXPECT(router.getFlags(ukey2) == HashRouterFlags::PRIVATE5);
117 BEAST_EXPECT(router.getFlags(ukey3) == HashRouterFlags::BAD);
118 // key1 : 3
119 // key2 : 3
120 // key3 : 3
121
122 ++stopwatch;
123 // t=4
124 // No insertion, no expiration
125 router.setFlags(ukey1, HashRouterFlags::SAVED);
126 BEAST_EXPECT(router.getFlags(ukey1) == HashRouterFlags::SAVED);
127 BEAST_EXPECT(router.getFlags(ukey2) == HashRouterFlags::PRIVATE5);
128 BEAST_EXPECT(router.getFlags(ukey3) == HashRouterFlags::BAD);
129 // key1 : 4
130 // key2 : 4
131 // key3 : 4
132
133 ++stopwatch;
134 ++stopwatch;
135
136 // t=6
137 router.setFlags(ukey4, HashRouterFlags::TRUSTED);
138 BEAST_EXPECT(router.getFlags(ukey1) == HashRouterFlags::UNDEFINED);
139 BEAST_EXPECT(router.getFlags(ukey2) == HashRouterFlags::UNDEFINED);
140 BEAST_EXPECT(router.getFlags(ukey3) == HashRouterFlags::UNDEFINED);
141 BEAST_EXPECT(router.getFlags(ukey4) == HashRouterFlags::TRUSTED);
142 // key1 : 6
143 // key2 : 6
144 // key3 : 6
145 // key4 : 6
146 }
147
148 void
150 {
151 testcase("Suppression");
152 // Normal HashRouter
153 using namespace std::chrono_literals;
155 HashRouter router(getSetup(2s, 1s), stopwatch);
156
157 uint256 const key1(1);
158 uint256 const key2(2);
159 uint256 const key3(3);
160 uint256 const key4(4);
161 BEAST_EXPECT(key1 != key2 && key2 != key3 && key3 != key4);
162
163 HashRouterFlags flags(HashRouterFlags::BAD); // This value is ignored
164 router.addSuppression(key1);
165 BEAST_EXPECT(router.addSuppressionPeer(key2, 15));
166 BEAST_EXPECT(router.addSuppressionPeer(key3, 20, flags));
167 BEAST_EXPECT(flags == HashRouterFlags::UNDEFINED);
168
169 ++stopwatch;
170
171 BEAST_EXPECT(!router.addSuppressionPeer(key1, 2));
172 BEAST_EXPECT(!router.addSuppressionPeer(key2, 3));
173 BEAST_EXPECT(!router.addSuppressionPeer(key3, 4, flags));
174 BEAST_EXPECT(flags == HashRouterFlags::UNDEFINED);
175 BEAST_EXPECT(router.addSuppressionPeer(key4, 5));
176 }
177
178 void
180 {
181 testcase("Set Flags");
182 using namespace std::chrono_literals;
184 HashRouter router(getSetup(2s, 1s), stopwatch);
185
186 uint256 const key1(1);
187 BEAST_EXPECT(router.setFlags(key1, HashRouterFlags::PRIVATE1));
188 BEAST_EXPECT(!router.setFlags(key1, HashRouterFlags::PRIVATE1));
189 BEAST_EXPECT(router.setFlags(key1, HashRouterFlags::PRIVATE2));
190 }
191
192 void
194 {
195 testcase("Relay");
196 using namespace std::chrono_literals;
198 HashRouter router(getSetup(50s, 1s), stopwatch);
199
200 uint256 const key1(1);
201
203
204 peers = router.shouldRelay(key1);
205 BEAST_EXPECT(peers && peers->empty());
206 router.addSuppressionPeer(key1, 1);
207 router.addSuppressionPeer(key1, 3);
208 router.addSuppressionPeer(key1, 5);
209 // No action, because relayed
210 BEAST_EXPECT(!router.shouldRelay(key1));
211 // Expire, but since the next search will
212 // be for this entry, it will get refreshed
213 // instead. However, the relay won't.
214 ++stopwatch;
215 // Get those peers we added earlier
216 peers = router.shouldRelay(key1);
217 BEAST_EXPECT(peers && peers->size() == 3);
218 router.addSuppressionPeer(key1, 2);
219 router.addSuppressionPeer(key1, 4);
220 // No action, because relayed
221 BEAST_EXPECT(!router.shouldRelay(key1));
222 // Expire, but since the next search will
223 // be for this entry, it will get refreshed
224 // instead. However, the relay won't.
225 ++stopwatch;
226 // Relay again
227 peers = router.shouldRelay(key1);
228 BEAST_EXPECT(peers && peers->size() == 2);
229 // Expire again
230 ++stopwatch;
231 // Confirm that peers list is empty.
232 peers = router.shouldRelay(key1);
233 BEAST_EXPECT(peers && peers->size() == 0);
234 }
235
236 void
238 {
239 testcase("Process");
240 using namespace std::chrono_literals;
242 HashRouter router(getSetup(5s, 1s), stopwatch);
243 uint256 const key(1);
246
247 BEAST_EXPECT(router.shouldProcess(key, peer, flags, 1s));
248 BEAST_EXPECT(!router.shouldProcess(key, peer, flags, 1s));
249 ++stopwatch;
250 ++stopwatch;
251 BEAST_EXPECT(router.shouldProcess(key, peer, flags, 1s));
252 }
253
254 void
256 {
257 testcase("setup_HashRouter");
258
259 using namespace std::chrono_literals;
260 {
261 Config cfg;
262 // default
263 auto const setup = setup_HashRouter(cfg);
264 BEAST_EXPECT(setup.holdTime == 300s);
265 BEAST_EXPECT(setup.relayTime == 30s);
266 }
267 {
268 Config cfg;
269 // non-default
270 auto& h = cfg.section("hashrouter");
271 h.set("hold_time", "600");
272 h.set("relay_time", "15");
273 auto const setup = setup_HashRouter(cfg);
274 BEAST_EXPECT(setup.holdTime == 600s);
275 BEAST_EXPECT(setup.relayTime == 15s);
276 }
277 {
278 Config cfg;
279 // equal
280 auto& h = cfg.section("hashrouter");
281 h.set("hold_time", "400");
282 h.set("relay_time", "400");
283 auto const setup = setup_HashRouter(cfg);
284 BEAST_EXPECT(setup.holdTime == 400s);
285 BEAST_EXPECT(setup.relayTime == 400s);
286 }
287 {
288 Config cfg;
289 // wrong order
290 auto& h = cfg.section("hashrouter");
291 h.set("hold_time", "60");
292 h.set("relay_time", "120");
293 try
294 {
295 setup_HashRouter(cfg);
296 fail();
297 }
298 catch (std::exception const& e)
299 {
300 std::string expected =
301 "HashRouter relay time must be less than or equal to hold "
302 "time";
303 BEAST_EXPECT(e.what() == expected);
304 }
305 }
306 {
307 Config cfg;
308 // too small hold
309 auto& h = cfg.section("hashrouter");
310 h.set("hold_time", "10");
311 h.set("relay_time", "120");
312 try
313 {
314 setup_HashRouter(cfg);
315 fail();
316 }
317 catch (std::exception const& e)
318 {
319 std::string expected =
320 "HashRouter hold time must be at least 12 seconds (the "
321 "approximate validation time for three "
322 "ledgers).";
323 BEAST_EXPECT(e.what() == expected);
324 }
325 }
326 {
327 Config cfg;
328 // too small relay
329 auto& h = cfg.section("hashrouter");
330 h.set("hold_time", "500");
331 h.set("relay_time", "6");
332 try
333 {
334 setup_HashRouter(cfg);
335 fail();
336 }
337 catch (std::exception const& e)
338 {
339 std::string expected =
340 "HashRouter relay time must be at least 8 seconds (the "
341 "approximate validation time for two ledgers).";
342 BEAST_EXPECT(e.what() == expected);
343 }
344 }
345 {
346 Config cfg;
347 // garbage
348 auto& h = cfg.section("hashrouter");
349 h.set("hold_time", "alice");
350 h.set("relay_time", "bob");
351 auto const setup = setup_HashRouter(cfg);
352 // The set function ignores values that don't covert, so the
353 // defaults are left unchanged
354 BEAST_EXPECT(setup.holdTime == 300s);
355 BEAST_EXPECT(setup.relayTime == 30s);
356 }
357 }
358
359 void
361 {
362 testcase("Bitwise Operations");
363
364 using HF = HashRouterFlags;
365 using UHF = std::underlying_type_t<HF>;
366
367 HF f1 = HF::BAD;
368 HF f2 = HF::SAVED;
369 HF combined = f1 | f2;
370
371 BEAST_EXPECT(
372 static_cast<UHF>(combined) ==
373 (static_cast<UHF>(f1) | static_cast<UHF>(f2)));
374
375 HF temp = f1;
376 temp |= f2;
377 BEAST_EXPECT(temp == combined);
378
379 HF intersect = combined & f1;
380 BEAST_EXPECT(intersect == f1);
381
382 HF temp2 = combined;
383 temp2 &= f1;
384 BEAST_EXPECT(temp2 == f1);
385
386 BEAST_EXPECT(any(f1));
387 BEAST_EXPECT(any(f2));
388 BEAST_EXPECT(any(combined));
389 BEAST_EXPECT(!any(HF::UNDEFINED));
390 }
391
392public:
393 void
394 run() override
395 {
399 testSetFlags();
400 testRelay();
401 testProcess();
402 testSetup();
403 testFlagsOps();
404 }
405};
406
407BEAST_DEFINE_TESTSUITE(HashRouter, app, ripple);
408
409} // namespace test
410} // namespace ripple
A testsuite class.
Definition suite.h:52
testcase_t testcase
Memberspace for declaring test cases.
Definition suite.h:152
void fail(String const &reason, char const *file, int line)
Record a failure.
Definition suite.h:530
Section & section(std::string const &name)
Returns the section with the given name.
Routing table for objects identified by hash.
Definition HashRouter.h:78
HashRouterFlags getFlags(uint256 const &key)
std::optional< std::set< PeerShortID > > shouldRelay(uint256 const &key)
Determines whether the hashed item should be relayed.
bool shouldProcess(uint256 const &key, PeerShortID peer, HashRouterFlags &flags, std::chrono::seconds tx_interval)
bool addSuppressionPeer(uint256 const &key, PeerShortID peer)
bool setFlags(uint256 const &key, HashRouterFlags flags)
Set the flags on a hash.
void addSuppression(uint256 const &key)
void set(std::string const &key, std::string const &value)
Set a key/value pair.
void run() override
Runs the suite.
HashRouter::Setup getSetup(std::chrono::seconds hold, std::chrono::seconds relay)
Match set account flags.
Definition flags.h:109
any_t const any
Returns an amount representing "any issuer".
Definition amount.cpp:114
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:6
HashRouterFlags
Definition HashRouter.h:15
HashRouter::Setup setup_HashRouter(Config const &config)
Stopwatch & stopwatch()
Returns an instance of a wall clock.
Definition chrono.h:100
Structure used to customize HashRouter behavior.
Definition HashRouter.h:92
seconds holdTime
Expiration time for a hash entry.
Definition HashRouter.h:100
seconds relayTime
Amount of time required before a relayed item will be relayed again.
Definition HashRouter.h:104
T what(T... args)