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 const ledgerA = h["a"];
237 Ledger ledgerAB = h["ab"];
238 Ledger ledgerAZ = h["az"];
239 Ledger ledgerABC = h["abc"];
240 Ledger const ledgerABCD = h["abcd"];
241 Ledger const 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 const 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 const 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 const ledgerAB = h["ab"];
368
369 using Trigger = std::function<void(TestValidations&)>;
370
371 std::vector<Trigger> const 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 const& trigger : triggers)
377 {
378 TestHarness harness(h.oracle);
379 Node const 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 const ledgerA = h["a"];
409 Ledger const ledgerAB = h["ab"];
410 Ledger const ledgerABC = h["abc"];
411 Ledger const ledgerAD = h["ad"];
412
413 TestHarness harness(h.oracle);
414 Node const trustedNode1 = harness.makeNode();
415 Node const trustedNode2 = harness.makeNode();
416 Node const trustedNode3 = harness.makeNode();
417
418 Node notTrustedNode = harness.makeNode();
419 notTrustedNode.untrust();
420
421 // first round a,b,c agree, d has is partial
422 BEAST_EXPECT(ValStatus::current == harness.add(trustedNode1.validate(ledgerA)));
423 BEAST_EXPECT(ValStatus::current == harness.add(trustedNode2.validate(ledgerA)));
424 BEAST_EXPECT(ValStatus::current == harness.add(notTrustedNode.validate(ledgerA)));
425 BEAST_EXPECT(ValStatus::current == harness.add(trustedNode3.partial(ledgerA)));
426
427 for (Ledger const& ledger : {ledgerA, ledgerAB, ledgerABC, ledgerAD})
428 BEAST_EXPECT(harness.vals().getNodesAfter(ledger, ledger.id()) == 0);
429
430 harness.clock().advance(5s);
431
432 BEAST_EXPECT(ValStatus::current == harness.add(trustedNode1.validate(ledgerAB)));
433 BEAST_EXPECT(ValStatus::current == harness.add(trustedNode2.validate(ledgerABC)));
434 BEAST_EXPECT(ValStatus::current == harness.add(notTrustedNode.validate(ledgerAB)));
435 BEAST_EXPECT(ValStatus::current == harness.add(trustedNode3.partial(ledgerABC)));
436
437 BEAST_EXPECT(harness.vals().getNodesAfter(ledgerA, ledgerA.id()) == 3);
438 BEAST_EXPECT(harness.vals().getNodesAfter(ledgerAB, ledgerAB.id()) == 2);
439 BEAST_EXPECT(harness.vals().getNodesAfter(ledgerABC, ledgerABC.id()) == 0);
440 BEAST_EXPECT(harness.vals().getNodesAfter(ledgerAD, ledgerAD.id()) == 0);
441
442 // If given a ledger inconsistent with the id, is still able to check using slower method
443 BEAST_EXPECT(harness.vals().getNodesAfter(ledgerAD, ledgerA.id()) == 1);
444 BEAST_EXPECT(harness.vals().getNodesAfter(ledgerAD, ledgerAB.id()) == 2);
445 }
446
447 void
449 {
450 using namespace std::chrono_literals;
451 testcase("Current trusted validations");
452
454 Ledger const ledgerA = h["a"];
455 Ledger const ledgerB = h["b"];
456 Ledger const ledgerAC = h["ac"];
457
458 TestHarness harness(h.oracle);
459 Node const a = harness.makeNode();
460 Node b = harness.makeNode();
461 b.untrust();
462
463 BEAST_EXPECT(ValStatus::current == harness.add(a.validate(ledgerA)));
464 BEAST_EXPECT(ValStatus::current == harness.add(b.validate(ledgerB)));
465
466 // Only a is trusted
467 BEAST_EXPECT(harness.vals().currentTrusted().size() == 1);
468 BEAST_EXPECT(harness.vals().currentTrusted()[0].ledgerID() == ledgerA.id());
469 BEAST_EXPECT(harness.vals().currentTrusted()[0].seq() == ledgerA.seq());
470
471 harness.clock().advance(3s);
472
473 for (auto const& node : {a, b})
474 BEAST_EXPECT(ValStatus::current == harness.add(node.validate(ledgerAC)));
475
476 // New validation for a
477 BEAST_EXPECT(harness.vals().currentTrusted().size() == 1);
478 BEAST_EXPECT(harness.vals().currentTrusted()[0].ledgerID() == ledgerAC.id());
479 BEAST_EXPECT(harness.vals().currentTrusted()[0].seq() == ledgerAC.seq());
480
481 // Pass enough time for it to go stale
482 harness.clock().advance(harness.parms().validationCURRENT_LOCAL);
483 BEAST_EXPECT(harness.vals().currentTrusted().empty());
484 }
485
486 void
488 {
489 using namespace std::chrono_literals;
490 testcase("Current public keys");
491
493 Ledger const ledgerA = h["a"];
494 Ledger const ledgerAC = h["ac"];
495
496 TestHarness harness(h.oracle);
497 Node a = harness.makeNode(), b = harness.makeNode();
498 b.untrust();
499
500 for (auto const& node : {a, b})
501 BEAST_EXPECT(ValStatus::current == harness.add(node.validate(ledgerA)));
502
503 {
504 hash_set<PeerID> const expectedKeys = {a.nodeID(), b.nodeID()};
505 BEAST_EXPECT(harness.vals().getCurrentNodeIDs() == expectedKeys);
506 }
507
508 harness.clock().advance(3s);
509
510 // Change keys and issue partials
511 a.advanceKey();
512 b.advanceKey();
513
514 for (auto const& node : {a, b})
515 BEAST_EXPECT(ValStatus::current == harness.add(node.partial(ledgerAC)));
516
517 {
518 hash_set<PeerID> const expectedKeys = {a.nodeID(), b.nodeID()};
519 BEAST_EXPECT(harness.vals().getCurrentNodeIDs() == expectedKeys);
520 }
521
522 // Pass enough time for them to go stale
523 harness.clock().advance(harness.parms().validationCURRENT_LOCAL);
524 BEAST_EXPECT(harness.vals().getCurrentNodeIDs().empty());
525 }
526
527 void
529 {
530 // Test the Validations functions that calculate a value by ledger ID
531 using namespace std::chrono_literals;
532 testcase("By ledger functions");
533
534 // Several Validations functions return a set of values associated
535 // with trusted ledgers sharing the same ledger ID. The tests below
536 // exercise this logic by saving the set of trusted Validations, and
537 // verifying that the Validations member functions all calculate the
538 // proper transformation of the available ledgers.
539
541 TestHarness harness(h.oracle);
542
543 Node a = harness.makeNode(), b = harness.makeNode(), c = harness.makeNode(),
544 d = harness.makeNode(), e = harness.makeNode();
545
546 c.untrust();
547 // Mix of load fees
548 a.setLoadFee(12);
549 b.setLoadFee(1);
550 c.setLoadFee(12);
551 e.setLoadFee(12);
552
554
555 //----------------------------------------------------------------------
556 // checkers
557 auto sorted = [](auto vec) {
558 std::sort(vec.begin(), vec.end());
559 return vec;
560 };
561 auto compare = [&]() {
562 for (auto& it : trustedValidations)
563 {
564 auto const& id = it.first.first;
565 auto const& seq = it.first.second;
566 auto const& expectedValidations = it.second;
567
568 BEAST_EXPECT(harness.vals().numTrustedForLedger(id) == expectedValidations.size());
569 BEAST_EXPECT(
570 sorted(harness.vals().getTrustedForLedger(id, seq)) ==
571 sorted(expectedValidations));
572
573 std::uint32_t const baseFee = 0;
574 std::vector<uint32_t> expectedFees;
575 expectedFees.reserve(expectedValidations.size());
576 for (auto const& val : expectedValidations)
577 {
578 expectedFees.push_back(val.loadFee().value_or(baseFee));
579 }
580
581 BEAST_EXPECT(sorted(harness.vals().fees(id, baseFee)) == sorted(expectedFees));
582 }
583 };
584
585 //----------------------------------------------------------------------
586 Ledger const ledgerA = h["a"];
587 Ledger const ledgerB = h["b"];
588 Ledger const ledgerAC = h["ac"];
589
590 // Add a dummy ID to cover unknown ledger identifiers
591 trustedValidations[{Ledger::ID{100}, Ledger::Seq{100}}] = {};
592
593 // first round a,b,c agree
594 for (auto const& node : {a, b, c})
595 {
596 auto const val = node.validate(ledgerA);
597 BEAST_EXPECT(ValStatus::current == harness.add(val));
598 if (val.trusted())
599 trustedValidations[{val.ledgerID(), val.seq()}].emplace_back(val);
600 }
601 // d disagrees
602 {
603 auto const val = d.validate(ledgerB);
604 BEAST_EXPECT(ValStatus::current == harness.add(val));
605 trustedValidations[{val.ledgerID(), val.seq()}].emplace_back(val);
606 }
607 // e only issues partials
608 {
609 BEAST_EXPECT(ValStatus::current == harness.add(e.partial(ledgerA)));
610 }
611
612 harness.clock().advance(5s);
613 // second round, a,b,c move to ledger 2
614 for (auto const& node : {a, b, c})
615 {
616 auto const val = node.validate(ledgerAC);
617 BEAST_EXPECT(ValStatus::current == harness.add(val));
618 if (val.trusted())
619 trustedValidations[{val.ledgerID(), val.seq()}].emplace_back(val);
620 }
621 // d now thinks ledger 1, but cannot re-issue a previously used seq
622 // and attempting it should generate a conflict.
623 {
624 BEAST_EXPECT(ValStatus::conflicting == harness.add(d.partial(ledgerA)));
625 }
626 // e only issues partials
627 {
628 BEAST_EXPECT(ValStatus::current == harness.add(e.partial(ledgerAC)));
629 }
630
631 compare();
632 }
633
634 void
636 {
637 // Verify expiring clears out validations stored by ledger
638 testcase("Expire validations");
639 SuiteJournal j("Validations_test", *this);
641 TestHarness harness(h.oracle);
642 Node const a = harness.makeNode();
643 constexpr Ledger::Seq one(1);
644 constexpr Ledger::Seq two(2);
645
646 // simple cases
647 Ledger const ledgerA = h["a"];
648 BEAST_EXPECT(ValStatus::current == harness.add(a.validate(ledgerA)));
649 BEAST_EXPECT(harness.vals().numTrustedForLedger(ledgerA.id()) == 1);
650 harness.vals().expire(j);
651 BEAST_EXPECT(harness.vals().numTrustedForLedger(ledgerA.id()) == 1);
652 harness.clock().advance(harness.parms().validationSET_EXPIRES);
653 harness.vals().expire(j);
654 BEAST_EXPECT(harness.vals().numTrustedForLedger(ledgerA.id()) == 0);
655
656 // use setSeqToKeep to keep the validation from expire
657 Ledger const ledgerB = h["ab"];
658 BEAST_EXPECT(ValStatus::current == harness.add(a.validate(ledgerB)));
659 BEAST_EXPECT(harness.vals().numTrustedForLedger(ledgerB.id()) == 1);
660 harness.vals().setSeqToKeep(ledgerB.seq(), ledgerB.seq() + one);
661 harness.clock().advance(harness.parms().validationSET_EXPIRES);
662 harness.vals().expire(j);
663 BEAST_EXPECT(harness.vals().numTrustedForLedger(ledgerB.id()) == 1);
664 // change toKeep
665 harness.vals().setSeqToKeep(ledgerB.seq() + one, ledgerB.seq() + two);
666 // advance clock slowly
667 int const loops =
668 harness.parms().validationSET_EXPIRES / harness.parms().validationFRESHNESS + 1;
669 for (int i = 0; i < loops; ++i)
670 {
671 harness.clock().advance(harness.parms().validationFRESHNESS);
672 harness.vals().expire(j);
673 }
674 BEAST_EXPECT(harness.vals().numTrustedForLedger(ledgerB.id()) == 0);
675
676 // Allow the validation with high seq to expire
677 Ledger const ledgerC = h["abc"];
678 BEAST_EXPECT(ValStatus::current == harness.add(a.validate(ledgerC)));
679 BEAST_EXPECT(harness.vals().numTrustedForLedger(ledgerC.id()) == 1);
680 harness.vals().setSeqToKeep(ledgerC.seq() - one, ledgerC.seq());
681 harness.clock().advance(harness.parms().validationSET_EXPIRES);
682 harness.vals().expire(j);
683 BEAST_EXPECT(harness.vals().numTrustedForLedger(ledgerC.id()) == 0);
684 }
685
686 void
688 {
689 // Test final flush of validations
690 using namespace std::chrono_literals;
691 testcase("Flush validations");
692
694 TestHarness harness(h.oracle);
695 Node const trustedNode1 = harness.makeNode();
696 Node const trustedNode2 = harness.makeNode();
697 Node notTrustedNode = harness.makeNode();
698 notTrustedNode.untrust();
699
700 Ledger const ledgerA = h["a"];
701 Ledger const ledgerAB = h["ab"];
702
704 for (auto const& node : {trustedNode1, trustedNode2, notTrustedNode})
705 {
706 auto const val = node.validate(ledgerA);
707 BEAST_EXPECT(ValStatus::current == harness.add(val));
708 expected.emplace(node.nodeID(), val);
709 }
710
711 // Send in a new validation for a, saving the new one into the expected
712 // map after setting the proper prior ledger ID it replaced
713 harness.clock().advance(1s);
714 auto newVal = trustedNode1.validate(ledgerAB);
715 BEAST_EXPECT(ValStatus::current == harness.add(newVal));
716 expected.find(trustedNode1.nodeID())->second = newVal;
717 }
718
719 void
721 {
722 using namespace std::chrono_literals;
723 testcase("Preferred Ledger");
724
726 TestHarness harness(h.oracle);
727 Node const trustedNode1 = harness.makeNode();
728 Node const trustedNode2 = harness.makeNode();
729 Node const trustedNode3 = harness.makeNode();
730
731 Node notTrustedNode = harness.makeNode();
732 notTrustedNode.untrust();
733
734 Ledger const ledgerA = h["a"];
735 Ledger const ledgerB = h["b"];
736 Ledger const ledgerAC = h["ac"];
737 Ledger const ledgerACD = h["acd"];
738
739 using Seq = Ledger::Seq;
740
741 auto pref = [](Ledger ledger) { return std::make_pair(ledger.seq(), ledger.id()); };
742
743 // Empty (no ledgers)
744 BEAST_EXPECT(harness.vals().getPreferred(ledgerA) == std::nullopt);
745
746 // Single ledger
747 BEAST_EXPECT(ValStatus::current == harness.add(trustedNode1.validate(ledgerB)));
748 BEAST_EXPECT(harness.vals().getPreferred(ledgerA) == pref(ledgerB));
749 BEAST_EXPECT(harness.vals().getPreferred(ledgerB) == pref(ledgerB));
750
751 // Minimum valid sequence
752 BEAST_EXPECT(harness.vals().getPreferred(ledgerA, Seq{10}) == ledgerA.id());
753
754 // Untrusted doesn't impact preferred ledger
755 // (ledgerB has tie-break over ledgerA)
756 BEAST_EXPECT(ValStatus::current == harness.add(trustedNode2.validate(ledgerA)));
757 BEAST_EXPECT(ValStatus::current == harness.add(notTrustedNode.validate(ledgerA)));
758 BEAST_EXPECT(ledgerB.id() > ledgerA.id());
759 BEAST_EXPECT(harness.vals().getPreferred(ledgerA) == pref(ledgerB));
760 BEAST_EXPECT(harness.vals().getPreferred(ledgerB) == pref(ledgerB));
761
762 // Partial does break ties
763 BEAST_EXPECT(ValStatus::current == harness.add(trustedNode3.partial(ledgerA)));
764 BEAST_EXPECT(harness.vals().getPreferred(ledgerA) == pref(ledgerA));
765 BEAST_EXPECT(harness.vals().getPreferred(ledgerB) == pref(ledgerA));
766
767 harness.clock().advance(5s);
768
769 // Parent of preferred-> stick with ledger
770 for (auto const& node : {trustedNode1, trustedNode2, notTrustedNode, trustedNode3})
771 BEAST_EXPECT(ValStatus::current == harness.add(node.validate(ledgerAC)));
772 // Parent of preferred stays put
773 BEAST_EXPECT(harness.vals().getPreferred(ledgerA) == pref(ledgerA));
774 // Earlier different chain, switch
775 BEAST_EXPECT(harness.vals().getPreferred(ledgerB) == pref(ledgerAC));
776 // Later on chain, stays where it is
777 BEAST_EXPECT(harness.vals().getPreferred(ledgerACD) == pref(ledgerACD));
778
779 // Any later grandchild or different chain is preferred
780 harness.clock().advance(5s);
781 for (auto const& node : {trustedNode1, trustedNode2, notTrustedNode, trustedNode3})
782 BEAST_EXPECT(ValStatus::current == harness.add(node.validate(ledgerACD)));
783 for (auto const& ledger : {ledgerA, ledgerB, ledgerACD})
784 BEAST_EXPECT(harness.vals().getPreferred(ledger) == pref(ledgerACD));
785 }
786
787 void
789 {
790 using namespace std::chrono_literals;
791 testcase("Get preferred LCL");
792
794 TestHarness harness(h.oracle);
795 Node const a = harness.makeNode();
796
797 Ledger const ledgerA = h["a"];
798 Ledger const ledgerB = h["b"];
799 Ledger const ledgerC = h["c"];
800
801 using ID = Ledger::ID;
802 using Seq = Ledger::Seq;
803
805
806 // No trusted validations or counts sticks with current ledger
807 BEAST_EXPECT(harness.vals().getPreferredLCL(ledgerA, Seq{0}, peerCounts) == ledgerA.id());
808
809 ++peerCounts[ledgerB.id()];
810
811 // No trusted validations, rely on peer counts
812 BEAST_EXPECT(harness.vals().getPreferredLCL(ledgerA, Seq{0}, peerCounts) == ledgerB.id());
813
814 ++peerCounts[ledgerC.id()];
815 // No trusted validations, tied peers goes with larger ID
816 BEAST_EXPECT(ledgerC.id() > ledgerB.id());
817
818 BEAST_EXPECT(harness.vals().getPreferredLCL(ledgerA, Seq{0}, peerCounts) == ledgerC.id());
819
820 peerCounts[ledgerC.id()] += 1000;
821
822 // Single trusted always wins over peer counts
823 BEAST_EXPECT(ValStatus::current == harness.add(a.validate(ledgerA)));
824 BEAST_EXPECT(harness.vals().getPreferredLCL(ledgerA, Seq{0}, peerCounts) == ledgerA.id());
825 BEAST_EXPECT(harness.vals().getPreferredLCL(ledgerB, Seq{0}, peerCounts) == ledgerA.id());
826 BEAST_EXPECT(harness.vals().getPreferredLCL(ledgerC, Seq{0}, peerCounts) == ledgerA.id());
827
828 // Stick with current ledger if trusted validation ledger has too old
829 // of a sequence
830 BEAST_EXPECT(harness.vals().getPreferredLCL(ledgerB, Seq{2}, peerCounts) == ledgerB.id());
831 }
832
833 void
835 {
836 using namespace std::chrono_literals;
837 testcase("Acquire validated ledger");
838
840 TestHarness harness(h.oracle);
841 Node const a = harness.makeNode();
842 Node const b = harness.makeNode();
843
844 using ID = Ledger::ID;
845 using Seq = Ledger::Seq;
846
847 // Validate the ledger before it is actually available
848 Validation const val = a.validate(ID{2}, Seq{2}, 0s, 0s, true);
849
850 BEAST_EXPECT(ValStatus::current == harness.add(val));
851 // Validation is available
852 BEAST_EXPECT(harness.vals().numTrustedForLedger(ID{2}) == 1);
853 // but ledger based data is not
854 BEAST_EXPECT(harness.vals().getNodesAfter(genesisLedger, ID{0}) == 0);
855 // Initial preferred branch falls back to the ledger we are trying to
856 // acquire
857 BEAST_EXPECT(harness.vals().getPreferred(genesisLedger) == std::make_pair(Seq{2}, ID{2}));
858
859 // After adding another unavailable validation, the preferred ledger
860 // breaks ties via higher ID
861 BEAST_EXPECT(ValStatus::current == harness.add(b.validate(ID{3}, Seq{2}, 0s, 0s, true)));
862 BEAST_EXPECT(harness.vals().getPreferred(genesisLedger) == std::make_pair(Seq{2}, ID{3}));
863
864 // Create the ledger
865 Ledger const ledgerAB = h["ab"];
866 // Now it should be available
867 BEAST_EXPECT(harness.vals().getNodesAfter(genesisLedger, ID{0}) == 1);
868
869 // Create a validation that is not available
870 harness.clock().advance(5s);
871 Validation const val2 = a.validate(ID{4}, Seq{4}, 0s, 0s, true);
872 BEAST_EXPECT(ValStatus::current == harness.add(val2));
873 BEAST_EXPECT(harness.vals().numTrustedForLedger(ID{4}) == 1);
874 BEAST_EXPECT(
875 harness.vals().getPreferred(genesisLedger) ==
876 std::make_pair(ledgerAB.seq(), ledgerAB.id()));
877
878 // Another node requesting that ledger still doesn't change things
879 Validation const val3 = b.validate(ID{4}, Seq{4}, 0s, 0s, true);
880 BEAST_EXPECT(ValStatus::current == harness.add(val3));
881 BEAST_EXPECT(harness.vals().numTrustedForLedger(ID{4}) == 2);
882 BEAST_EXPECT(
883 harness.vals().getPreferred(genesisLedger) ==
884 std::make_pair(ledgerAB.seq(), ledgerAB.id()));
885
886 // Switch to validation that is available
887 harness.clock().advance(5s);
888 Ledger const ledgerABCDE = h["abcde"];
889 BEAST_EXPECT(ValStatus::current == harness.add(a.partial(ledgerABCDE)));
890 BEAST_EXPECT(ValStatus::current == harness.add(b.partial(ledgerABCDE)));
891 BEAST_EXPECT(
892 harness.vals().getPreferred(genesisLedger) ==
893 std::make_pair(ledgerABCDE.seq(), ledgerABCDE.id()));
894 }
895
896 void
898 {
899 testcase("NumTrustedForLedger");
901 TestHarness harness(h.oracle);
902 Node const a = harness.makeNode();
903 Node const b = harness.makeNode();
904 Ledger const ledgerA = h["a"];
905
906 BEAST_EXPECT(ValStatus::current == harness.add(a.partial(ledgerA)));
907 BEAST_EXPECT(harness.vals().numTrustedForLedger(ledgerA.id()) == 0);
908
909 BEAST_EXPECT(ValStatus::current == harness.add(b.validate(ledgerA)));
910 BEAST_EXPECT(harness.vals().numTrustedForLedger(ledgerA.id()) == 1);
911 }
912
913 void
915 {
916 testcase("SeqEnforcer");
917 using Seq = Ledger::Seq;
918 using namespace std::chrono;
919
921 SeqEnforcer<Seq> enforcer;
922
923 ValidationParms const p;
924
925 BEAST_EXPECT(enforcer(clock.now(), Seq{1}, p));
926 BEAST_EXPECT(enforcer(clock.now(), Seq{10}, p));
927 BEAST_EXPECT(!enforcer(clock.now(), Seq{5}, p));
928 BEAST_EXPECT(!enforcer(clock.now(), Seq{9}, p));
929 clock.advance(p.validationSET_EXPIRES - 1ms);
930 BEAST_EXPECT(!enforcer(clock.now(), Seq{1}, p));
931 clock.advance(2ms);
932 BEAST_EXPECT(enforcer(clock.now(), Seq{1}, p));
933 }
934
935 void
937 {
938 testcase("TrustChanged");
939 using namespace std::chrono;
940
941 auto checker = [this](
942 TestValidations& vals,
943 hash_set<PeerID> const& listed,
944 std::vector<Validation> const& trustedVals) {
945 Ledger::ID const testID =
946 trustedVals.empty() ? this->genesisLedger.id() : trustedVals[0].ledgerID();
947 Ledger::Seq const testSeq =
948 trustedVals.empty() ? this->genesisLedger.seq() : trustedVals[0].seq();
949 BEAST_EXPECT(vals.currentTrusted() == trustedVals);
950 BEAST_EXPECT(vals.getCurrentNodeIDs() == listed);
951 BEAST_EXPECT(
952 vals.getNodesAfter(this->genesisLedger, genesisLedger.id()) == trustedVals.size());
953 if (trustedVals.empty())
954 {
955 BEAST_EXPECT(vals.getPreferred(this->genesisLedger) == std::nullopt);
956 }
957 else
958 {
959 BEAST_EXPECT(vals.getPreferred(this->genesisLedger)->second == testID);
960 }
961 BEAST_EXPECT(vals.getTrustedForLedger(testID, testSeq) == trustedVals);
962 BEAST_EXPECT(vals.numTrustedForLedger(testID) == trustedVals.size());
963 };
964
965 {
966 // Trusted to untrusted
968 TestHarness harness(h.oracle);
969 Node const a = harness.makeNode();
970 Ledger const ledgerAB = h["ab"];
971 Validation const v = a.validate(ledgerAB);
972 BEAST_EXPECT(ValStatus::current == harness.add(v));
973
974 hash_set<PeerID> const listed({a.nodeID()});
975 std::vector<Validation> trustedVals({v});
976 checker(harness.vals(), listed, trustedVals);
977
978 trustedVals.clear();
979 harness.vals().trustChanged({}, {a.nodeID()});
980 checker(harness.vals(), listed, trustedVals);
981 }
982
983 {
984 // Untrusted to trusted
986 TestHarness harness(h.oracle);
987 Node a = harness.makeNode();
988 a.untrust();
989 Ledger const ledgerAB = h["ab"];
990 Validation const v = a.validate(ledgerAB);
991 BEAST_EXPECT(ValStatus::current == harness.add(v));
992
993 hash_set<PeerID> const listed({a.nodeID()});
994 std::vector<Validation> trustedVals;
995 checker(harness.vals(), listed, trustedVals);
996
997 trustedVals.push_back(v);
998 harness.vals().trustChanged({a.nodeID()}, {});
999 checker(harness.vals(), listed, trustedVals);
1000 }
1001
1002 {
1003 // Trusted but not acquired -> untrusted
1005 TestHarness harness(h.oracle);
1006 Node const a = harness.makeNode();
1007 Validation const v = a.validate(Ledger::ID{2}, Ledger::Seq{2}, 0s, 0s, true);
1008 BEAST_EXPECT(ValStatus::current == harness.add(v));
1009
1010 hash_set<PeerID> const listed({a.nodeID()});
1011 std::vector<Validation> trustedVals({v});
1012 auto& vals = harness.vals();
1013 BEAST_EXPECT(vals.currentTrusted() == trustedVals);
1014
1015 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
1016 BEAST_EXPECT(vals.getPreferred(genesisLedger)->second == v.ledgerID());
1017 BEAST_EXPECT(vals.getNodesAfter(genesisLedger, genesisLedger.id()) == 0);
1018
1019 trustedVals.clear();
1020 harness.vals().trustChanged({}, {a.nodeID()});
1021 // make acquiring ledger available
1022 h["ab"];
1023 BEAST_EXPECT(vals.currentTrusted() == trustedVals);
1024 BEAST_EXPECT(vals.getPreferred(genesisLedger) == std::nullopt);
1025 BEAST_EXPECT(vals.getNodesAfter(genesisLedger, genesisLedger.id()) == 0);
1026 }
1027 }
1028
1029 void
1047};
1048
1049BEAST_DEFINE_TESTSUITE(Validations, consensus, xrpl);
1050} // namespace csf
1051} // namespace test
1052} // 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:229
std::optional< Ledger > lookup(Ledger::ID const &id) const
Find the ledger with the given ID.
Definition ledgers.cpp:115
A ledger is a set of observed transactions and a sequence number identifying the ledger.
Definition ledgers.h:43
tagged_integer< std::uint32_t, IdTag > ID
Definition ledgers.h:51
tagged_integer< std::uint32_t, SeqTag > Seq
Definition ledgers.h:48
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 reserve(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:304
Set the sequence number on a JTx.
Definition seq.h:14