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