rippled
Loading...
Searching...
No Matches
Validations_test.cpp
1#include <test/csf/Validation.h>
2#include <test/unit_test/SuiteJournal.h>
3
4#include <xrpld/consensus/Validations.h>
5
6#include <xrpl/basics/tagged_integer.h>
7#include <xrpl/beast/clock/manual_clock.h>
8#include <xrpl/beast/unit_test.h>
9
10#include <vector>
11
12namespace xrpl {
13namespace test {
14namespace csf {
16{
18
19 // Helper to convert steady_clock to a reasonable NetClock
20 // This allows a single manual clock in the unit tests
23 {
24 // We don't care about the actual epochs, but do want the
25 // generated NetClock time to be well past its epoch to ensure
26 // any subtractions are positive
27 using namespace std::chrono;
28 return NetClock::time_point(duration_cast<NetClock::duration>(c.now().time_since_epoch() + 86400s));
29 }
30
31 // Represents a node that can issue validations
32 class Node
33 {
34 clock_type const& c_;
36 bool trusted_ = true;
39
40 public:
42 {
43 }
44
45 void
47 {
48 trusted_ = false;
49 }
50
51 void
53 {
54 trusted_ = true;
55 }
56
57 void
62
63 PeerID
64 nodeID() const
65 {
66 return nodeID_;
67 }
68
69 void
71 {
72 signIdx_++;
73 }
74
76 currKey() const
77 {
79 }
80
82 masterKey() const
83 {
84 return std::make_pair(nodeID_, 0);
85 }
87 now() const
88 {
89 return toNetClock(c_);
90 }
91
92 // Issue a new validation with given sequence number and id and
93 // with signing and seen times offset from the common clock
96 Ledger::ID id,
98 NetClock::duration signOffset,
99 NetClock::duration seenOffset,
100 bool full) const
101 {
102 Validation v{id, seq, now() + signOffset, now() + seenOffset, currKey(), nodeID_, full, loadFee_};
103 if (trusted_)
104 v.setTrusted();
105 return v;
106 }
107
109 validate(Ledger ledger, NetClock::duration signOffset, NetClock::duration seenOffset) const
110 {
111 return validate(ledger.id(), ledger.seq(), signOffset, seenOffset, true);
112 }
113
115 validate(Ledger ledger) const
116 {
117 return validate(ledger.id(), ledger.seq(), NetClock::duration{0}, NetClock::duration{0}, true);
118 }
119
121 partial(Ledger ledger) const
122 {
123 return validate(ledger.id(), ledger.seq(), NetClock::duration{0}, NetClock::duration{0}, false);
124 }
125 };
126
127 // Generic Validations adaptor
129 {
132
133 public:
134 // Non-locking mutex to avoid locks in generic Validations
135 struct Mutex
136 {
137 void
139 {
140 }
141
142 void
144 {
145 }
146 };
147
150
152 {
153 }
154
156 now() const
157 {
158 return toNetClock(c_);
159 }
160
163 {
164 return oracle_.lookup(id);
165 }
166 };
167
168 // Specialize generic Validations using the above types
170
171 // Gather the dependencies of TestValidations in a single class and provide
172 // accessors for simplifying test logic
174 {
179
180 public:
182 {
183 }
184
186 add(Validation const& v)
187 {
188 return tv_.add(v.nodeID(), v);
189 }
190
193 {
194 return tv_;
195 }
196
197 Node
199 {
200 return Node(nextNodeId_++, clock_);
201 }
202
204 parms() const
205 {
206 return p_;
207 }
208
209 auto&
211 {
212 return clock_;
213 }
214 };
215
217
218 void
220 {
221 using namespace std::chrono_literals;
222
223 testcase("Add validation");
225 Ledger ledgerA = h["a"];
226 Ledger ledgerAB = h["ab"];
227 Ledger ledgerAZ = h["az"];
228 Ledger ledgerABC = h["abc"];
229 Ledger ledgerABCD = h["abcd"];
230 Ledger ledgerABCDE = h["abcde"];
231
232 {
233 TestHarness harness(h.oracle);
234 Node n = harness.makeNode();
235
236 auto const v = n.validate(ledgerA);
237
238 // Add a current validation
239 BEAST_EXPECT(ValStatus::current == harness.add(v));
240
241 // Re-adding violates the increasing seq requirement for full
242 // validations
243 BEAST_EXPECT(ValStatus::badSeq == harness.add(v));
244
245 harness.clock().advance(1s);
246
247 BEAST_EXPECT(ValStatus::current == harness.add(n.validate(ledgerAB)));
248
249 // Test the node changing signing key
250
251 // Confirm old ledger on hand, but not new ledger
252 BEAST_EXPECT(harness.vals().numTrustedForLedger(ledgerAB.id()) == 1);
253 BEAST_EXPECT(harness.vals().numTrustedForLedger(ledgerABC.id()) == 0);
254
255 // Rotate signing keys
256 n.advanceKey();
257
258 harness.clock().advance(1s);
259
260 // Cannot re-do the same full validation sequence
261 BEAST_EXPECT(ValStatus::conflicting == harness.add(n.validate(ledgerAB)));
262 // Cannot send the same partial validation sequence
263 BEAST_EXPECT(ValStatus::conflicting == harness.add(n.partial(ledgerAB)));
264
265 // Now trusts the newest ledger too
266 harness.clock().advance(1s);
267 BEAST_EXPECT(ValStatus::current == harness.add(n.validate(ledgerABC)));
268 BEAST_EXPECT(harness.vals().numTrustedForLedger(ledgerAB.id()) == 1);
269 BEAST_EXPECT(harness.vals().numTrustedForLedger(ledgerABC.id()) == 1);
270
271 // Processing validations out of order should ignore the older
272 // validation
273 harness.clock().advance(2s);
274 auto const valABCDE = n.validate(ledgerABCDE);
275
276 harness.clock().advance(4s);
277 auto const valABCD = n.validate(ledgerABCD);
278
279 BEAST_EXPECT(ValStatus::current == harness.add(valABCD));
280
281 BEAST_EXPECT(ValStatus::stale == harness.add(valABCDE));
282 }
283
284 {
285 // Process validations out of order with shifted times
286
287 TestHarness harness(h.oracle);
288 Node n = harness.makeNode();
289
290 // Establish a new current validation
291 BEAST_EXPECT(ValStatus::current == harness.add(n.validate(ledgerA)));
292
293 // Process a validation that has "later" seq but early sign time
294 BEAST_EXPECT(ValStatus::stale == harness.add(n.validate(ledgerAB, -1s, -1s)));
295
296 // Process a validation that has a later seq and later sign
297 // time
298 BEAST_EXPECT(ValStatus::current == harness.add(n.validate(ledgerABC, 1s, 1s)));
299 }
300
301 {
302 // Test stale on arrival validations
303 TestHarness harness(h.oracle);
304 Node n = harness.makeNode();
305
306 BEAST_EXPECT(
307 ValStatus::stale == harness.add(n.validate(ledgerA, -harness.parms().validationCURRENT_EARLY, 0s)));
308
309 BEAST_EXPECT(
310 ValStatus::stale == harness.add(n.validate(ledgerA, harness.parms().validationCURRENT_WALL, 0s)));
311
312 BEAST_EXPECT(
313 ValStatus::stale == harness.add(n.validate(ledgerA, 0s, harness.parms().validationCURRENT_LOCAL)));
314 }
315
316 {
317 // Test that full or partials cannot be sent for older sequence
318 // numbers, unless time-out has happened
319 for (bool doFull : {true, false})
320 {
321 TestHarness harness(h.oracle);
322 Node n = harness.makeNode();
323
324 auto process = [&](Ledger& lgr) {
325 if (doFull)
326 return harness.add(n.validate(lgr));
327 return harness.add(n.partial(lgr));
328 };
329
330 BEAST_EXPECT(ValStatus::current == process(ledgerABC));
331 harness.clock().advance(1s);
332 BEAST_EXPECT(ledgerAB.seq() < ledgerABC.seq());
333 BEAST_EXPECT(ValStatus::badSeq == process(ledgerAB));
334
335 // If we advance far enough for AB to expire, we can fully
336 // validate or partially validate that sequence number again
337 BEAST_EXPECT(ValStatus::conflicting == process(ledgerAZ));
338 harness.clock().advance(harness.parms().validationSET_EXPIRES + 1ms);
339 BEAST_EXPECT(ValStatus::current == process(ledgerAZ));
340 }
341 }
342 }
343
344 void
346 {
347 testcase("Stale validation");
348 // Verify validation becomes stale based solely on time passing, but
349 // use different functions to trigger the check for staleness
350
352 Ledger ledgerA = h["a"];
353 Ledger ledgerAB = h["ab"];
354
355 using Trigger = std::function<void(TestValidations&)>;
356
357 std::vector<Trigger> triggers = {
358 [&](TestValidations& vals) { vals.currentTrusted(); },
359 [&](TestValidations& vals) { vals.getCurrentNodeIDs(); },
360 [&](TestValidations& vals) { vals.getPreferred(genesisLedger); },
361 [&](TestValidations& vals) { vals.getNodesAfter(ledgerA, ledgerA.id()); }};
362 for (Trigger trigger : triggers)
363 {
364 TestHarness harness(h.oracle);
365 Node n = harness.makeNode();
366
367 BEAST_EXPECT(ValStatus::current == harness.add(n.validate(ledgerAB)));
368 trigger(harness.vals());
369 BEAST_EXPECT(harness.vals().getNodesAfter(ledgerA, ledgerA.id()) == 1);
370 BEAST_EXPECT(harness.vals().getPreferred(genesisLedger) == std::make_pair(ledgerAB.seq(), ledgerAB.id()));
371 harness.clock().advance(harness.parms().validationCURRENT_LOCAL);
372
373 // trigger check for stale
374 trigger(harness.vals());
375
376 BEAST_EXPECT(harness.vals().getNodesAfter(ledgerA, ledgerA.id()) == 0);
377 BEAST_EXPECT(harness.vals().getPreferred(genesisLedger) == std::nullopt);
378 }
379 }
380
381 void
383 {
384 // Test getting number of nodes working on a validation descending
385 // a prescribed one. This count should only be for trusted nodes, but
386 // includes partial and full validations
387
388 using namespace std::chrono_literals;
389 testcase("Get nodes after");
390
392 Ledger ledgerA = h["a"];
393 Ledger ledgerAB = h["ab"];
394 Ledger ledgerABC = h["abc"];
395 Ledger ledgerAD = h["ad"];
396
397 TestHarness harness(h.oracle);
398 Node a = harness.makeNode(), b = harness.makeNode(), c = harness.makeNode(), d = harness.makeNode();
399 c.untrust();
400
401 // first round a,b,c agree, d has is partial
402 BEAST_EXPECT(ValStatus::current == harness.add(a.validate(ledgerA)));
403 BEAST_EXPECT(ValStatus::current == harness.add(b.validate(ledgerA)));
404 BEAST_EXPECT(ValStatus::current == harness.add(c.validate(ledgerA)));
405 BEAST_EXPECT(ValStatus::current == harness.add(d.partial(ledgerA)));
406
407 for (Ledger const& ledger : {ledgerA, ledgerAB, ledgerABC, ledgerAD})
408 BEAST_EXPECT(harness.vals().getNodesAfter(ledger, ledger.id()) == 0);
409
410 harness.clock().advance(5s);
411
412 BEAST_EXPECT(ValStatus::current == harness.add(a.validate(ledgerAB)));
413 BEAST_EXPECT(ValStatus::current == harness.add(b.validate(ledgerABC)));
414 BEAST_EXPECT(ValStatus::current == harness.add(c.validate(ledgerAB)));
415 BEAST_EXPECT(ValStatus::current == harness.add(d.partial(ledgerABC)));
416
417 BEAST_EXPECT(harness.vals().getNodesAfter(ledgerA, ledgerA.id()) == 3);
418 BEAST_EXPECT(harness.vals().getNodesAfter(ledgerAB, ledgerAB.id()) == 2);
419 BEAST_EXPECT(harness.vals().getNodesAfter(ledgerABC, ledgerABC.id()) == 0);
420 BEAST_EXPECT(harness.vals().getNodesAfter(ledgerAD, ledgerAD.id()) == 0);
421
422 // If given a ledger inconsistent with the id, is still able to check
423 // using slower method
424 BEAST_EXPECT(harness.vals().getNodesAfter(ledgerAD, ledgerA.id()) == 1);
425 BEAST_EXPECT(harness.vals().getNodesAfter(ledgerAD, ledgerAB.id()) == 2);
426 }
427
428 void
430 {
431 using namespace std::chrono_literals;
432 testcase("Current trusted validations");
433
435 Ledger ledgerA = h["a"];
436 Ledger ledgerB = h["b"];
437 Ledger ledgerAC = h["ac"];
438
439 TestHarness harness(h.oracle);
440 Node a = harness.makeNode(), b = harness.makeNode();
441 b.untrust();
442
443 BEAST_EXPECT(ValStatus::current == harness.add(a.validate(ledgerA)));
444 BEAST_EXPECT(ValStatus::current == harness.add(b.validate(ledgerB)));
445
446 // Only a is trusted
447 BEAST_EXPECT(harness.vals().currentTrusted().size() == 1);
448 BEAST_EXPECT(harness.vals().currentTrusted()[0].ledgerID() == ledgerA.id());
449 BEAST_EXPECT(harness.vals().currentTrusted()[0].seq() == ledgerA.seq());
450
451 harness.clock().advance(3s);
452
453 for (auto const& node : {a, b})
454 BEAST_EXPECT(ValStatus::current == harness.add(node.validate(ledgerAC)));
455
456 // New validation for a
457 BEAST_EXPECT(harness.vals().currentTrusted().size() == 1);
458 BEAST_EXPECT(harness.vals().currentTrusted()[0].ledgerID() == ledgerAC.id());
459 BEAST_EXPECT(harness.vals().currentTrusted()[0].seq() == ledgerAC.seq());
460
461 // Pass enough time for it to go stale
462 harness.clock().advance(harness.parms().validationCURRENT_LOCAL);
463 BEAST_EXPECT(harness.vals().currentTrusted().empty());
464 }
465
466 void
468 {
469 using namespace std::chrono_literals;
470 testcase("Current public keys");
471
473 Ledger ledgerA = h["a"];
474 Ledger ledgerAC = h["ac"];
475
476 TestHarness harness(h.oracle);
477 Node a = harness.makeNode(), b = harness.makeNode();
478 b.untrust();
479
480 for (auto const& node : {a, b})
481 BEAST_EXPECT(ValStatus::current == harness.add(node.validate(ledgerA)));
482
483 {
484 hash_set<PeerID> const expectedKeys = {a.nodeID(), b.nodeID()};
485 BEAST_EXPECT(harness.vals().getCurrentNodeIDs() == expectedKeys);
486 }
487
488 harness.clock().advance(3s);
489
490 // Change keys and issue partials
491 a.advanceKey();
492 b.advanceKey();
493
494 for (auto const& node : {a, b})
495 BEAST_EXPECT(ValStatus::current == harness.add(node.partial(ledgerAC)));
496
497 {
498 hash_set<PeerID> const expectedKeys = {a.nodeID(), b.nodeID()};
499 BEAST_EXPECT(harness.vals().getCurrentNodeIDs() == expectedKeys);
500 }
501
502 // Pass enough time for them to go stale
503 harness.clock().advance(harness.parms().validationCURRENT_LOCAL);
504 BEAST_EXPECT(harness.vals().getCurrentNodeIDs().empty());
505 }
506
507 void
509 {
510 // Test the Validations functions that calculate a value by ledger ID
511 using namespace std::chrono_literals;
512 testcase("By ledger functions");
513
514 // Several Validations functions return a set of values associated
515 // with trusted ledgers sharing the same ledger ID. The tests below
516 // exercise this logic by saving the set of trusted Validations, and
517 // verifying that the Validations member functions all calculate the
518 // proper transformation of the available ledgers.
519
521 TestHarness harness(h.oracle);
522
523 Node a = harness.makeNode(), b = harness.makeNode(), c = harness.makeNode(), d = harness.makeNode(),
524 e = harness.makeNode();
525
526 c.untrust();
527 // Mix of load fees
528 a.setLoadFee(12);
529 b.setLoadFee(1);
530 c.setLoadFee(12);
531 e.setLoadFee(12);
532
534
535 //----------------------------------------------------------------------
536 // checkers
537 auto sorted = [](auto vec) {
538 std::sort(vec.begin(), vec.end());
539 return vec;
540 };
541 auto compare = [&]() {
542 for (auto& it : trustedValidations)
543 {
544 auto const& id = it.first.first;
545 auto const& seq = it.first.second;
546 auto const& expectedValidations = it.second;
547
548 BEAST_EXPECT(harness.vals().numTrustedForLedger(id) == expectedValidations.size());
549 BEAST_EXPECT(sorted(harness.vals().getTrustedForLedger(id, seq)) == sorted(expectedValidations));
550
551 std::uint32_t baseFee = 0;
552 std::vector<uint32_t> expectedFees;
553 for (auto const& val : expectedValidations)
554 {
555 expectedFees.push_back(val.loadFee().value_or(baseFee));
556 }
557
558 BEAST_EXPECT(sorted(harness.vals().fees(id, baseFee)) == sorted(expectedFees));
559 }
560 };
561
562 //----------------------------------------------------------------------
563 Ledger ledgerA = h["a"];
564 Ledger ledgerB = h["b"];
565 Ledger ledgerAC = h["ac"];
566
567 // Add a dummy ID to cover unknown ledger identifiers
568 trustedValidations[{Ledger::ID{100}, Ledger::Seq{100}}] = {};
569
570 // first round a,b,c agree
571 for (auto const& node : {a, b, c})
572 {
573 auto const val = node.validate(ledgerA);
574 BEAST_EXPECT(ValStatus::current == harness.add(val));
575 if (val.trusted())
576 trustedValidations[{val.ledgerID(), val.seq()}].emplace_back(val);
577 }
578 // d disagrees
579 {
580 auto const val = d.validate(ledgerB);
581 BEAST_EXPECT(ValStatus::current == harness.add(val));
582 trustedValidations[{val.ledgerID(), val.seq()}].emplace_back(val);
583 }
584 // e only issues partials
585 {
586 BEAST_EXPECT(ValStatus::current == harness.add(e.partial(ledgerA)));
587 }
588
589 harness.clock().advance(5s);
590 // second round, a,b,c move to ledger 2
591 for (auto const& node : {a, b, c})
592 {
593 auto const val = node.validate(ledgerAC);
594 BEAST_EXPECT(ValStatus::current == harness.add(val));
595 if (val.trusted())
596 trustedValidations[{val.ledgerID(), val.seq()}].emplace_back(val);
597 }
598 // d now thinks ledger 1, but cannot re-issue a previously used seq
599 // and attempting it should generate a conflict.
600 {
601 BEAST_EXPECT(ValStatus::conflicting == harness.add(d.partial(ledgerA)));
602 }
603 // e only issues partials
604 {
605 BEAST_EXPECT(ValStatus::current == harness.add(e.partial(ledgerAC)));
606 }
607
608 compare();
609 }
610
611 void
613 {
614 // Verify expiring clears out validations stored by ledger
615 testcase("Expire validations");
616 SuiteJournal j("Validations_test", *this);
618 TestHarness harness(h.oracle);
619 Node const a = harness.makeNode();
620 constexpr Ledger::Seq one(1);
621 constexpr Ledger::Seq two(2);
622
623 // simple cases
624 Ledger const ledgerA = h["a"];
625 BEAST_EXPECT(ValStatus::current == harness.add(a.validate(ledgerA)));
626 BEAST_EXPECT(harness.vals().numTrustedForLedger(ledgerA.id()) == 1);
627 harness.vals().expire(j);
628 BEAST_EXPECT(harness.vals().numTrustedForLedger(ledgerA.id()) == 1);
629 harness.clock().advance(harness.parms().validationSET_EXPIRES);
630 harness.vals().expire(j);
631 BEAST_EXPECT(harness.vals().numTrustedForLedger(ledgerA.id()) == 0);
632
633 // use setSeqToKeep to keep the validation from expire
634 Ledger const ledgerB = h["ab"];
635 BEAST_EXPECT(ValStatus::current == harness.add(a.validate(ledgerB)));
636 BEAST_EXPECT(harness.vals().numTrustedForLedger(ledgerB.id()) == 1);
637 harness.vals().setSeqToKeep(ledgerB.seq(), ledgerB.seq() + one);
638 harness.clock().advance(harness.parms().validationSET_EXPIRES);
639 harness.vals().expire(j);
640 BEAST_EXPECT(harness.vals().numTrustedForLedger(ledgerB.id()) == 1);
641 // change toKeep
642 harness.vals().setSeqToKeep(ledgerB.seq() + one, ledgerB.seq() + two);
643 // advance clock slowly
644 int const loops = harness.parms().validationSET_EXPIRES / harness.parms().validationFRESHNESS + 1;
645 for (int i = 0; i < loops; ++i)
646 {
647 harness.clock().advance(harness.parms().validationFRESHNESS);
648 harness.vals().expire(j);
649 }
650 BEAST_EXPECT(harness.vals().numTrustedForLedger(ledgerB.id()) == 0);
651
652 // Allow the validation with high seq to expire
653 Ledger const ledgerC = h["abc"];
654 BEAST_EXPECT(ValStatus::current == harness.add(a.validate(ledgerC)));
655 BEAST_EXPECT(harness.vals().numTrustedForLedger(ledgerC.id()) == 1);
656 harness.vals().setSeqToKeep(ledgerC.seq() - one, ledgerC.seq());
657 harness.clock().advance(harness.parms().validationSET_EXPIRES);
658 harness.vals().expire(j);
659 BEAST_EXPECT(harness.vals().numTrustedForLedger(ledgerC.id()) == 0);
660 }
661
662 void
664 {
665 // Test final flush of validations
666 using namespace std::chrono_literals;
667 testcase("Flush validations");
668
670 TestHarness harness(h.oracle);
671 Node a = harness.makeNode(), b = harness.makeNode(), c = harness.makeNode();
672 c.untrust();
673
674 Ledger ledgerA = h["a"];
675 Ledger ledgerAB = h["ab"];
676
678 for (auto const& node : {a, b, c})
679 {
680 auto const val = node.validate(ledgerA);
681 BEAST_EXPECT(ValStatus::current == harness.add(val));
682 expected.emplace(node.nodeID(), val);
683 }
684
685 // Send in a new validation for a, saving the new one into the expected
686 // map after setting the proper prior ledger ID it replaced
687 harness.clock().advance(1s);
688 auto newVal = a.validate(ledgerAB);
689 BEAST_EXPECT(ValStatus::current == harness.add(newVal));
690 expected.find(a.nodeID())->second = newVal;
691 }
692
693 void
695 {
696 using namespace std::chrono_literals;
697 testcase("Preferred Ledger");
698
700 TestHarness harness(h.oracle);
701 Node a = harness.makeNode(), b = harness.makeNode(), c = harness.makeNode(), d = harness.makeNode();
702 c.untrust();
703
704 Ledger ledgerA = h["a"];
705 Ledger ledgerB = h["b"];
706 Ledger ledgerAC = h["ac"];
707 Ledger ledgerACD = h["acd"];
708
709 using Seq = Ledger::Seq;
710
711 auto pref = [](Ledger ledger) { return std::make_pair(ledger.seq(), ledger.id()); };
712
713 // Empty (no ledgers)
714 BEAST_EXPECT(harness.vals().getPreferred(ledgerA) == std::nullopt);
715
716 // Single ledger
717 BEAST_EXPECT(ValStatus::current == harness.add(a.validate(ledgerB)));
718 BEAST_EXPECT(harness.vals().getPreferred(ledgerA) == pref(ledgerB));
719 BEAST_EXPECT(harness.vals().getPreferred(ledgerB) == pref(ledgerB));
720
721 // Minimum valid sequence
722 BEAST_EXPECT(harness.vals().getPreferred(ledgerA, Seq{10}) == ledgerA.id());
723
724 // Untrusted doesn't impact preferred ledger
725 // (ledgerB has tie-break over ledgerA)
726 BEAST_EXPECT(ValStatus::current == harness.add(b.validate(ledgerA)));
727 BEAST_EXPECT(ValStatus::current == harness.add(c.validate(ledgerA)));
728 BEAST_EXPECT(ledgerB.id() > ledgerA.id());
729 BEAST_EXPECT(harness.vals().getPreferred(ledgerA) == pref(ledgerB));
730 BEAST_EXPECT(harness.vals().getPreferred(ledgerB) == pref(ledgerB));
731
732 // Partial does break ties
733 BEAST_EXPECT(ValStatus::current == harness.add(d.partial(ledgerA)));
734 BEAST_EXPECT(harness.vals().getPreferred(ledgerA) == pref(ledgerA));
735 BEAST_EXPECT(harness.vals().getPreferred(ledgerB) == pref(ledgerA));
736
737 harness.clock().advance(5s);
738
739 // Parent of preferred-> stick with ledger
740 for (auto const& node : {a, b, c, d})
741 BEAST_EXPECT(ValStatus::current == harness.add(node.validate(ledgerAC)));
742 // Parent of preferred stays put
743 BEAST_EXPECT(harness.vals().getPreferred(ledgerA) == pref(ledgerA));
744 // Earlier different chain, switch
745 BEAST_EXPECT(harness.vals().getPreferred(ledgerB) == pref(ledgerAC));
746 // Later on chain, stays where it is
747 BEAST_EXPECT(harness.vals().getPreferred(ledgerACD) == pref(ledgerACD));
748
749 // Any later grandchild or different chain is preferred
750 harness.clock().advance(5s);
751 for (auto const& node : {a, b, c, d})
752 BEAST_EXPECT(ValStatus::current == harness.add(node.validate(ledgerACD)));
753 for (auto const& ledger : {ledgerA, ledgerB, ledgerACD})
754 BEAST_EXPECT(harness.vals().getPreferred(ledger) == pref(ledgerACD));
755 }
756
757 void
759 {
760 using namespace std::chrono_literals;
761 testcase("Get preferred LCL");
762
764 TestHarness harness(h.oracle);
765 Node a = harness.makeNode();
766
767 Ledger ledgerA = h["a"];
768 Ledger ledgerB = h["b"];
769 Ledger ledgerC = h["c"];
770
771 using ID = Ledger::ID;
772 using Seq = Ledger::Seq;
773
775
776 // No trusted validations or counts sticks with current ledger
777 BEAST_EXPECT(harness.vals().getPreferredLCL(ledgerA, Seq{0}, peerCounts) == ledgerA.id());
778
779 ++peerCounts[ledgerB.id()];
780
781 // No trusted validations, rely on peer counts
782 BEAST_EXPECT(harness.vals().getPreferredLCL(ledgerA, Seq{0}, peerCounts) == ledgerB.id());
783
784 ++peerCounts[ledgerC.id()];
785 // No trusted validations, tied peers goes with larger ID
786 BEAST_EXPECT(ledgerC.id() > ledgerB.id());
787
788 BEAST_EXPECT(harness.vals().getPreferredLCL(ledgerA, Seq{0}, peerCounts) == ledgerC.id());
789
790 peerCounts[ledgerC.id()] += 1000;
791
792 // Single trusted always wins over peer counts
793 BEAST_EXPECT(ValStatus::current == harness.add(a.validate(ledgerA)));
794 BEAST_EXPECT(harness.vals().getPreferredLCL(ledgerA, Seq{0}, peerCounts) == ledgerA.id());
795 BEAST_EXPECT(harness.vals().getPreferredLCL(ledgerB, Seq{0}, peerCounts) == ledgerA.id());
796 BEAST_EXPECT(harness.vals().getPreferredLCL(ledgerC, Seq{0}, peerCounts) == ledgerA.id());
797
798 // Stick with current ledger if trusted validation ledger has too old
799 // of a sequence
800 BEAST_EXPECT(harness.vals().getPreferredLCL(ledgerB, Seq{2}, peerCounts) == ledgerB.id());
801 }
802
803 void
805 {
806 using namespace std::chrono_literals;
807 testcase("Acquire validated ledger");
808
810 TestHarness harness(h.oracle);
811 Node a = harness.makeNode();
812 Node b = harness.makeNode();
813
814 using ID = Ledger::ID;
815 using Seq = Ledger::Seq;
816
817 // Validate the ledger before it is actually available
818 Validation val = a.validate(ID{2}, Seq{2}, 0s, 0s, true);
819
820 BEAST_EXPECT(ValStatus::current == harness.add(val));
821 // Validation is available
822 BEAST_EXPECT(harness.vals().numTrustedForLedger(ID{2}) == 1);
823 // but ledger based data is not
824 BEAST_EXPECT(harness.vals().getNodesAfter(genesisLedger, ID{0}) == 0);
825 // Initial preferred branch falls back to the ledger we are trying to
826 // acquire
827 BEAST_EXPECT(harness.vals().getPreferred(genesisLedger) == std::make_pair(Seq{2}, ID{2}));
828
829 // After adding another unavailable validation, the preferred ledger
830 // breaks ties via higher ID
831 BEAST_EXPECT(ValStatus::current == harness.add(b.validate(ID{3}, Seq{2}, 0s, 0s, true)));
832 BEAST_EXPECT(harness.vals().getPreferred(genesisLedger) == std::make_pair(Seq{2}, ID{3}));
833
834 // Create the ledger
835 Ledger ledgerAB = h["ab"];
836 // Now it should be available
837 BEAST_EXPECT(harness.vals().getNodesAfter(genesisLedger, ID{0}) == 1);
838
839 // Create a validation that is not available
840 harness.clock().advance(5s);
841 Validation val2 = a.validate(ID{4}, Seq{4}, 0s, 0s, true);
842 BEAST_EXPECT(ValStatus::current == harness.add(val2));
843 BEAST_EXPECT(harness.vals().numTrustedForLedger(ID{4}) == 1);
844 BEAST_EXPECT(harness.vals().getPreferred(genesisLedger) == std::make_pair(ledgerAB.seq(), ledgerAB.id()));
845
846 // Another node requesting that ledger still doesn't change things
847 Validation val3 = b.validate(ID{4}, Seq{4}, 0s, 0s, true);
848 BEAST_EXPECT(ValStatus::current == harness.add(val3));
849 BEAST_EXPECT(harness.vals().numTrustedForLedger(ID{4}) == 2);
850 BEAST_EXPECT(harness.vals().getPreferred(genesisLedger) == std::make_pair(ledgerAB.seq(), ledgerAB.id()));
851
852 // Switch to validation that is available
853 harness.clock().advance(5s);
854 Ledger ledgerABCDE = h["abcde"];
855 BEAST_EXPECT(ValStatus::current == harness.add(a.partial(ledgerABCDE)));
856 BEAST_EXPECT(ValStatus::current == harness.add(b.partial(ledgerABCDE)));
857 BEAST_EXPECT(harness.vals().getPreferred(genesisLedger) == std::make_pair(ledgerABCDE.seq(), ledgerABCDE.id()));
858 }
859
860 void
862 {
863 testcase("NumTrustedForLedger");
865 TestHarness harness(h.oracle);
866 Node a = harness.makeNode();
867 Node b = harness.makeNode();
868 Ledger ledgerA = h["a"];
869
870 BEAST_EXPECT(ValStatus::current == harness.add(a.partial(ledgerA)));
871 BEAST_EXPECT(harness.vals().numTrustedForLedger(ledgerA.id()) == 0);
872
873 BEAST_EXPECT(ValStatus::current == harness.add(b.validate(ledgerA)));
874 BEAST_EXPECT(harness.vals().numTrustedForLedger(ledgerA.id()) == 1);
875 }
876
877 void
879 {
880 testcase("SeqEnforcer");
881 using Seq = Ledger::Seq;
882 using namespace std::chrono;
883
885 SeqEnforcer<Seq> enforcer;
886
888
889 BEAST_EXPECT(enforcer(clock.now(), Seq{1}, p));
890 BEAST_EXPECT(enforcer(clock.now(), Seq{10}, p));
891 BEAST_EXPECT(!enforcer(clock.now(), Seq{5}, p));
892 BEAST_EXPECT(!enforcer(clock.now(), Seq{9}, p));
893 clock.advance(p.validationSET_EXPIRES - 1ms);
894 BEAST_EXPECT(!enforcer(clock.now(), Seq{1}, p));
895 clock.advance(2ms);
896 BEAST_EXPECT(enforcer(clock.now(), Seq{1}, p));
897 }
898
899 void
901 {
902 testcase("TrustChanged");
903 using namespace std::chrono;
904
905 auto checker =
906 [this](TestValidations& vals, hash_set<PeerID> const& listed, std::vector<Validation> const& trustedVals) {
907 Ledger::ID testID = trustedVals.empty() ? this->genesisLedger.id() : trustedVals[0].ledgerID();
908 Ledger::Seq testSeq = trustedVals.empty() ? this->genesisLedger.seq() : trustedVals[0].seq();
909 BEAST_EXPECT(vals.currentTrusted() == trustedVals);
910 BEAST_EXPECT(vals.getCurrentNodeIDs() == listed);
911 BEAST_EXPECT(vals.getNodesAfter(this->genesisLedger, genesisLedger.id()) == trustedVals.size());
912 if (trustedVals.empty())
913 BEAST_EXPECT(vals.getPreferred(this->genesisLedger) == std::nullopt);
914 else
915 BEAST_EXPECT(vals.getPreferred(this->genesisLedger)->second == testID);
916 BEAST_EXPECT(vals.getTrustedForLedger(testID, testSeq) == trustedVals);
917 BEAST_EXPECT(vals.numTrustedForLedger(testID) == trustedVals.size());
918 };
919
920 {
921 // Trusted to untrusted
923 TestHarness harness(h.oracle);
924 Node a = harness.makeNode();
925 Ledger ledgerAB = h["ab"];
926 Validation v = a.validate(ledgerAB);
927 BEAST_EXPECT(ValStatus::current == harness.add(v));
928
929 hash_set<PeerID> listed({a.nodeID()});
930 std::vector<Validation> trustedVals({v});
931 checker(harness.vals(), listed, trustedVals);
932
933 trustedVals.clear();
934 harness.vals().trustChanged({}, {a.nodeID()});
935 checker(harness.vals(), listed, trustedVals);
936 }
937
938 {
939 // Untrusted to trusted
941 TestHarness harness(h.oracle);
942 Node a = harness.makeNode();
943 a.untrust();
944 Ledger ledgerAB = h["ab"];
945 Validation v = a.validate(ledgerAB);
946 BEAST_EXPECT(ValStatus::current == harness.add(v));
947
948 hash_set<PeerID> listed({a.nodeID()});
949 std::vector<Validation> trustedVals;
950 checker(harness.vals(), listed, trustedVals);
951
952 trustedVals.push_back(v);
953 harness.vals().trustChanged({a.nodeID()}, {});
954 checker(harness.vals(), listed, trustedVals);
955 }
956
957 {
958 // Trusted but not acquired -> untrusted
960 TestHarness harness(h.oracle);
961 Node a = harness.makeNode();
962 Validation v = a.validate(Ledger::ID{2}, Ledger::Seq{2}, 0s, 0s, true);
963 BEAST_EXPECT(ValStatus::current == harness.add(v));
964
965 hash_set<PeerID> listed({a.nodeID()});
966 std::vector<Validation> trustedVals({v});
967 auto& vals = harness.vals();
968 BEAST_EXPECT(vals.currentTrusted() == trustedVals);
969 BEAST_EXPECT(vals.getPreferred(genesisLedger)->second == v.ledgerID());
970 BEAST_EXPECT(vals.getNodesAfter(genesisLedger, genesisLedger.id()) == 0);
971
972 trustedVals.clear();
973 harness.vals().trustChanged({}, {a.nodeID()});
974 // make acquiring ledger available
975 h["ab"];
976 BEAST_EXPECT(vals.currentTrusted() == trustedVals);
977 BEAST_EXPECT(vals.getPreferred(genesisLedger) == std::nullopt);
978 BEAST_EXPECT(vals.getNodesAfter(genesisLedger, genesisLedger.id()) == 0);
979 }
980 }
981
982 void
1000};
1001
1002BEAST_DEFINE_TESTSUITE(Validations, consensus, xrpl);
1003} // namespace csf
1004} // namespace test
1005} // namespace xrpl
Abstract interface to a clock.
Manual clock implementation.
A testsuite class.
Definition suite.h:52
testcase_t testcase
Memberspace for declaring test cases.
Definition suite.h:148
std::chrono::time_point< NetClock > time_point
Definition chrono.h:46
Enforce validation increasing sequence requirement.
Definition Validations.h:81
Maintains current and recent ledger validations.
ID getPreferredLCL(Ledger const &lcl, Seq minSeq, hash_map< ID, std::uint32_t > const &peerCounts)
Determine the preferred last closed ledger for the next consensus round.
std::size_t getNodesAfter(Ledger const &ledger, ID const &ledgerID)
Count the number of current trusted validators working on a ledger after the specified one.
auto getCurrentNodeIDs() -> hash_set< NodeID >
Get the set of node ids associated with current validations.
std::size_t numTrustedForLedger(ID const &ledgerID)
Count the number of trusted full validations for the given ledger.
std::vector< WrappedValidationType > getTrustedForLedger(ID const &ledgerID, Seq const &seq)
Get trusted full validations for a specific ledger.
ValStatus add(NodeID const &nodeID, Validation const &val)
Add a new validation.
void trustChanged(hash_set< NodeID > const &added, hash_set< NodeID > const &removed)
Update trust status of validations.
std::optional< std::pair< Seq, ID > > getPreferred(Ledger const &curr)
Return the sequence number and ID of the preferred working ledger.
std::vector< WrappedValidationType > currentTrusted()
Get the currently trusted full validations.
void setSeqToKeep(Seq const &low, Seq const &high)
Set the range [low, high) of validations to keep from expire.
void expire(beast::Journal &j)
Expire old validation sets.
std::vector< std::uint32_t > fees(ID const &ledgerID, std::uint32_t baseFee)
Returns fees reported by trusted full validators in the given ledger.
Oracle maintaining unique ledgers for a simulation.
Definition ledgers.h:224
std::optional< Ledger > lookup(Ledger::ID const &id) const
Find the ledger with the given ID.
Definition ledgers.cpp:109
A ledger is a set of observed transactions and a sequence number identifying the ledger.
Definition ledgers.h:45
tagged_integer< std::uint32_t, IdTag > ID
Definition ledgers.h:53
tagged_integer< std::uint32_t, SeqTag > Seq
Definition ledgers.h:50
Validation of a specific ledger by a specific Peer.
Definition Validation.h:31
PeerID const & nodeID() const
Definition Validation.h:101
Ledger::ID ledgerID() const
Definition Validation.h:71
Adaptor(clock_type &c, LedgerOracle &o)
std::optional< Ledger > acquire(Ledger::ID const &id)
std::optional< std::uint32_t > loadFee_
Validation validate(Ledger::ID id, Ledger::Seq seq, NetClock::duration signOffset, NetClock::duration seenOffset, bool full) const
Node(PeerID nodeID, clock_type const &c)
Validation validate(Ledger ledger) const
Validation partial(Ledger ledger) const
Validation validate(Ledger ledger, NetClock::duration signOffset, NetClock::duration seenOffset) const
beast::manual_clock< std::chrono::steady_clock > clock_
static NetClock::time_point toNetClock(clock_type const &c)
beast::abstract_clock< std::chrono::steady_clock > const clock_type
void run() override
Runs the suite.
Set the fee on a JTx.
Definition fee.h:18
T emplace(T... args)
T find(T... args)
T is_same_v
T make_pair(T... args)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:6
ValStatus
Status of validation we received.
@ badSeq
A validation violates the increasing seq requirement.
@ stale
Not current or was older than current from this node.
@ current
This was a new validation and was added.
@ conflicting
Multiple validations by a validator for different ledgers.
T push_back(T... args)
T sort(T... args)
Timing parameters to control validation staleness and expiration.
Definition Validations.h:28
std::chrono::seconds validationFRESHNESS
How long we consider a validation fresh.
Definition Validations.h:70
std::chrono::seconds validationSET_EXPIRES
Duration a set of validations for a given ledger hash remain valid.
Definition Validations.h:60
std::chrono::seconds validationCURRENT_EARLY
Duration pre-close in which validations are acceptable.
Definition Validations.h:52
std::chrono::seconds validationCURRENT_LOCAL
Duration a validation remains current after first observed.
Definition Validations.h:45
std::chrono::seconds validationCURRENT_WALL
The number of seconds a validation remains current after its ledger's close time.
Definition Validations.h:37
Helper for writing unit tests with controlled ledger histories.
Definition ledgers.h:299
Set the sequence number on a JTx.
Definition seq.h:15