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