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