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;
29 duration_cast<NetClock::duration>(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
118 validate(Ledger ledger, NetClock::duration signOffset, NetClock::duration seenOffset) const
119 {
120 return validate(ledger.id(), ledger.seq(), signOffset, seenOffset, true);
121 }
122
124 validate(Ledger ledger) const
125 {
126 return validate(
127 ledger.id(), ledger.seq(), NetClock::duration{0}, NetClock::duration{0}, true);
128 }
129
131 partial(Ledger ledger) const
132 {
133 return validate(
134 ledger.id(), ledger.seq(), NetClock::duration{0}, NetClock::duration{0}, false);
135 }
136 };
137
138 // Generic Validations adaptor
140 {
143
144 public:
145 // Non-locking mutex to avoid locks in generic Validations
146 struct Mutex
147 {
148 void
150 {
151 }
152
153 void
155 {
156 }
157 };
158
161
163 {
164 }
165
167 now() const
168 {
169 return toNetClock(c_);
170 }
171
174 {
175 return oracle_.lookup(id);
176 }
177 };
178
179 // Specialize generic Validations using the above types
181
182 // Gather the dependencies of TestValidations in a single class and provide
183 // accessors for simplifying test logic
185 {
190
191 public:
193 {
194 }
195
197 add(Validation const& v)
198 {
199 return tv_.add(v.nodeID(), v);
200 }
201
204 {
205 return tv_;
206 }
207
208 Node
210 {
211 return Node(nextNodeId_++, clock_);
212 }
213
215 parms() const
216 {
217 return p_;
218 }
219
220 auto&
222 {
223 return clock_;
224 }
225 };
226
228
229 void
231 {
232 using namespace std::chrono_literals;
233
234 testcase("Add validation");
236 Ledger ledgerA = h["a"];
237 Ledger ledgerAB = h["ab"];
238 Ledger ledgerAZ = h["az"];
239 Ledger ledgerABC = h["abc"];
240 Ledger ledgerABCD = h["abcd"];
241 Ledger ledgerABCDE = h["abcde"];
242
243 {
244 TestHarness harness(h.oracle);
245 Node n = harness.makeNode();
246
247 auto const v = n.validate(ledgerA);
248
249 // Add a current validation
250 BEAST_EXPECT(ValStatus::current == harness.add(v));
251
252 // Re-adding violates the increasing seq requirement for full
253 // validations
254 BEAST_EXPECT(ValStatus::badSeq == harness.add(v));
255
256 harness.clock().advance(1s);
257
258 BEAST_EXPECT(ValStatus::current == harness.add(n.validate(ledgerAB)));
259
260 // Test the node changing signing key
261
262 // Confirm old ledger on hand, but not new ledger
263 BEAST_EXPECT(harness.vals().numTrustedForLedger(ledgerAB.id()) == 1);
264 BEAST_EXPECT(harness.vals().numTrustedForLedger(ledgerABC.id()) == 0);
265
266 // Rotate signing keys
267 n.advanceKey();
268
269 harness.clock().advance(1s);
270
271 // Cannot re-do the same full validation sequence
272 BEAST_EXPECT(ValStatus::conflicting == harness.add(n.validate(ledgerAB)));
273 // Cannot send the same partial validation sequence
274 BEAST_EXPECT(ValStatus::conflicting == harness.add(n.partial(ledgerAB)));
275
276 // Now trusts the newest ledger too
277 harness.clock().advance(1s);
278 BEAST_EXPECT(ValStatus::current == harness.add(n.validate(ledgerABC)));
279 BEAST_EXPECT(harness.vals().numTrustedForLedger(ledgerAB.id()) == 1);
280 BEAST_EXPECT(harness.vals().numTrustedForLedger(ledgerABC.id()) == 1);
281
282 // Processing validations out of order should ignore the older
283 // validation
284 harness.clock().advance(2s);
285 auto const valABCDE = n.validate(ledgerABCDE);
286
287 harness.clock().advance(4s);
288 auto const valABCD = n.validate(ledgerABCD);
289
290 BEAST_EXPECT(ValStatus::current == harness.add(valABCD));
291
292 BEAST_EXPECT(ValStatus::stale == harness.add(valABCDE));
293 }
294
295 {
296 // Process validations out of order with shifted times
297
298 TestHarness harness(h.oracle);
299 Node n = harness.makeNode();
300
301 // Establish a new current validation
302 BEAST_EXPECT(ValStatus::current == harness.add(n.validate(ledgerA)));
303
304 // Process a validation that has "later" seq but early sign time
305 BEAST_EXPECT(ValStatus::stale == harness.add(n.validate(ledgerAB, -1s, -1s)));
306
307 // Process a validation that has a later seq and later sign
308 // time
309 BEAST_EXPECT(ValStatus::current == harness.add(n.validate(ledgerABC, 1s, 1s)));
310 }
311
312 {
313 // Test stale on arrival validations
314 TestHarness harness(h.oracle);
315 Node n = harness.makeNode();
316
317 BEAST_EXPECT(
319 harness.add(n.validate(ledgerA, -harness.parms().validationCURRENT_EARLY, 0s)));
320
321 BEAST_EXPECT(
323 harness.add(n.validate(ledgerA, harness.parms().validationCURRENT_WALL, 0s)));
324
325 BEAST_EXPECT(
327 harness.add(n.validate(ledgerA, 0s, harness.parms().validationCURRENT_LOCAL)));
328 }
329
330 {
331 // Test that full or partials cannot be sent for older sequence
332 // numbers, unless time-out has happened
333 for (bool doFull : {true, false})
334 {
335 TestHarness harness(h.oracle);
336 Node n = harness.makeNode();
337
338 auto process = [&](Ledger& lgr) {
339 if (doFull)
340 return harness.add(n.validate(lgr));
341 return harness.add(n.partial(lgr));
342 };
343
344 BEAST_EXPECT(ValStatus::current == process(ledgerABC));
345 harness.clock().advance(1s);
346 BEAST_EXPECT(ledgerAB.seq() < ledgerABC.seq());
347 BEAST_EXPECT(ValStatus::badSeq == process(ledgerAB));
348
349 // If we advance far enough for AB to expire, we can fully
350 // validate or partially validate that sequence number again
351 BEAST_EXPECT(ValStatus::conflicting == process(ledgerAZ));
352 harness.clock().advance(harness.parms().validationSET_EXPIRES + 1ms);
353 BEAST_EXPECT(ValStatus::current == process(ledgerAZ));
354 }
355 }
356 }
357
358 void
360 {
361 testcase("Stale validation");
362 // Verify validation becomes stale based solely on time passing, but
363 // use different functions to trigger the check for staleness
364
366 Ledger ledgerA = h["a"];
367 Ledger ledgerAB = h["ab"];
368
369 using Trigger = std::function<void(TestValidations&)>;
370
371 std::vector<Trigger> triggers = {
372 [&](TestValidations& vals) { vals.currentTrusted(); },
373 [&](TestValidations& vals) { vals.getCurrentNodeIDs(); },
374 [&](TestValidations& vals) { vals.getPreferred(genesisLedger); },
375 [&](TestValidations& vals) { vals.getNodesAfter(ledgerA, ledgerA.id()); }};
376 for (Trigger trigger : triggers)
377 {
378 TestHarness harness(h.oracle);
379 Node n = harness.makeNode();
380
381 BEAST_EXPECT(ValStatus::current == harness.add(n.validate(ledgerAB)));
382 trigger(harness.vals());
383 BEAST_EXPECT(harness.vals().getNodesAfter(ledgerA, ledgerA.id()) == 1);
384 BEAST_EXPECT(
385 harness.vals().getPreferred(genesisLedger) ==
386 std::make_pair(ledgerAB.seq(), ledgerAB.id()));
387 harness.clock().advance(harness.parms().validationCURRENT_LOCAL);
388
389 // trigger check for stale
390 trigger(harness.vals());
391
392 BEAST_EXPECT(harness.vals().getNodesAfter(ledgerA, ledgerA.id()) == 0);
393 BEAST_EXPECT(harness.vals().getPreferred(genesisLedger) == std::nullopt);
394 }
395 }
396
397 void
399 {
400 // Test getting number of nodes working on a validation descending
401 // a prescribed one. This count should only be for trusted nodes, but
402 // includes partial and full validations
403
404 using namespace std::chrono_literals;
405 testcase("Get nodes after");
406
408 Ledger ledgerA = h["a"];
409 Ledger ledgerAB = h["ab"];
410 Ledger ledgerABC = h["abc"];
411 Ledger ledgerAD = h["ad"];
412
413 TestHarness harness(h.oracle);
414 Node a = harness.makeNode(), b = harness.makeNode(), c = harness.makeNode(),
415 d = harness.makeNode();
416 c.untrust();
417
418 // first round a,b,c agree, d has is partial
419 BEAST_EXPECT(ValStatus::current == harness.add(a.validate(ledgerA)));
420 BEAST_EXPECT(ValStatus::current == harness.add(b.validate(ledgerA)));
421 BEAST_EXPECT(ValStatus::current == harness.add(c.validate(ledgerA)));
422 BEAST_EXPECT(ValStatus::current == harness.add(d.partial(ledgerA)));
423
424 for (Ledger const& ledger : {ledgerA, ledgerAB, ledgerABC, ledgerAD})
425 BEAST_EXPECT(harness.vals().getNodesAfter(ledger, ledger.id()) == 0);
426
427 harness.clock().advance(5s);
428
429 BEAST_EXPECT(ValStatus::current == harness.add(a.validate(ledgerAB)));
430 BEAST_EXPECT(ValStatus::current == harness.add(b.validate(ledgerABC)));
431 BEAST_EXPECT(ValStatus::current == harness.add(c.validate(ledgerAB)));
432 BEAST_EXPECT(ValStatus::current == harness.add(d.partial(ledgerABC)));
433
434 BEAST_EXPECT(harness.vals().getNodesAfter(ledgerA, ledgerA.id()) == 3);
435 BEAST_EXPECT(harness.vals().getNodesAfter(ledgerAB, ledgerAB.id()) == 2);
436 BEAST_EXPECT(harness.vals().getNodesAfter(ledgerABC, ledgerABC.id()) == 0);
437 BEAST_EXPECT(harness.vals().getNodesAfter(ledgerAD, ledgerAD.id()) == 0);
438
439 // If given a ledger inconsistent with the id, is still able to check
440 // using slower method
441 BEAST_EXPECT(harness.vals().getNodesAfter(ledgerAD, ledgerA.id()) == 1);
442 BEAST_EXPECT(harness.vals().getNodesAfter(ledgerAD, ledgerAB.id()) == 2);
443 }
444
445 void
447 {
448 using namespace std::chrono_literals;
449 testcase("Current trusted validations");
450
452 Ledger ledgerA = h["a"];
453 Ledger ledgerB = h["b"];
454 Ledger ledgerAC = h["ac"];
455
456 TestHarness harness(h.oracle);
457 Node a = harness.makeNode(), b = harness.makeNode();
458 b.untrust();
459
460 BEAST_EXPECT(ValStatus::current == harness.add(a.validate(ledgerA)));
461 BEAST_EXPECT(ValStatus::current == harness.add(b.validate(ledgerB)));
462
463 // Only a is trusted
464 BEAST_EXPECT(harness.vals().currentTrusted().size() == 1);
465 BEAST_EXPECT(harness.vals().currentTrusted()[0].ledgerID() == ledgerA.id());
466 BEAST_EXPECT(harness.vals().currentTrusted()[0].seq() == ledgerA.seq());
467
468 harness.clock().advance(3s);
469
470 for (auto const& node : {a, b})
471 BEAST_EXPECT(ValStatus::current == harness.add(node.validate(ledgerAC)));
472
473 // New validation for a
474 BEAST_EXPECT(harness.vals().currentTrusted().size() == 1);
475 BEAST_EXPECT(harness.vals().currentTrusted()[0].ledgerID() == ledgerAC.id());
476 BEAST_EXPECT(harness.vals().currentTrusted()[0].seq() == ledgerAC.seq());
477
478 // Pass enough time for it to go stale
479 harness.clock().advance(harness.parms().validationCURRENT_LOCAL);
480 BEAST_EXPECT(harness.vals().currentTrusted().empty());
481 }
482
483 void
485 {
486 using namespace std::chrono_literals;
487 testcase("Current public keys");
488
490 Ledger ledgerA = h["a"];
491 Ledger ledgerAC = h["ac"];
492
493 TestHarness harness(h.oracle);
494 Node a = harness.makeNode(), b = harness.makeNode();
495 b.untrust();
496
497 for (auto const& node : {a, b})
498 BEAST_EXPECT(ValStatus::current == harness.add(node.validate(ledgerA)));
499
500 {
501 hash_set<PeerID> const expectedKeys = {a.nodeID(), b.nodeID()};
502 BEAST_EXPECT(harness.vals().getCurrentNodeIDs() == expectedKeys);
503 }
504
505 harness.clock().advance(3s);
506
507 // Change keys and issue partials
508 a.advanceKey();
509 b.advanceKey();
510
511 for (auto const& node : {a, b})
512 BEAST_EXPECT(ValStatus::current == harness.add(node.partial(ledgerAC)));
513
514 {
515 hash_set<PeerID> const expectedKeys = {a.nodeID(), b.nodeID()};
516 BEAST_EXPECT(harness.vals().getCurrentNodeIDs() == expectedKeys);
517 }
518
519 // Pass enough time for them to go stale
520 harness.clock().advance(harness.parms().validationCURRENT_LOCAL);
521 BEAST_EXPECT(harness.vals().getCurrentNodeIDs().empty());
522 }
523
524 void
526 {
527 // Test the Validations functions that calculate a value by ledger ID
528 using namespace std::chrono_literals;
529 testcase("By ledger functions");
530
531 // Several Validations functions return a set of values associated
532 // with trusted ledgers sharing the same ledger ID. The tests below
533 // exercise this logic by saving the set of trusted Validations, and
534 // verifying that the Validations member functions all calculate the
535 // proper transformation of the available ledgers.
536
538 TestHarness harness(h.oracle);
539
540 Node a = harness.makeNode(), b = harness.makeNode(), c = harness.makeNode(),
541 d = harness.makeNode(), e = harness.makeNode();
542
543 c.untrust();
544 // Mix of load fees
545 a.setLoadFee(12);
546 b.setLoadFee(1);
547 c.setLoadFee(12);
548 e.setLoadFee(12);
549
551
552 //----------------------------------------------------------------------
553 // checkers
554 auto sorted = [](auto vec) {
555 std::sort(vec.begin(), vec.end());
556 return vec;
557 };
558 auto compare = [&]() {
559 for (auto& it : trustedValidations)
560 {
561 auto const& id = it.first.first;
562 auto const& seq = it.first.second;
563 auto const& expectedValidations = it.second;
564
565 BEAST_EXPECT(harness.vals().numTrustedForLedger(id) == expectedValidations.size());
566 BEAST_EXPECT(
567 sorted(harness.vals().getTrustedForLedger(id, seq)) ==
568 sorted(expectedValidations));
569
570 std::uint32_t baseFee = 0;
571 std::vector<uint32_t> expectedFees;
572 for (auto const& val : expectedValidations)
573 {
574 expectedFees.push_back(val.loadFee().value_or(baseFee));
575 }
576
577 BEAST_EXPECT(sorted(harness.vals().fees(id, baseFee)) == sorted(expectedFees));
578 }
579 };
580
581 //----------------------------------------------------------------------
582 Ledger ledgerA = h["a"];
583 Ledger ledgerB = h["b"];
584 Ledger ledgerAC = h["ac"];
585
586 // Add a dummy ID to cover unknown ledger identifiers
587 trustedValidations[{Ledger::ID{100}, Ledger::Seq{100}}] = {};
588
589 // first round a,b,c agree
590 for (auto const& node : {a, b, c})
591 {
592 auto const val = node.validate(ledgerA);
593 BEAST_EXPECT(ValStatus::current == harness.add(val));
594 if (val.trusted())
595 trustedValidations[{val.ledgerID(), val.seq()}].emplace_back(val);
596 }
597 // d disagrees
598 {
599 auto const val = d.validate(ledgerB);
600 BEAST_EXPECT(ValStatus::current == harness.add(val));
601 trustedValidations[{val.ledgerID(), val.seq()}].emplace_back(val);
602 }
603 // e only issues partials
604 {
605 BEAST_EXPECT(ValStatus::current == harness.add(e.partial(ledgerA)));
606 }
607
608 harness.clock().advance(5s);
609 // second round, a,b,c move to ledger 2
610 for (auto const& node : {a, b, c})
611 {
612 auto const val = node.validate(ledgerAC);
613 BEAST_EXPECT(ValStatus::current == harness.add(val));
614 if (val.trusted())
615 trustedValidations[{val.ledgerID(), val.seq()}].emplace_back(val);
616 }
617 // d now thinks ledger 1, but cannot re-issue a previously used seq
618 // and attempting it should generate a conflict.
619 {
620 BEAST_EXPECT(ValStatus::conflicting == harness.add(d.partial(ledgerA)));
621 }
622 // e only issues partials
623 {
624 BEAST_EXPECT(ValStatus::current == harness.add(e.partial(ledgerAC)));
625 }
626
627 compare();
628 }
629
630 void
632 {
633 // Verify expiring clears out validations stored by ledger
634 testcase("Expire validations");
635 SuiteJournal j("Validations_test", *this);
637 TestHarness harness(h.oracle);
638 Node const a = harness.makeNode();
639 constexpr Ledger::Seq one(1);
640 constexpr Ledger::Seq two(2);
641
642 // simple cases
643 Ledger const ledgerA = h["a"];
644 BEAST_EXPECT(ValStatus::current == harness.add(a.validate(ledgerA)));
645 BEAST_EXPECT(harness.vals().numTrustedForLedger(ledgerA.id()) == 1);
646 harness.vals().expire(j);
647 BEAST_EXPECT(harness.vals().numTrustedForLedger(ledgerA.id()) == 1);
648 harness.clock().advance(harness.parms().validationSET_EXPIRES);
649 harness.vals().expire(j);
650 BEAST_EXPECT(harness.vals().numTrustedForLedger(ledgerA.id()) == 0);
651
652 // use setSeqToKeep to keep the validation from expire
653 Ledger const ledgerB = h["ab"];
654 BEAST_EXPECT(ValStatus::current == harness.add(a.validate(ledgerB)));
655 BEAST_EXPECT(harness.vals().numTrustedForLedger(ledgerB.id()) == 1);
656 harness.vals().setSeqToKeep(ledgerB.seq(), ledgerB.seq() + one);
657 harness.clock().advance(harness.parms().validationSET_EXPIRES);
658 harness.vals().expire(j);
659 BEAST_EXPECT(harness.vals().numTrustedForLedger(ledgerB.id()) == 1);
660 // change toKeep
661 harness.vals().setSeqToKeep(ledgerB.seq() + one, ledgerB.seq() + two);
662 // advance clock slowly
663 int const loops =
664 harness.parms().validationSET_EXPIRES / harness.parms().validationFRESHNESS + 1;
665 for (int i = 0; i < loops; ++i)
666 {
667 harness.clock().advance(harness.parms().validationFRESHNESS);
668 harness.vals().expire(j);
669 }
670 BEAST_EXPECT(harness.vals().numTrustedForLedger(ledgerB.id()) == 0);
671
672 // Allow the validation with high seq to expire
673 Ledger const ledgerC = h["abc"];
674 BEAST_EXPECT(ValStatus::current == harness.add(a.validate(ledgerC)));
675 BEAST_EXPECT(harness.vals().numTrustedForLedger(ledgerC.id()) == 1);
676 harness.vals().setSeqToKeep(ledgerC.seq() - one, ledgerC.seq());
677 harness.clock().advance(harness.parms().validationSET_EXPIRES);
678 harness.vals().expire(j);
679 BEAST_EXPECT(harness.vals().numTrustedForLedger(ledgerC.id()) == 0);
680 }
681
682 void
684 {
685 // Test final flush of validations
686 using namespace std::chrono_literals;
687 testcase("Flush validations");
688
690 TestHarness harness(h.oracle);
691 Node a = harness.makeNode(), b = harness.makeNode(), c = harness.makeNode();
692 c.untrust();
693
694 Ledger ledgerA = h["a"];
695 Ledger ledgerAB = h["ab"];
696
698 for (auto const& node : {a, b, c})
699 {
700 auto const val = node.validate(ledgerA);
701 BEAST_EXPECT(ValStatus::current == harness.add(val));
702 expected.emplace(node.nodeID(), val);
703 }
704
705 // Send in a new validation for a, saving the new one into the expected
706 // map after setting the proper prior ledger ID it replaced
707 harness.clock().advance(1s);
708 auto newVal = a.validate(ledgerAB);
709 BEAST_EXPECT(ValStatus::current == harness.add(newVal));
710 expected.find(a.nodeID())->second = newVal;
711 }
712
713 void
715 {
716 using namespace std::chrono_literals;
717 testcase("Preferred Ledger");
718
720 TestHarness harness(h.oracle);
721 Node a = harness.makeNode(), b = harness.makeNode(), c = harness.makeNode(),
722 d = harness.makeNode();
723 c.untrust();
724
725 Ledger ledgerA = h["a"];
726 Ledger ledgerB = h["b"];
727 Ledger ledgerAC = h["ac"];
728 Ledger ledgerACD = h["acd"];
729
730 using Seq = Ledger::Seq;
731
732 auto pref = [](Ledger ledger) { return std::make_pair(ledger.seq(), ledger.id()); };
733
734 // Empty (no ledgers)
735 BEAST_EXPECT(harness.vals().getPreferred(ledgerA) == std::nullopt);
736
737 // Single ledger
738 BEAST_EXPECT(ValStatus::current == harness.add(a.validate(ledgerB)));
739 BEAST_EXPECT(harness.vals().getPreferred(ledgerA) == pref(ledgerB));
740 BEAST_EXPECT(harness.vals().getPreferred(ledgerB) == pref(ledgerB));
741
742 // Minimum valid sequence
743 BEAST_EXPECT(harness.vals().getPreferred(ledgerA, Seq{10}) == ledgerA.id());
744
745 // Untrusted doesn't impact preferred ledger
746 // (ledgerB has tie-break over ledgerA)
747 BEAST_EXPECT(ValStatus::current == harness.add(b.validate(ledgerA)));
748 BEAST_EXPECT(ValStatus::current == harness.add(c.validate(ledgerA)));
749 BEAST_EXPECT(ledgerB.id() > ledgerA.id());
750 BEAST_EXPECT(harness.vals().getPreferred(ledgerA) == pref(ledgerB));
751 BEAST_EXPECT(harness.vals().getPreferred(ledgerB) == pref(ledgerB));
752
753 // Partial does break ties
754 BEAST_EXPECT(ValStatus::current == harness.add(d.partial(ledgerA)));
755 BEAST_EXPECT(harness.vals().getPreferred(ledgerA) == pref(ledgerA));
756 BEAST_EXPECT(harness.vals().getPreferred(ledgerB) == pref(ledgerA));
757
758 harness.clock().advance(5s);
759
760 // Parent of preferred-> stick with ledger
761 for (auto const& node : {a, b, c, d})
762 BEAST_EXPECT(ValStatus::current == harness.add(node.validate(ledgerAC)));
763 // Parent of preferred stays put
764 BEAST_EXPECT(harness.vals().getPreferred(ledgerA) == pref(ledgerA));
765 // Earlier different chain, switch
766 BEAST_EXPECT(harness.vals().getPreferred(ledgerB) == pref(ledgerAC));
767 // Later on chain, stays where it is
768 BEAST_EXPECT(harness.vals().getPreferred(ledgerACD) == pref(ledgerACD));
769
770 // Any later grandchild or different chain is preferred
771 harness.clock().advance(5s);
772 for (auto const& node : {a, b, c, d})
773 BEAST_EXPECT(ValStatus::current == harness.add(node.validate(ledgerACD)));
774 for (auto const& ledger : {ledgerA, ledgerB, ledgerACD})
775 BEAST_EXPECT(harness.vals().getPreferred(ledger) == pref(ledgerACD));
776 }
777
778 void
780 {
781 using namespace std::chrono_literals;
782 testcase("Get preferred LCL");
783
785 TestHarness harness(h.oracle);
786 Node a = harness.makeNode();
787
788 Ledger ledgerA = h["a"];
789 Ledger ledgerB = h["b"];
790 Ledger ledgerC = h["c"];
791
792 using ID = Ledger::ID;
793 using Seq = Ledger::Seq;
794
796
797 // No trusted validations or counts sticks with current ledger
798 BEAST_EXPECT(harness.vals().getPreferredLCL(ledgerA, Seq{0}, peerCounts) == ledgerA.id());
799
800 ++peerCounts[ledgerB.id()];
801
802 // No trusted validations, rely on peer counts
803 BEAST_EXPECT(harness.vals().getPreferredLCL(ledgerA, Seq{0}, peerCounts) == ledgerB.id());
804
805 ++peerCounts[ledgerC.id()];
806 // No trusted validations, tied peers goes with larger ID
807 BEAST_EXPECT(ledgerC.id() > ledgerB.id());
808
809 BEAST_EXPECT(harness.vals().getPreferredLCL(ledgerA, Seq{0}, peerCounts) == ledgerC.id());
810
811 peerCounts[ledgerC.id()] += 1000;
812
813 // Single trusted always wins over peer counts
814 BEAST_EXPECT(ValStatus::current == harness.add(a.validate(ledgerA)));
815 BEAST_EXPECT(harness.vals().getPreferredLCL(ledgerA, Seq{0}, peerCounts) == ledgerA.id());
816 BEAST_EXPECT(harness.vals().getPreferredLCL(ledgerB, Seq{0}, peerCounts) == ledgerA.id());
817 BEAST_EXPECT(harness.vals().getPreferredLCL(ledgerC, Seq{0}, peerCounts) == ledgerA.id());
818
819 // Stick with current ledger if trusted validation ledger has too old
820 // of a sequence
821 BEAST_EXPECT(harness.vals().getPreferredLCL(ledgerB, Seq{2}, peerCounts) == ledgerB.id());
822 }
823
824 void
826 {
827 using namespace std::chrono_literals;
828 testcase("Acquire validated ledger");
829
831 TestHarness harness(h.oracle);
832 Node a = harness.makeNode();
833 Node b = harness.makeNode();
834
835 using ID = Ledger::ID;
836 using Seq = Ledger::Seq;
837
838 // Validate the ledger before it is actually available
839 Validation val = a.validate(ID{2}, Seq{2}, 0s, 0s, true);
840
841 BEAST_EXPECT(ValStatus::current == harness.add(val));
842 // Validation is available
843 BEAST_EXPECT(harness.vals().numTrustedForLedger(ID{2}) == 1);
844 // but ledger based data is not
845 BEAST_EXPECT(harness.vals().getNodesAfter(genesisLedger, ID{0}) == 0);
846 // Initial preferred branch falls back to the ledger we are trying to
847 // acquire
848 BEAST_EXPECT(harness.vals().getPreferred(genesisLedger) == std::make_pair(Seq{2}, ID{2}));
849
850 // After adding another unavailable validation, the preferred ledger
851 // breaks ties via higher ID
852 BEAST_EXPECT(ValStatus::current == harness.add(b.validate(ID{3}, Seq{2}, 0s, 0s, true)));
853 BEAST_EXPECT(harness.vals().getPreferred(genesisLedger) == std::make_pair(Seq{2}, ID{3}));
854
855 // Create the ledger
856 Ledger ledgerAB = h["ab"];
857 // Now it should be available
858 BEAST_EXPECT(harness.vals().getNodesAfter(genesisLedger, ID{0}) == 1);
859
860 // Create a validation that is not available
861 harness.clock().advance(5s);
862 Validation val2 = a.validate(ID{4}, Seq{4}, 0s, 0s, true);
863 BEAST_EXPECT(ValStatus::current == harness.add(val2));
864 BEAST_EXPECT(harness.vals().numTrustedForLedger(ID{4}) == 1);
865 BEAST_EXPECT(
866 harness.vals().getPreferred(genesisLedger) ==
867 std::make_pair(ledgerAB.seq(), ledgerAB.id()));
868
869 // Another node requesting that ledger still doesn't change things
870 Validation val3 = b.validate(ID{4}, Seq{4}, 0s, 0s, true);
871 BEAST_EXPECT(ValStatus::current == harness.add(val3));
872 BEAST_EXPECT(harness.vals().numTrustedForLedger(ID{4}) == 2);
873 BEAST_EXPECT(
874 harness.vals().getPreferred(genesisLedger) ==
875 std::make_pair(ledgerAB.seq(), ledgerAB.id()));
876
877 // Switch to validation that is available
878 harness.clock().advance(5s);
879 Ledger ledgerABCDE = h["abcde"];
880 BEAST_EXPECT(ValStatus::current == harness.add(a.partial(ledgerABCDE)));
881 BEAST_EXPECT(ValStatus::current == harness.add(b.partial(ledgerABCDE)));
882 BEAST_EXPECT(
883 harness.vals().getPreferred(genesisLedger) ==
884 std::make_pair(ledgerABCDE.seq(), ledgerABCDE.id()));
885 }
886
887 void
889 {
890 testcase("NumTrustedForLedger");
892 TestHarness harness(h.oracle);
893 Node a = harness.makeNode();
894 Node b = harness.makeNode();
895 Ledger ledgerA = h["a"];
896
897 BEAST_EXPECT(ValStatus::current == harness.add(a.partial(ledgerA)));
898 BEAST_EXPECT(harness.vals().numTrustedForLedger(ledgerA.id()) == 0);
899
900 BEAST_EXPECT(ValStatus::current == harness.add(b.validate(ledgerA)));
901 BEAST_EXPECT(harness.vals().numTrustedForLedger(ledgerA.id()) == 1);
902 }
903
904 void
906 {
907 testcase("SeqEnforcer");
908 using Seq = Ledger::Seq;
909 using namespace std::chrono;
910
912 SeqEnforcer<Seq> enforcer;
913
915
916 BEAST_EXPECT(enforcer(clock.now(), Seq{1}, p));
917 BEAST_EXPECT(enforcer(clock.now(), Seq{10}, p));
918 BEAST_EXPECT(!enforcer(clock.now(), Seq{5}, p));
919 BEAST_EXPECT(!enforcer(clock.now(), Seq{9}, p));
920 clock.advance(p.validationSET_EXPIRES - 1ms);
921 BEAST_EXPECT(!enforcer(clock.now(), Seq{1}, p));
922 clock.advance(2ms);
923 BEAST_EXPECT(enforcer(clock.now(), Seq{1}, p));
924 }
925
926 void
928 {
929 testcase("TrustChanged");
930 using namespace std::chrono;
931
932 auto checker = [this](
933 TestValidations& vals,
934 hash_set<PeerID> const& listed,
935 std::vector<Validation> const& trustedVals) {
936 Ledger::ID testID =
937 trustedVals.empty() ? this->genesisLedger.id() : trustedVals[0].ledgerID();
938 Ledger::Seq testSeq =
939 trustedVals.empty() ? this->genesisLedger.seq() : trustedVals[0].seq();
940 BEAST_EXPECT(vals.currentTrusted() == trustedVals);
941 BEAST_EXPECT(vals.getCurrentNodeIDs() == listed);
942 BEAST_EXPECT(
943 vals.getNodesAfter(this->genesisLedger, genesisLedger.id()) == trustedVals.size());
944 if (trustedVals.empty())
945 BEAST_EXPECT(vals.getPreferred(this->genesisLedger) == std::nullopt);
946 else
947 BEAST_EXPECT(vals.getPreferred(this->genesisLedger)->second == testID);
948 BEAST_EXPECT(vals.getTrustedForLedger(testID, testSeq) == trustedVals);
949 BEAST_EXPECT(vals.numTrustedForLedger(testID) == trustedVals.size());
950 };
951
952 {
953 // Trusted to untrusted
955 TestHarness harness(h.oracle);
956 Node a = harness.makeNode();
957 Ledger ledgerAB = h["ab"];
958 Validation v = a.validate(ledgerAB);
959 BEAST_EXPECT(ValStatus::current == harness.add(v));
960
961 hash_set<PeerID> listed({a.nodeID()});
962 std::vector<Validation> trustedVals({v});
963 checker(harness.vals(), listed, trustedVals);
964
965 trustedVals.clear();
966 harness.vals().trustChanged({}, {a.nodeID()});
967 checker(harness.vals(), listed, trustedVals);
968 }
969
970 {
971 // Untrusted to trusted
973 TestHarness harness(h.oracle);
974 Node a = harness.makeNode();
975 a.untrust();
976 Ledger ledgerAB = h["ab"];
977 Validation v = a.validate(ledgerAB);
978 BEAST_EXPECT(ValStatus::current == harness.add(v));
979
980 hash_set<PeerID> listed({a.nodeID()});
981 std::vector<Validation> trustedVals;
982 checker(harness.vals(), listed, trustedVals);
983
984 trustedVals.push_back(v);
985 harness.vals().trustChanged({a.nodeID()}, {});
986 checker(harness.vals(), listed, trustedVals);
987 }
988
989 {
990 // Trusted but not acquired -> untrusted
992 TestHarness harness(h.oracle);
993 Node a = harness.makeNode();
994 Validation v = a.validate(Ledger::ID{2}, Ledger::Seq{2}, 0s, 0s, true);
995 BEAST_EXPECT(ValStatus::current == harness.add(v));
996
997 hash_set<PeerID> listed({a.nodeID()});
998 std::vector<Validation> trustedVals({v});
999 auto& vals = harness.vals();
1000 BEAST_EXPECT(vals.currentTrusted() == trustedVals);
1001 BEAST_EXPECT(vals.getPreferred(genesisLedger)->second == v.ledgerID());
1002 BEAST_EXPECT(vals.getNodesAfter(genesisLedger, genesisLedger.id()) == 0);
1003
1004 trustedVals.clear();
1005 harness.vals().trustChanged({}, {a.nodeID()});
1006 // make acquiring ledger available
1007 h["ab"];
1008 BEAST_EXPECT(vals.currentTrusted() == trustedVals);
1009 BEAST_EXPECT(vals.getPreferred(genesisLedger) == std::nullopt);
1010 BEAST_EXPECT(vals.getNodesAfter(genesisLedger, genesisLedger.id()) == 0);
1011 }
1012 }
1013
1014 void
1032};
1033
1034BEAST_DEFINE_TESTSUITE(Validations, consensus, xrpl);
1035} // namespace csf
1036} // namespace test
1037} // namespace xrpl
Abstract interface to a clock.
Manual clock implementation.
A testsuite class.
Definition suite.h:51
testcase_t testcase
Memberspace for declaring test cases.
Definition suite.h:150
std::chrono::time_point< NetClock > time_point
Definition chrono.h:46
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.
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:230
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:44
tagged_integer< std::uint32_t, IdTag > ID
Definition ledgers.h:52
tagged_integer< std::uint32_t, SeqTag > Seq
Definition ledgers.h:49
Validation of a specific ledger by a specific Peer.
Definition Validation.h:30
PeerID const & nodeID() const
Definition Validation.h:100
Ledger::ID ledgerID() const
Definition Validation.h:70
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:17
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:5
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:27
std::chrono::seconds validationFRESHNESS
How long we consider a validation fresh.
Definition Validations.h:69
std::chrono::seconds validationSET_EXPIRES
Duration a set of validations for a given ledger hash remain valid.
Definition Validations.h:59
std::chrono::seconds validationCURRENT_EARLY
Duration pre-close in which validations are acceptable.
Definition Validations.h:51
std::chrono::seconds validationCURRENT_LOCAL
Duration a validation remains current after first observed.
Definition Validations.h:44
std::chrono::seconds validationCURRENT_WALL
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:305
Set the sequence number on a JTx.
Definition seq.h:14