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