rippled
Loading...
Searching...
No Matches
HashRouter_test.cpp
1//------------------------------------------------------------------------------
2/*
3 This file is part of rippled: https://github.com/ripple/rippled
4 Copyright (c) 2012-2015 Ripple Labs Inc.
5
6 Permission to use, copy, modify, and/or distribute this software for any
7 purpose with or without fee is hereby granted, provided that the above
8 copyright notice and this permission notice appear in all copies.
9
10 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17*/
18//==============================================================================
19
20#include <xrpld/app/misc/HashRouter.h>
21#include <xrpld/core/Config.h>
22
23#include <xrpl/basics/chrono.h>
24#include <xrpl/beast/unit_test.h>
25
26namespace ripple {
27namespace test {
28
30{
33 {
35 setup.holdTime = hold;
36 setup.relayTime = relay;
37 return setup;
38 }
39
40 void
42 {
43 testcase("Non-expiration");
44 using namespace std::chrono_literals;
46 HashRouter router(getSetup(2s, 1s), stopwatch);
47
51
52 auto const ukey1 = uint256{static_cast<std::uint64_t>(key1)};
53 auto const ukey2 = uint256{static_cast<std::uint64_t>(key2)};
54 auto const ukey3 = uint256{static_cast<std::uint64_t>(key3)};
55
56 // t=0
58 BEAST_EXPECT(router.getFlags(ukey1) == HashRouterFlags::PRIVATE1);
60 BEAST_EXPECT(router.getFlags(ukey2) == HashRouterFlags::PRIVATE2);
61 // key1 : 0
62 // key2 : 0
63 // key3: null
64
65 ++stopwatch;
66
67 // Because we are accessing key1 here, it
68 // will NOT be expired for another two ticks
69 BEAST_EXPECT(router.getFlags(ukey1) == HashRouterFlags::PRIVATE1);
70 // key1 : 1
71 // key2 : 0
72 // key3 null
73
74 ++stopwatch;
75
76 // t=3
77 router.setFlags(ukey3, HashRouterFlags::PRIVATE3); // force expiration
78 BEAST_EXPECT(router.getFlags(ukey1) == HashRouterFlags::PRIVATE1);
79 BEAST_EXPECT(router.getFlags(ukey2) == HashRouterFlags::UNDEFINED);
80 }
81
82 void
84 {
85 testcase("Expiration");
86 using namespace std::chrono_literals;
88 HashRouter router(getSetup(2s, 1s), stopwatch);
89
94
95 auto const ukey1 = uint256{static_cast<std::uint64_t>(key1)};
96 auto const ukey2 = uint256{static_cast<std::uint64_t>(key2)};
97 auto const ukey3 = uint256{static_cast<std::uint64_t>(key3)};
98 auto const ukey4 = uint256{static_cast<std::uint64_t>(key4)};
99
100 BEAST_EXPECT(key1 != key2 && key2 != key3 && key3 != key4);
101
102 // t=0
103 router.setFlags(ukey1, HashRouterFlags::BAD);
104 BEAST_EXPECT(router.getFlags(ukey1) == HashRouterFlags::BAD);
105 // key1 : 0
106 // key2 : null
107 // key3 : null
108
109 ++stopwatch;
110
111 // Expiration is triggered by insertion,
112 // and timestamps are updated on access,
113 // so key1 will be expired after the second
114 // call to setFlags.
115 // t=1
116
117 router.setFlags(ukey2, HashRouterFlags::PRIVATE5);
118 BEAST_EXPECT(router.getFlags(ukey1) == HashRouterFlags::BAD);
119 BEAST_EXPECT(router.getFlags(ukey2) == HashRouterFlags::PRIVATE5);
120 // key1 : 1
121 // key2 : 1
122 // key3 : null
123
124 ++stopwatch;
125 // t=2
126 BEAST_EXPECT(router.getFlags(ukey2) == HashRouterFlags::PRIVATE5);
127 // key1 : 1
128 // key2 : 2
129 // key3 : null
130
131 ++stopwatch;
132 // t=3
133 router.setFlags(ukey3, HashRouterFlags::BAD);
134 BEAST_EXPECT(router.getFlags(ukey1) == HashRouterFlags::UNDEFINED);
135 BEAST_EXPECT(router.getFlags(ukey2) == HashRouterFlags::PRIVATE5);
136 BEAST_EXPECT(router.getFlags(ukey3) == HashRouterFlags::BAD);
137 // key1 : 3
138 // key2 : 3
139 // key3 : 3
140
141 ++stopwatch;
142 // t=4
143 // No insertion, no expiration
144 router.setFlags(ukey1, HashRouterFlags::SAVED);
145 BEAST_EXPECT(router.getFlags(ukey1) == HashRouterFlags::SAVED);
146 BEAST_EXPECT(router.getFlags(ukey2) == HashRouterFlags::PRIVATE5);
147 BEAST_EXPECT(router.getFlags(ukey3) == HashRouterFlags::BAD);
148 // key1 : 4
149 // key2 : 4
150 // key3 : 4
151
152 ++stopwatch;
153 ++stopwatch;
154
155 // t=6
156 router.setFlags(ukey4, HashRouterFlags::TRUSTED);
157 BEAST_EXPECT(router.getFlags(ukey1) == HashRouterFlags::UNDEFINED);
158 BEAST_EXPECT(router.getFlags(ukey2) == HashRouterFlags::UNDEFINED);
159 BEAST_EXPECT(router.getFlags(ukey3) == HashRouterFlags::UNDEFINED);
160 BEAST_EXPECT(router.getFlags(ukey4) == HashRouterFlags::TRUSTED);
161 // key1 : 6
162 // key2 : 6
163 // key3 : 6
164 // key4 : 6
165 }
166
167 void
169 {
170 testcase("Suppression");
171 // Normal HashRouter
172 using namespace std::chrono_literals;
174 HashRouter router(getSetup(2s, 1s), stopwatch);
175
176 uint256 const key1(1);
177 uint256 const key2(2);
178 uint256 const key3(3);
179 uint256 const key4(4);
180 BEAST_EXPECT(key1 != key2 && key2 != key3 && key3 != key4);
181
182 HashRouterFlags flags(HashRouterFlags::BAD); // This value is ignored
183 router.addSuppression(key1);
184 BEAST_EXPECT(router.addSuppressionPeer(key2, 15));
185 BEAST_EXPECT(router.addSuppressionPeer(key3, 20, flags));
186 BEAST_EXPECT(flags == HashRouterFlags::UNDEFINED);
187
188 ++stopwatch;
189
190 BEAST_EXPECT(!router.addSuppressionPeer(key1, 2));
191 BEAST_EXPECT(!router.addSuppressionPeer(key2, 3));
192 BEAST_EXPECT(!router.addSuppressionPeer(key3, 4, flags));
193 BEAST_EXPECT(flags == HashRouterFlags::UNDEFINED);
194 BEAST_EXPECT(router.addSuppressionPeer(key4, 5));
195 }
196
197 void
199 {
200 testcase("Set Flags");
201 using namespace std::chrono_literals;
203 HashRouter router(getSetup(2s, 1s), stopwatch);
204
205 uint256 const key1(1);
206 BEAST_EXPECT(router.setFlags(key1, HashRouterFlags::PRIVATE1));
207 BEAST_EXPECT(!router.setFlags(key1, HashRouterFlags::PRIVATE1));
208 BEAST_EXPECT(router.setFlags(key1, HashRouterFlags::PRIVATE2));
209 }
210
211 void
213 {
214 testcase("Relay");
215 using namespace std::chrono_literals;
217 HashRouter router(getSetup(50s, 1s), stopwatch);
218
219 uint256 const key1(1);
220
222
223 peers = router.shouldRelay(key1);
224 BEAST_EXPECT(peers && peers->empty());
225 router.addSuppressionPeer(key1, 1);
226 router.addSuppressionPeer(key1, 3);
227 router.addSuppressionPeer(key1, 5);
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 // Get those peers we added earlier
235 peers = router.shouldRelay(key1);
236 BEAST_EXPECT(peers && peers->size() == 3);
237 router.addSuppressionPeer(key1, 2);
238 router.addSuppressionPeer(key1, 4);
239 // No action, because relayed
240 BEAST_EXPECT(!router.shouldRelay(key1));
241 // Expire, but since the next search will
242 // be for this entry, it will get refreshed
243 // instead. However, the relay won't.
244 ++stopwatch;
245 // Relay again
246 peers = router.shouldRelay(key1);
247 BEAST_EXPECT(peers && peers->size() == 2);
248 // Expire again
249 ++stopwatch;
250 // Confirm that peers list is empty.
251 peers = router.shouldRelay(key1);
252 BEAST_EXPECT(peers && peers->size() == 0);
253 }
254
255 void
257 {
258 testcase("Process");
259 using namespace std::chrono_literals;
261 HashRouter router(getSetup(5s, 1s), stopwatch);
262 uint256 const key(1);
265
266 BEAST_EXPECT(router.shouldProcess(key, peer, flags, 1s));
267 BEAST_EXPECT(!router.shouldProcess(key, peer, flags, 1s));
268 ++stopwatch;
269 ++stopwatch;
270 BEAST_EXPECT(router.shouldProcess(key, peer, flags, 1s));
271 }
272
273 void
275 {
276 testcase("setup_HashRouter");
277
278 using namespace std::chrono_literals;
279 {
280 Config cfg;
281 // default
282 auto const setup = setup_HashRouter(cfg);
283 BEAST_EXPECT(setup.holdTime == 300s);
284 BEAST_EXPECT(setup.relayTime == 30s);
285 }
286 {
287 Config cfg;
288 // non-default
289 auto& h = cfg.section("hashrouter");
290 h.set("hold_time", "600");
291 h.set("relay_time", "15");
292 auto const setup = setup_HashRouter(cfg);
293 BEAST_EXPECT(setup.holdTime == 600s);
294 BEAST_EXPECT(setup.relayTime == 15s);
295 }
296 {
297 Config cfg;
298 // equal
299 auto& h = cfg.section("hashrouter");
300 h.set("hold_time", "400");
301 h.set("relay_time", "400");
302 auto const setup = setup_HashRouter(cfg);
303 BEAST_EXPECT(setup.holdTime == 400s);
304 BEAST_EXPECT(setup.relayTime == 400s);
305 }
306 {
307 Config cfg;
308 // wrong order
309 auto& h = cfg.section("hashrouter");
310 h.set("hold_time", "60");
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 relay time must be less than or equal to hold "
321 "time";
322 BEAST_EXPECT(e.what() == expected);
323 }
324 }
325 {
326 Config cfg;
327 // too small hold
328 auto& h = cfg.section("hashrouter");
329 h.set("hold_time", "10");
330 h.set("relay_time", "120");
331 try
332 {
333 setup_HashRouter(cfg);
334 fail();
335 }
336 catch (std::exception const& e)
337 {
338 std::string expected =
339 "HashRouter hold time must be at least 12 seconds (the "
340 "approximate validation time for three "
341 "ledgers).";
342 BEAST_EXPECT(e.what() == expected);
343 }
344 }
345 {
346 Config cfg;
347 // too small relay
348 auto& h = cfg.section("hashrouter");
349 h.set("hold_time", "500");
350 h.set("relay_time", "6");
351 try
352 {
353 setup_HashRouter(cfg);
354 fail();
355 }
356 catch (std::exception const& e)
357 {
358 std::string expected =
359 "HashRouter relay time must be at least 8 seconds (the "
360 "approximate validation time for two ledgers).";
361 BEAST_EXPECT(e.what() == expected);
362 }
363 }
364 {
365 Config cfg;
366 // garbage
367 auto& h = cfg.section("hashrouter");
368 h.set("hold_time", "alice");
369 h.set("relay_time", "bob");
370 auto const setup = setup_HashRouter(cfg);
371 // The set function ignores values that don't covert, so the
372 // defaults are left unchanged
373 BEAST_EXPECT(setup.holdTime == 300s);
374 BEAST_EXPECT(setup.relayTime == 30s);
375 }
376 }
377
378 void
380 {
381 testcase("Bitwise Operations");
382
383 using HF = HashRouterFlags;
384 using UHF = std::underlying_type_t<HF>;
385
386 HF f1 = HF::BAD;
387 HF f2 = HF::SAVED;
388 HF combined = f1 | f2;
389
390 BEAST_EXPECT(
391 static_cast<UHF>(combined) ==
392 (static_cast<UHF>(f1) | static_cast<UHF>(f2)));
393
394 HF temp = f1;
395 temp |= f2;
396 BEAST_EXPECT(temp == combined);
397
398 HF intersect = combined & f1;
399 BEAST_EXPECT(intersect == f1);
400
401 HF temp2 = combined;
402 temp2 &= f1;
403 BEAST_EXPECT(temp2 == f1);
404
405 BEAST_EXPECT(any(f1));
406 BEAST_EXPECT(any(f2));
407 BEAST_EXPECT(any(combined));
408 BEAST_EXPECT(!any(HF::UNDEFINED));
409 }
410
411public:
412 void
413 run() override
414 {
418 testSetFlags();
419 testRelay();
420 testProcess();
421 testSetup();
422 testFlagsOps();
423 }
424};
425
426BEAST_DEFINE_TESTSUITE(HashRouter, app, ripple);
427
428} // namespace test
429} // namespace ripple
A testsuite class.
Definition: suite.h:55
testcase_t testcase
Memberspace for declaring test cases.
Definition: suite.h:155
void fail(String const &reason, char const *file, int line)
Record a failure.
Definition: suite.h:533
Section & section(std::string const &name)
Returns the section with the given name.
Routing table for objects identified by hash.
Definition: HashRouter.h:97
HashRouterFlags getFlags(uint256 const &key)
Definition: HashRouter.cpp:98
std::optional< std::set< PeerShortID > > shouldRelay(uint256 const &key)
Determines whether the hashed item should be relayed.
Definition: HashRouter.cpp:123
bool shouldProcess(uint256 const &key, PeerShortID peer, HashRouterFlags &flags, std::chrono::seconds tx_interval)
Definition: HashRouter.cpp:82
bool addSuppressionPeer(uint256 const &key, PeerShortID peer)
Definition: HashRouter.cpp:52
bool setFlags(uint256 const &key, HashRouterFlags flags)
Set the flags on a hash.
Definition: HashRouter.cpp:106
void addSuppression(uint256 const &key)
Definition: HashRouter.cpp:44
void set(std::string const &key, std::string const &value)
Set a key/value pair.
Definition: BasicConfig.cpp:41
void run() override
Runs the suite.
HashRouter::Setup getSetup(std::chrono::seconds hold, std::chrono::seconds relay)
Match set account flags.
Definition: flags.h:128
any_t const any
Returns an amount representing "any issuer".
Definition: amount.cpp:127
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: algorithm.h:25
HashRouterFlags
Definition: HashRouter.h:34
HashRouter::Setup setup_HashRouter(Config const &config)
Definition: HashRouter.cpp:137
Stopwatch & stopwatch()
Returns an instance of a wall clock.
Definition: chrono.h:119
Structure used to customize HashRouter behavior.
Definition: HashRouter.h:111
seconds holdTime
Expiration time for a hash entry.
Definition: HashRouter.h:119
seconds relayTime
Amount of time required before a relayed item will be relayed again.
Definition: HashRouter.h:123
T what(T... args)