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