rippled
Loading...
Searching...
No Matches
RCLConsensus.cpp
1//------------------------------------------------------------------------------
2/*
3 This file is part of rippled: https://github.com/ripple/rippled
4 Copyright (c) 2012, 2013 Ripple Labs Inc.
5
6 Permission to use, copy, modify, and/or distribute this software for any
7 purpose with or without fee is hereby granted, provided that the above
8 copyright notice and this permission notice appear in all copies.
9
10 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17*/
18//==============================================================================
19
20#include <xrpld/app/consensus/RCLConsensus.h>
21#include <xrpld/app/consensus/RCLValidations.h>
22#include <xrpld/app/ledger/BuildLedger.h>
23#include <xrpld/app/ledger/InboundLedgers.h>
24#include <xrpld/app/ledger/InboundTransactions.h>
25#include <xrpld/app/ledger/Ledger.h>
26#include <xrpld/app/ledger/LedgerMaster.h>
27#include <xrpld/app/ledger/LocalTxs.h>
28#include <xrpld/app/ledger/OpenLedger.h>
29#include <xrpld/app/misc/AmendmentTable.h>
30#include <xrpld/app/misc/HashRouter.h>
31#include <xrpld/app/misc/LoadFeeTrack.h>
32#include <xrpld/app/misc/NegativeUNLVote.h>
33#include <xrpld/app/misc/NetworkOPs.h>
34#include <xrpld/app/misc/TxQ.h>
35#include <xrpld/app/misc/ValidatorKeys.h>
36#include <xrpld/app/misc/ValidatorList.h>
37#include <xrpld/consensus/LedgerTiming.h>
38#include <xrpld/overlay/Overlay.h>
39#include <xrpld/overlay/predicates.h>
40
41#include <xrpl/basics/random.h>
42#include <xrpl/beast/core/LexicalCast.h>
43#include <xrpl/beast/utility/instrumentation.h>
44#include <xrpl/protocol/BuildInfo.h>
45#include <xrpl/protocol/Feature.h>
46#include <xrpl/protocol/digest.h>
47
48#include <algorithm>
49#include <iomanip>
50#include <mutex>
51
52namespace ripple {
53
55 Application& app,
58 LocalTxs& localTxs,
59 InboundTransactions& inboundTransactions,
61 ValidatorKeys const& validatorKeys,
62 beast::Journal journal)
63 : adaptor_(
64 app,
65 std::move(feeVote),
67 localTxs,
68 inboundTransactions,
69 validatorKeys,
70 journal)
71 , consensus_(clock, adaptor_, journal)
72 , j_(journal)
73{
74}
75
77 Application& app,
80 LocalTxs& localTxs,
81 InboundTransactions& inboundTransactions,
82 ValidatorKeys const& validatorKeys,
83 beast::Journal journal)
84 : app_(app)
85 , feeVote_(std::move(feeVote))
86 , ledgerMaster_(ledgerMaster)
87 , localTxs_(localTxs)
88 , inboundTransactions_{inboundTransactions}
89 , j_(journal)
90 , validatorKeys_(validatorKeys)
91 , valCookie_(
92 1 +
95 std::numeric_limits<std::uint64_t>::max() - 1))
96 , nUnlVote_(validatorKeys_.nodeID, j_)
97{
98 XRPL_ASSERT(
99 valCookie_, "ripple::RCLConsensus::Adaptor::Adaptor : nonzero cookie");
100
101 JLOG(j_.info()) << "Consensus engine started (cookie: " +
103
104 if (validatorKeys_.nodeID != beast::zero && validatorKeys_.keys)
105 {
107
108 JLOG(j_.info()) << "Validator identity: "
109 << toBase58(
111 validatorKeys_.keys->masterPublicKey);
112
113 if (validatorKeys_.keys->masterPublicKey !=
114 validatorKeys_.keys->publicKey)
115 {
116 JLOG(j_.debug())
117 << "Validator ephemeral signing key: "
118 << toBase58(
120 << " (seq: " << std::to_string(validatorKeys_.sequence) << ")";
121 }
122 }
123}
124
127{
128 // we need to switch the ledger we're working from
129 auto built = ledgerMaster_.getLedgerByHash(hash);
130 if (!built)
131 {
132 if (acquiringLedger_ != hash)
133 {
134 // need to start acquiring the correct consensus LCL
135 JLOG(j_.warn()) << "Need consensus ledger " << hash;
136
137 // Tell the ledger acquire system that we need the consensus ledger
138 acquiringLedger_ = hash;
139
140 app_.getJobQueue().addJob(
141 jtADVANCE,
142 "getConsensusLedger1",
143 [id = hash, &app = app_, this]() {
144 JLOG(j_.debug())
145 << "JOB advanceLedger getConsensusLedger1 started";
146 app.getInboundLedgers().acquireAsync(
148 });
149 }
150 return std::nullopt;
151 }
152
153 XRPL_ASSERT(
154 !built->open() && built->isImmutable(),
155 "ripple::RCLConsensus::Adaptor::acquireLedger : valid ledger state");
156 XRPL_ASSERT(
157 built->info().hash == hash,
158 "ripple::RCLConsensus::Adaptor::acquireLedger : ledger hash match");
159
160 // Notify inbound transactions of the new ledger sequence number
161 inboundTransactions_.newRound(built->info().seq);
162
163 return RCLCxLedger(built);
164}
165
166void
168{
169 protocol::TMProposeSet prop;
170
171 auto const& proposal = peerPos.proposal();
172
173 prop.set_proposeseq(proposal.proposeSeq());
174 prop.set_closetime(proposal.closeTime().time_since_epoch().count());
175
176 prop.set_currenttxhash(
177 proposal.position().begin(), proposal.position().size());
178 prop.set_previousledger(
179 proposal.prevLedger().begin(), proposal.position().size());
180
181 auto const pk = peerPos.publicKey().slice();
182 prop.set_nodepubkey(pk.data(), pk.size());
183
184 auto const sig = peerPos.signature();
185 prop.set_signature(sig.data(), sig.size());
186
187 app_.overlay().relay(prop, peerPos.suppressionID(), peerPos.publicKey());
188}
189
190void
192{
193 // If we didn't relay this transaction recently, relay it to all peers
194 if (app_.getHashRouter().shouldRelay(tx.id()))
195 {
196 JLOG(j_.debug()) << "Relaying disputed tx " << tx.id();
197 auto const slice = tx.tx_->slice();
198 protocol::TMTransaction msg;
199 msg.set_rawtransaction(slice.data(), slice.size());
200 msg.set_status(protocol::tsNEW);
201 msg.set_receivetimestamp(
202 app_.timeKeeper().now().time_since_epoch().count());
203 static std::set<Peer::id_t> skip{};
204 app_.overlay().relay(tx.id(), msg, skip);
205 }
206 else
207 {
208 JLOG(j_.debug()) << "Not relaying disputed tx " << tx.id();
209 }
210}
211void
213{
214 JLOG(j_.trace()) << (proposal.isBowOut() ? "We bow out: " : "We propose: ")
215 << ripple::to_string(proposal.prevLedger()) << " -> "
216 << ripple::to_string(proposal.position());
217
218 protocol::TMProposeSet prop;
219
220 prop.set_currenttxhash(
221 proposal.position().begin(), proposal.position().size());
222 prop.set_previousledger(
223 proposal.prevLedger().begin(), proposal.prevLedger().size());
224 prop.set_proposeseq(proposal.proposeSeq());
225 prop.set_closetime(proposal.closeTime().time_since_epoch().count());
226
227 if (!validatorKeys_.keys)
228 {
229 JLOG(j_.warn()) << "RCLConsensus::Adaptor::propose: ValidatorKeys "
230 "not set: \n";
231 return;
232 }
233
234 auto const& keys = *validatorKeys_.keys;
235
236 prop.set_nodepubkey(keys.publicKey.data(), keys.publicKey.size());
237
238 auto sig =
239 signDigest(keys.publicKey, keys.secretKey, proposal.signingHash());
240
241 prop.set_signature(sig.data(), sig.size());
242
243 auto const suppression = proposalUniqueId(
244 proposal.position(),
245 proposal.prevLedger(),
246 proposal.proposeSeq(),
247 proposal.closeTime(),
248 keys.publicKey,
249 sig);
250
251 app_.getHashRouter().addSuppression(suppression);
252
253 app_.overlay().broadcast(prop);
254}
255
256void
258{
259 inboundTransactions_.giveSet(txns.id(), txns.map_, false);
260}
261
264{
265 if (auto txns = inboundTransactions_.getSet(setId, true))
266 {
267 return RCLTxSet{std::move(txns)};
268 }
269 return std::nullopt;
270}
271
272bool
274{
275 return !app_.openLedger().empty();
276}
277
280{
281 return app_.getValidations().numTrustedForLedger(h);
282}
283
286 RCLCxLedger const& ledger,
287 LedgerHash const& h) const
288{
289 RCLValidations& vals = app_.getValidations();
290 return vals.getNodesAfter(
291 RCLValidatedLedger(ledger.ledger_, vals.adaptor().journal()), h);
292}
293
296 uint256 ledgerID,
297 RCLCxLedger const& ledger,
299{
300 RCLValidations& vals = app_.getValidations();
301 uint256 netLgr = vals.getPreferred(
302 RCLValidatedLedger{ledger.ledger_, vals.adaptor().journal()},
303 ledgerMaster_.getValidLedgerIndex());
304
305 if (netLgr != ledgerID)
306 {
308 app_.getOPs().consensusViewChange();
309
310 JLOG(j_.debug()) << Json::Compact(app_.getValidations().getJsonTrie());
311 }
312
313 return netLgr;
314}
315
316auto
318 RCLCxLedger const& ledger,
319 NetClock::time_point const& closeTime,
321{
322 bool const wrongLCL = mode == ConsensusMode::wrongLedger;
324
325 notify(protocol::neCLOSING_LEDGER, ledger, !wrongLCL);
326
327 auto const& prevLedger = ledger.ledger_;
328
329 ledgerMaster_.applyHeldTransactions();
330 // Tell the ledger master not to acquire the ledger we're probably building
331 ledgerMaster_.setBuildingLedger(prevLedger->info().seq + 1);
332
333 auto initialLedger = app_.openLedger().current();
334
335 auto initialSet =
337 initialSet->setUnbacked();
338
339 // Build SHAMap containing all transactions in our open ledger
340 for (auto const& tx : initialLedger->txs)
341 {
342 JLOG(j_.trace()) << "Adding open ledger TX "
343 << tx.first->getTransactionID();
344 Serializer s(2048);
345 tx.first->add(s);
346 initialSet->addItem(
348 make_shamapitem(tx.first->getTransactionID(), s.slice()));
349 }
350
351 // Add pseudo-transactions to the set
352 if (app_.config().standalone() || (proposing && !wrongLCL))
353 {
354 if (prevLedger->isFlagLedger())
355 {
356 // previous ledger was flag ledger, add fee and amendment
357 // pseudo-transactions
358 auto validations = app_.validators().negativeUNLFilter(
359 app_.getValidations().getTrustedForLedger(
360 prevLedger->info().parentHash, prevLedger->seq() - 1));
361 if (validations.size() >= app_.validators().quorum())
362 {
363 feeVote_->doVoting(prevLedger, validations, initialSet);
364 app_.getAmendmentTable().doVoting(
365 prevLedger, validations, initialSet, j_);
366 }
367 }
368 else if (
369 prevLedger->isVotingLedger() &&
370 prevLedger->rules().enabled(featureNegativeUNL))
371 {
372 // previous ledger was a voting ledger,
373 // so the current consensus session is for a flag ledger,
374 // add negative UNL pseudo-transactions
375 nUnlVote_.doVoting(
376 prevLedger,
377 app_.validators().getTrustedMasterKeys(),
378 app_.getValidations(),
379 initialSet);
380 }
381 }
382
383 // Now we need an immutable snapshot
384 initialSet = initialSet->snapShot(false);
385
386 if (!wrongLCL)
387 {
388 LedgerIndex const seq = prevLedger->info().seq + 1;
390
391 initialSet->visitLeaves(
392 [&proposed,
393 seq](boost::intrusive_ptr<SHAMapItem const> const& item) {
394 proposed.emplace_back(item->key(), seq);
395 });
396
397 censorshipDetector_.propose(std::move(proposed));
398 }
399
400 // Needed because of the move below.
401 auto const setHash = initialSet->getHash().as_uint256();
402
403 return Result{
404 std::move(initialSet),
406 initialLedger->info().parentHash,
408 setHash,
409 closeTime,
410 app_.timeKeeper().closeTime(),
411 validatorKeys_.nodeID}};
412}
413
414void
416 Result const& result,
417 RCLCxLedger const& prevLedger,
418 NetClock::duration const& closeResolution,
419 ConsensusCloseTimes const& rawCloseTimes,
420 ConsensusMode const& mode,
421 Json::Value&& consensusJson)
422{
423 doAccept(
424 result,
425 prevLedger,
426 closeResolution,
427 rawCloseTimes,
428 mode,
429 std::move(consensusJson));
430}
431
432void
434 Result const& result,
435 RCLCxLedger const& prevLedger,
436 NetClock::duration const& closeResolution,
437 ConsensusCloseTimes const& rawCloseTimes,
438 ConsensusMode const& mode,
439 Json::Value&& consensusJson,
440 bool const validating)
441{
442 app_.getJobQueue().addJob(
443 jtACCEPT,
444 "acceptLedger",
445 [=, this, cj = std::move(consensusJson)]() mutable {
446 // Note that no lock is held or acquired during this job.
447 // This is because generic Consensus guarantees that once a ledger
448 // is accepted, the consensus results and capture by reference state
449 // will not change until startRound is called (which happens via
450 // endConsensus).
451 RclConsensusLogger clog("onAccept", validating, j_);
452 this->doAccept(
453 result,
454 prevLedger,
455 closeResolution,
456 rawCloseTimes,
457 mode,
458 std::move(cj));
459 this->app_.getOPs().endConsensus(clog.ss());
460 });
461}
462
463void
465 Result const& result,
466 RCLCxLedger const& prevLedger,
467 NetClock::duration closeResolution,
468 ConsensusCloseTimes const& rawCloseTimes,
469 ConsensusMode const& mode,
470 Json::Value&& consensusJson)
471{
472 prevProposers_ = result.proposers;
473 prevRoundTime_ = result.roundTime.read();
474
475 bool closeTimeCorrect;
476
478 bool const haveCorrectLCL = mode != ConsensusMode::wrongLedger;
479 bool const consensusFail = result.state == ConsensusState::MovedOn;
480
481 auto consensusCloseTime = result.position.closeTime();
482
483 if (consensusCloseTime == NetClock::time_point{})
484 {
485 // We agreed to disagree on the close time
486 using namespace std::chrono_literals;
487 consensusCloseTime = prevLedger.closeTime() + 1s;
488 closeTimeCorrect = false;
489 }
490 else
491 {
492 // We agreed on a close time
493 consensusCloseTime = effCloseTime(
494 consensusCloseTime, closeResolution, prevLedger.closeTime());
495 closeTimeCorrect = true;
496 }
497
498 JLOG(j_.debug()) << "Report: Prop=" << (proposing ? "yes" : "no")
499 << " val=" << (validating_ ? "yes" : "no")
500 << " corLCL=" << (haveCorrectLCL ? "yes" : "no")
501 << " fail=" << (consensusFail ? "yes" : "no");
502 JLOG(j_.debug()) << "Report: Prev = " << prevLedger.id() << ":"
503 << prevLedger.seq();
504
505 //--------------------------------------------------------------------------
506 std::set<TxID> failed;
507
508 // We want to put transactions in an unpredictable but deterministic order:
509 // we use the hash of the set.
510 //
511 // FIXME: Use a std::vector and a custom sorter instead of CanonicalTXSet?
512 CanonicalTXSet retriableTxs{result.txns.map_->getHash().as_uint256()};
513
514 JLOG(j_.debug()) << "Building canonical tx set: " << retriableTxs.key();
515
516 for (auto const& item : *result.txns.map_)
517 {
518 try
519 {
520 retriableTxs.insert(
522 JLOG(j_.debug()) << " Tx: " << item.key();
523 }
524 catch (std::exception const& ex)
525 {
526 failed.insert(item.key());
527 JLOG(j_.warn())
528 << " Tx: " << item.key() << " throws: " << ex.what();
529 }
530 }
531
532 auto built = buildLCL(
533 prevLedger,
534 retriableTxs,
535 consensusCloseTime,
536 closeTimeCorrect,
537 closeResolution,
538 result.roundTime.read(),
539 failed);
540
541 auto const newLCLHash = built.id();
542 JLOG(j_.debug()) << "Built ledger #" << built.seq() << ": " << newLCLHash;
543
544 // Tell directly connected peers that we have a new LCL
545 notify(protocol::neACCEPTED_LEDGER, built, haveCorrectLCL);
546
547 // As long as we're in sync with the network, attempt to detect attempts
548 // at censorship of transaction by tracking which ones don't make it in
549 // after a period of time.
550 if (haveCorrectLCL && result.state == ConsensusState::Yes)
551 {
553
554 result.txns.map_->visitLeaves(
555 [&accepted](boost::intrusive_ptr<SHAMapItem const> const& item) {
556 accepted.push_back(item->key());
557 });
558
559 // Track all the transactions which failed or were marked as retriable
560 for (auto const& r : retriableTxs)
561 failed.insert(r.first.getTXID());
562
563 censorshipDetector_.check(
564 std::move(accepted),
565 [curr = built.seq(),
566 j = app_.journal("CensorshipDetector"),
567 &failed](uint256 const& id, LedgerIndex seq) {
568 if (failed.count(id))
569 return true;
570
571 auto const wait = curr - seq;
572
573 if (wait && (wait % censorshipWarnInternal == 0))
574 {
576 ss << "Potential Censorship: Eligible tx " << id
577 << ", which we are tracking since ledger " << seq
578 << " has not been included as of ledger " << curr << ".";
579
580 JLOG(j.warn()) << ss.str();
581 }
582
583 return false;
584 });
585 }
586
587 if (validating_)
588 validating_ = ledgerMaster_.isCompatible(
589 *built.ledger_, j_.warn(), "Not validating");
590
591 if (validating_ && !consensusFail &&
592 app_.getValidations().canValidateSeq(built.seq()))
593 {
594 validate(built, result.txns, proposing);
595 JLOG(j_.info()) << "CNF Val " << newLCLHash;
596 }
597 else
598 JLOG(j_.info()) << "CNF buildLCL " << newLCLHash;
599
600 // See if we can accept a ledger as fully-validated
601 ledgerMaster_.consensusBuilt(
602 built.ledger_, result.txns.id(), std::move(consensusJson));
603
604 //-------------------------------------------------------------------------
605 {
606 // Apply disputed transactions that didn't get in
607 //
608 // The first crack of transactions to get into the new
609 // open ledger goes to transactions proposed by a validator
610 // we trust but not included in the consensus set.
611 //
612 // These are done first because they are the most likely
613 // to receive agreement during consensus. They are also
614 // ordered logically "sooner" than transactions not mentioned
615 // in the previous consensus round.
616 //
617 bool anyDisputes = false;
618 for (auto const& [_, dispute] : result.disputes)
619 {
620 (void)_;
621 if (!dispute.getOurVote())
622 {
623 // we voted NO
624 try
625 {
626 JLOG(j_.debug())
627 << "Test applying disputed transaction that did"
628 << " not get in " << dispute.tx().id();
629
630 SerialIter sit(dispute.tx().tx_->slice());
631 auto txn = std::make_shared<STTx const>(sit);
632
633 // Disputed pseudo-transactions that were not accepted
634 // can't be successfully applied in the next ledger
635 if (isPseudoTx(*txn))
636 continue;
637
638 retriableTxs.insert(txn);
639
640 anyDisputes = true;
641 }
642 catch (std::exception const& ex)
643 {
644 JLOG(j_.debug()) << "Failed to apply transaction we voted "
645 "NO on. Exception: "
646 << ex.what();
647 }
648 }
649 }
650
651 // Build new open ledger
652 std::unique_lock lock{app_.getMasterMutex(), std::defer_lock};
653 std::unique_lock sl{ledgerMaster_.peekMutex(), std::defer_lock};
654 std::lock(lock, sl);
655
656 auto const lastVal = ledgerMaster_.getValidatedLedger();
658 if (lastVal)
659 rules = makeRulesGivenLedger(*lastVal, app_.config().features);
660 else
661 rules.emplace(app_.config().features);
662 app_.openLedger().accept(
663 app_,
664 *rules,
665 built.ledger_,
666 localTxs_.getTxSet(),
667 anyDisputes,
668 retriableTxs,
669 tapNONE,
670 "consensus",
671 [&](OpenView& view, beast::Journal j) {
672 // Stuff the ledger with transactions from the queue.
673 return app_.getTxQ().accept(app_, view);
674 });
675
676 // Signal a potential fee change to subscribers after the open ledger
677 // is created
678 app_.getOPs().reportFeeChange();
679 }
680
681 //-------------------------------------------------------------------------
682 {
683 ledgerMaster_.switchLCL(built.ledger_);
684
685 // Do these need to exist?
686 XRPL_ASSERT(
687 ledgerMaster_.getClosedLedger()->info().hash == built.id(),
688 "ripple::RCLConsensus::Adaptor::doAccept : ledger hash match");
689 XRPL_ASSERT(
690 app_.openLedger().current()->info().parentHash == built.id(),
691 "ripple::RCLConsensus::Adaptor::doAccept : parent hash match");
692 }
693
694 //-------------------------------------------------------------------------
695 // we entered the round with the network,
696 // see how close our close time is to other node's
697 // close time reports, and update our clock.
700 !consensusFail)
701 {
702 auto closeTime = rawCloseTimes.self;
703
704 JLOG(j_.info()) << "We closed at "
705 << closeTime.time_since_epoch().count();
707 usec64_t closeTotal =
708 std::chrono::duration_cast<usec64_t>(closeTime.time_since_epoch());
709 int closeCount = 1;
710
711 for (auto const& [t, v] : rawCloseTimes.peers)
712 {
713 JLOG(j_.info()) << std::to_string(v) << " time votes for "
714 << std::to_string(t.time_since_epoch().count());
715 closeCount += v;
716 closeTotal +=
717 std::chrono::duration_cast<usec64_t>(t.time_since_epoch()) * v;
718 }
719
720 closeTotal += usec64_t(closeCount / 2); // for round to nearest
721 closeTotal /= closeCount;
722
723 // Use signed times since we are subtracting
726 auto offset = time_point{closeTotal} -
727 std::chrono::time_point_cast<duration>(closeTime);
728 JLOG(j_.info()) << "Our close offset is estimated at " << offset.count()
729 << " (" << closeCount << ")";
730
731 app_.timeKeeper().adjustCloseTime(offset);
732 }
733}
734
735void
737 protocol::NodeEvent ne,
738 RCLCxLedger const& ledger,
739 bool haveCorrectLCL)
740{
741 protocol::TMStatusChange s;
742
743 if (!haveCorrectLCL)
744 s.set_newevent(protocol::neLOST_SYNC);
745 else
746 s.set_newevent(ne);
747
748 s.set_ledgerseq(ledger.seq());
749 s.set_networktime(app_.timeKeeper().now().time_since_epoch().count());
750 s.set_ledgerhashprevious(
751 ledger.parentID().begin(),
752 std::decay_t<decltype(ledger.parentID())>::bytes);
753 s.set_ledgerhash(
754 ledger.id().begin(), std::decay_t<decltype(ledger.id())>::bytes);
755
756 std::uint32_t uMin, uMax;
757 if (!ledgerMaster_.getFullValidatedRange(uMin, uMax))
758 {
759 uMin = 0;
760 uMax = 0;
761 }
762 else
763 {
764 // Don't advertise ledgers we're not willing to serve
765 uMin = std::max(uMin, ledgerMaster_.getEarliestFetch());
766 }
767 s.set_firstseq(uMin);
768 s.set_lastseq(uMax);
769 app_.overlay().foreach(
770 send_always(std::make_shared<Message>(s, protocol::mtSTATUS_CHANGE)));
771 JLOG(j_.trace()) << "send status change to peer";
772}
773
776 RCLCxLedger const& previousLedger,
777 CanonicalTXSet& retriableTxs,
778 NetClock::time_point closeTime,
779 bool closeTimeCorrect,
780 NetClock::duration closeResolution,
782 std::set<TxID>& failedTxs)
783{
784 std::shared_ptr<Ledger> built = [&]() {
785 if (auto const replayData = ledgerMaster_.releaseReplay())
786 {
787 XRPL_ASSERT(
788 replayData->parent()->info().hash == previousLedger.id(),
789 "ripple::RCLConsensus::Adaptor::buildLCL : parent hash match");
790 return buildLedger(*replayData, tapNONE, app_, j_);
791 }
792 return buildLedger(
793 previousLedger.ledger_,
794 closeTime,
795 closeTimeCorrect,
796 closeResolution,
797 app_,
798 retriableTxs,
799 failedTxs,
800 j_);
801 }();
802
803 // Update fee computations based on accepted txs
804 using namespace std::chrono_literals;
805 app_.getTxQ().processClosedLedger(app_, *built, roundTime > 5s);
806
807 // And stash the ledger in the ledger master
808 if (ledgerMaster_.storeLedger(built))
809 JLOG(j_.debug()) << "Consensus built ledger we already had";
810 else if (app_.getInboundLedgers().find(built->info().hash))
811 JLOG(j_.debug()) << "Consensus built ledger we were acquiring";
812 else
813 JLOG(j_.debug()) << "Consensus built new ledger";
814 return RCLCxLedger{std::move(built)};
815}
816
817void
819 RCLCxLedger const& ledger,
820 RCLTxSet const& txns,
821 bool proposing)
822{
823 using namespace std::chrono_literals;
824
825 auto validationTime = app_.timeKeeper().closeTime();
826 if (validationTime <= lastValidationTime_)
827 validationTime = lastValidationTime_ + 1s;
828 lastValidationTime_ = validationTime;
829
830 if (!validatorKeys_.keys)
831 {
832 JLOG(j_.warn()) << "RCLConsensus::Adaptor::validate: ValidatorKeys "
833 "not set\n";
834 return;
835 }
836
837 auto const& keys = *validatorKeys_.keys;
838
840 lastValidationTime_,
841 keys.publicKey,
842 keys.secretKey,
843 validatorKeys_.nodeID,
844 [&](STValidation& v) {
845 v.setFieldH256(sfLedgerHash, ledger.id());
846 v.setFieldH256(sfConsensusHash, txns.id());
847
848 v.setFieldU32(sfLedgerSequence, ledger.seq());
849
850 if (proposing)
851 v.setFlag(vfFullValidation);
852
853 if (ledger.ledger_->rules().enabled(featureHardenedValidations))
854 {
855 // Attest to the hash of what we consider to be the last fully
856 // validated ledger. This may be the hash of the ledger we are
857 // validating here, and that's fine.
858 if (auto const vl = ledgerMaster_.getValidatedLedger())
859 v.setFieldH256(sfValidatedHash, vl->info().hash);
860
861 v.setFieldU64(sfCookie, valCookie_);
862
863 // Report our server version every flag ledger:
864 if (ledger.ledger_->isVotingLedger())
865 v.setFieldU64(
866 sfServerVersion, BuildInfo::getEncodedVersion());
867 }
868
869 // Report our load
870 {
871 auto const& ft = app_.getFeeTrack();
872 auto const fee = std::max(ft.getLocalFee(), ft.getClusterFee());
873 if (fee > ft.getLoadBase())
874 v.setFieldU32(sfLoadFee, fee);
875 }
876
877 // If the next ledger is a flag ledger, suggest fee changes and
878 // new features:
879 if (ledger.ledger_->isVotingLedger())
880 {
881 // Fees:
882 feeVote_->doValidation(
883 ledger.ledger_->fees(), ledger.ledger_->rules(), v);
884
885 // Amendments
886 // FIXME: pass `v` and have the function insert the array
887 // directly?
888 auto const amendments = app_.getAmendmentTable().doValidation(
889 getEnabledAmendments(*ledger.ledger_));
890
891 if (!amendments.empty())
892 v.setFieldV256(
893 sfAmendments, STVector256(sfAmendments, amendments));
894 }
895 });
896
897 auto const serialized = v->getSerialized();
898
899 // suppress it if we receive it
900 app_.getHashRouter().addSuppression(sha512Half(makeSlice(serialized)));
901
902 handleNewValidation(app_, v, "local");
903
904 // Broadcast to all our peers:
905 protocol::TMValidation val;
906 val.set_validation(serialized.data(), serialized.size());
907 app_.overlay().broadcast(val);
908
909 // Publish to all our subscribers:
910 app_.getOPs().pubValidation(v);
911}
912
913void
915{
916 JLOG(j_.info()) << "Consensus mode change before=" << to_string(before)
917 << ", after=" << to_string(after);
918
919 // If we were proposing but aren't any longer, we need to reset the
920 // censorship tracking to avoid bogus warnings.
921 if ((before == ConsensusMode::proposing ||
922 before == ConsensusMode::observing) &&
923 before != after)
924 censorshipDetector_.reset();
925
926 mode_ = after;
927}
928
930RCLConsensus::getJson(bool full) const
931{
932 Json::Value ret;
933 {
935 ret = consensus_.getJson(full);
936 }
937 ret["validating"] = adaptor_.validating();
938 return ret;
939}
940
941void
943 NetClock::time_point const& now,
945{
946 try
947 {
949 consensus_.timerEntry(now, clog);
950 }
951 catch (SHAMapMissingNode const& mn)
952 {
953 // This should never happen
955 ss << "During consensus timerEntry: " << mn.what();
956 JLOG(j_.error()) << ss.str();
957 CLOG(clog) << ss.str();
958 Rethrow();
959 }
960}
961
962void
964{
965 try
966 {
968 consensus_.gotTxSet(now, txSet);
969 }
970 catch (SHAMapMissingNode const& mn)
971 {
972 // This should never happen
973 JLOG(j_.error()) << "During consensus gotTxSet: " << mn.what();
974 Rethrow();
975 }
976}
977
979
980void
982 NetClock::time_point const& now,
984{
986 consensus_.simulate(now, consensusDelay);
987}
988
989bool
991 NetClock::time_point const& now,
992 RCLCxPeerPos const& newProposal)
993{
995 return consensus_.peerProposal(now, newProposal);
996}
997
998bool
1000 RCLCxLedger const& prevLgr,
1001 hash_set<NodeID> const& nowTrusted)
1002{
1003 // We have a key, we do not want out of sync validations after a restart
1004 // and are not amendment blocked.
1005 validating_ = validatorKeys_.keys &&
1006 prevLgr.seq() >= app_.getMaxDisallowedLedger() &&
1007 !app_.getOPs().isBlocked();
1008
1009 // If we are not running in standalone mode and there's a configured UNL,
1010 // check to make sure that it's not expired.
1011 if (validating_ && !app_.config().standalone() && app_.validators().count())
1012 {
1013 auto const when = app_.validators().expires();
1014
1015 if (!when || *when < app_.timeKeeper().now())
1016 {
1017 JLOG(j_.error()) << "Voluntarily bowing out of consensus process "
1018 "because of an expired validator list.";
1019 validating_ = false;
1020 }
1021 }
1022
1023 bool const synced = app_.getOPs().getOperatingMode() == OperatingMode::FULL;
1024
1025 if (validating_)
1026 {
1027 JLOG(j_.info()) << "Entering consensus process, validating, synced="
1028 << (synced ? "yes" : "no");
1029 }
1030 else
1031 {
1032 // Otherwise we just want to monitor the validation process.
1033 JLOG(j_.info()) << "Entering consensus process, watching, synced="
1034 << (synced ? "yes" : "no");
1035 }
1036
1037 // Notify inbound ledgers that we are starting a new round
1038 inboundTransactions_.newRound(prevLgr.seq());
1039
1040 // Notify NegativeUNLVote that new validators are added
1041 if (prevLgr.ledger_->rules().enabled(featureNegativeUNL) &&
1042 !nowTrusted.empty())
1043 nUnlVote_.newValidators(prevLgr.seq() + 1, nowTrusted);
1044
1045 // propose only if we're in sync with the network (and validating)
1046 return validating_ && synced;
1047}
1048
1049bool
1051{
1052 return ledgerMaster_.haveValidated();
1053}
1054
1057{
1058 return ledgerMaster_.getValidLedgerIndex();
1059}
1060
1063{
1064 return app_.validators().getQuorumKeys();
1065}
1066
1069 Ledger_t::Seq const seq,
1071{
1072 return app_.getValidations().laggards(seq, trustedKeys);
1073}
1074
1075bool
1077{
1078 return validatorKeys_.keys.has_value();
1079}
1080
1081void
1083{
1084 if (!positions && app_.getOPs().isFull())
1085 app_.getOPs().setMode(OperatingMode::CONNECTED);
1086}
1087
1088void
1090 NetClock::time_point const& now,
1091 RCLCxLedger::ID const& prevLgrId,
1092 RCLCxLedger const& prevLgr,
1093 hash_set<NodeID> const& nowUntrusted,
1094 hash_set<NodeID> const& nowTrusted,
1096{
1098 consensus_.startRound(
1099 now,
1100 prevLgrId,
1101 prevLgr,
1102 nowUntrusted,
1103 adaptor_.preStartRound(prevLgr, nowTrusted),
1104 clog);
1105}
1106
1108 char const* label,
1109 bool const validating,
1111 : j_(j)
1112{
1113 if (!validating && !j.info())
1114 return;
1117 header_ = "ConsensusLogger ";
1118 header_ += label;
1119 header_ += ": ";
1120}
1121
1123{
1124 if (!ss_)
1125 return;
1126 auto const duration = std::chrono::duration_cast<std::chrono::milliseconds>(
1128 std::stringstream outSs;
1129 outSs << header_ << "duration " << (duration.count() / 1000) << '.'
1130 << std::setw(3) << std::setfill('0') << (duration.count() % 1000)
1131 << "s. " << ss_->str();
1133}
1134
1135} // namespace ripple
Decorator for streaming out compact json.
Represents a JSON value.
Definition json_value.h:149
virtual void writeAlways(Severity level, std::string const &text)=0
Bypass filter and write text to the sink at the specified severity.
A generic endpoint for log messages.
Definition Journal.h:60
Stream error() const
Definition Journal.h:346
Stream debug() const
Definition Journal.h:328
Sink & sink() const
Returns the Sink associated with this Journal.
Definition Journal.h:297
Stream info() const
Definition Journal.h:334
Stream trace() const
Severity stream access functions.
Definition Journal.h:322
Stream warn() const
Definition Journal.h:340
Holds transactions which were deferred to the next pass of consensus.
NetClock::time_point const & closeTime() const
The current position on the consensus close time.
std::chrono::milliseconds read() const
Manages the acquisition and lifetime of transaction sets.
Writable ledger view that accumulates state and tx changes.
Definition OpenView.h:66
Slice slice() const noexcept
Definition PublicKey.h:122
void propose(RCLCxPeerPos::Proposal const &proposal)
Propose the given position to my peers.
ValidatorKeys const & validatorKeys_
beast::Journal const j_
std::atomic< ConsensusMode > mode_
Result onClose(RCLCxLedger const &ledger, NetClock::time_point const &closeTime, ConsensusMode mode)
Close the open ledger and return initial consensus position.
void share(RCLCxPeerPos const &peerPos)
Share the given proposal with all peers.
void doAccept(Result const &result, RCLCxLedger const &prevLedger, NetClock::duration closeResolution, ConsensusCloseTimes const &rawCloseTimes, ConsensusMode const &mode, Json::Value &&consensusJson)
Accept a new ledger based on the given transactions.
Adaptor(Application &app, std::unique_ptr< FeeVote > &&feeVote, LedgerMaster &ledgerMaster, LocalTxs &localTxs, InboundTransactions &inboundTransactions, ValidatorKeys const &validatorKeys, beast::Journal journal)
void onModeChange(ConsensusMode before, ConsensusMode after)
Notified of change in consensus mode.
std::size_t laggards(Ledger_t::Seq const seq, hash_set< NodeKey_t > &trustedKeys) const
RCLCensorshipDetector< TxID, LedgerIndex > censorshipDetector_
void onForceAccept(Result const &result, RCLCxLedger const &prevLedger, NetClock::duration const &closeResolution, ConsensusCloseTimes const &rawCloseTimes, ConsensusMode const &mode, Json::Value &&consensusJson)
Process the accepted ledger that was a result of simulation/force accept.
bool validator() const
Whether I am a validator.
RCLCxLedger buildLCL(RCLCxLedger const &previousLedger, CanonicalTXSet &retriableTxs, NetClock::time_point closeTime, bool closeTimeCorrect, NetClock::duration closeResolution, std::chrono::milliseconds roundTime, std::set< TxID > &failedTxs)
Build the new last closed ledger.
std::optional< RCLCxLedger > acquireLedger(LedgerHash const &hash)
Attempt to acquire a specific ledger.
LedgerIndex getValidLedgerIndex() const
std::size_t proposersFinished(RCLCxLedger const &ledger, LedgerHash const &h) const
Number of proposers that have validated a ledger descended from requested ledger.
std::optional< RCLTxSet > acquireTxSet(RCLTxSet::ID const &setId)
Acquire the transaction set associated with a proposal.
bool hasOpenTransactions() const
Whether the open ledger has any transactions.
void onAccept(Result const &result, RCLCxLedger const &prevLedger, NetClock::duration const &closeResolution, ConsensusCloseTimes const &rawCloseTimes, ConsensusMode const &mode, Json::Value &&consensusJson, bool const validating)
Process the accepted ledger.
void validate(RCLCxLedger const &ledger, RCLTxSet const &txns, bool proposing)
Validate the given ledger and share with peers as necessary.
std::uint64_t const valCookie_
bool preStartRound(RCLCxLedger const &prevLedger, hash_set< NodeID > const &nowTrusted)
Called before kicking off a new consensus round.
uint256 getPrevLedger(uint256 ledgerID, RCLCxLedger const &ledger, ConsensusMode mode)
Get the ID of the previous ledger/last closed ledger(LCL) on the network.
std::size_t proposersValidated(LedgerHash const &h) const
Number of proposers that have validated the given ledger.
void notify(protocol::NodeEvent ne, RCLCxLedger const &ledger, bool haveCorrectLCL)
Notify peers of a consensus state change.
void updateOperatingMode(std::size_t const positions) const
Update operating mode based on current peer positions.
std::pair< std::size_t, hash_set< NodeKey_t > > getQuorumKeys() const
void gotTxSet(NetClock::time_point const &now, RCLTxSet const &txSet)
void simulate(NetClock::time_point const &now, std::optional< std::chrono::milliseconds > consensusDelay)
bool validating() const
Whether we are validating consensus ledgers.
RCLConsensus(Application &app, std::unique_ptr< FeeVote > &&feeVote, LedgerMaster &ledgerMaster, LocalTxs &localTxs, InboundTransactions &inboundTransactions, Consensus< Adaptor >::clock_type const &clock, ValidatorKeys const &validatorKeys, beast::Journal journal)
Constructor.
ConsensusMode mode() const
static constexpr unsigned int censorshipWarnInternal
Warn for transactions that haven't been included every so many ledgers.
std::recursive_mutex mutex_
Consensus< Adaptor > consensus_
bool peerProposal(NetClock::time_point const &now, RCLCxPeerPos const &newProposal)
void startRound(NetClock::time_point const &now, RCLCxLedger::ID const &prevLgrId, RCLCxLedger const &prevLgr, hash_set< NodeID > const &nowUntrusted, hash_set< NodeID > const &nowTrusted, std::unique_ptr< std::stringstream > const &clog)
Adjust the set of trusted validators and kick-off the next round of consensus.
void timerEntry(NetClock::time_point const &now, std::unique_ptr< std::stringstream > const &clog={})
Json::Value getJson(bool full) const
beast::Journal const j_
Represents a ledger in RCLConsensus.
Definition RCLCxLedger.h:36
Seq const & seq() const
Sequence number of the ledger.
Definition RCLCxLedger.h:61
ID const & id() const
Unique identifier (hash) of this ledger.
Definition RCLCxLedger.h:68
std::shared_ptr< Ledger const > ledger_
The ledger instance.
NetClock::time_point closeTime() const
The close time of this ledger.
Definition RCLCxLedger.h:96
ID const & parentID() const
Unique identifier (hash) of this ledger's parent.
Definition RCLCxLedger.h:75
A peer's signed, proposed position for use in RCLConsensus.
Proposal const & proposal() const
PublicKey const & publicKey() const
Public key of peer that sent the proposal.
Slice signature() const
Signature of the proposal (not necessarily verified)
uint256 const & suppressionID() const
Unique id used by hash router to suppress duplicates.
Represents a transaction in RCLConsensus.
Definition RCLCxTx.h:33
ID const & id() const
The unique identifier/hash of the transaction.
Definition RCLCxTx.h:48
boost::intrusive_ptr< SHAMapItem const > tx_
The SHAMapItem that represents the transaction.
Definition RCLCxTx.h:54
Represents a set of transactions in RCLConsensus.
Definition RCLCxTx.h:63
ID id() const
The unique ID/hash of the transaction set.
Definition RCLCxTx.h:153
std::shared_ptr< SHAMap > map_
The SHAMap representing the transactions.
Definition RCLCxTx.h:188
Wraps a ledger instance for use in generic Validations LedgerTrie.
beast::Journal journal() const
Collects logging information.
std::unique_ptr< std::stringstream > const & ss()
RclConsensusLogger(char const *label, bool validating, beast::Journal j)
std::chrono::steady_clock::time_point start_
std::unique_ptr< std::stringstream > ss_
Slice slice() const noexcept
Definition Serializer.h:66
std::optional< std::pair< Seq, ID > > getPreferred(Ledger const &curr)
Return the sequence number and ID of the preferred working ledger.
Adaptor const & adaptor() const
Return the adaptor instance.
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.
Validator keys and manifest as set in configuration file.
std::optional< Keys > keys
std::uint32_t sequence
iterator begin()
Definition base_uint.h:136
T count(T... args)
T emplace_back(T... args)
T emplace(T... args)
T empty(T... args)
T insert(T... args)
T is_same_v
T lock(T... args)
T max(T... args)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:25
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
uint256 proposalUniqueId(uint256 const &proposeHash, uint256 const &previousLedger, std::uint32_t proposeSeq, NetClock::time_point closeTime, Slice const &publicKey, Slice const &signature)
Calculate a unique identifier for a signed proposal.
std::enable_if_t< std::is_integral< Integral >::value, Integral > rand_int()
std::chrono::time_point< Clock, Duration > effCloseTime(std::chrono::time_point< Clock, Duration > closeTime, std::chrono::duration< Rep, Period > resolution, std::chrono::time_point< Clock, Duration > priorCloseTime)
Calculate the effective ledger close time.
bool isPseudoTx(STObject const &tx)
Check whether a transaction is a pseudo-transaction.
Definition STTx.cpp:820
ConsensusMode
Represents how a node currently participates in Consensus.
@ wrongLedger
We have the wrong ledger and are attempting to acquire it.
@ proposing
We are normal participant in consensus and propose our position.
@ observing
We are observing peer positions, but not proposing our position.
void handleNewValidation(Application &app, std::shared_ptr< STValidation > const &val, std::string const &source, BypassAccept const bypassAccept, std::optional< beast::Journal > j)
Handle a new validation.
@ CONNECTED
convinced we are talking to the network
@ FULL
we have the ledger and can even validate
csprng_engine & crypto_prng()
The default cryptographically secure PRNG.
@ MovedOn
The network has consensus without us.
@ Yes
We have consensus along with the network.
@ accepted
Manifest is valid.
std::enable_if_t< std::is_same< T, char >::value||std::is_same< T, unsigned char >::value, Slice > makeSlice(std::array< T, N > const &a)
Definition Slice.h:244
std::shared_ptr< Ledger > buildLedger(std::shared_ptr< Ledger const > const &parent, NetClock::time_point closeTime, bool const closeTimeCorrect, NetClock::duration closeResolution, Application &app, CanonicalTXSet &txns, std::set< TxID > &failedTxs, beast::Journal j)
Build a new ledger by applying consensus transactions.
Buffer signDigest(PublicKey const &pk, SecretKey const &sk, uint256 const &digest)
Generate a signature for a message digest.
boost::intrusive_ptr< SHAMapItem > make_shamapitem(uint256 const &tag, Slice data)
Definition SHAMapItem.h:161
Rules makeRulesGivenLedger(DigestAwareReadView const &ledger, Rules const &current)
Definition ReadView.cpp:69
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:630
bool after(NetClock::time_point now, std::uint32_t mark)
Has the specified time passed?
Definition View.cpp:3179
@ tapNONE
Definition ApplyView.h:32
@ ledgerMaster
ledger master data for signing
@ proposal
proposal for signing
void Rethrow()
Rethrow the exception currently being handled.
Definition contract.h:48
@ jtACCEPT
Definition Job.h:73
@ jtADVANCE
Definition Job.h:67
sha512_half_hasher::result_type sha512Half(Args const &... args)
Returns the SHA512-Half of a series of objects.
Definition digest.h:224
STL namespace.
T setfill(T... args)
T setw(T... args)
T str(T... args)
Stores the set of initial close times.
std::map< NetClock::time_point, int > peers
Close time estimates, keep ordered for predictable traverse.
NetClock::time_point self
Our close time estimate.
Encapsulates the result of consensus.
hash_map< typename Tx_t::ID, Dispute_t > disputes
Transactions which are under dispute with our peers.
TxSet_t txns
The set of transactions consensus agrees go in the ledger.
Proposal_t position
Our proposed position on transactions/close time.
Sends a message to all peers.
Definition predicates.h:32
T to_string(T... args)
T what(T... args)