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 ripple {
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>(
29 c.now().time_since_epoch() + 86400s));
30 }
31
32 // Represents a node that can issue validations
33 class Node
34 {
35 clock_type const& c_;
37 bool trusted_ = true;
40
41 public:
43 {
44 }
45
46 void
48 {
49 trusted_ = false;
50 }
51
52 void
54 {
55 trusted_ = true;
56 }
57
58 void
63
64 PeerID
65 nodeID() const
66 {
67 return nodeID_;
68 }
69
70 void
72 {
73 signIdx_++;
74 }
75
77 currKey() const
78 {
80 }
81
83 masterKey() const
84 {
85 return std::make_pair(nodeID_, 0);
86 }
88 now() const
89 {
90 return toNetClock(c_);
91 }
92
93 // Issue a new validation with given sequence number and id and
94 // with signing and seen times offset from the common clock
97 Ledger::ID id,
99 NetClock::duration signOffset,
100 NetClock::duration seenOffset,
101 bool full) const
102 {
103 Validation v{
104 id,
105 seq,
106 now() + signOffset,
107 now() + seenOffset,
108 currKey(),
109 nodeID_,
110 full,
111 loadFee_};
112 if (trusted_)
113 v.setTrusted();
114 return v;
115 }
116
119 Ledger ledger,
120 NetClock::duration signOffset,
121 NetClock::duration seenOffset) const
122 {
123 return validate(
124 ledger.id(), ledger.seq(), signOffset, seenOffset, true);
125 }
126
128 validate(Ledger ledger) const
129 {
130 return validate(
131 ledger.id(),
132 ledger.seq(),
135 true);
136 }
137
139 partial(Ledger ledger) const
140 {
141 return validate(
142 ledger.id(),
143 ledger.seq(),
146 false);
147 }
148 };
149
150 // Generic Validations adaptor
152 {
155
156 public:
157 // Non-locking mutex to avoid locks in generic Validations
158 struct Mutex
159 {
160 void
162 {
163 }
164
165 void
167 {
168 }
169 };
170
173
175 {
176 }
177
179 now() const
180 {
181 return toNetClock(c_);
182 }
183
186 {
187 return oracle_.lookup(id);
188 }
189 };
190
191 // Specialize generic Validations using the above types
193
194 // Gather the dependencies of TestValidations in a single class and provide
195 // accessors for simplifying test logic
197 {
202
203 public:
205 {
206 }
207
209 add(Validation const& v)
210 {
211 return tv_.add(v.nodeID(), v);
212 }
213
216 {
217 return tv_;
218 }
219
220 Node
222 {
223 return Node(nextNodeId_++, clock_);
224 }
225
227 parms() const
228 {
229 return p_;
230 }
231
232 auto&
234 {
235 return clock_;
236 }
237 };
238
240
241 void
243 {
244 using namespace std::chrono_literals;
245
246 testcase("Add validation");
248 Ledger ledgerA = h["a"];
249 Ledger ledgerAB = h["ab"];
250 Ledger ledgerAZ = h["az"];
251 Ledger ledgerABC = h["abc"];
252 Ledger ledgerABCD = h["abcd"];
253 Ledger ledgerABCDE = h["abcde"];
254
255 {
256 TestHarness harness(h.oracle);
257 Node n = harness.makeNode();
258
259 auto const v = n.validate(ledgerA);
260
261 // Add a current validation
262 BEAST_EXPECT(ValStatus::current == harness.add(v));
263
264 // Re-adding violates the increasing seq requirement for full
265 // validations
266 BEAST_EXPECT(ValStatus::badSeq == harness.add(v));
267
268 harness.clock().advance(1s);
269
270 BEAST_EXPECT(
271 ValStatus::current == harness.add(n.validate(ledgerAB)));
272
273 // Test the node changing signing key
274
275 // Confirm old ledger on hand, but not new ledger
276 BEAST_EXPECT(
277 harness.vals().numTrustedForLedger(ledgerAB.id()) == 1);
278 BEAST_EXPECT(
279 harness.vals().numTrustedForLedger(ledgerABC.id()) == 0);
280
281 // Rotate signing keys
282 n.advanceKey();
283
284 harness.clock().advance(1s);
285
286 // Cannot re-do the same full validation sequence
287 BEAST_EXPECT(
288 ValStatus::conflicting == harness.add(n.validate(ledgerAB)));
289 // Cannot send the same partial validation sequence
290 BEAST_EXPECT(
291 ValStatus::conflicting == harness.add(n.partial(ledgerAB)));
292
293 // Now trusts the newest ledger too
294 harness.clock().advance(1s);
295 BEAST_EXPECT(
296 ValStatus::current == harness.add(n.validate(ledgerABC)));
297 BEAST_EXPECT(
298 harness.vals().numTrustedForLedger(ledgerAB.id()) == 1);
299 BEAST_EXPECT(
300 harness.vals().numTrustedForLedger(ledgerABC.id()) == 1);
301
302 // Processing validations out of order should ignore the older
303 // validation
304 harness.clock().advance(2s);
305 auto const valABCDE = n.validate(ledgerABCDE);
306
307 harness.clock().advance(4s);
308 auto const valABCD = n.validate(ledgerABCD);
309
310 BEAST_EXPECT(ValStatus::current == harness.add(valABCD));
311
312 BEAST_EXPECT(ValStatus::stale == harness.add(valABCDE));
313 }
314
315 {
316 // Process validations out of order with shifted times
317
318 TestHarness harness(h.oracle);
319 Node n = harness.makeNode();
320
321 // Establish a new current validation
322 BEAST_EXPECT(
323 ValStatus::current == harness.add(n.validate(ledgerA)));
324
325 // Process a validation that has "later" seq but early sign time
326 BEAST_EXPECT(
328 harness.add(n.validate(ledgerAB, -1s, -1s)));
329
330 // Process a validation that has a later seq and later sign
331 // time
332 BEAST_EXPECT(
334 harness.add(n.validate(ledgerABC, 1s, 1s)));
335 }
336
337 {
338 // Test stale on arrival validations
339 TestHarness harness(h.oracle);
340 Node n = harness.makeNode();
341
342 BEAST_EXPECT(
344 harness.add(n.validate(
345 ledgerA, -harness.parms().validationCURRENT_EARLY, 0s)));
346
347 BEAST_EXPECT(
349 harness.add(n.validate(
350 ledgerA, harness.parms().validationCURRENT_WALL, 0s)));
351
352 BEAST_EXPECT(
354 harness.add(n.validate(
355 ledgerA, 0s, harness.parms().validationCURRENT_LOCAL)));
356 }
357
358 {
359 // Test that full or partials cannot be sent for older sequence
360 // numbers, unless time-out has happened
361 for (bool doFull : {true, false})
362 {
363 TestHarness harness(h.oracle);
364 Node n = harness.makeNode();
365
366 auto process = [&](Ledger& lgr) {
367 if (doFull)
368 return harness.add(n.validate(lgr));
369 return harness.add(n.partial(lgr));
370 };
371
372 BEAST_EXPECT(ValStatus::current == process(ledgerABC));
373 harness.clock().advance(1s);
374 BEAST_EXPECT(ledgerAB.seq() < ledgerABC.seq());
375 BEAST_EXPECT(ValStatus::badSeq == process(ledgerAB));
376
377 // If we advance far enough for AB to expire, we can fully
378 // validate or partially validate that sequence number again
379 BEAST_EXPECT(ValStatus::conflicting == process(ledgerAZ));
380 harness.clock().advance(
381 harness.parms().validationSET_EXPIRES + 1ms);
382 BEAST_EXPECT(ValStatus::current == process(ledgerAZ));
383 }
384 }
385 }
386
387 void
389 {
390 testcase("Stale validation");
391 // Verify validation becomes stale based solely on time passing, but
392 // use different functions to trigger the check for staleness
393
395 Ledger ledgerA = h["a"];
396 Ledger ledgerAB = h["ab"];
397
398 using Trigger = std::function<void(TestValidations&)>;
399
400 std::vector<Trigger> triggers = {
401 [&](TestValidations& vals) { vals.currentTrusted(); },
402 [&](TestValidations& vals) { vals.getCurrentNodeIDs(); },
403 [&](TestValidations& vals) { vals.getPreferred(genesisLedger); },
404 [&](TestValidations& vals) {
405 vals.getNodesAfter(ledgerA, ledgerA.id());
406 }};
407 for (Trigger trigger : triggers)
408 {
409 TestHarness harness(h.oracle);
410 Node n = harness.makeNode();
411
412 BEAST_EXPECT(
413 ValStatus::current == harness.add(n.validate(ledgerAB)));
414 trigger(harness.vals());
415 BEAST_EXPECT(
416 harness.vals().getNodesAfter(ledgerA, ledgerA.id()) == 1);
417 BEAST_EXPECT(
418 harness.vals().getPreferred(genesisLedger) ==
419 std::make_pair(ledgerAB.seq(), ledgerAB.id()));
420 harness.clock().advance(harness.parms().validationCURRENT_LOCAL);
421
422 // trigger check for stale
423 trigger(harness.vals());
424
425 BEAST_EXPECT(
426 harness.vals().getNodesAfter(ledgerA, ledgerA.id()) == 0);
427 BEAST_EXPECT(
429 }
430 }
431
432 void
434 {
435 // Test getting number of nodes working on a validation descending
436 // a prescribed one. This count should only be for trusted nodes, but
437 // includes partial and full validations
438
439 using namespace std::chrono_literals;
440 testcase("Get nodes after");
441
443 Ledger ledgerA = h["a"];
444 Ledger ledgerAB = h["ab"];
445 Ledger ledgerABC = h["abc"];
446 Ledger ledgerAD = h["ad"];
447
448 TestHarness harness(h.oracle);
449 Node a = harness.makeNode(), b = harness.makeNode(),
450 c = harness.makeNode(), d = harness.makeNode();
451 c.untrust();
452
453 // first round a,b,c agree, d has is partial
454 BEAST_EXPECT(ValStatus::current == harness.add(a.validate(ledgerA)));
455 BEAST_EXPECT(ValStatus::current == harness.add(b.validate(ledgerA)));
456 BEAST_EXPECT(ValStatus::current == harness.add(c.validate(ledgerA)));
457 BEAST_EXPECT(ValStatus::current == harness.add(d.partial(ledgerA)));
458
459 for (Ledger const& ledger : {ledgerA, ledgerAB, ledgerABC, ledgerAD})
460 BEAST_EXPECT(
461 harness.vals().getNodesAfter(ledger, ledger.id()) == 0);
462
463 harness.clock().advance(5s);
464
465 BEAST_EXPECT(ValStatus::current == harness.add(a.validate(ledgerAB)));
466 BEAST_EXPECT(ValStatus::current == harness.add(b.validate(ledgerABC)));
467 BEAST_EXPECT(ValStatus::current == harness.add(c.validate(ledgerAB)));
468 BEAST_EXPECT(ValStatus::current == harness.add(d.partial(ledgerABC)));
469
470 BEAST_EXPECT(harness.vals().getNodesAfter(ledgerA, ledgerA.id()) == 3);
471 BEAST_EXPECT(
472 harness.vals().getNodesAfter(ledgerAB, ledgerAB.id()) == 2);
473 BEAST_EXPECT(
474 harness.vals().getNodesAfter(ledgerABC, ledgerABC.id()) == 0);
475 BEAST_EXPECT(
476 harness.vals().getNodesAfter(ledgerAD, ledgerAD.id()) == 0);
477
478 // If given a ledger inconsistent with the id, is still able to check
479 // using slower method
480 BEAST_EXPECT(harness.vals().getNodesAfter(ledgerAD, ledgerA.id()) == 1);
481 BEAST_EXPECT(
482 harness.vals().getNodesAfter(ledgerAD, ledgerAB.id()) == 2);
483 }
484
485 void
487 {
488 using namespace std::chrono_literals;
489 testcase("Current trusted validations");
490
492 Ledger ledgerA = h["a"];
493 Ledger ledgerB = h["b"];
494 Ledger ledgerAC = h["ac"];
495
496 TestHarness harness(h.oracle);
497 Node a = harness.makeNode(), b = harness.makeNode();
498 b.untrust();
499
500 BEAST_EXPECT(ValStatus::current == harness.add(a.validate(ledgerA)));
501 BEAST_EXPECT(ValStatus::current == harness.add(b.validate(ledgerB)));
502
503 // Only a is trusted
504 BEAST_EXPECT(harness.vals().currentTrusted().size() == 1);
505 BEAST_EXPECT(
506 harness.vals().currentTrusted()[0].ledgerID() == ledgerA.id());
507 BEAST_EXPECT(harness.vals().currentTrusted()[0].seq() == ledgerA.seq());
508
509 harness.clock().advance(3s);
510
511 for (auto const& node : {a, b})
512 BEAST_EXPECT(
513 ValStatus::current == harness.add(node.validate(ledgerAC)));
514
515 // New validation for a
516 BEAST_EXPECT(harness.vals().currentTrusted().size() == 1);
517 BEAST_EXPECT(
518 harness.vals().currentTrusted()[0].ledgerID() == ledgerAC.id());
519 BEAST_EXPECT(
520 harness.vals().currentTrusted()[0].seq() == ledgerAC.seq());
521
522 // Pass enough time for it to go stale
523 harness.clock().advance(harness.parms().validationCURRENT_LOCAL);
524 BEAST_EXPECT(harness.vals().currentTrusted().empty());
525 }
526
527 void
529 {
530 using namespace std::chrono_literals;
531 testcase("Current public keys");
532
534 Ledger ledgerA = h["a"];
535 Ledger ledgerAC = h["ac"];
536
537 TestHarness harness(h.oracle);
538 Node a = harness.makeNode(), b = harness.makeNode();
539 b.untrust();
540
541 for (auto const& node : {a, b})
542 BEAST_EXPECT(
543 ValStatus::current == harness.add(node.validate(ledgerA)));
544
545 {
546 hash_set<PeerID> const expectedKeys = {a.nodeID(), b.nodeID()};
547 BEAST_EXPECT(harness.vals().getCurrentNodeIDs() == expectedKeys);
548 }
549
550 harness.clock().advance(3s);
551
552 // Change keys and issue partials
553 a.advanceKey();
554 b.advanceKey();
555
556 for (auto const& node : {a, b})
557 BEAST_EXPECT(
558 ValStatus::current == harness.add(node.partial(ledgerAC)));
559
560 {
561 hash_set<PeerID> const expectedKeys = {a.nodeID(), b.nodeID()};
562 BEAST_EXPECT(harness.vals().getCurrentNodeIDs() == expectedKeys);
563 }
564
565 // Pass enough time for them to go stale
566 harness.clock().advance(harness.parms().validationCURRENT_LOCAL);
567 BEAST_EXPECT(harness.vals().getCurrentNodeIDs().empty());
568 }
569
570 void
572 {
573 // Test the Validations functions that calculate a value by ledger ID
574 using namespace std::chrono_literals;
575 testcase("By ledger functions");
576
577 // Several Validations functions return a set of values associated
578 // with trusted ledgers sharing the same ledger ID. The tests below
579 // exercise this logic by saving the set of trusted Validations, and
580 // verifying that the Validations member functions all calculate the
581 // proper transformation of the available ledgers.
582
584 TestHarness harness(h.oracle);
585
586 Node a = harness.makeNode(), b = harness.makeNode(),
587 c = harness.makeNode(), d = harness.makeNode(),
588 e = harness.makeNode();
589
590 c.untrust();
591 // Mix of load fees
592 a.setLoadFee(12);
593 b.setLoadFee(1);
594 c.setLoadFee(12);
595 e.setLoadFee(12);
596
598 trustedValidations;
599
600 //----------------------------------------------------------------------
601 // checkers
602 auto sorted = [](auto vec) {
603 std::sort(vec.begin(), vec.end());
604 return vec;
605 };
606 auto compare = [&]() {
607 for (auto& it : trustedValidations)
608 {
609 auto const& id = it.first.first;
610 auto const& seq = it.first.second;
611 auto const& expectedValidations = it.second;
612
613 BEAST_EXPECT(
614 harness.vals().numTrustedForLedger(id) ==
615 expectedValidations.size());
616 BEAST_EXPECT(
617 sorted(harness.vals().getTrustedForLedger(id, seq)) ==
618 sorted(expectedValidations));
619
620 std::uint32_t baseFee = 0;
621 std::vector<uint32_t> expectedFees;
622 for (auto const& val : expectedValidations)
623 {
624 expectedFees.push_back(val.loadFee().value_or(baseFee));
625 }
626
627 BEAST_EXPECT(
628 sorted(harness.vals().fees(id, baseFee)) ==
629 sorted(expectedFees));
630 }
631 };
632
633 //----------------------------------------------------------------------
634 Ledger ledgerA = h["a"];
635 Ledger ledgerB = h["b"];
636 Ledger ledgerAC = h["ac"];
637
638 // Add a dummy ID to cover unknown ledger identifiers
639 trustedValidations[{Ledger::ID{100}, Ledger::Seq{100}}] = {};
640
641 // first round a,b,c agree
642 for (auto const& node : {a, b, c})
643 {
644 auto const val = node.validate(ledgerA);
645 BEAST_EXPECT(ValStatus::current == harness.add(val));
646 if (val.trusted())
647 trustedValidations[{val.ledgerID(), val.seq()}].emplace_back(
648 val);
649 }
650 // d disagrees
651 {
652 auto const val = d.validate(ledgerB);
653 BEAST_EXPECT(ValStatus::current == harness.add(val));
654 trustedValidations[{val.ledgerID(), val.seq()}].emplace_back(val);
655 }
656 // e only issues partials
657 {
658 BEAST_EXPECT(ValStatus::current == harness.add(e.partial(ledgerA)));
659 }
660
661 harness.clock().advance(5s);
662 // second round, a,b,c move to ledger 2
663 for (auto const& node : {a, b, c})
664 {
665 auto const val = node.validate(ledgerAC);
666 BEAST_EXPECT(ValStatus::current == harness.add(val));
667 if (val.trusted())
668 trustedValidations[{val.ledgerID(), val.seq()}].emplace_back(
669 val);
670 }
671 // d now thinks ledger 1, but cannot re-issue a previously used seq
672 // and attempting it should generate a conflict.
673 {
674 BEAST_EXPECT(
675 ValStatus::conflicting == harness.add(d.partial(ledgerA)));
676 }
677 // e only issues partials
678 {
679 BEAST_EXPECT(
680 ValStatus::current == harness.add(e.partial(ledgerAC)));
681 }
682
683 compare();
684 }
685
686 void
688 {
689 // Verify expiring clears out validations stored by ledger
690 testcase("Expire validations");
691 SuiteJournal j("Validations_test", *this);
693 TestHarness harness(h.oracle);
694 Node const a = harness.makeNode();
695 constexpr Ledger::Seq one(1);
696 constexpr Ledger::Seq two(2);
697
698 // simple cases
699 Ledger const ledgerA = h["a"];
700 BEAST_EXPECT(ValStatus::current == harness.add(a.validate(ledgerA)));
701 BEAST_EXPECT(harness.vals().numTrustedForLedger(ledgerA.id()) == 1);
702 harness.vals().expire(j);
703 BEAST_EXPECT(harness.vals().numTrustedForLedger(ledgerA.id()) == 1);
704 harness.clock().advance(harness.parms().validationSET_EXPIRES);
705 harness.vals().expire(j);
706 BEAST_EXPECT(harness.vals().numTrustedForLedger(ledgerA.id()) == 0);
707
708 // use setSeqToKeep to keep the validation from expire
709 Ledger const ledgerB = h["ab"];
710 BEAST_EXPECT(ValStatus::current == harness.add(a.validate(ledgerB)));
711 BEAST_EXPECT(harness.vals().numTrustedForLedger(ledgerB.id()) == 1);
712 harness.vals().setSeqToKeep(ledgerB.seq(), ledgerB.seq() + one);
713 harness.clock().advance(harness.parms().validationSET_EXPIRES);
714 harness.vals().expire(j);
715 BEAST_EXPECT(harness.vals().numTrustedForLedger(ledgerB.id()) == 1);
716 // change toKeep
717 harness.vals().setSeqToKeep(ledgerB.seq() + one, ledgerB.seq() + two);
718 // advance clock slowly
719 int const loops = harness.parms().validationSET_EXPIRES /
720 harness.parms().validationFRESHNESS +
721 1;
722 for (int i = 0; i < loops; ++i)
723 {
724 harness.clock().advance(harness.parms().validationFRESHNESS);
725 harness.vals().expire(j);
726 }
727 BEAST_EXPECT(harness.vals().numTrustedForLedger(ledgerB.id()) == 0);
728
729 // Allow the validation with high seq to expire
730 Ledger const ledgerC = h["abc"];
731 BEAST_EXPECT(ValStatus::current == harness.add(a.validate(ledgerC)));
732 BEAST_EXPECT(harness.vals().numTrustedForLedger(ledgerC.id()) == 1);
733 harness.vals().setSeqToKeep(ledgerC.seq() - one, ledgerC.seq());
734 harness.clock().advance(harness.parms().validationSET_EXPIRES);
735 harness.vals().expire(j);
736 BEAST_EXPECT(harness.vals().numTrustedForLedger(ledgerC.id()) == 0);
737 }
738
739 void
741 {
742 // Test final flush of validations
743 using namespace std::chrono_literals;
744 testcase("Flush validations");
745
747 TestHarness harness(h.oracle);
748 Node a = harness.makeNode(), b = harness.makeNode(),
749 c = harness.makeNode();
750 c.untrust();
751
752 Ledger ledgerA = h["a"];
753 Ledger ledgerAB = h["ab"];
754
756 for (auto const& node : {a, b, c})
757 {
758 auto const val = node.validate(ledgerA);
759 BEAST_EXPECT(ValStatus::current == harness.add(val));
760 expected.emplace(node.nodeID(), val);
761 }
762
763 // Send in a new validation for a, saving the new one into the expected
764 // map after setting the proper prior ledger ID it replaced
765 harness.clock().advance(1s);
766 auto newVal = a.validate(ledgerAB);
767 BEAST_EXPECT(ValStatus::current == harness.add(newVal));
768 expected.find(a.nodeID())->second = newVal;
769 }
770
771 void
773 {
774 using namespace std::chrono_literals;
775 testcase("Preferred Ledger");
776
778 TestHarness harness(h.oracle);
779 Node a = harness.makeNode(), b = harness.makeNode(),
780 c = harness.makeNode(), d = harness.makeNode();
781 c.untrust();
782
783 Ledger ledgerA = h["a"];
784 Ledger ledgerB = h["b"];
785 Ledger ledgerAC = h["ac"];
786 Ledger ledgerACD = h["acd"];
787
788 using Seq = Ledger::Seq;
789
790 auto pref = [](Ledger ledger) {
791 return std::make_pair(ledger.seq(), ledger.id());
792 };
793
794 // Empty (no ledgers)
795 BEAST_EXPECT(harness.vals().getPreferred(ledgerA) == std::nullopt);
796
797 // Single ledger
798 BEAST_EXPECT(ValStatus::current == harness.add(a.validate(ledgerB)));
799 BEAST_EXPECT(harness.vals().getPreferred(ledgerA) == pref(ledgerB));
800 BEAST_EXPECT(harness.vals().getPreferred(ledgerB) == pref(ledgerB));
801
802 // Minimum valid sequence
803 BEAST_EXPECT(
804 harness.vals().getPreferred(ledgerA, Seq{10}) == ledgerA.id());
805
806 // Untrusted doesn't impact preferred ledger
807 // (ledgerB has tie-break over ledgerA)
808 BEAST_EXPECT(ValStatus::current == harness.add(b.validate(ledgerA)));
809 BEAST_EXPECT(ValStatus::current == harness.add(c.validate(ledgerA)));
810 BEAST_EXPECT(ledgerB.id() > ledgerA.id());
811 BEAST_EXPECT(harness.vals().getPreferred(ledgerA) == pref(ledgerB));
812 BEAST_EXPECT(harness.vals().getPreferred(ledgerB) == pref(ledgerB));
813
814 // Partial does break ties
815 BEAST_EXPECT(ValStatus::current == harness.add(d.partial(ledgerA)));
816 BEAST_EXPECT(harness.vals().getPreferred(ledgerA) == pref(ledgerA));
817 BEAST_EXPECT(harness.vals().getPreferred(ledgerB) == pref(ledgerA));
818
819 harness.clock().advance(5s);
820
821 // Parent of preferred-> stick with ledger
822 for (auto const& node : {a, b, c, d})
823 BEAST_EXPECT(
824 ValStatus::current == harness.add(node.validate(ledgerAC)));
825 // Parent of preferred stays put
826 BEAST_EXPECT(harness.vals().getPreferred(ledgerA) == pref(ledgerA));
827 // Earlier different chain, switch
828 BEAST_EXPECT(harness.vals().getPreferred(ledgerB) == pref(ledgerAC));
829 // Later on chain, stays where it is
830 BEAST_EXPECT(harness.vals().getPreferred(ledgerACD) == pref(ledgerACD));
831
832 // Any later grandchild or different chain is preferred
833 harness.clock().advance(5s);
834 for (auto const& node : {a, b, c, d})
835 BEAST_EXPECT(
836 ValStatus::current == harness.add(node.validate(ledgerACD)));
837 for (auto const& ledger : {ledgerA, ledgerB, ledgerACD})
838 BEAST_EXPECT(
839 harness.vals().getPreferred(ledger) == pref(ledgerACD));
840 }
841
842 void
844 {
845 using namespace std::chrono_literals;
846 testcase("Get preferred LCL");
847
849 TestHarness harness(h.oracle);
850 Node a = harness.makeNode();
851
852 Ledger ledgerA = h["a"];
853 Ledger ledgerB = h["b"];
854 Ledger ledgerC = h["c"];
855
856 using ID = Ledger::ID;
857 using Seq = Ledger::Seq;
858
860
861 // No trusted validations or counts sticks with current ledger
862 BEAST_EXPECT(
863 harness.vals().getPreferredLCL(ledgerA, Seq{0}, peerCounts) ==
864 ledgerA.id());
865
866 ++peerCounts[ledgerB.id()];
867
868 // No trusted validations, rely on peer counts
869 BEAST_EXPECT(
870 harness.vals().getPreferredLCL(ledgerA, Seq{0}, peerCounts) ==
871 ledgerB.id());
872
873 ++peerCounts[ledgerC.id()];
874 // No trusted validations, tied peers goes with larger ID
875 BEAST_EXPECT(ledgerC.id() > ledgerB.id());
876
877 BEAST_EXPECT(
878 harness.vals().getPreferredLCL(ledgerA, Seq{0}, peerCounts) ==
879 ledgerC.id());
880
881 peerCounts[ledgerC.id()] += 1000;
882
883 // Single trusted always wins over peer counts
884 BEAST_EXPECT(ValStatus::current == harness.add(a.validate(ledgerA)));
885 BEAST_EXPECT(
886 harness.vals().getPreferredLCL(ledgerA, Seq{0}, peerCounts) ==
887 ledgerA.id());
888 BEAST_EXPECT(
889 harness.vals().getPreferredLCL(ledgerB, Seq{0}, peerCounts) ==
890 ledgerA.id());
891 BEAST_EXPECT(
892 harness.vals().getPreferredLCL(ledgerC, Seq{0}, peerCounts) ==
893 ledgerA.id());
894
895 // Stick with current ledger if trusted validation ledger has too old
896 // of a sequence
897 BEAST_EXPECT(
898 harness.vals().getPreferredLCL(ledgerB, Seq{2}, peerCounts) ==
899 ledgerB.id());
900 }
901
902 void
904 {
905 using namespace std::chrono_literals;
906 testcase("Acquire validated ledger");
907
909 TestHarness harness(h.oracle);
910 Node a = harness.makeNode();
911 Node b = harness.makeNode();
912
913 using ID = Ledger::ID;
914 using Seq = Ledger::Seq;
915
916 // Validate the ledger before it is actually available
917 Validation val = a.validate(ID{2}, Seq{2}, 0s, 0s, true);
918
919 BEAST_EXPECT(ValStatus::current == harness.add(val));
920 // Validation is available
921 BEAST_EXPECT(harness.vals().numTrustedForLedger(ID{2}) == 1);
922 // but ledger based data is not
923 BEAST_EXPECT(harness.vals().getNodesAfter(genesisLedger, ID{0}) == 0);
924 // Initial preferred branch falls back to the ledger we are trying to
925 // acquire
926 BEAST_EXPECT(
927 harness.vals().getPreferred(genesisLedger) ==
928 std::make_pair(Seq{2}, ID{2}));
929
930 // After adding another unavailable validation, the preferred ledger
931 // breaks ties via higher ID
932 BEAST_EXPECT(
934 harness.add(b.validate(ID{3}, Seq{2}, 0s, 0s, true)));
935 BEAST_EXPECT(
936 harness.vals().getPreferred(genesisLedger) ==
937 std::make_pair(Seq{2}, ID{3}));
938
939 // Create the ledger
940 Ledger ledgerAB = h["ab"];
941 // Now it should be available
942 BEAST_EXPECT(harness.vals().getNodesAfter(genesisLedger, ID{0}) == 1);
943
944 // Create a validation that is not available
945 harness.clock().advance(5s);
946 Validation val2 = a.validate(ID{4}, Seq{4}, 0s, 0s, true);
947 BEAST_EXPECT(ValStatus::current == harness.add(val2));
948 BEAST_EXPECT(harness.vals().numTrustedForLedger(ID{4}) == 1);
949 BEAST_EXPECT(
950 harness.vals().getPreferred(genesisLedger) ==
951 std::make_pair(ledgerAB.seq(), ledgerAB.id()));
952
953 // Another node requesting that ledger still doesn't change things
954 Validation val3 = b.validate(ID{4}, Seq{4}, 0s, 0s, true);
955 BEAST_EXPECT(ValStatus::current == harness.add(val3));
956 BEAST_EXPECT(harness.vals().numTrustedForLedger(ID{4}) == 2);
957 BEAST_EXPECT(
958 harness.vals().getPreferred(genesisLedger) ==
959 std::make_pair(ledgerAB.seq(), ledgerAB.id()));
960
961 // Switch to validation that is available
962 harness.clock().advance(5s);
963 Ledger ledgerABCDE = h["abcde"];
964 BEAST_EXPECT(ValStatus::current == harness.add(a.partial(ledgerABCDE)));
965 BEAST_EXPECT(ValStatus::current == harness.add(b.partial(ledgerABCDE)));
966 BEAST_EXPECT(
967 harness.vals().getPreferred(genesisLedger) ==
968 std::make_pair(ledgerABCDE.seq(), ledgerABCDE.id()));
969 }
970
971 void
973 {
974 testcase("NumTrustedForLedger");
976 TestHarness harness(h.oracle);
977 Node a = harness.makeNode();
978 Node b = harness.makeNode();
979 Ledger ledgerA = h["a"];
980
981 BEAST_EXPECT(ValStatus::current == harness.add(a.partial(ledgerA)));
982 BEAST_EXPECT(harness.vals().numTrustedForLedger(ledgerA.id()) == 0);
983
984 BEAST_EXPECT(ValStatus::current == harness.add(b.validate(ledgerA)));
985 BEAST_EXPECT(harness.vals().numTrustedForLedger(ledgerA.id()) == 1);
986 }
987
988 void
990 {
991 testcase("SeqEnforcer");
992 using Seq = Ledger::Seq;
993 using namespace std::chrono;
994
996 SeqEnforcer<Seq> enforcer;
997
999
1000 BEAST_EXPECT(enforcer(clock.now(), Seq{1}, p));
1001 BEAST_EXPECT(enforcer(clock.now(), Seq{10}, p));
1002 BEAST_EXPECT(!enforcer(clock.now(), Seq{5}, p));
1003 BEAST_EXPECT(!enforcer(clock.now(), Seq{9}, p));
1004 clock.advance(p.validationSET_EXPIRES - 1ms);
1005 BEAST_EXPECT(!enforcer(clock.now(), Seq{1}, p));
1006 clock.advance(2ms);
1007 BEAST_EXPECT(enforcer(clock.now(), Seq{1}, p));
1008 }
1009
1010 void
1012 {
1013 testcase("TrustChanged");
1014 using namespace std::chrono;
1015
1016 auto checker = [this](
1017 TestValidations& vals,
1018 hash_set<PeerID> const& listed,
1019 std::vector<Validation> const& trustedVals) {
1020 Ledger::ID testID = trustedVals.empty() ? this->genesisLedger.id()
1021 : trustedVals[0].ledgerID();
1022 Ledger::Seq testSeq = trustedVals.empty()
1023 ? this->genesisLedger.seq()
1024 : trustedVals[0].seq();
1025 BEAST_EXPECT(vals.currentTrusted() == trustedVals);
1026 BEAST_EXPECT(vals.getCurrentNodeIDs() == listed);
1027 BEAST_EXPECT(
1028 vals.getNodesAfter(this->genesisLedger, genesisLedger.id()) ==
1029 trustedVals.size());
1030 if (trustedVals.empty())
1031 BEAST_EXPECT(
1032 vals.getPreferred(this->genesisLedger) == std::nullopt);
1033 else
1034 BEAST_EXPECT(
1035 vals.getPreferred(this->genesisLedger)->second == testID);
1036 BEAST_EXPECT(
1037 vals.getTrustedForLedger(testID, testSeq) == trustedVals);
1038 BEAST_EXPECT(
1039 vals.numTrustedForLedger(testID) == trustedVals.size());
1040 };
1041
1042 {
1043 // Trusted to untrusted
1045 TestHarness harness(h.oracle);
1046 Node a = harness.makeNode();
1047 Ledger ledgerAB = h["ab"];
1048 Validation v = a.validate(ledgerAB);
1049 BEAST_EXPECT(ValStatus::current == harness.add(v));
1050
1051 hash_set<PeerID> listed({a.nodeID()});
1052 std::vector<Validation> trustedVals({v});
1053 checker(harness.vals(), listed, trustedVals);
1054
1055 trustedVals.clear();
1056 harness.vals().trustChanged({}, {a.nodeID()});
1057 checker(harness.vals(), listed, trustedVals);
1058 }
1059
1060 {
1061 // Untrusted to trusted
1063 TestHarness harness(h.oracle);
1064 Node a = harness.makeNode();
1065 a.untrust();
1066 Ledger ledgerAB = h["ab"];
1067 Validation v = a.validate(ledgerAB);
1068 BEAST_EXPECT(ValStatus::current == harness.add(v));
1069
1070 hash_set<PeerID> listed({a.nodeID()});
1071 std::vector<Validation> trustedVals;
1072 checker(harness.vals(), listed, trustedVals);
1073
1074 trustedVals.push_back(v);
1075 harness.vals().trustChanged({a.nodeID()}, {});
1076 checker(harness.vals(), listed, trustedVals);
1077 }
1078
1079 {
1080 // Trusted but not acquired -> untrusted
1082 TestHarness harness(h.oracle);
1083 Node a = harness.makeNode();
1084 Validation v =
1085 a.validate(Ledger::ID{2}, Ledger::Seq{2}, 0s, 0s, true);
1086 BEAST_EXPECT(ValStatus::current == harness.add(v));
1087
1088 hash_set<PeerID> listed({a.nodeID()});
1089 std::vector<Validation> trustedVals({v});
1090 auto& vals = harness.vals();
1091 BEAST_EXPECT(vals.currentTrusted() == trustedVals);
1092 BEAST_EXPECT(
1093 vals.getPreferred(genesisLedger)->second == v.ledgerID());
1094 BEAST_EXPECT(
1095 vals.getNodesAfter(genesisLedger, genesisLedger.id()) == 0);
1096
1097 trustedVals.clear();
1098 harness.vals().trustChanged({}, {a.nodeID()});
1099 // make acquiring ledger available
1100 h["ab"];
1101 BEAST_EXPECT(vals.currentTrusted() == trustedVals);
1102 BEAST_EXPECT(vals.getPreferred(genesisLedger) == std::nullopt);
1103 BEAST_EXPECT(
1104 vals.getNodesAfter(genesisLedger, genesisLedger.id()) == 0);
1105 }
1106 }
1107
1108 void
1126};
1127
1128BEAST_DEFINE_TESTSUITE(Validations, consensus, ripple);
1129} // namespace csf
1130} // namespace test
1131} // namespace ripple
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:152
std::chrono::time_point< NetClock > time_point
Definition chrono.h:50
Enforce validation increasing sequence requirement.
Definition Validations.h:81
Maintains current and recent ledger validations.
std::optional< std::pair< Seq, ID > > getPreferred(Ledger const &curr)
Return the sequence number and ID of the preferred working ledger.
std::size_t numTrustedForLedger(ID const &ledgerID)
Count the number of trusted full validations for the given ledger.
auto getCurrentNodeIDs() -> hash_set< NodeID >
Get the set of node ids associated with current validations.
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.
std::vector< WrappedValidationType > getTrustedForLedger(ID const &ledgerID, Seq const &seq)
Get trusted full validations for a specific ledger.
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.
void trustChanged(hash_set< NodeID > const &added, hash_set< NodeID > const &removed)
Update trust status of validations.
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.
ValStatus add(NodeID const &nodeID, Validation const &val)
Add a new validation.
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:231
std::optional< Ledger > lookup(Ledger::ID const &id) const
Find the ledger with the given ID.
Definition ledgers.cpp:110
A ledger is a set of observed transactions and a sequence number identifying the ledger.
Definition ledgers.h:45
tagged_integer< std::uint32_t, SeqTag > Seq
Definition ledgers.h:50
tagged_integer< std::uint32_t, IdTag > ID
Definition ledgers.h:53
Validation of a specific ledger by a specific Peer.
Definition Validation.h:31
Ledger::ID ledgerID() const
Definition Validation.h:71
PeerID const & nodeID() const
Definition Validation.h:101
std::optional< Ledger > acquire(Ledger::ID const &id)
Validation partial(Ledger ledger) const
std::optional< std::uint32_t > loadFee_
Validation validate(Ledger ledger) const
Node(PeerID nodeID, clock_type const &c)
Validation validate(Ledger ledger, NetClock::duration signOffset, NetClock::duration seenOffset) const
Validation validate(Ledger::ID id, Ledger::Seq seq, NetClock::duration signOffset, NetClock::duration seenOffset, bool full) const
beast::manual_clock< std::chrono::steady_clock > clock_
beast::abstract_clock< std::chrono::steady_clock > const clock_type
void run() override
Runs the suite.
static NetClock::time_point toNetClock(clock_type const &c)
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
constexpr Number one
Definition Number.cpp:156
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 validationCURRENT_LOCAL
Duration a validation remains current after first observed.
Definition Validations.h:45
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_WALL
The number of seconds a validation remains current after its ledger's close time.
Definition Validations.h:37
std::chrono::seconds validationCURRENT_EARLY
Duration pre-close in which validations are acceptable.
Definition Validations.h:52
Helper for writing unit tests with controlled ledger histories.
Definition ledgers.h:310
Set the sequence number on a JTx.
Definition seq.h:15