20 #include <ripple/app/consensus/RCLConsensus.h>
21 #include <ripple/app/consensus/RCLValidations.h>
22 #include <ripple/app/ledger/BuildLedger.h>
23 #include <ripple/app/ledger/InboundLedgers.h>
24 #include <ripple/app/ledger/InboundTransactions.h>
25 #include <ripple/app/ledger/LedgerMaster.h>
26 #include <ripple/app/ledger/LocalTxs.h>
27 #include <ripple/app/ledger/OpenLedger.h>
28 #include <ripple/app/misc/AmendmentTable.h>
29 #include <ripple/app/misc/HashRouter.h>
30 #include <ripple/app/misc/LoadFeeTrack.h>
31 #include <ripple/app/misc/NetworkOPs.h>
32 #include <ripple/app/misc/TxQ.h>
33 #include <ripple/app/misc/ValidatorKeys.h>
34 #include <ripple/app/misc/ValidatorList.h>
35 #include <ripple/basics/random.h>
36 #include <ripple/beast/core/LexicalCast.h>
37 #include <ripple/consensus/LedgerTiming.h>
38 #include <ripple/nodestore/DatabaseShard.h>
39 #include <ripple/overlay/Overlay.h>
40 #include <ripple/overlay/predicates.h>
41 #include <ripple/protocol/BuildInfo.h>
42 #include <ripple/protocol/Feature.h>
43 #include <ripple/protocol/digest.h>
67 , consensus_(clock, adaptor_, journal)
82 , feeVote_(
std::move(feeVote))
85 , inboundTransactions_{inboundTransactions}
87 , nodeID_{validatorKeys.nodeID}
88 , valPublic_{validatorKeys.publicKey}
89 , valSecret_{validatorKeys.secretKey}
93 assert(valCookie_ != 0);
95 JLOG(
j_.
info()) <<
"Consensus engine started"
97 <<
", Cookie: " << valCookie_ <<
")";
100 boost::optional<RCLCxLedger>
104 auto built = ledgerMaster_.getLedgerByHash(hash);
107 if (acquiringLedger_ != hash)
110 JLOG(
j_.
warn()) <<
"Need consensus ledger " << hash;
113 acquiringLedger_ = hash;
115 app_.getJobQueue().addJob(
117 "getConsensusLedger",
118 [
id = hash, &app = app_](
Job&) {
119 app.getInboundLedgers().acquire(
126 assert(!built->open() && built->isImmutable());
127 assert(built->info().hash == hash);
130 inboundTransactions_.newRound(built->info().seq);
138 protocol::TMProposeSet prop;
142 prop.set_proposeseq(
proposal.proposeSeq());
143 prop.set_closetime(
proposal.closeTime().time_since_epoch().count());
145 prop.set_currenttxhash(
147 prop.set_previousledger(
151 prop.set_nodepubkey(pk.data(), pk.size());
154 prop.set_signature(sig.data(), sig.size());
163 if (app_.getHashRouter().shouldRelay(tx.
id()))
165 JLOG(
j_.
debug()) <<
"Relaying disputed tx " << tx.
id();
167 protocol::TMTransaction msg;
168 msg.set_rawtransaction(slice.data(), slice.size());
169 msg.set_status(protocol::tsNEW);
170 msg.set_receivetimestamp(
171 app_.timeKeeper().now().time_since_epoch().count());
173 std::make_shared<Message>(msg, protocol::mtTRANSACTION)));
177 JLOG(
j_.
debug()) <<
"Not relaying disputed tx " << tx.
id();
183 JLOG(
j_.
trace()) <<
"We propose: "
188 protocol::TMProposeSet prop;
190 prop.set_currenttxhash(
192 prop.set_previousledger(
194 prop.set_proposeseq(
proposal.proposeSeq());
195 prop.set_closetime(
proposal.closeTime().time_since_epoch().count());
197 prop.set_nodepubkey(valPublic_.data(), valPublic_.size());
202 proposal.closeTime().time_since_epoch().count(),
206 auto sig =
signDigest(valPublic_, valSecret_, signingHash);
208 prop.set_signature(sig.data(), sig.size());
218 app_.getHashRouter().addSuppression(suppression);
220 app_.overlay().send(prop);
226 inboundTransactions_.giveSet(txns.
id(), txns.
map_,
false);
229 boost::optional<RCLTxSet>
232 if (
auto txns = inboundTransactions_.getSet(setId,
true))
242 return !app_.openLedger().empty();
248 return app_.getValidations().numTrustedForLedger(h);
270 ledgerMaster_.getValidLedgerIndex());
272 if (netLgr != ledgerID)
275 app_.getOPs().consensusViewChange();
292 notify(protocol::neCLOSING_LEDGER, ledger, !wrongLCL);
294 auto const& prevLedger = ledger.ledger_;
296 ledgerMaster_.applyHeldTransactions();
298 ledgerMaster_.setBuildingLedger(prevLedger->info().seq + 1);
300 auto initialLedger = app_.openLedger().current();
304 initialSet->setUnbacked();
307 for (
auto const& tx : initialLedger->txs)
309 JLOG(
j_.
trace()) <<
"Adding open ledger TX "
310 << tx.first->getTransactionID();
314 SHAMapItem(tx.first->getTransactionID(), std::move(s)),
320 if ((app_.config().standalone() || (
proposing && !wrongLCL)) &&
321 ((prevLedger->info().seq % 256) == 0))
324 auto const validations = app_.getValidations().getTrustedForLedger(
325 prevLedger->info().parentHash);
327 if (validations.size() >= app_.validators().quorum())
329 feeVote_->doVoting(prevLedger, validations, initialSet);
330 app_.getAmendmentTable().doVoting(
331 prevLedger, validations, initialSet);
336 initialSet = initialSet->snapShot(
false);
340 LedgerIndex const seq = prevLedger->info().seq + 1;
343 initialSet->visitLeaves(
348 censorshipDetector_.propose(std::move(proposed));
352 auto const setHash = initialSet->getHash().as_uint256();
355 std::move(initialSet),
357 initialLedger->info().parentHash,
361 app_.timeKeeper().closeTime(),
380 std::move(consensusJson));
392 app_.getJobQueue().addJob(
395 [=, cj = std::move(consensusJson)](
auto&)
mutable {
408 this->app_.getOPs().endConsensus();
424 bool closeTimeCorrect;
435 using namespace std::chrono_literals;
436 consensusCloseTime = prevLedger.
closeTime() + 1s;
437 closeTimeCorrect =
false;
443 consensusCloseTime, closeResolution, prevLedger.
closeTime());
444 closeTimeCorrect =
true;
448 <<
" val=" << (validating_ ?
"yes" :
"no")
449 <<
" corLCL=" << (haveCorrectLCL ?
"yes" :
"no")
450 <<
" fail=" << (consensusFail ?
"yes" :
"no");
451 JLOG(
j_.
debug()) <<
"Report: Prev = " << prevLedger.
id() <<
":"
463 JLOG(
j_.
debug()) <<
"Building canonical tx set: " << retriableTxs.key();
465 for (
auto const& item : *result.
txns.map_)
470 std::make_shared<STTx const>(
SerialIter{item.slice()}));
471 JLOG(
j_.
debug()) <<
" Tx: " << item.key();
475 failed.
insert(item.key());
476 JLOG(
j_.
warn()) <<
" Tx: " << item.key() <<
" throws!";
480 auto built = buildLCL(
489 auto const newLCLHash = built.id();
490 JLOG(
j_.
debug()) <<
"Built ledger #" << built.seq() <<
": " << newLCLHash;
493 notify(protocol::neACCEPTED_LEDGER, built, haveCorrectLCL);
502 result.
txns.map_->visitLeaves(
508 for (
auto const& r : retriableTxs)
509 failed.
insert(r.first.getTXID());
511 censorshipDetector_.check(
514 j = app_.journal(
"CensorshipDetector"),
516 if (failed.count(id))
519 auto const wait = curr - seq;
521 if (wait && (wait % censorshipWarnInternal == 0))
523 std::ostringstream ss;
524 ss <<
"Potential Censorship: Eligible tx " << id
525 <<
", which we are tracking since ledger " << seq
526 <<
" has not been included as of ledger " << curr <<
".";
528 JLOG(j.warn()) << ss.str();
536 validating_ = ledgerMaster_.isCompatible(
537 *built.ledger_,
j_.
warn(),
"Not validating");
539 if (validating_ && !consensusFail &&
540 app_.getValidations().canValidateSeq(built.seq()))
542 validate(built, result.txns, proposing);
543 JLOG(
j_.
info()) <<
"CNF Val " << newLCLHash;
546 JLOG(
j_.
info()) <<
"CNF buildLCL " << newLCLHash;
549 ledgerMaster_.consensusBuilt(
550 built.ledger_, result.txns.id(), std::move(consensusJson));
565 bool anyDisputes =
false;
566 for (
auto const& [_, dispute] : result.disputes)
569 if (!dispute.getOurVote())
575 <<
"Test applying disputed transaction that did"
576 <<
" not get in " << dispute.tx().id();
579 auto txn = std::make_shared<STTx const>(sit);
586 retriableTxs.insert(txn);
593 <<
"Failed to apply transaction we voted NO on";
603 auto const lastVal = ledgerMaster_.getValidatedLedger();
604 boost::optional<Rules> rules;
606 rules.emplace(*lastVal, app_.config().features);
608 rules.emplace(app_.config().features);
609 app_.openLedger().accept(
613 localTxs_.getTxSet(),
620 return app_.getTxQ().accept(app_, view);
625 app_.getOPs().reportFeeChange();
630 ledgerMaster_.switchLCL(built.ledger_);
633 assert(ledgerMaster_.getClosedLedger()->info().hash == built.id());
634 assert(app_.openLedger().current()->info().parentHash == built.id());
645 auto closeTime = rawCloseTimes.self;
647 JLOG(
j_.
info()) <<
"We closed at "
648 << closeTime.time_since_epoch().count();
650 usec64_t closeTotal =
651 std::chrono::duration_cast<usec64_t>(closeTime.time_since_epoch());
654 for (
auto const& [t, v] : rawCloseTimes.peers)
660 std::chrono::duration_cast<usec64_t>(t.time_since_epoch()) * v;
663 closeTotal += usec64_t(closeCount / 2);
664 closeTotal /= closeCount;
669 auto offset = time_point{closeTotal} -
670 std::chrono::time_point_cast<duration>(closeTime);
671 JLOG(
j_.
info()) <<
"Our close offset is estimated at " << offset.count()
672 <<
" (" << closeCount <<
")";
674 app_.timeKeeper().adjustCloseTime(offset);
680 protocol::NodeEvent ne,
684 protocol::TMStatusChange s;
687 s.set_newevent(protocol::neLOST_SYNC);
691 s.set_ledgerseq(ledger.
seq());
692 s.set_networktime(app_.timeKeeper().now().time_since_epoch().count());
693 s.set_ledgerhashprevious(
700 if (!ledgerMaster_.getFullValidatedRange(uMin, uMax))
708 uMin =
std::max(uMin, ledgerMaster_.getEarliestFetch());
710 s.set_firstseq(uMin);
712 app_.overlay().foreach(
713 send_always(std::make_shared<Message>(s, protocol::mtSTATUS_CHANGE)));
714 JLOG(
j_.
trace()) <<
"send status change to peer";
722 bool closeTimeCorrect,
728 if (
auto const replayData = ledgerMaster_.releaseReplay())
730 assert(replayData->parent()->info().hash == previousLedger.
id());
745 using namespace std::chrono_literals;
746 app_.getTxQ().processClosedLedger(app_, *built, roundTime > 5s);
749 if (ledgerMaster_.storeLedger(built))
750 JLOG(
j_.
debug()) <<
"Consensus built ledger we already had";
751 else if (app_.getInboundLedgers().find(built->info().hash))
752 JLOG(
j_.
debug()) <<
"Consensus built ledger we were acquiring";
754 JLOG(
j_.
debug()) <<
"Consensus built new ledger";
764 using namespace std::chrono_literals;
766 auto validationTime = app_.timeKeeper().closeTime();
767 if (validationTime <= lastValidationTime_)
768 validationTime = lastValidationTime_ + 1s;
769 lastValidationTime_ = validationTime;
771 auto v = std::make_shared<STValidation>(
790 if (auto const vl = ledgerMaster_.getValidatedLedger())
791 v.setFieldH256(sfValidatedHash, vl->info().hash);
793 v.setFieldU64(sfCookie, valCookie_);
796 if ((ledger.seq() + 1) % 256 == 0)
798 sfServerVersion, BuildInfo::getEncodedVersion());
803 auto const& ft = app_.getFeeTrack();
804 auto const fee = std::max(ft.getLocalFee(), ft.getClusterFee());
805 if (fee > ft.getLoadBase())
806 v.setFieldU32(sfLoadFee, fee);
811 if ((ledger.
seq() + 1) % 256 == 0)
814 feeVote_->doValidation(ledger.ledger_->fees(), v);
819 auto const amendments = app_.getAmendmentTable().doValidation(
820 getEnabledAmendments(*ledger.ledger_));
822 if (!amendments.empty())
824 sfAmendments, STVector256(sfAmendments, amendments));
829 app_.getHashRouter().addSuppression(
833 protocol::TMValidation val;
836 app_.overlay().send(val);
842 JLOG(
j_.
info()) <<
"Consensus mode change before=" <<
to_string(before)
850 censorshipDetector_.reset();
878 JLOG(
j_.
error()) <<
"During consensus timerEntry: " << mn.
what();
894 JLOG(
j_.
error()) <<
"During consensus gotTxSet: " << mn.
what();
904 boost::optional<std::chrono::milliseconds> consensusDelay)
916 return consensus_.peerProposal(now, newProposal);
924 validating_ = valPublic_.size() != 0 &&
925 prevLgr.
seq() >= app_.getMaxDisallowedLedger() &&
926 !app_.getOPs().isAmendmentBlocked();
930 if (validating_ && !app_.config().standalone() && app_.validators().count())
932 auto const when = app_.validators().expires();
934 if (!when || *when < app_.timeKeeper().now())
936 JLOG(
j_.
error()) <<
"Voluntarily bowing out of consensus process "
937 "because of an expired validator list.";
946 JLOG(
j_.
info()) <<
"Entering consensus process, validating, synced="
947 << (synced ?
"yes" :
"no");
952 JLOG(
j_.
info()) <<
"Entering consensus process, watching, synced="
953 << (synced ?
"yes" :
"no");
957 inboundTransactions_.newRound(prevLgr.
seq());
960 return validating_ && synced;
966 return ledgerMaster_.haveValidated();
972 return ledgerMaster_.getValidLedgerIndex();
978 return app_.validators().getQuorumKeys();
986 return app_.getValidations().laggards(seq, trustedKeys);
992 return !valPublic_.empty();
998 if (!positions && app_.getOPs().isFull())