xrpld
Loading...
Searching...
No Matches
Consensus_test.cpp
1#include <test/csf/Peer.h>
2#include <test/csf/PeerGroup.h>
3#include <test/csf/Sim.h>
4#include <test/csf/SimTime.h>
5#include <test/csf/collectors.h>
6#include <test/csf/events.h>
7#include <test/csf/random.h>
8#include <test/csf/submitters.h>
9#include <test/unit_test/SuiteJournal.h>
10
11#include <xrpld/consensus/Consensus.h>
12#include <xrpld/consensus/ConsensusParms.h>
13#include <xrpld/consensus/ConsensusTypes.h>
14#include <xrpld/consensus/DisputedTx.h>
15
16#include <xrpl/basics/Log.h>
17#include <xrpl/basics/UnorderedContainers.h>
18#include <xrpl/basics/chrono.h>
19#include <xrpl/beast/unit_test/suite.h>
20#include <xrpl/beast/utility/Journal.h>
21#include <xrpl/ledger/LedgerTiming.h>
22
23#include <chrono>
24#include <cstddef>
25#include <cstdint>
26#include <memory>
27#include <sstream>
28#include <string>
29#include <vector>
30
31namespace xrpl::test {
32
34{
36
37public:
38 Consensus_test() : journal_("Consensus_test", *this)
39 {
40 }
41
42 void
44 {
45 using namespace std::chrono_literals;
46 testcase("should close ledger");
47
48 // Use default parameters
49 ConsensusParms const p{};
50
51 // Bizarre times forcibly close
52 BEAST_EXPECT(shouldCloseLedger(true, 10, 10, 10, -10s, 10s, 1s, 1s, p, journal_));
53 BEAST_EXPECT(shouldCloseLedger(true, 10, 10, 10, 100h, 10s, 1s, 1s, p, journal_));
54 BEAST_EXPECT(shouldCloseLedger(true, 10, 10, 10, 10s, 100h, 1s, 1s, p, journal_));
55
56 // Rest of network has closed
57 BEAST_EXPECT(shouldCloseLedger(true, 10, 3, 5, 10s, 10s, 10s, 10s, p, journal_));
58
59 // No transactions means wait until end of internval
60 BEAST_EXPECT(!shouldCloseLedger(false, 10, 0, 0, 1s, 1s, 1s, 10s, p, journal_));
61 BEAST_EXPECT(shouldCloseLedger(false, 10, 0, 0, 1s, 10s, 1s, 10s, p, journal_));
62
63 // Enforce minimum ledger open time
64 BEAST_EXPECT(!shouldCloseLedger(true, 10, 0, 0, 10s, 10s, 1s, 10s, p, journal_));
65
66 // Don't go too much faster than last time
67 BEAST_EXPECT(!shouldCloseLedger(true, 10, 0, 0, 10s, 10s, 3s, 10s, p, journal_));
68
69 BEAST_EXPECT(shouldCloseLedger(true, 10, 0, 0, 10s, 10s, 10s, 10s, p, journal_));
70 }
71
72 void
74 {
75 using namespace std::chrono_literals;
76 testcase("check consensus");
77
78 // Use default parameters
79 ConsensusParms const p{};
80
82 // Disputes still in doubt
83 //
84 // Not enough time has elapsed
85 BEAST_EXPECT(
86 ConsensusState::No == checkConsensus(10, 2, 2, 0, 3s, 2s, false, p, true, journal_));
87
88 // If not enough peers have proposed, ensure
89 // more time for proposals
90 BEAST_EXPECT(
91 ConsensusState::No == checkConsensus(10, 2, 2, 0, 3s, 4s, false, p, true, journal_));
92
93 // Enough time has elapsed and we all agree
94 BEAST_EXPECT(
95 ConsensusState::Yes == checkConsensus(10, 2, 2, 0, 3s, 10s, false, p, true, journal_));
96
97 // Enough time has elapsed and we don't yet agree
98 BEAST_EXPECT(
99 ConsensusState::No == checkConsensus(10, 2, 1, 0, 3s, 10s, false, p, true, journal_));
100
101 // Our peers have moved on
102 // Enough time has elapsed and we all agree
103 BEAST_EXPECT(
105 checkConsensus(10, 2, 1, 8, 3s, 10s, false, p, true, journal_));
106
107 // If no peers, don't agree until time has passed.
108 BEAST_EXPECT(
109 ConsensusState::No == checkConsensus(0, 0, 0, 0, 3s, 10s, false, p, true, journal_));
110
111 // Agree if no peers and enough time has passed.
112 BEAST_EXPECT(
113 ConsensusState::Yes == checkConsensus(0, 0, 0, 0, 3s, 16s, false, p, true, journal_));
114
115 // Expire if too much time has passed without agreement
116 BEAST_EXPECT(
118 checkConsensus(10, 8, 1, 0, 1s, 19s, false, p, true, journal_));
119
121 // Stalled
122 //
123 // Not enough time has elapsed
124 BEAST_EXPECT(
125 ConsensusState::No == checkConsensus(10, 2, 2, 0, 3s, 2s, true, p, true, journal_));
126
127 // If not enough peers have proposed, ensure
128 // more time for proposals
129 BEAST_EXPECT(
130 ConsensusState::No == checkConsensus(10, 2, 2, 0, 3s, 4s, true, p, true, journal_));
131
132 // Enough time has elapsed and we all agree
133 BEAST_EXPECT(
134 ConsensusState::Yes == checkConsensus(10, 2, 2, 0, 3s, 10s, true, p, true, journal_));
135
136 // Enough time has elapsed and we don't yet agree, but there's nothing
137 // left to dispute
138 BEAST_EXPECT(
139 ConsensusState::Yes == checkConsensus(10, 2, 1, 0, 3s, 10s, true, p, true, journal_));
140
141 // Our peers have moved on
142 // Enough time has elapsed and we all agree, nothing left to dispute
143 BEAST_EXPECT(
144 ConsensusState::Yes == checkConsensus(10, 2, 1, 8, 3s, 10s, true, p, true, journal_));
145
146 // If no peers, don't agree until time has passed.
147 BEAST_EXPECT(
148 ConsensusState::No == checkConsensus(0, 0, 0, 0, 3s, 10s, true, p, true, journal_));
149
150 // Agree if no peers and enough time has passed.
151 BEAST_EXPECT(
152 ConsensusState::Yes == checkConsensus(0, 0, 0, 0, 3s, 16s, true, p, true, journal_));
153
154 // We are done if there's nothing left to dispute, no matter how much
155 // time has passed
156 BEAST_EXPECT(
157 ConsensusState::Yes == checkConsensus(10, 8, 1, 0, 1s, 19s, true, p, true, journal_));
158 }
159
160 void
162 {
163 using namespace std::chrono_literals;
164 using namespace csf;
165 testcase("standalone");
166
167 Sim s;
168 PeerGroup const peers = s.createGroup(1);
169 Peer* peer = peers[0];
170 peer->targetLedgers = 1;
171 peer->start();
172 peer->submit(Tx{1});
173
174 s.scheduler.step();
175
176 // Inspect that the proper ledger was created
177 auto const& lcl = peer->lastClosedLedger;
178 BEAST_EXPECT(peer->prevLedgerID() == lcl.id());
179 BEAST_EXPECT(lcl.seq() == Ledger::Seq{1});
180 BEAST_EXPECT(lcl.txs().size() == 1);
181 BEAST_EXPECT(lcl.txs().contains(Tx{1}));
182 BEAST_EXPECT(peer->prevProposers == 0);
183 }
184
185 void
187 {
188 using namespace csf;
189 using namespace std::chrono;
190 testcase("peers agree");
191
192 ConsensusParms const parms{};
193 Sim sim;
194 PeerGroup peers = sim.createGroup(5);
195
196 // Connected trust and network graphs with single fixed delay
197 peers.trustAndConnect(peers, round<milliseconds>(0.2 * parms.ledgerGRANULARITY));
198
199 // everyone submits their own ID as a TX
200 for (Peer* p : peers)
201 p->submit(Tx(static_cast<std::uint32_t>(p->id)));
202
203 sim.run(1);
204
205 // All peers are in sync
206 if (BEAST_EXPECT(sim.synchronized()))
207 {
208 for (Peer const* peer : peers)
209 {
210 auto const& lcl = peer->lastClosedLedger;
211 BEAST_EXPECT(lcl.id() == peer->prevLedgerID());
212 BEAST_EXPECT(lcl.seq() == Ledger::Seq{1});
213 // All peers proposed
214 BEAST_EXPECT(peer->prevProposers == peers.size() - 1);
215 // All transactions were accepted
216 for (std::uint32_t i = 0; i < peers.size(); ++i)
217 BEAST_EXPECT(lcl.txs().contains(Tx{i}));
218 }
219 }
220 }
221
222 void
224 {
225 using namespace csf;
226 using namespace std::chrono;
227 testcase("slow peers");
228
229 // Several tests of a complete trust graph with a subset of peers
230 // that have significantly longer network delays to the rest of the
231 // network
232
233 // Test when a slow peer doesn't delay a consensus quorum (4/5 agree)
234 {
235 ConsensusParms const parms{};
236 Sim sim;
237 PeerGroup slow = sim.createGroup(1);
238 PeerGroup fast = sim.createGroup(4);
239 PeerGroup network = fast + slow;
240
241 // Fully connected trust graph
242 network.trust(network);
243
244 // Fast and slow network connections
245 fast.connect(fast, round<milliseconds>(0.2 * parms.ledgerGRANULARITY));
246
247 slow.connect(network, round<milliseconds>(1.1 * parms.ledgerGRANULARITY));
248
249 // All peers submit their own ID as a transaction
250 for (Peer* peer : network)
251 peer->submit(Tx{static_cast<std::uint32_t>(peer->id)});
252
253 sim.run(1);
254
255 // Verify all peers have same LCL but are missing transaction 0
256 // All peers are in sync even with a slower peer 0
257 if (BEAST_EXPECT(sim.synchronized()))
258 {
259 for (Peer const* peer : network)
260 {
261 auto const& lcl = peer->lastClosedLedger;
262 BEAST_EXPECT(lcl.id() == peer->prevLedgerID());
263 BEAST_EXPECT(lcl.seq() == Ledger::Seq{1});
264
265 BEAST_EXPECT(peer->prevProposers == network.size() - 1);
266 BEAST_EXPECT(peer->prevRoundTime == network[0]->prevRoundTime);
267
268 BEAST_EXPECT(not lcl.txs().contains(Tx{0}));
269 for (std::uint32_t i = 2; i < network.size(); ++i)
270 BEAST_EXPECT(lcl.txs().contains(Tx{i}));
271
272 // Tx 0 didn't make it
273 BEAST_EXPECT(peer->openTxs.contains(Tx{0}));
274 }
275 }
276 }
277
278 // Test when the slow peers delay a consensus quorum (4/6 agree)
279 {
280 // Run two tests
281 // 1. The slow peers are participating in consensus
282 // 2. The slow peers are just observing
283
284 for (auto isParticipant : {true, false})
285 {
286 ConsensusParms const parms{};
287
288 Sim sim;
289 PeerGroup slow = sim.createGroup(2);
290 PeerGroup fast = sim.createGroup(4);
291 PeerGroup network = fast + slow;
292
293 // Connected trust graph
294 network.trust(network);
295
296 // Fast and slow network connections
297 fast.connect(fast, round<milliseconds>(0.2 * parms.ledgerGRANULARITY));
298
299 slow.connect(network, round<milliseconds>(1.1 * parms.ledgerGRANULARITY));
300
301 for (Peer* peer : slow)
302 peer->runAsValidator = isParticipant;
303
304 // All peers submit their own ID as a transaction and relay it
305 // to peers
306 for (Peer* peer : network)
307 peer->submit(Tx{static_cast<std::uint32_t>(peer->id)});
308
309 sim.run(1);
310
311 if (BEAST_EXPECT(sim.synchronized()))
312 {
313 // Verify all peers have same LCL but are missing
314 // transaction 0,1 which was not received by all peers
315 // before the ledger closed
316 for (Peer const* peer : network)
317 {
318 // Closed ledger has all but transaction 0,1
319 auto const& lcl = peer->lastClosedLedger;
320 BEAST_EXPECT(lcl.seq() == Ledger::Seq{1});
321 BEAST_EXPECT(not lcl.txs().contains(Tx{0}));
322 BEAST_EXPECT(not lcl.txs().contains(Tx{1}));
323 for (std::uint32_t i = slow.size(); i < network.size(); ++i)
324 BEAST_EXPECT(lcl.txs().contains(Tx{i}));
325
326 // Tx 0-1 didn't make it
327 BEAST_EXPECT(peer->openTxs.contains(Tx{0}));
328 BEAST_EXPECT(peer->openTxs.contains(Tx{1}));
329 }
330
331 Peer const* slowPeer = slow[0];
332 if (isParticipant)
333 {
334 BEAST_EXPECT(slowPeer->prevProposers == network.size() - 1);
335 }
336 else
337 {
338 BEAST_EXPECT(slowPeer->prevProposers == fast.size());
339 }
340
341 for (Peer const* peer : fast)
342 {
343 // Due to the network link delay settings
344 // Peer 0 initially proposes {0}
345 // Peer 1 initially proposes {1}
346 // Peers 2-5 initially propose {2,3,4,5}
347 // Since peers 2-5 agree, 4/6 > the initial 50% needed
348 // to include a disputed transaction, so Peer 0/1 switch
349 // to agree with those peers. Peer 0/1 then closes with
350 // an 80% quorum of agreeing positions (5/6) match.
351 //
352 // Peers 2-5 do not change position, since tx 0 or tx 1
353 // have less than the 50% initial threshold. They also
354 // cannot declare consensus, since 4/6 agreeing
355 // positions are < 80% threshold. They therefore need an
356 // additional timerEntry call to see the updated
357 // positions from Peer 0 & 1.
358
359 if (isParticipant)
360 {
361 BEAST_EXPECT(peer->prevProposers == network.size() - 1);
362 BEAST_EXPECT(peer->prevRoundTime > slowPeer->prevRoundTime);
363 }
364 else
365 {
366 BEAST_EXPECT(peer->prevProposers == fast.size() - 1);
367 // so all peers should have closed together
368 BEAST_EXPECT(peer->prevRoundTime == slowPeer->prevRoundTime);
369 }
370 }
371 }
372 }
373 }
374 }
375
376 void
378 {
379 using namespace csf;
380 using namespace std::chrono;
381 testcase("close time disagree");
382
383 // This is a very specialized test to get ledgers to disagree on
384 // the close time. It unfortunately assumes knowledge about current
385 // timing constants. This is a necessary evil to get coverage up
386 // pending more extensive refactorings of timing constants.
387
388 // In order to agree-to-disagree on the close time, there must be no
389 // clear majority of nodes agreeing on a close time. This test
390 // sets a relative offset to the peers internal clocks so that they
391 // send proposals with differing times.
392
393 // However, agreement is on the effective close time, not the
394 // exact close time. The minimum closeTimeResolution is given by
395 // ledgerPossibleTimeResolutions[0], which is currently 10s. This means
396 // the skews need to be at least 10 seconds to have different effective
397 // close times.
398
399 // Complicating this matter is that nodes will ignore proposals
400 // with times more than proposeFRESHNESS =20s in the past. So at
401 // the minimum granularity, we have at most 3 types of skews
402 // (0s,10s,20s).
403
404 // This test therefore has 6 nodes, with 2 nodes having each type of
405 // skew. Then no majority (1/3 < 1/2) of nodes will agree on an
406 // actual close time.
407
408 ConsensusParms const parms{};
409 Sim sim;
410
411 PeerGroup groupA = sim.createGroup(2);
412 PeerGroup const groupB = sim.createGroup(2);
413 PeerGroup const groupC = sim.createGroup(2);
414 PeerGroup network = groupA + groupB + groupC;
415
416 network.trust(network);
417 network.connect(network, round<milliseconds>(0.2 * parms.ledgerGRANULARITY));
418
419 // Run consensus without skew until we have a short close time
420 // resolution
421 Peer const* firstPeer = *groupA.begin();
422 while (firstPeer->lastClosedLedger.closeTimeResolution() >= parms.proposeFRESHNESS)
423 sim.run(1);
424
425 // Introduce a shift on the time of 2/3 of peers
426 for (Peer* peer : groupA)
427 peer->clockSkew = parms.proposeFRESHNESS / 2;
428 for (Peer* peer : groupB)
429 peer->clockSkew = parms.proposeFRESHNESS;
430
431 sim.run(1);
432
433 // All nodes agreed to disagree on the close time
434 if (BEAST_EXPECT(sim.synchronized()))
435 {
436 for (Peer const* peer : network)
437 BEAST_EXPECT(!peer->lastClosedLedger.closeAgree());
438 }
439 }
440
441 void
443 {
444 using namespace csf;
445 using namespace std::chrono;
446 testcase("wrong LCL");
447
448 // Specialized test to exercise a temporary fork in which some peers
449 // are working on an incorrect prior ledger.
450
451 ConsensusParms const parms{};
452
453 // Vary the time it takes to process validations to exercise detecting
454 // the wrong LCL at different phases of consensus
455 for (auto validationDelay : {0ms, parms.ledgerMinClose})
456 {
457 // Consider 10 peers:
458 // 0 1 2 3 4 5 6 7 8 9
459 // minority majorityA majorityB
460 //
461 // Nodes 0-1 trust nodes 0-4
462 // Nodes 2-9 trust nodes 2-9
463 //
464 // By submitting tx 0 to nodes 0-4 and tx 1 to nodes 5-9,
465 // nodes 0-1 will generate the wrong LCL (with tx 0). The remaining
466 // nodes will instead accept the ledger with tx 1.
467
468 // Nodes 0-1 will detect this mismatch during a subsequent round
469 // since nodes 2-4 will validate a different ledger.
470
471 // Nodes 0-1 will acquire the proper ledger from the network and
472 // resume consensus and eventually generate the dominant network
473 // ledger.
474
475 // This topology can potentially fork with the above trust relations
476 // but that is intended for this test.
477
478 Sim sim;
479
480 PeerGroup minority = sim.createGroup(2);
481 PeerGroup const majorityA = sim.createGroup(3);
482 PeerGroup const majorityB = sim.createGroup(5);
483
484 PeerGroup majority = majorityA + majorityB;
485 PeerGroup const network = minority + majority;
486
487 SimDuration const delay = round<milliseconds>(0.2 * parms.ledgerGRANULARITY);
488 minority.trustAndConnect(minority + majorityA, delay);
489 majority.trustAndConnect(majority, delay);
490
491 CollectByNode<JumpCollector> jumps;
492 sim.collectors.add(jumps);
493
494 BEAST_EXPECT(sim.trustGraph.canFork(parms.minConsensusPct / 100.));
495
496 // initial round to set prior state
497 sim.run(1);
498
499 // Nodes in smaller UNL have seen tx 0, nodes in other unl have seen
500 // tx 1
501 for (Peer* peer : network)
502 peer->delays.recvValidation = validationDelay;
503 for (Peer* peer : (minority + majorityA))
504 peer->openTxs.insert(Tx{0});
505 for (Peer* peer : majorityB)
506 peer->openTxs.insert(Tx{1});
507
508 // Run for additional rounds
509 // With no validation delay, only 2 more rounds are needed.
510 // 1. Round to generate different ledgers
511 // 2. Round to detect different prior ledgers (but still generate
512 // wrong ones) and recover within that round since wrong LCL
513 // is detected before we close
514 //
515 // With a validation delay of ledgerMIN_CLOSE, we need 3 more
516 // rounds.
517 // 1. Round to generate different ledgers
518 // 2. Round to detect different prior ledgers (but still generate
519 // wrong ones) but end up declaring consensus on wrong LCL (but
520 // with the right transaction set!). This is because we detect
521 // the wrong LCL after we have closed the ledger, so we declare
522 // consensus based solely on our peer proposals. But we haven't
523 // had time to acquire the right ledger.
524 // 3. Round to correct
525 sim.run(3);
526
527 // The network never actually forks, since node 0-1 never see a
528 // quorum of validations to fully validate the incorrect chain.
529
530 // However, for a non zero-validation delay, the network is not
531 // synchronized because nodes 0 and 1 are running one ledger behind
532 if (BEAST_EXPECT(sim.branches() == 1))
533 {
534 for (Peer const* peer : majority)
535 {
536 // No jumps for majority nodes
537 BEAST_EXPECT(jumps[peer->id].closeJumps.empty());
538 BEAST_EXPECT(jumps[peer->id].fullyValidatedJumps.empty());
539 }
540 for (Peer const* peer : minority)
541 {
542 auto& peerJumps = jumps[peer->id];
543 // last closed ledger jump between chains
544 {
545 if (BEAST_EXPECT(peerJumps.closeJumps.size() == 1))
546 {
547 JumpCollector::Jump const& jump = peerJumps.closeJumps.front();
548 // Jump is to a different chain
549 BEAST_EXPECT(jump.from.seq() <= jump.to.seq());
550 BEAST_EXPECT(!jump.to.isAncestor(jump.from));
551 }
552 }
553 // fully validated jump forward in same chain
554 {
555 if (BEAST_EXPECT(peerJumps.fullyValidatedJumps.size() == 1))
556 {
557 JumpCollector::Jump const& jump = peerJumps.fullyValidatedJumps.front();
558 // Jump is to a different chain with same seq
559 BEAST_EXPECT(jump.from.seq() < jump.to.seq());
560 BEAST_EXPECT(jump.to.isAncestor(jump.from));
561 }
562 }
563 }
564 }
565 }
566
567 {
568 // Additional test engineered to switch LCL during the establish
569 // phase. This was added to trigger a scenario that previously
570 // crashed, in which switchLCL switched from establish to open
571 // phase, but still processed the establish phase logic.
572
573 // Loner node will accept an initial ledger A, but all other nodes
574 // accept ledger B a bit later. By delaying the time it takes
575 // to process a validation, loner node will detect the wrongLCL
576 // after it is already in the establish phase of the next round.
577
578 Sim sim;
579 PeerGroup loner = sim.createGroup(1);
580 PeerGroup const friends = sim.createGroup(3);
581 loner.trust(loner + friends);
582
583 PeerGroup const others = sim.createGroup(6);
584 PeerGroup clique = friends + others;
585 clique.trust(clique);
586
587 PeerGroup network = loner + clique;
588 network.connect(network, round<milliseconds>(0.2 * parms.ledgerGRANULARITY));
589
590 // initial round to set prior state
591 sim.run(1);
592 for (Peer* peer : (loner + friends))
593 peer->openTxs.insert(Tx(0));
594 for (Peer* peer : others)
595 peer->openTxs.insert(Tx(1));
596
597 // Delay validation processing
598 for (Peer* peer : network)
599 peer->delays.recvValidation = parms.ledgerGRANULARITY;
600
601 // additional rounds to generate wrongLCL and recover
602 sim.run(2);
603
604 // Check all peers recovered
605 for (Peer const* p : network)
606 BEAST_EXPECT(p->prevLedgerID() == network[0]->prevLedgerID());
607 }
608 }
609
610 void
612 {
613 using namespace csf;
614 using namespace std::chrono;
615 testcase("consensus close time rounding");
616
617 // This is a specialized test engineered to yield ledgers with different
618 // close times even though the peers believe they had close time
619 // consensus on the ledger.
620 ConsensusParms const parms;
621
622 Sim sim;
623
624 // This requires a group of 4 fast and 2 slow peers to create a
625 // situation in which a subset of peers requires seeing additional
626 // proposals to declare consensus.
627 PeerGroup slow = sim.createGroup(2);
628 PeerGroup fast = sim.createGroup(4);
629 PeerGroup network = fast + slow;
630
631 // Connected trust graph
632 network.trust(network);
633
634 // Fast and slow network connections
635 fast.connect(fast, round<milliseconds>(0.2 * parms.ledgerGRANULARITY));
636 slow.connect(network, round<milliseconds>(1.1 * parms.ledgerGRANULARITY));
637
638 // Run to the ledger *prior* to decreasing the resolution
640
641 // In order to create the discrepancy, we want a case where if
642 // X = effCloseTime(closeTime, resolution, parentCloseTime)
643 // X != effCloseTime(X, resolution, parentCloseTime)
644 //
645 // That is, the effective close time is not a fixed point. This can
646 // happen if X = parentCloseTime + 1, but a subsequent rounding goes
647 // to the next highest multiple of resolution.
648
649 // So we want to find an offset (now + offset) % 30s = 15
650 // (now + offset) % 20s = 15
651 // This way, the next ledger will close and round up Due to the
652 // network delay settings, the round of consensus will take 5s, so
653 // the next ledger's close time will
654
655 NetClock::duration when = network[0]->now().time_since_epoch();
656
657 // Check we are before the 30s to 20s transition
658 NetClock::duration const resolution = network[0]->lastClosedLedger.closeTimeResolution();
659 BEAST_EXPECT(resolution == NetClock::duration{30s});
660
661 while (((when % NetClock::duration{30s}) != NetClock::duration{15s}) ||
662 ((when % NetClock::duration{20s}) != NetClock::duration{15s}))
663 when += 1s;
664 // Advance the clock without consensus running (IS THIS WHAT
665 // PREVENTS IT IN PRACTICE?)
666 sim.scheduler.stepFor(NetClock::time_point{when} - network[0]->now());
667
668 // Run one more ledger with 30s resolution
669 sim.run(1);
670 if (BEAST_EXPECT(sim.synchronized()))
671 {
672 // close time should be ahead of clock time since we engineered
673 // the close time to round up
674 for (Peer const* peer : network)
675 {
676 BEAST_EXPECT(peer->lastClosedLedger.closeTime() > peer->now());
677 BEAST_EXPECT(peer->lastClosedLedger.closeAgree());
678 }
679 }
680
681 // All peers submit their own ID as a transaction
682 for (Peer* peer : network)
683 peer->submit(Tx{static_cast<std::uint32_t>(peer->id)});
684
685 // Run 1 more round, this time it will have a decreased
686 // resolution of 20 seconds.
687
688 // The network delays are engineered so that the slow peers
689 // initially have the wrong tx hash, but they see a majority
690 // of agreement from their peers and declare consensus
691 //
692 // The trick is that everyone starts with a raw close time of
693 // 84681s
694 // Which has
695 // effCloseTime(86481s, 20s, 86490s) = 86491s
696 // However, when the slow peers update their position, they change
697 // the close time to 86451s. The fast peers declare consensus with
698 // the 86481s as their position still.
699 //
700 // When accepted the ledger
701 // - fast peers use eff(86481s) -> 86491s as the close time
702 // - slow peers use eff(eff(86481s)) -> eff(86491s) -> 86500s!
703
704 sim.run(1);
705
706 BEAST_EXPECT(sim.synchronized());
707 }
708
709 void
711 {
712 using namespace csf;
713 using namespace std::chrono;
714 testcase("fork");
715
716 std::uint32_t const numPeers = 10;
717 // Vary overlap between two UNLs
718 for (std::uint32_t overlap = 0; overlap <= numPeers; ++overlap)
719 {
720 ConsensusParms const parms{};
721 Sim sim;
722
723 std::uint32_t const numA = (numPeers - overlap) / 2;
724 std::uint32_t const numB = numPeers - numA - overlap;
725
726 PeerGroup const aOnly = sim.createGroup(numA);
727 PeerGroup const bOnly = sim.createGroup(numB);
728 PeerGroup const commonOnly = sim.createGroup(overlap);
729
730 PeerGroup a = aOnly + commonOnly;
731 PeerGroup b = bOnly + commonOnly;
732
733 PeerGroup const network = a + b;
734
735 SimDuration const delay = round<milliseconds>(0.2 * parms.ledgerGRANULARITY);
736 a.trustAndConnect(a, delay);
737 b.trustAndConnect(b, delay);
738
739 // Initial round to set prior state
740 sim.run(1);
741 for (Peer* peer : network)
742 {
743 // Nodes have only seen transactions from their neighbors
744 peer->openTxs.insert(Tx{static_cast<std::uint32_t>(peer->id)});
745 for (Peer const* to : sim.trustGraph.trustedPeers(peer))
746 peer->openTxs.insert(Tx{static_cast<std::uint32_t>(to->id)});
747 }
748 sim.run(1);
749
750 // Fork should not happen for 40% or greater overlap
751 // Since the overlapped nodes have a UNL that is the union of the
752 // two cliques, the maximum sized UNL list is the number of peers
753 if (overlap > 0.4 * numPeers)
754 {
755 BEAST_EXPECT(sim.synchronized());
756 }
757 else
758 {
759 // Even if we do fork, there shouldn't be more than 3 ledgers
760 // One for cliqueA, one for cliqueB and one for nodes in both
761 BEAST_EXPECT(sim.branches() <= 3);
762 }
763 }
764 }
765
766 void
768 {
769 using namespace csf;
770 using namespace std::chrono;
771 testcase("hub network");
772
773 // Simulate a set of 5 validators that aren't directly connected but
774 // rely on a single hub node for communication
775
776 ConsensusParms const parms{};
777 Sim sim;
778 PeerGroup validators = sim.createGroup(5);
779 PeerGroup center = sim.createGroup(1);
780 validators.trust(validators);
781 center.trust(validators);
782
783 SimDuration const delay = round<milliseconds>(0.2 * parms.ledgerGRANULARITY);
784 validators.connect(center, delay);
785
786 center[0]->runAsValidator = false;
787
788 // prep round to set initial state.
789 sim.run(1);
790
791 // everyone submits their own ID as a TX and relay it to peers
792 for (Peer* p : validators)
793 p->submit(Tx(static_cast<std::uint32_t>(p->id)));
794
795 sim.run(1);
796
797 // All peers are in sync
798 BEAST_EXPECT(sim.synchronized());
799 }
800
801 // Helper collector for testPreferredByBranch
802 // Invasively disconnects network at bad times to cause splits
804 {
809 bool reconnected = false;
810
812 : network(net), groupCfast(c), groupCsplit(split), delay(d)
813 {
814 }
815
816 template <class E>
817 void
819 {
820 }
821
822 void
824 {
825 using namespace std::chrono;
826 // As soon as the fastC node fully validates C, disconnect
827 // ALL c nodes from the network. The fast C node needs to disconnect
828 // as well to prevent it from relaying the validations it did see
829 if (who == groupCfast[0]->id && e.ledger.seq() == csf::Ledger::Seq{2})
830 {
831 network.disconnect(groupCsplit);
832 network.disconnect(groupCfast);
833 }
834 }
835
836 void
838 {
839 // As soon as anyone generates a child of B or C, reconnect the
840 // network so those validations make it through
841 if (!reconnected && e.ledger.seq() == csf::Ledger::Seq{3})
842 {
843 reconnected = true;
844 network.connect(groupCsplit, delay);
845 }
846 }
847 };
848
849 void
851 {
852 using namespace csf;
853 using namespace std::chrono;
854 testcase("preferred by branch");
855
856 // Simulate network splits that are prevented from forking when using
857 // preferred ledger by trie. This is a contrived example that involves
858 // excessive network splits, but demonstrates the safety improvement
859 // from the preferred ledger by trie approach.
860
861 // Consider 10 validating nodes that comprise a single common UNL
862 // Ledger history:
863 // 1: A
864 // _/ \_
865 // 2: B C
866 // _/ _/ \_
867 // 3: D C' |||||||| (8 different ledgers)
868
869 // - All nodes generate the common ledger A
870 // - 2 nodes generate B and 8 nodes generate C
871 // - Only 1 of the C nodes sees all the C validations and fully
872 // validates C. The rest of the C nodes split at just the right time
873 // such that they never see any C validations but their own.
874 // - The C nodes continue and generate 8 different child ledgers.
875 // - Meanwhile, the D nodes only saw 1 validation for C and 2
876 // validations
877 // for B.
878 // - The network reconnects and the validations for generation 3 ledgers
879 // are observed (D and the 8 C's)
880 // - In the old approach, 2 votes for D outweighs 1 vote for each C'
881 // so the network would avalanche towards D and fully validate it
882 // EVEN though C was fully validated by one node
883 // - In the new approach, 2 votes for D are not enough to outweight the
884 // 8 implicit votes for C, so nodes will avalanche to C instead
885
886 ConsensusParms const parms{};
887 Sim sim;
888
889 // Goes A->B->D
890 PeerGroup const groupABD = sim.createGroup(2);
891 // Single node that initially fully validates C before the split
892 PeerGroup groupCfast = sim.createGroup(1);
893 // Generates C, but fails to fully validate before the split
894 PeerGroup groupCsplit = sim.createGroup(7);
895
896 PeerGroup groupNotFastC = groupABD + groupCsplit;
897 PeerGroup network = groupABD + groupCsplit + groupCfast;
898
899 SimDuration const delay = round<milliseconds>(0.2 * parms.ledgerGRANULARITY);
900 SimDuration const fDelay = round<milliseconds>(0.1 * parms.ledgerGRANULARITY);
901
902 network.trust(network);
903 // C must have a shorter delay to see all the validations before the
904 // other nodes
905 network.connect(groupCfast, fDelay);
906 // The rest of the network is connected at the same speed
907 groupNotFastC.connect(groupNotFastC, delay);
908
909 Disruptor dc(network, groupCfast, groupCsplit, delay);
910 sim.collectors.add(dc);
911
912 // Consensus round to generate ledger A
913 sim.run(1);
914 BEAST_EXPECT(sim.synchronized());
915
916 // Next round generates B and C
917 // To force B, we inject an extra transaction in to those nodes
918 for (Peer* peer : groupABD)
919 {
920 peer->txInjections.emplace(peer->lastClosedLedger.seq(), Tx{42});
921 }
922 // The Disruptor will ensure that nodes disconnect before the C
923 // validations make it to all but the fastC node
924 sim.run(1);
925
926 // We are no longer in sync, but have not yet forked:
927 // 9 nodes consider A the last fully validated ledger and fastC sees C
928 BEAST_EXPECT(!sim.synchronized());
929 BEAST_EXPECT(sim.branches() == 1);
930
931 // Run another round to generate the 8 different C' ledgers
932 for (Peer* p : network)
933 p->submit(Tx(static_cast<std::uint32_t>(p->id)));
934 sim.run(1);
935
936 // Still not forked
937 BEAST_EXPECT(!sim.synchronized());
938 BEAST_EXPECT(sim.branches() == 1);
939
940 // Disruptor will reconnect all but the fastC node
941 sim.run(1);
942
943 if (BEAST_EXPECT(sim.branches() == 1))
944 {
945 BEAST_EXPECT(sim.synchronized());
946 }
947 else // old approach caused a fork
948 {
949 BEAST_EXPECT(sim.branches(groupNotFastC) == 1);
950 BEAST_EXPECT(sim.synchronized(groupNotFastC) == 1);
951 }
952 }
953
954 // Helper collector for testPauseForLaggards
955 // This will remove the ledgerAccept delay used to
956 // initially create the slow vs. fast validator groups.
958 {
960
962 {
963 }
964
965 template <class E>
966 void
968 {
969 }
970
971 void
973 {
974 for (csf::Peer* p : g)
975 {
976 if (p->id == who)
977 p->delays.ledgerAccept = std::chrono::seconds{0};
978 }
979 }
980 };
981
982 void
984 {
985 using namespace csf;
986 using namespace std::chrono;
987 testcase("pause for laggards");
988
989 // Test that validators that jump ahead of the network slow
990 // down.
991
992 // We engineer the following validated ledger history scenario:
993 //
994 // / --> B1 --> C1 --> ... -> G1 "ahead"
995 // A
996 // \ --> B2 --> C2 "behind"
997 //
998 // After validating a common ledger A, a set of "behind" validators
999 // briefly run slower and validate the lower chain of ledgers.
1000 // The "ahead" validators run normal speed and run ahead validating the
1001 // upper chain of ledgers.
1002 //
1003 // Due to the uncommitted support definition of the preferred branch
1004 // protocol, even if the "behind" validators are a majority, the "ahead"
1005 // validators cannot jump to the proper branch until the "behind"
1006 // validators catch up to the same sequence number. For this test to
1007 // succeed, the ahead validators need to briefly slow down consensus.
1008
1009 ConsensusParms const parms{};
1010 Sim sim;
1011 SimDuration const delay = round<milliseconds>(0.2 * parms.ledgerGRANULARITY);
1012
1013 PeerGroup behind = sim.createGroup(3);
1014 PeerGroup const ahead = sim.createGroup(2);
1015 PeerGroup network = ahead + behind;
1016
1017 hash_set<Peer::NodeKey_t> trustedKeys;
1018 for (Peer const* p : network)
1019 trustedKeys.insert(p->key);
1020 for (Peer* p : network)
1021 p->trustedKeys = trustedKeys;
1022
1023 network.trustAndConnect(network, delay);
1024
1025 // Initial seed round to set prior state
1026 sim.run(1);
1027
1028 // Have the "behind" group initially take a really long time to
1029 // accept a ledger after ending deliberation
1030 for (Peer* p : behind)
1031 p->delays.ledgerAccept = 20s;
1032
1033 // Use the collector to revert the delay after the single
1034 // slow ledger is generated
1035 UndoDelay undoDelay{behind};
1036 sim.collectors.add(undoDelay);
1037
1038#if 0
1039 // Have all beast::journal output printed to stdout
1040 for (Peer* p : network)
1041 p->sink.threshold(beast::Severity::All);
1042
1043 // Print ledger accept and fully validated events to stdout
1044 StreamCollector sc{std::cout};
1045 sim.collectors.add(sc);
1046#endif
1047 // Run the simulation for 100 seconds of simulation time with
1048 std::chrono::nanoseconds const simDuration = 100s;
1049
1050 // Simulate clients submitting 1 tx every 5 seconds to a random
1051 // validator
1052 Rate const rate{.count = 1, .duration = 5s};
1053 auto peerSelector = makeSelector(
1054 network.begin(), network.end(), std::vector<double>(network.size(), 1.), sim.rng);
1055 auto txSubmitter = makeSubmitter(
1056 ConstantDistribution{rate.inv()},
1057 sim.scheduler.now(),
1058 sim.scheduler.now() + simDuration,
1059 peerSelector,
1060 sim.scheduler,
1061 sim.rng);
1062
1063 // Run simulation
1064 sim.run(simDuration);
1065
1066 // Verify that the network recovered
1067 BEAST_EXPECT(sim.synchronized());
1068 }
1069
1070 void
1072 {
1073 testcase("disputes");
1074
1075 using namespace csf;
1076
1077 // Test dispute objects directly
1078 using Dispute = DisputedTx<Tx, PeerID>;
1079
1080 Tx const txTrue{99};
1081 Tx const txFalse{98};
1082 Tx const txFollowingTrue{97};
1083 Tx const txFollowingFalse{96};
1084 int const numPeers = 100;
1085 ConsensusParms const p;
1086 std::size_t peersUnchanged = 0;
1087
1089 auto j = logs->journal("Test");
1091
1092 // Three cases:
1093 // 1 proposing, initial vote yes
1094 // 2 proposing, initial vote no
1095 // 3 not proposing, initial vote doesn't matter after the first update,
1096 // use yes
1097 {
1098 Dispute proposingTrue{txTrue.id(), true, numPeers, journal_};
1099 Dispute proposingFalse{txFalse.id(), false, numPeers, journal_};
1100 Dispute followingTrue{txFollowingTrue.id(), true, numPeers, journal_};
1101 Dispute followingFalse{txFollowingFalse.id(), false, numPeers, journal_};
1102 BEAST_EXPECT(proposingTrue.id() == 99);
1103 BEAST_EXPECT(proposingFalse.id() == 98);
1104 BEAST_EXPECT(followingTrue.id() == 97);
1105 BEAST_EXPECT(followingFalse.id() == 96);
1106
1107 // Create an even split in the peer votes
1108 for (int i = 0; i < numPeers; ++i)
1109 {
1110 BEAST_EXPECT(proposingTrue.setVote(PeerID(i), i < 50));
1111 BEAST_EXPECT(proposingFalse.setVote(PeerID(i), i < 50));
1112 BEAST_EXPECT(followingTrue.setVote(PeerID(i), i < 50));
1113 BEAST_EXPECT(followingFalse.setVote(PeerID(i), i < 50));
1114 }
1115 // Switch the middle vote to match mine
1116 BEAST_EXPECT(proposingTrue.setVote(PeerID(50), true));
1117 BEAST_EXPECT(proposingFalse.setVote(PeerID(49), false));
1118 BEAST_EXPECT(followingTrue.setVote(PeerID(50), true));
1119 BEAST_EXPECT(followingFalse.setVote(PeerID(49), false));
1120
1121 // no changes yet
1122 BEAST_EXPECT(proposingTrue.getOurVote() == true);
1123 BEAST_EXPECT(proposingFalse.getOurVote() == false);
1124 BEAST_EXPECT(followingTrue.getOurVote() == true);
1125 BEAST_EXPECT(followingFalse.getOurVote() == false);
1126 BEAST_EXPECT(!proposingTrue.stalled(p, true, peersUnchanged, j, clog));
1127 BEAST_EXPECT(!proposingFalse.stalled(p, true, peersUnchanged, j, clog));
1128 BEAST_EXPECT(!followingTrue.stalled(p, false, peersUnchanged, j, clog));
1129 BEAST_EXPECT(!followingFalse.stalled(p, false, peersUnchanged, j, clog));
1130 BEAST_EXPECT(clog->str().empty());
1131
1132 // I'm in the majority, my vote should not change
1133 BEAST_EXPECT(!proposingTrue.updateVote(5, true, p));
1134 BEAST_EXPECT(!proposingFalse.updateVote(5, true, p));
1135 BEAST_EXPECT(!followingTrue.updateVote(5, false, p));
1136 BEAST_EXPECT(!followingFalse.updateVote(5, false, p));
1137
1138 BEAST_EXPECT(!proposingTrue.updateVote(10, true, p));
1139 BEAST_EXPECT(!proposingFalse.updateVote(10, true, p));
1140 BEAST_EXPECT(!followingTrue.updateVote(10, false, p));
1141 BEAST_EXPECT(!followingFalse.updateVote(10, false, p));
1142
1143 peersUnchanged = 2;
1144 BEAST_EXPECT(!proposingTrue.stalled(p, true, peersUnchanged, j, clog));
1145 BEAST_EXPECT(!proposingFalse.stalled(p, true, peersUnchanged, j, clog));
1146 BEAST_EXPECT(!followingTrue.stalled(p, false, peersUnchanged, j, clog));
1147 BEAST_EXPECT(!followingFalse.stalled(p, false, peersUnchanged, j, clog));
1148 BEAST_EXPECT(clog->str().empty());
1149
1150 // Right now, the vote is 51%. The requirement is about to jump to
1151 // 65%
1152 BEAST_EXPECT(proposingTrue.updateVote(55, true, p));
1153 BEAST_EXPECT(!proposingFalse.updateVote(55, true, p));
1154 BEAST_EXPECT(!followingTrue.updateVote(55, false, p));
1155 BEAST_EXPECT(!followingFalse.updateVote(55, false, p));
1156
1157 BEAST_EXPECT(proposingTrue.getOurVote() == false);
1158 BEAST_EXPECT(proposingFalse.getOurVote() == false);
1159 BEAST_EXPECT(followingTrue.getOurVote() == true);
1160 BEAST_EXPECT(followingFalse.getOurVote() == false);
1161 // 16 validators change their vote to match my original vote
1162 for (int i = 0; i < 16; ++i)
1163 {
1164 auto pTrue = PeerID(numPeers - i - 1);
1165 auto pFalse = PeerID(i);
1166 BEAST_EXPECT(proposingTrue.setVote(pTrue, true));
1167 BEAST_EXPECT(proposingFalse.setVote(pFalse, false));
1168 BEAST_EXPECT(followingTrue.setVote(pTrue, true));
1169 BEAST_EXPECT(followingFalse.setVote(pFalse, false));
1170 }
1171 // The vote should now be 66%, threshold is 65%
1172 BEAST_EXPECT(proposingTrue.updateVote(60, true, p));
1173 BEAST_EXPECT(!proposingFalse.updateVote(60, true, p));
1174 BEAST_EXPECT(!followingTrue.updateVote(60, false, p));
1175 BEAST_EXPECT(!followingFalse.updateVote(60, false, p));
1176
1177 BEAST_EXPECT(proposingTrue.getOurVote() == true);
1178 BEAST_EXPECT(proposingFalse.getOurVote() == false);
1179 BEAST_EXPECT(followingTrue.getOurVote() == true);
1180 BEAST_EXPECT(followingFalse.getOurVote() == false);
1181
1182 // Threshold jumps to 70%
1183 BEAST_EXPECT(proposingTrue.updateVote(86, true, p));
1184 BEAST_EXPECT(!proposingFalse.updateVote(86, true, p));
1185 BEAST_EXPECT(!followingTrue.updateVote(86, false, p));
1186 BEAST_EXPECT(!followingFalse.updateVote(86, false, p));
1187
1188 BEAST_EXPECT(proposingTrue.getOurVote() == false);
1189 BEAST_EXPECT(proposingFalse.getOurVote() == false);
1190 BEAST_EXPECT(followingTrue.getOurVote() == true);
1191 BEAST_EXPECT(followingFalse.getOurVote() == false);
1192
1193 // 5 more validators change their vote to match my original vote
1194 for (int i = 16; i < 21; ++i)
1195 {
1196 auto pTrue = PeerID(numPeers - i - 1);
1197 auto pFalse = PeerID(i);
1198 BEAST_EXPECT(proposingTrue.setVote(pTrue, true));
1199 BEAST_EXPECT(proposingFalse.setVote(pFalse, false));
1200 BEAST_EXPECT(followingTrue.setVote(pTrue, true));
1201 BEAST_EXPECT(followingFalse.setVote(pFalse, false));
1202 }
1203
1204 // The vote should now be 71%, threshold is 70%
1205 BEAST_EXPECT(proposingTrue.updateVote(90, true, p));
1206 BEAST_EXPECT(!proposingFalse.updateVote(90, true, p));
1207 BEAST_EXPECT(!followingTrue.updateVote(90, false, p));
1208 BEAST_EXPECT(!followingFalse.updateVote(90, false, p));
1209
1210 BEAST_EXPECT(proposingTrue.getOurVote() == true);
1211 BEAST_EXPECT(proposingFalse.getOurVote() == false);
1212 BEAST_EXPECT(followingTrue.getOurVote() == true);
1213 BEAST_EXPECT(followingFalse.getOurVote() == false);
1214
1215 // The vote should now be 71%, threshold is 70%
1216 BEAST_EXPECT(!proposingTrue.updateVote(150, true, p));
1217 BEAST_EXPECT(!proposingFalse.updateVote(150, true, p));
1218 BEAST_EXPECT(!followingTrue.updateVote(150, false, p));
1219 BEAST_EXPECT(!followingFalse.updateVote(150, false, p));
1220
1221 BEAST_EXPECT(proposingTrue.getOurVote() == true);
1222 BEAST_EXPECT(proposingFalse.getOurVote() == false);
1223 BEAST_EXPECT(followingTrue.getOurVote() == true);
1224 BEAST_EXPECT(followingFalse.getOurVote() == false);
1225
1226 // The vote should now be 71%, threshold is 70%
1227 BEAST_EXPECT(!proposingTrue.updateVote(190, true, p));
1228 BEAST_EXPECT(!proposingFalse.updateVote(190, true, p));
1229 BEAST_EXPECT(!followingTrue.updateVote(190, false, p));
1230 BEAST_EXPECT(!followingFalse.updateVote(190, false, p));
1231
1232 BEAST_EXPECT(proposingTrue.getOurVote() == true);
1233 BEAST_EXPECT(proposingFalse.getOurVote() == false);
1234 BEAST_EXPECT(followingTrue.getOurVote() == true);
1235 BEAST_EXPECT(followingFalse.getOurVote() == false);
1236
1237 peersUnchanged = 3;
1238 BEAST_EXPECT(!proposingTrue.stalled(p, true, peersUnchanged, j, clog));
1239 BEAST_EXPECT(!proposingFalse.stalled(p, true, peersUnchanged, j, clog));
1240 BEAST_EXPECT(!followingTrue.stalled(p, false, peersUnchanged, j, clog));
1241 BEAST_EXPECT(!followingFalse.stalled(p, false, peersUnchanged, j, clog));
1242 BEAST_EXPECT(clog->str().empty());
1243
1244 // Threshold jumps to 95%
1245 BEAST_EXPECT(proposingTrue.updateVote(220, true, p));
1246 BEAST_EXPECT(!proposingFalse.updateVote(220, true, p));
1247 BEAST_EXPECT(!followingTrue.updateVote(220, false, p));
1248 BEAST_EXPECT(!followingFalse.updateVote(220, false, p));
1249
1250 BEAST_EXPECT(proposingTrue.getOurVote() == false);
1251 BEAST_EXPECT(proposingFalse.getOurVote() == false);
1252 BEAST_EXPECT(followingTrue.getOurVote() == true);
1253 BEAST_EXPECT(followingFalse.getOurVote() == false);
1254
1255 // 25 more validators change their vote to match my original vote
1256 for (int i = 21; i < 46; ++i)
1257 {
1258 auto pTrue = PeerID(numPeers - i - 1);
1259 auto pFalse = PeerID(i);
1260 BEAST_EXPECT(proposingTrue.setVote(pTrue, true));
1261 BEAST_EXPECT(proposingFalse.setVote(pFalse, false));
1262 BEAST_EXPECT(followingTrue.setVote(pTrue, true));
1263 BEAST_EXPECT(followingFalse.setVote(pFalse, false));
1264 }
1265
1266 // The vote should now be 96%, threshold is 95%
1267 BEAST_EXPECT(proposingTrue.updateVote(250, true, p));
1268 BEAST_EXPECT(!proposingFalse.updateVote(250, true, p));
1269 BEAST_EXPECT(!followingTrue.updateVote(250, false, p));
1270 BEAST_EXPECT(!followingFalse.updateVote(250, false, p));
1271
1272 BEAST_EXPECT(proposingTrue.getOurVote() == true);
1273 BEAST_EXPECT(proposingFalse.getOurVote() == false);
1274 BEAST_EXPECT(followingTrue.getOurVote() == true);
1275 BEAST_EXPECT(followingFalse.getOurVote() == false);
1276
1277 for (peersUnchanged = 0; peersUnchanged < 6; ++peersUnchanged)
1278 {
1279 BEAST_EXPECT(!proposingTrue.stalled(p, true, peersUnchanged, j, clog));
1280 BEAST_EXPECT(!proposingFalse.stalled(p, true, peersUnchanged, j, clog));
1281 BEAST_EXPECT(!followingTrue.stalled(p, false, peersUnchanged, j, clog));
1282 BEAST_EXPECT(!followingFalse.stalled(p, false, peersUnchanged, j, clog));
1283 BEAST_EXPECT(clog->str().empty());
1284 }
1285
1286 auto expectStalled = [this, &clog](
1287 int txid,
1288 bool ourVote,
1289 int ourTime,
1290 int peerTime,
1291 int support,
1292 std::uint32_t line) {
1293 using namespace std::string_literals;
1294
1295 auto const s = clog->str();
1296 expect(s.find("stalled"), s, __FILE__, line);
1297 expect(s.starts_with("Transaction "s + std::to_string(txid)), s, __FILE__, line);
1298 expect(s.contains("voting "s + (ourVote ? "YES" : "NO")), s, __FILE__, line);
1299 expect(
1300 s.contains("for "s + std::to_string(ourTime) + " rounds."s), s, __FILE__, line);
1301 expect(
1302 s.contains("votes in "s + std::to_string(peerTime) + " rounds."),
1303 s,
1304 __FILE__,
1305 line);
1306 expect(
1307 s.ends_with("has "s + std::to_string(support) + "% support. "s),
1308 s,
1309 __FILE__,
1310 line);
1312 };
1313
1314 for (int i = 0; i < 1; ++i)
1315 {
1316 BEAST_EXPECT(!proposingTrue.updateVote(250 + (10 * i), true, p));
1317 BEAST_EXPECT(!proposingFalse.updateVote(250 + (10 * i), true, p));
1318 BEAST_EXPECT(!followingTrue.updateVote(250 + (10 * i), false, p));
1319 BEAST_EXPECT(!followingFalse.updateVote(250 + (10 * i), false, p));
1320
1321 BEAST_EXPECT(proposingTrue.getOurVote() == true);
1322 BEAST_EXPECT(proposingFalse.getOurVote() == false);
1323 BEAST_EXPECT(followingTrue.getOurVote() == true);
1324 BEAST_EXPECT(followingFalse.getOurVote() == false);
1325
1326 // true vote has changed recently, so not stalled
1327 BEAST_EXPECT(!proposingTrue.stalled(p, true, 0, j, clog));
1328 BEAST_EXPECT(clog->str().empty());
1329 // remaining votes have been unchanged in so long that we only
1330 // need to hit the second round at 95% to be stalled, regardless
1331 // of peers
1332 BEAST_EXPECT(proposingFalse.stalled(p, true, 0, j, clog));
1333 expectStalled(98, false, 11, 0, 2, __LINE__);
1334 BEAST_EXPECT(followingTrue.stalled(p, false, 0, j, clog));
1335 expectStalled(97, true, 11, 0, 97, __LINE__);
1336 BEAST_EXPECT(followingFalse.stalled(p, false, 0, j, clog));
1337 expectStalled(96, false, 11, 0, 3, __LINE__);
1338
1339 // true vote has changed recently, so not stalled
1340 BEAST_EXPECT(!proposingTrue.stalled(p, true, peersUnchanged, j, clog));
1341 BEAST_EXPECTS(clog->str().empty(), clog->str());
1342 // remaining votes have been unchanged in so long that we only
1343 // need to hit the second round at 95% to be stalled, regardless
1344 // of peers
1345 BEAST_EXPECT(proposingFalse.stalled(p, true, peersUnchanged, j, clog));
1346 expectStalled(98, false, 11, 6, 2, __LINE__);
1347 BEAST_EXPECT(followingTrue.stalled(p, false, peersUnchanged, j, clog));
1348 expectStalled(97, true, 11, 6, 97, __LINE__);
1349 BEAST_EXPECT(followingFalse.stalled(p, false, peersUnchanged, j, clog));
1350 expectStalled(96, false, 11, 6, 3, __LINE__);
1351 }
1352 for (int i = 1; i < 3; ++i)
1353 {
1354 BEAST_EXPECT(!proposingTrue.updateVote(250 + (10 * i), true, p));
1355 BEAST_EXPECT(!proposingFalse.updateVote(250 + (10 * i), true, p));
1356 BEAST_EXPECT(!followingTrue.updateVote(250 + (10 * i), false, p));
1357 BEAST_EXPECT(!followingFalse.updateVote(250 + (10 * i), false, p));
1358
1359 BEAST_EXPECT(proposingTrue.getOurVote() == true);
1360 BEAST_EXPECT(proposingFalse.getOurVote() == false);
1361 BEAST_EXPECT(followingTrue.getOurVote() == true);
1362 BEAST_EXPECT(followingFalse.getOurVote() == false);
1363
1364 // true vote changed 2 rounds ago, and peers are changing, so
1365 // not stalled
1366 BEAST_EXPECT(!proposingTrue.stalled(p, true, 0, j, clog));
1367 BEAST_EXPECTS(clog->str().empty(), clog->str());
1368 // still stalled
1369 BEAST_EXPECT(proposingFalse.stalled(p, true, 0, j, clog));
1370 expectStalled(98, false, 11 + i, 0, 2, __LINE__);
1371 BEAST_EXPECT(followingTrue.stalled(p, false, 0, j, clog));
1372 expectStalled(97, true, 11 + i, 0, 97, __LINE__);
1373 BEAST_EXPECT(followingFalse.stalled(p, false, 0, j, clog));
1374 expectStalled(96, false, 11 + i, 0, 3, __LINE__);
1375
1376 // true vote changed 2 rounds ago, and peers are NOT changing,
1377 // so stalled
1378 BEAST_EXPECT(proposingTrue.stalled(p, true, peersUnchanged, j, clog));
1379 expectStalled(99, true, 1 + i, 6, 97, __LINE__);
1380 // still stalled
1381 BEAST_EXPECT(proposingFalse.stalled(p, true, peersUnchanged, j, clog));
1382 expectStalled(98, false, 11 + i, 6, 2, __LINE__);
1383 BEAST_EXPECT(followingTrue.stalled(p, false, peersUnchanged, j, clog));
1384 expectStalled(97, true, 11 + i, 6, 97, __LINE__);
1385 BEAST_EXPECT(followingFalse.stalled(p, false, peersUnchanged, j, clog));
1386 expectStalled(96, false, 11 + i, 6, 3, __LINE__);
1387 }
1388 for (int i = 3; i < 5; ++i)
1389 {
1390 BEAST_EXPECT(!proposingTrue.updateVote(250 + (10 * i), true, p));
1391 BEAST_EXPECT(!proposingFalse.updateVote(250 + (10 * i), true, p));
1392 BEAST_EXPECT(!followingTrue.updateVote(250 + (10 * i), false, p));
1393 BEAST_EXPECT(!followingFalse.updateVote(250 + (10 * i), false, p));
1394
1395 BEAST_EXPECT(proposingTrue.getOurVote() == true);
1396 BEAST_EXPECT(proposingFalse.getOurVote() == false);
1397 BEAST_EXPECT(followingTrue.getOurVote() == true);
1398 BEAST_EXPECT(followingFalse.getOurVote() == false);
1399
1400 BEAST_EXPECT(proposingTrue.stalled(p, true, 0, j, clog));
1401 expectStalled(99, true, 1 + i, 0, 97, __LINE__);
1402 BEAST_EXPECT(proposingFalse.stalled(p, true, 0, j, clog));
1403 expectStalled(98, false, 11 + i, 0, 2, __LINE__);
1404 BEAST_EXPECT(followingTrue.stalled(p, false, 0, j, clog));
1405 expectStalled(97, true, 11 + i, 0, 97, __LINE__);
1406 BEAST_EXPECT(followingFalse.stalled(p, false, 0, j, clog));
1407 expectStalled(96, false, 11 + i, 0, 3, __LINE__);
1408
1409 BEAST_EXPECT(proposingTrue.stalled(p, true, peersUnchanged, j, clog));
1410 expectStalled(99, true, 1 + i, 6, 97, __LINE__);
1411 BEAST_EXPECT(proposingFalse.stalled(p, true, peersUnchanged, j, clog));
1412 expectStalled(98, false, 11 + i, 6, 2, __LINE__);
1413 BEAST_EXPECT(followingTrue.stalled(p, false, peersUnchanged, j, clog));
1414 expectStalled(97, true, 11 + i, 6, 97, __LINE__);
1415 BEAST_EXPECT(followingFalse.stalled(p, false, peersUnchanged, j, clog));
1416 expectStalled(96, false, 11 + i, 6, 3, __LINE__);
1417 }
1418 }
1419 }
1420
1421 void
1439};
1440
1442} // namespace xrpl::test
A testsuite class.
Definition suite.h:50
bool expect(Condition const &shouldBeTrue)
Evaluate a test condition.
Definition suite.h:223
TestcaseT testcase
Memberspace for declaring test cases.
Definition suite.h:149
Generic implementation of consensus algorithm.
Definition Consensus.h:278
A transaction discovered to be in dispute during consensus.
Definition DisputedTx.h:31
std::chrono::time_point< NetClock > time_point
Definition chrono.h:46
std::chrono::duration< rep, period > duration
Definition chrono.h:45
Represents a peer connection in the overlay.
virtual id_t id() const =0
void run() override
Runs the suite.
TaggedInteger< std::uint32_t, SeqTag > Seq
Definition ledgers.h:46
A group of simulation Peers.
Definition PeerGroup.h:20
T insert(T... args)
T make_unique(T... args)
SimClock::duration SimDuration
Definition SimTime.h:14
TaggedInteger< std::uint32_t, PeerIDTag > PeerID
Definition Validation.h:15
SimClock::time_point SimTime
Definition SimTime.h:15
json::Value rate(Account const &account, double multiplier)
Set a transfer rate.
Definition rate.cpp:15
BEAST_DEFINE_TESTSUITE(AMMClawback, app, xrpl)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
constexpr auto kIncreaseLedgerTimeResolutionEvery
How often we increase the close time resolution (in numbers of ledgers).
std::unordered_set< Value, Hash, Pred, Allocator > hash_set
ConsensusState checkConsensus(std::size_t prevProposers, std::size_t currentProposers, std::size_t currentAgree, std::size_t currentFinished, std::chrono::milliseconds previousAgreeTime, std::chrono::milliseconds currentAgreeTime, bool stalled, ConsensusParms const &parms, bool proposing, beast::Journal j, std::unique_ptr< std::stringstream > const &clog)
Determine whether the network reached consensus and whether we joined.
@ Expired
Consensus time limit has hard-expired.
@ MovedOn
The network has consensus without us.
@ Yes
We have consensus along with the network.
@ No
We do not have consensus.
bool shouldCloseLedger(bool anyTransactions, std::size_t prevProposers, std::size_t proposersClosed, std::size_t proposersValidated, std::chrono::milliseconds prevRoundTime, std::chrono::milliseconds timeSincePrevClose, std::chrono::milliseconds openTime, std::chrono::milliseconds idleInterval, ConsensusParms const &parms, beast::Journal j, std::unique_ptr< std::stringstream > const &clog)
Determines whether the current ledger should close at this time.
Definition Consensus.cpp:18
T round(T... args)
Consensus algorithm parameters.
std::chrono::milliseconds const ledgerMinClose
Minimum number of seconds to wait to ensure others have computed the LCL.
std::chrono::seconds const proposeFRESHNESS
How long we consider a proposal fresh.
std::chrono::milliseconds const ledgerGRANULARITY
How often we check state or change positions.
std::size_t const minConsensusPct
The percentage threshold above which we can declare consensus.
Represents a transfer rate.
Definition Rate.h:20
void on(csf::PeerID who, csf::SimTime, csf::FullyValidateLedger const &e)
void on(csf::PeerID, csf::SimTime, E const &)
Disruptor(csf::PeerGroup &net, csf::PeerGroup &c, csf::PeerGroup &split, csf::SimDuration d)
void on(csf::PeerID who, csf::SimTime, csf::AcceptLedger const &e)
void on(csf::PeerID who, csf::SimTime, csf::AcceptLedger const &e)
void on(csf::PeerID, csf::SimTime, E const &)
Peer accepted consensus results.
Definition events.h:99
Peer fully validated a new ledger.
Definition events.h:118
Ledger ledger
The new fully validated ledger.
Definition events.h:120
A single peer in the simulation.
T to_string(T... args)