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