rippled
Loading...
Searching...
No Matches
Consensus.h
1//------------------------------------------------------------------------------
2/*
3 This file is part of rippled: https://github.com/ripple/rippled
4 Copyright (c) 2012-2017 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#ifndef RIPPLE_CONSENSUS_CONSENSUS_H_INCLUDED
21#define RIPPLE_CONSENSUS_CONSENSUS_H_INCLUDED
22
23#include <xrpld/consensus/ConsensusParms.h>
24#include <xrpld/consensus/ConsensusProposal.h>
25#include <xrpld/consensus/ConsensusTypes.h>
26#include <xrpld/consensus/DisputedTx.h>
27#include <xrpld/consensus/LedgerTiming.h>
28#include <xrpl/basics/Log.h>
29#include <xrpl/basics/chrono.h>
30#include <xrpl/beast/utility/Journal.h>
31#include <xrpl/json/json_writer.h>
32
33#include <chrono>
34#include <deque>
35#include <optional>
36#include <sstream>
37
38namespace ripple {
39
59bool
61 bool anyTransactions,
62 std::size_t prevProposers,
63 std::size_t proposersClosed,
64 std::size_t proposersValidated,
65 std::chrono::milliseconds prevRoundTime,
66 std::chrono::milliseconds timeSincePrevClose,
68 std::chrono::milliseconds idleInterval,
69 ConsensusParms const& parms,
71 std::unique_ptr<std::stringstream> const& clog = {});
72
90 std::size_t prevProposers,
91 std::size_t currentProposers,
92 std::size_t currentAgree,
93 std::size_t currentFinished,
94 std::chrono::milliseconds previousAgreeTime,
95 std::chrono::milliseconds currentAgreeTime,
96 ConsensusParms const& parms,
97 bool proposing,
99 std::unique_ptr<std::stringstream> const& clog = {});
100
289template <class Adaptor>
291{
292 using Ledger_t = typename Adaptor::Ledger_t;
293 using TxSet_t = typename Adaptor::TxSet_t;
294 using NodeID_t = typename Adaptor::NodeID_t;
295 using Tx_t = typename TxSet_t::Tx;
296 using PeerPosition_t = typename Adaptor::PeerPosition_t;
298 NodeID_t,
299 typename Ledger_t::ID,
300 typename TxSet_t::ID>;
301
303
304 // Helper class to ensure adaptor is notified whenever the ConsensusMode
305 // changes
307 {
309
310 public:
312 {
313 }
315 get() const
316 {
317 return mode_;
318 }
319
320 void
321 set(ConsensusMode mode, Adaptor& a)
322 {
323 a.onModeChange(mode_, mode);
324 mode_ = mode;
325 }
326 };
327
328public:
331
332 Consensus(Consensus&&) noexcept = default;
333
340 Consensus(clock_type const& clock, Adaptor& adaptor, beast::Journal j);
341
357 void
359 NetClock::time_point const& now,
360 typename Ledger_t::ID const& prevLedgerID,
361 Ledger_t prevLedger,
362 hash_set<NodeID_t> const& nowUntrusted,
363 bool proposing,
364 std::unique_ptr<std::stringstream> const& clog = {});
365
372 bool
374 NetClock::time_point const& now,
375 PeerPosition_t const& newProposal);
376
382 void
384 NetClock::time_point const& now,
385 std::unique_ptr<std::stringstream> const& clog = {});
386
392 void
393 gotTxSet(NetClock::time_point const& now, TxSet_t const& txSet);
394
411 void
413 NetClock::time_point const& now,
415
423 typename Ledger_t::ID
425 {
426 return prevLedgerID_;
427 }
428
430 phase() const
431 {
432 return phase_;
433 }
434
443 getJson(bool full) const;
444
445private:
446 void
448 NetClock::time_point const& now,
449 typename Ledger_t::ID const& prevLedgerID,
450 Ledger_t const& prevLedger,
451 ConsensusMode mode,
453
454 // Change our view of the previous ledger
455 void
457 typename Ledger_t::ID const& lgrId,
459
465 void
467
471 void
473
476 bool
478 NetClock::time_point const& now,
479 PeerPosition_t const& newProposal);
480
487 void
489
498 void
500
523 bool
525
526 // Close the open ledger and establish initial position.
527 void
529
530 // Adjust our positions to try to agree with other validators.
531 void
533
534 bool
536
537 // Create disputes between our position and the provided one.
538 void
540 TxSet_t const& o,
541 std::unique_ptr<std::stringstream> const& clog = {});
542
543 // Update our disputes given that this node has adopted a new position.
544 // Will call createDisputes as needed.
545 void
546 updateDisputes(NodeID_t const& node, TxSet_t const& other);
547
548 // Revoke our outstanding proposal, if any, and cease proposing
549 // until this round ends.
550 void
552
553 // The rounded or effective close time estimate from a proposer
556
557private:
558 Adaptor& adaptor_;
559
562 bool firstRound_ = true;
564
566
567 // How long the consensus convergence has taken, expressed as
568 // a percentage of the time that we expected it to take.
570
571 // How long has this round been open
573
575
576 // Time it took for the last consensus round to converge
578
579 //-------------------------------------------------------------------------
580 // Network time measurements of consensus progress
581
582 // The current network adjusted time. This is the network time the
583 // ledger would close if it closed now
586
587 //-------------------------------------------------------------------------
588 // Non-peer (self) consensus data
589
590 // Last validated ledger ID provided to consensus
591 typename Ledger_t::ID prevLedgerID_;
592 // Last validated ledger seen by consensus
594
595 // Transaction Sets, indexed by hash of transaction tree
597
600
601 //-------------------------------------------------------------------------
602 // Peer related consensus data
603
604 // Peer proposed positions for the current round
606
607 // Recently received peer positions, available when transitioning between
608 // ledgers or rounds
610
611 // The number of proposers who participated in the last consensus round
613
614 // nodes that have bowed out of this consensus process
616
617 // Journal for debugging
619};
620
621template <class Adaptor>
623 clock_type const& clock,
624 Adaptor& adaptor,
625 beast::Journal journal)
626 : adaptor_(adaptor), clock_(clock), j_{journal}
627{
628 JLOG(j_.debug()) << "Creating consensus object";
629}
630
631template <class Adaptor>
632void
634 NetClock::time_point const& now,
635 typename Ledger_t::ID const& prevLedgerID,
636 Ledger_t prevLedger,
637 hash_set<NodeID_t> const& nowUntrusted,
638 bool proposing,
640{
641 if (firstRound_)
642 {
643 // take our initial view of closeTime_ from the seed ledger
644 prevRoundTime_ = adaptor_.parms().ledgerIDLE_INTERVAL;
645 prevCloseTime_ = prevLedger.closeTime();
646 firstRound_ = false;
647 }
648 else
649 {
650 prevCloseTime_ = rawCloseTimes_.self;
651 }
652
653 for (NodeID_t const& n : nowUntrusted)
654 recentPeerPositions_.erase(n);
655
656 ConsensusMode startMode =
658
659 // We were handed the wrong ledger
660 if (prevLedger.id() != prevLedgerID)
661 {
662 // try to acquire the correct one
663 if (auto newLedger = adaptor_.acquireLedger(prevLedgerID))
664 {
665 prevLedger = *newLedger;
666 }
667 else // Unable to acquire the correct ledger
668 {
669 startMode = ConsensusMode::wrongLedger;
670 JLOG(j_.info())
671 << "Entering consensus with: " << previousLedger_.id();
672 JLOG(j_.info()) << "Correct LCL is: " << prevLedgerID;
673 }
674 }
675
676 startRoundInternal(now, prevLedgerID, prevLedger, startMode, clog);
677}
678template <class Adaptor>
679void
681 NetClock::time_point const& now,
682 typename Ledger_t::ID const& prevLedgerID,
683 Ledger_t const& prevLedger,
684 ConsensusMode mode,
686{
687 phase_ = ConsensusPhase::open;
688 JLOG(j_.debug()) << "transitioned to ConsensusPhase::open ";
689 CLOG(clog) << "startRoundInternal transitioned to ConsensusPhase::open, "
690 "previous ledgerID: "
691 << prevLedgerID << ", seq: " << prevLedger.seq() << ". ";
692 mode_.set(mode, adaptor_);
693 now_ = now;
694 prevLedgerID_ = prevLedgerID;
695 previousLedger_ = prevLedger;
696 result_.reset();
697 convergePercent_ = 0;
698 haveCloseTimeConsensus_ = false;
699 openTime_.reset(clock_.now());
700 currPeerPositions_.clear();
701 acquired_.clear();
702 rawCloseTimes_.peers.clear();
703 rawCloseTimes_.self = {};
704 deadNodes_.clear();
705
706 closeResolution_ = getNextLedgerTimeResolution(
707 previousLedger_.closeTimeResolution(),
708 previousLedger_.closeAgree(),
709 previousLedger_.seq() + typename Ledger_t::Seq{1});
710
711 playbackProposals();
712 CLOG(clog) << "number of peer proposals,previous proposers: "
713 << currPeerPositions_.size() << ',' << prevProposers_ << ". ";
714 if (currPeerPositions_.size() > (prevProposers_ / 2))
715 {
716 // We may be falling behind, don't wait for the timer
717 // consider closing the ledger immediately
718 CLOG(clog) << "consider closing the ledger immediately. ";
719 timerEntry(now_, clog);
720 }
721}
722
723template <class Adaptor>
724bool
726 NetClock::time_point const& now,
727 PeerPosition_t const& newPeerPos)
728{
729 JLOG(j_.debug()) << "PROPOSAL " << newPeerPos.render();
730 auto const& peerID = newPeerPos.proposal().nodeID();
731
732 // Always need to store recent positions
733 {
734 auto& props = recentPeerPositions_[peerID];
735
736 if (props.size() >= 10)
737 props.pop_front();
738
739 props.push_back(newPeerPos);
740 }
741 return peerProposalInternal(now, newPeerPos);
742}
743
744template <class Adaptor>
745bool
747 NetClock::time_point const& now,
748 PeerPosition_t const& newPeerPos)
749{
750 // Nothing to do for now if we are currently working on a ledger
751 if (phase_ == ConsensusPhase::accepted)
752 return false;
753
754 now_ = now;
755
756 auto const& newPeerProp = newPeerPos.proposal();
757
758 if (newPeerProp.prevLedger() != prevLedgerID_)
759 {
760 JLOG(j_.debug()) << "Got proposal for " << newPeerProp.prevLedger()
761 << " but we are on " << prevLedgerID_;
762 return false;
763 }
764
765 auto const& peerID = newPeerProp.nodeID();
766
767 if (deadNodes_.find(peerID) != deadNodes_.end())
768 {
769 JLOG(j_.info()) << "Position from dead node: " << peerID;
770 return false;
771 }
772
773 {
774 // update current position
775 auto peerPosIt = currPeerPositions_.find(peerID);
776
777 if (peerPosIt != currPeerPositions_.end())
778 {
779 if (newPeerProp.proposeSeq() <=
780 peerPosIt->second.proposal().proposeSeq())
781 {
782 return false;
783 }
784 }
785
786 if (newPeerProp.isBowOut())
787 {
788 JLOG(j_.info()) << "Peer " << peerID << " bows out";
789 if (result_)
790 {
791 for (auto& it : result_->disputes)
792 it.second.unVote(peerID);
793 }
794 if (peerPosIt != currPeerPositions_.end())
795 currPeerPositions_.erase(peerID);
796 deadNodes_.insert(peerID);
797
798 return true;
799 }
800
801 if (peerPosIt != currPeerPositions_.end())
802 peerPosIt->second = newPeerPos;
803 else
804 currPeerPositions_.emplace(peerID, newPeerPos);
805 }
806
807 if (newPeerProp.isInitial())
808 {
809 // Record the close time estimate
810 JLOG(j_.trace()) << "Peer reports close time as "
811 << newPeerProp.closeTime().time_since_epoch().count();
812 ++rawCloseTimes_.peers[newPeerProp.closeTime()];
813 }
814
815 JLOG(j_.trace()) << "Processing peer proposal " << newPeerProp.proposeSeq()
816 << "/" << newPeerProp.position();
817
818 {
819 auto const ait = acquired_.find(newPeerProp.position());
820 if (ait == acquired_.end())
821 {
822 // acquireTxSet will return the set if it is available, or
823 // spawn a request for it and return nullopt/nullptr. It will call
824 // gotTxSet once it arrives
825 if (auto set = adaptor_.acquireTxSet(newPeerProp.position()))
826 gotTxSet(now_, *set);
827 else
828 JLOG(j_.debug()) << "Don't have tx set for peer";
829 }
830 else if (result_)
831 {
832 updateDisputes(newPeerProp.nodeID(), ait->second);
833 }
834 }
835
836 return true;
837}
838
839template <class Adaptor>
840void
842 NetClock::time_point const& now,
844{
845 CLOG(clog) << "Consensus<Adaptor>::timerEntry. ";
846 // Nothing to do if we are currently working on a ledger
847 if (phase_ == ConsensusPhase::accepted)
848 {
849 CLOG(clog) << "Nothing to do during accepted phase. ";
850 return;
851 }
852
853 now_ = now;
854 CLOG(clog) << "Set network adjusted time to " << to_string(now) << ". ";
855
856 // Check we are on the proper ledger (this may change phase_)
857 const auto phaseOrig = phase_;
858 CLOG(clog) << "Phase " << to_string(phaseOrig) << ". ";
859 checkLedger(clog);
860 if (phaseOrig != phase_)
861 {
862 CLOG(clog) << "Changed phase to << " << to_string(phase_) << ". ";
863 }
864
865 if (phase_ == ConsensusPhase::open)
866 phaseOpen(clog);
867 else if (phase_ == ConsensusPhase::establish)
868 phaseEstablish(clog);
869 CLOG(clog) << "timerEntry finishing in phase " << to_string(phase_) << ". ";
870}
871
872template <class Adaptor>
873void
875 NetClock::time_point const& now,
876 TxSet_t const& txSet)
877{
878 // Nothing to do if we've finished work on a ledger
879 if (phase_ == ConsensusPhase::accepted)
880 return;
881
882 now_ = now;
883
884 auto id = txSet.id();
885
886 // If we've already processed this transaction set since requesting
887 // it from the network, there is nothing to do now
888 if (!acquired_.emplace(id, txSet).second)
889 return;
890
891 if (!result_)
892 {
893 JLOG(j_.debug()) << "Not creating disputes: no position yet.";
894 }
895 else
896 {
897 // Our position is added to acquired_ as soon as we create it,
898 // so this txSet must differ
899 XRPL_ASSERT(
900 id != result_->position.position(),
901 "ripple::Consensus::gotTxSet : updated transaction set");
902 bool any = false;
903 for (auto const& [nodeId, peerPos] : currPeerPositions_)
904 {
905 if (peerPos.proposal().position() == id)
906 {
907 updateDisputes(nodeId, txSet);
908 any = true;
909 }
910 }
911
912 if (!any)
913 {
914 JLOG(j_.warn())
915 << "By the time we got " << id << " no peers were proposing it";
916 }
917 }
918}
919
920template <class Adaptor>
921void
923 NetClock::time_point const& now,
925{
926 using namespace std::chrono_literals;
927 JLOG(j_.info()) << "Simulating consensus";
928 now_ = now;
929 closeLedger({});
930 result_->roundTime.tick(consensusDelay.value_or(100ms));
931 result_->proposers = prevProposers_ = currPeerPositions_.size();
932 prevRoundTime_ = result_->roundTime.read();
934 adaptor_.onForceAccept(
935 *result_,
936 previousLedger_,
937 closeResolution_,
938 rawCloseTimes_,
939 mode_.get(),
940 getJson(true));
941 JLOG(j_.info()) << "Simulation complete";
942}
943
944template <class Adaptor>
947{
948 using std::to_string;
949 using Int = Json::Value::Int;
950
952
953 ret["proposing"] = (mode_.get() == ConsensusMode::proposing);
954 ret["proposers"] = static_cast<int>(currPeerPositions_.size());
955
956 if (mode_.get() != ConsensusMode::wrongLedger)
957 {
958 ret["synched"] = true;
959 ret["ledger_seq"] =
960 static_cast<std::uint32_t>(previousLedger_.seq()) + 1;
961 ret["close_granularity"] = static_cast<Int>(closeResolution_.count());
962 }
963 else
964 ret["synched"] = false;
965
966 ret["phase"] = to_string(phase_);
967
968 if (result_ && !result_->disputes.empty() && !full)
969 ret["disputes"] = static_cast<Int>(result_->disputes.size());
970
971 if (result_)
972 ret["our_position"] = result_->position.getJson();
973
974 if (full)
975 {
976 if (result_)
977 ret["current_ms"] =
978 static_cast<Int>(result_->roundTime.read().count());
979 ret["converge_percent"] = convergePercent_;
980 ret["close_resolution"] = static_cast<Int>(closeResolution_.count());
981 ret["have_time_consensus"] = haveCloseTimeConsensus_;
982 ret["previous_proposers"] = static_cast<Int>(prevProposers_);
983 ret["previous_mseconds"] = static_cast<Int>(prevRoundTime_.count());
984
985 if (!currPeerPositions_.empty())
986 {
988
989 for (auto const& [nodeId, peerPos] : currPeerPositions_)
990 {
991 ppj[to_string(nodeId)] = peerPos.getJson();
992 }
993 ret["peer_positions"] = std::move(ppj);
994 }
995
996 if (!acquired_.empty())
997 {
999 for (auto const& at : acquired_)
1000 {
1001 acq.append(to_string(at.first));
1002 }
1003 ret["acquired"] = std::move(acq);
1004 }
1005
1006 if (result_ && !result_->disputes.empty())
1007 {
1009 for (auto const& [txId, dispute] : result_->disputes)
1010 {
1011 dsj[to_string(txId)] = dispute.getJson();
1012 }
1013 ret["disputes"] = std::move(dsj);
1014 }
1015
1016 if (!rawCloseTimes_.peers.empty())
1017 {
1019 for (auto const& ct : rawCloseTimes_.peers)
1020 {
1021 ctj[std::to_string(ct.first.time_since_epoch().count())] =
1022 ct.second;
1023 }
1024 ret["close_times"] = std::move(ctj);
1025 }
1026
1027 if (!deadNodes_.empty())
1028 {
1030 for (auto const& dn : deadNodes_)
1031 {
1032 dnj.append(to_string(dn));
1033 }
1034 ret["dead_nodes"] = std::move(dnj);
1035 }
1036 }
1037
1038 return ret;
1039}
1040
1041// Handle a change in the prior ledger during a consensus round
1042template <class Adaptor>
1043void
1045 typename Ledger_t::ID const& lgrId,
1047{
1048 CLOG(clog) << "handleWrongLedger. ";
1049 XRPL_ASSERT(
1050 lgrId != prevLedgerID_ || previousLedger_.id() != lgrId,
1051 "ripple::Consensus::handleWrongLedger : have wrong ledger");
1052
1053 // Stop proposing because we are out of sync
1054 leaveConsensus(clog);
1055
1056 // First time switching to this ledger
1057 if (prevLedgerID_ != lgrId)
1058 {
1059 prevLedgerID_ = lgrId;
1060
1061 // Clear out state
1062 if (result_)
1063 {
1064 result_->disputes.clear();
1065 result_->compares.clear();
1066 }
1067
1068 currPeerPositions_.clear();
1069 rawCloseTimes_.peers.clear();
1070 deadNodes_.clear();
1071
1072 // Get back in sync, this will also recreate disputes
1073 playbackProposals();
1074 }
1075
1076 if (previousLedger_.id() == prevLedgerID_)
1077 {
1078 CLOG(clog) << "previousLedger_.id() == prevLeverID_ " << prevLedgerID_
1079 << ". ";
1080 return;
1081 }
1082
1083 // we need to switch the ledger we're working from
1084 if (auto newLedger = adaptor_.acquireLedger(prevLedgerID_))
1085 {
1086 JLOG(j_.info()) << "Have the consensus ledger " << prevLedgerID_;
1087 CLOG(clog) << "Have the consensus ledger " << prevLedgerID_ << ". ";
1088 startRoundInternal(
1089 now_, lgrId, *newLedger, ConsensusMode::switchedLedger, clog);
1090 }
1091 else
1092 {
1093 CLOG(clog) << "Still on wrong ledger. ";
1094 mode_.set(ConsensusMode::wrongLedger, adaptor_);
1095 }
1096}
1097
1098template <class Adaptor>
1099void
1101{
1102 CLOG(clog) << "checkLedger. ";
1103
1104 auto netLgr =
1105 adaptor_.getPrevLedger(prevLedgerID_, previousLedger_, mode_.get());
1106 CLOG(clog) << "network ledgerid " << netLgr << ", " << "previous ledger "
1107 << prevLedgerID_ << ". ";
1108
1109 if (netLgr != prevLedgerID_)
1110 {
1112 ss << "View of consensus changed during " << to_string(phase_)
1113 << " mode=" << to_string(mode_.get()) << ", " << prevLedgerID_
1114 << " to " << netLgr << ", "
1115 << Json::Compact{previousLedger_.getJson()} << ". ";
1116 JLOG(j_.warn()) << ss.str();
1117 CLOG(clog) << ss.str();
1118 CLOG(clog) << "State on consensus change "
1119 << Json::Compact{getJson(true)} << ". ";
1120 handleWrongLedger(netLgr, clog);
1121 }
1122 else if (previousLedger_.id() != prevLedgerID_)
1123 {
1124 CLOG(clog) << "previousLedger_.id() != prevLedgerID_: "
1125 << previousLedger_.id() << ',' << to_string(prevLedgerID_)
1126 << ". ";
1127 handleWrongLedger(netLgr, clog);
1128 }
1129}
1130
1131template <class Adaptor>
1132void
1134{
1135 for (auto const& it : recentPeerPositions_)
1136 {
1137 for (auto const& pos : it.second)
1138 {
1139 if (pos.proposal().prevLedger() == prevLedgerID_)
1140 {
1141 if (peerProposalInternal(now_, pos))
1142 adaptor_.share(pos);
1143 }
1144 }
1145 }
1146}
1147
1148template <class Adaptor>
1149void
1151{
1152 CLOG(clog) << "phaseOpen. ";
1153 using namespace std::chrono;
1154
1155 // it is shortly before ledger close time
1156 bool anyTransactions = adaptor_.hasOpenTransactions();
1157 auto proposersClosed = currPeerPositions_.size();
1158 auto proposersValidated = adaptor_.proposersValidated(prevLedgerID_);
1159
1160 openTime_.tick(clock_.now());
1161
1162 // This computes how long since last ledger's close time
1163 milliseconds sinceClose;
1164 {
1165 auto const mode = mode_.get();
1166 bool const closeAgree = previousLedger_.closeAgree();
1167 auto const prevCloseTime = previousLedger_.closeTime();
1168 auto const prevParentCloseTimePlus1 =
1169 previousLedger_.parentCloseTime() + 1s;
1170 bool const previousCloseCorrect =
1171 (mode != ConsensusMode::wrongLedger) && closeAgree &&
1172 (prevCloseTime != prevParentCloseTimePlus1);
1173
1174 auto const lastCloseTime = previousCloseCorrect
1175 ? prevCloseTime // use consensus timing
1176 : prevCloseTime_; // use the time we saw internally
1177
1178 if (now_ >= lastCloseTime)
1179 sinceClose = duration_cast<milliseconds>(now_ - lastCloseTime);
1180 else
1181 sinceClose = -duration_cast<milliseconds>(lastCloseTime - now_);
1182 CLOG(clog) << "calculating how long since last ledger's close time "
1183 "based on mode : "
1184 << to_string(mode) << ", previous closeAgree: " << closeAgree
1185 << ", previous close time: " << to_string(prevCloseTime)
1186 << ", previous parent close time + 1s: "
1187 << to_string(prevParentCloseTimePlus1)
1188 << ", previous close time seen internally: "
1189 << to_string(prevCloseTime_)
1190 << ", last close time: " << to_string(lastCloseTime)
1191 << ", since close: " << sinceClose.count() << ". ";
1192 }
1193
1194 auto const idleInterval = std::max<milliseconds>(
1195 adaptor_.parms().ledgerIDLE_INTERVAL,
1196 2 * previousLedger_.closeTimeResolution());
1197 CLOG(clog) << "idle interval set to " << idleInterval.count()
1198 << "ms based on " << "ledgerIDLE_INTERVAL: "
1199 << adaptor_.parms().ledgerIDLE_INTERVAL.count()
1200 << ", previous ledger close time resolution: "
1201 << previousLedger_.closeTimeResolution().count() << "ms. ";
1202
1203 // Decide if we should close the ledger
1205 anyTransactions,
1206 prevProposers_,
1207 proposersClosed,
1208 proposersValidated,
1209 prevRoundTime_,
1210 sinceClose,
1211 openTime_.read(),
1212 idleInterval,
1213 adaptor_.parms(),
1214 j_,
1215 clog))
1216 {
1217 CLOG(clog) << "closing ledger. ";
1218 closeLedger(clog);
1219 }
1220}
1221
1222template <class Adaptor>
1223bool
1226{
1227 CLOG(clog) << "shouldPause? ";
1228 auto const& parms = adaptor_.parms();
1229 std::uint32_t const ahead(
1230 previousLedger_.seq() -
1231 std::min(adaptor_.getValidLedgerIndex(), previousLedger_.seq()));
1232 auto [quorum, trustedKeys] = adaptor_.getQuorumKeys();
1233 std::size_t const totalValidators = trustedKeys.size();
1234 std::size_t laggards =
1235 adaptor_.laggards(previousLedger_.seq(), trustedKeys);
1236 std::size_t const offline = trustedKeys.size();
1237
1238 std::stringstream vars;
1239 vars << " consensuslog (working seq: " << previousLedger_.seq() << ", "
1240 << "validated seq: " << adaptor_.getValidLedgerIndex() << ", "
1241 << "am validator: " << adaptor_.validator() << ", "
1242 << "have validated: " << adaptor_.haveValidated() << ", "
1243 << "roundTime: " << result_->roundTime.read().count() << ", "
1244 << "max consensus time: " << parms.ledgerMAX_CONSENSUS.count() << ", "
1245 << "validators: " << totalValidators << ", "
1246 << "laggards: " << laggards << ", " << "offline: " << offline << ", "
1247 << "quorum: " << quorum << ")";
1248
1249 if (!ahead || !laggards || !totalValidators || !adaptor_.validator() ||
1250 !adaptor_.haveValidated() ||
1251 result_->roundTime.read() > parms.ledgerMAX_CONSENSUS)
1252 {
1253 j_.debug() << "not pausing (early)" << vars.str();
1254 CLOG(clog) << "Not pausing (early). ";
1255 return false;
1256 }
1257
1258 bool willPause = false;
1259
1273 constexpr static std::size_t maxPausePhase = 4;
1274
1294 std::size_t const phase = (ahead - 1) % (maxPausePhase + 1);
1295
1296 // validators that remain after the laggards() function are considered
1297 // offline, and should be considered as laggards for purposes of
1298 // evaluating whether the threshold for non-laggards has been reached.
1299 switch (phase)
1300 {
1301 case 0:
1302 // Laggards and offline shouldn't preclude consensus.
1303 if (laggards + offline > totalValidators - quorum)
1304 willPause = true;
1305 break;
1306 case maxPausePhase:
1307 // No tolerance.
1308 willPause = true;
1309 break;
1310 default:
1311 // Ensure that sufficient validators are known to be not lagging.
1312 // Their sufficiently most recent validation sequence was equal to
1313 // or greater than our own.
1314 //
1315 // The threshold is the amount required for quorum plus
1316 // the proportion of the remainder based on number of intermediate
1317 // phases between 0 and max.
1318 float const nonLaggards = totalValidators - (laggards + offline);
1319 float const quorumRatio =
1320 static_cast<float>(quorum) / totalValidators;
1321 float const allowedDissent = 1.0f - quorumRatio;
1322 float const phaseFactor = static_cast<float>(phase) / maxPausePhase;
1323
1324 if (nonLaggards / totalValidators <
1325 quorumRatio + (allowedDissent * phaseFactor))
1326 {
1327 willPause = true;
1328 }
1329 }
1330
1331 if (willPause)
1332 {
1333 j_.warn() << "pausing" << vars.str();
1334 CLOG(clog) << "pausing " << vars.str() << ". ";
1335 }
1336 else
1337 {
1338 j_.debug() << "not pausing" << vars.str();
1339 CLOG(clog) << "not pausing. ";
1340 }
1341 return willPause;
1342}
1343
1344template <class Adaptor>
1345void
1348{
1349 CLOG(clog) << "phaseEstablish. ";
1350 // can only establish consensus if we already took a stance
1351 XRPL_ASSERT(result_, "ripple::Consensus::phaseEstablish : result is set");
1352
1353 using namespace std::chrono;
1354 ConsensusParms const& parms = adaptor_.parms();
1355
1356 result_->roundTime.tick(clock_.now());
1357 result_->proposers = currPeerPositions_.size();
1358
1359 convergePercent_ = result_->roundTime.read() * 100 /
1360 std::max<milliseconds>(prevRoundTime_, parms.avMIN_CONSENSUS_TIME);
1361 CLOG(clog) << "convergePercent_ " << convergePercent_
1362 << " is based on round duration so far: "
1363 << result_->roundTime.read().count() << "ms, "
1364 << "previous round duration: " << prevRoundTime_.count()
1365 << "ms, "
1366 << "avMIN_CONSENSUS_TIME: " << parms.avMIN_CONSENSUS_TIME.count()
1367 << "ms. ";
1368
1369 // Give everyone a chance to take an initial position
1370 if (result_->roundTime.read() < parms.ledgerMIN_CONSENSUS)
1371 {
1372 CLOG(clog) << "ledgerMIN_CONSENSUS not reached: "
1373 << parms.ledgerMIN_CONSENSUS.count() << "ms. ";
1374 return;
1375 }
1376
1377 updateOurPositions(clog);
1378
1379 // Nothing to do if too many laggards or we don't have consensus.
1380 if (shouldPause(clog) || !haveConsensus(clog))
1381 return;
1382
1383 if (!haveCloseTimeConsensus_)
1384 {
1385 JLOG(j_.info()) << "We have TX consensus but not CT consensus";
1386 CLOG(clog) << "We have TX consensus but not CT consensus. ";
1387 return;
1388 }
1389
1390 JLOG(j_.info()) << "Converge cutoff (" << currPeerPositions_.size()
1391 << " participants)";
1392 CLOG(clog) << "Converge cutoff (" << currPeerPositions_.size()
1393 << " participants). Transitioned to ConsensusPhase::accepted. ";
1394 adaptor_.updateOperatingMode(currPeerPositions_.size());
1395 prevProposers_ = currPeerPositions_.size();
1396 prevRoundTime_ = result_->roundTime.read();
1397 phase_ = ConsensusPhase::accepted;
1398 JLOG(j_.debug()) << "transitioned to ConsensusPhase::accepted";
1399 adaptor_.onAccept(
1400 *result_,
1401 previousLedger_,
1402 closeResolution_,
1403 rawCloseTimes_,
1404 mode_.get(),
1405 getJson(true),
1406 adaptor_.validating());
1407}
1408
1409template <class Adaptor>
1410void
1412{
1413 // We should not be closing if we already have a position
1414 XRPL_ASSERT(!result_, "ripple::Consensus::closeLedger : result is not set");
1415
1417 JLOG(j_.debug()) << "transitioned to ConsensusPhase::establish";
1418 rawCloseTimes_.self = now_;
1419
1420 result_.emplace(adaptor_.onClose(previousLedger_, now_, mode_.get()));
1421 result_->roundTime.reset(clock_.now());
1422 // Share the newly created transaction set if we haven't already
1423 // received it from a peer
1424 if (acquired_.emplace(result_->txns.id(), result_->txns).second)
1425 adaptor_.share(result_->txns);
1426
1427 const auto mode = mode_.get();
1428 CLOG(clog)
1429 << "closeLedger transitioned to ConsensusPhase::establish, mode: "
1430 << to_string(mode)
1431 << ", number of peer positions: " << currPeerPositions_.size() << ". ";
1432 if (mode == ConsensusMode::proposing)
1433 adaptor_.propose(result_->position);
1434
1435 // Create disputes with any peer positions we have transactions for
1436 for (auto const& pit : currPeerPositions_)
1437 {
1438 auto const& pos = pit.second.proposal().position();
1439 auto const it = acquired_.find(pos);
1440 if (it != acquired_.end())
1441 createDisputes(it->second, clog);
1442 }
1443}
1444
1457inline int
1458participantsNeeded(int participants, int percent)
1459{
1460 int result = ((participants * percent) + (percent / 2)) / 100;
1461
1462 return (result == 0) ? 1 : result;
1463}
1464
1465template <class Adaptor>
1466void
1469{
1470 // We must have a position if we are updating it
1471 XRPL_ASSERT(
1472 result_, "ripple::Consensus::updateOurPositions : result is set");
1473 ConsensusParms const& parms = adaptor_.parms();
1474
1475 // Compute a cutoff time
1476 auto const peerCutoff = now_ - parms.proposeFRESHNESS;
1477 auto const ourCutoff = now_ - parms.proposeINTERVAL;
1478 CLOG(clog) << "updateOurPositions. peerCutoff " << to_string(peerCutoff)
1479 << ", ourCutoff " << to_string(ourCutoff) << ". ";
1480
1481 // Verify freshness of peer positions and compute close times
1483 {
1484 auto it = currPeerPositions_.begin();
1485 while (it != currPeerPositions_.end())
1486 {
1487 Proposal_t const& peerProp = it->second.proposal();
1488 if (peerProp.isStale(peerCutoff))
1489 {
1490 // peer's proposal is stale, so remove it
1491 NodeID_t const& peerID = peerProp.nodeID();
1492 JLOG(j_.warn()) << "Removing stale proposal from " << peerID;
1493 for (auto& dt : result_->disputes)
1494 dt.second.unVote(peerID);
1495 it = currPeerPositions_.erase(it);
1496 }
1497 else
1498 {
1499 // proposal is still fresh
1500 ++closeTimeVotes[asCloseTime(peerProp.closeTime())];
1501 ++it;
1502 }
1503 }
1504 }
1505
1506 // This will stay unseated unless there are any changes
1507 std::optional<TxSet_t> ourNewSet;
1508
1509 // Update votes on disputed transactions
1510 {
1512 for (auto& [txId, dispute] : result_->disputes)
1513 {
1514 // Because the threshold for inclusion increases,
1515 // time can change our position on a dispute
1516 if (dispute.updateVote(
1517 convergePercent_,
1518 mode_.get() == ConsensusMode::proposing,
1519 parms))
1520 {
1521 if (!mutableSet)
1522 mutableSet.emplace(result_->txns);
1523
1524 if (dispute.getOurVote())
1525 {
1526 // now a yes
1527 mutableSet->insert(dispute.tx());
1528 }
1529 else
1530 {
1531 // now a no
1532 mutableSet->erase(txId);
1533 }
1534 }
1535 }
1536
1537 if (mutableSet)
1538 ourNewSet.emplace(std::move(*mutableSet));
1539 }
1540
1541 NetClock::time_point consensusCloseTime = {};
1542 haveCloseTimeConsensus_ = false;
1543
1544 if (currPeerPositions_.empty())
1545 {
1546 // no other times
1547 haveCloseTimeConsensus_ = true;
1548 consensusCloseTime = asCloseTime(result_->position.closeTime());
1549 }
1550 else
1551 {
1552 int neededWeight;
1553
1554 if (convergePercent_ < parms.avMID_CONSENSUS_TIME)
1555 neededWeight = parms.avINIT_CONSENSUS_PCT;
1556 else if (convergePercent_ < parms.avLATE_CONSENSUS_TIME)
1557 neededWeight = parms.avMID_CONSENSUS_PCT;
1558 else if (convergePercent_ < parms.avSTUCK_CONSENSUS_TIME)
1559 neededWeight = parms.avLATE_CONSENSUS_PCT;
1560 else
1561 neededWeight = parms.avSTUCK_CONSENSUS_PCT;
1562 CLOG(clog) << "neededWeight " << neededWeight << ". ";
1563
1564 int participants = currPeerPositions_.size();
1565 if (mode_.get() == ConsensusMode::proposing)
1566 {
1567 ++closeTimeVotes[asCloseTime(result_->position.closeTime())];
1568 ++participants;
1569 }
1570
1571 // Threshold for non-zero vote
1572 int threshVote = participantsNeeded(participants, neededWeight);
1573
1574 // Threshold to declare consensus
1575 int const threshConsensus =
1576 participantsNeeded(participants, parms.avCT_CONSENSUS_PCT);
1577
1579 ss << "Proposers:" << currPeerPositions_.size()
1580 << " nw:" << neededWeight << " thrV:" << threshVote
1581 << " thrC:" << threshConsensus;
1582 JLOG(j_.info()) << ss.str();
1583 CLOG(clog) << ss.str();
1584
1585 for (auto const& [t, v] : closeTimeVotes)
1586 {
1587 JLOG(j_.debug())
1588 << "CCTime: seq "
1589 << static_cast<std::uint32_t>(previousLedger_.seq()) + 1 << ": "
1590 << t.time_since_epoch().count() << " has " << v << ", "
1591 << threshVote << " required";
1592
1593 if (v >= threshVote)
1594 {
1595 // A close time has enough votes for us to try to agree
1596 consensusCloseTime = t;
1597 threshVote = v;
1598
1599 if (threshVote >= threshConsensus)
1600 haveCloseTimeConsensus_ = true;
1601 }
1602 }
1603
1604 if (!haveCloseTimeConsensus_)
1605 {
1606 JLOG(j_.debug())
1607 << "No CT consensus:" << " Proposers:"
1608 << currPeerPositions_.size()
1609 << " Mode:" << to_string(mode_.get())
1610 << " Thresh:" << threshConsensus
1611 << " Pos:" << consensusCloseTime.time_since_epoch().count();
1612 CLOG(clog) << "No close time consensus. ";
1613 }
1614 }
1615
1616 if (!ourNewSet &&
1617 ((consensusCloseTime != asCloseTime(result_->position.closeTime())) ||
1618 result_->position.isStale(ourCutoff)))
1619 {
1620 // close time changed or our position is stale
1621 ourNewSet.emplace(result_->txns);
1622 }
1623
1624 if (ourNewSet)
1625 {
1626 auto newID = ourNewSet->id();
1627
1628 result_->txns = std::move(*ourNewSet);
1629
1631 ss << "Position change: CTime "
1632 << consensusCloseTime.time_since_epoch().count() << ", tx " << newID;
1633 JLOG(j_.info()) << ss.str();
1634 CLOG(clog) << ss.str();
1635
1636 result_->position.changePosition(newID, consensusCloseTime, now_);
1637
1638 // Share our new transaction set and update disputes
1639 // if we haven't already received it
1640 if (acquired_.emplace(newID, result_->txns).second)
1641 {
1642 if (!result_->position.isBowOut())
1643 adaptor_.share(result_->txns);
1644
1645 for (auto const& [nodeId, peerPos] : currPeerPositions_)
1646 {
1647 Proposal_t const& p = peerPos.proposal();
1648 if (p.position() == newID)
1649 updateDisputes(nodeId, result_->txns);
1650 }
1651 }
1652
1653 // Share our new position if we are still participating this round
1654 if (!result_->position.isBowOut() &&
1655 (mode_.get() == ConsensusMode::proposing))
1656 adaptor_.propose(result_->position);
1657 }
1658}
1659
1660template <class Adaptor>
1661bool
1664{
1665 // Must have a stance if we are checking for consensus
1666 XRPL_ASSERT(result_, "ripple::Consensus::haveConsensus : has result");
1667
1668 // CHECKME: should possibly count unacquired TX sets as disagreeing
1669 int agree = 0, disagree = 0;
1670
1671 auto ourPosition = result_->position.position();
1672
1673 // Count number of agreements/disagreements with our position
1674 for (auto const& [nodeId, peerPos] : currPeerPositions_)
1675 {
1676 Proposal_t const& peerProp = peerPos.proposal();
1677 if (peerProp.position() == ourPosition)
1678 {
1679 ++agree;
1680 }
1681 else
1682 {
1683 JLOG(j_.debug()) << nodeId << " has " << peerProp.position();
1684 ++disagree;
1685 }
1686 }
1687 auto currentFinished =
1688 adaptor_.proposersFinished(previousLedger_, prevLedgerID_);
1689
1690 JLOG(j_.debug()) << "Checking for TX consensus: agree=" << agree
1691 << ", disagree=" << disagree;
1692
1693 // Determine if we actually have consensus or not
1694 result_->state = checkConsensus(
1695 prevProposers_,
1696 agree + disagree,
1697 agree,
1698 currentFinished,
1699 prevRoundTime_,
1700 result_->roundTime.read(),
1701 adaptor_.parms(),
1702 mode_.get() == ConsensusMode::proposing,
1703 j_,
1704 clog);
1705
1706 if (result_->state == ConsensusState::No)
1707 {
1708 CLOG(clog) << "No consensus. ";
1709 return false;
1710 }
1711
1712 // There is consensus, but we need to track if the network moved on
1713 // without us.
1714 if (result_->state == ConsensusState::MovedOn)
1715 {
1716 JLOG(j_.error()) << "Unable to reach consensus";
1717 JLOG(j_.error()) << Json::Compact{getJson(true)};
1718 CLOG(clog) << "Unable to reach consensus "
1719 << Json::Compact{getJson(true)} << ". ";
1720 }
1721
1722 CLOG(clog) << "Consensus has been reached. ";
1723 return true;
1724}
1725
1726template <class Adaptor>
1727void
1730{
1731 if (mode_.get() == ConsensusMode::proposing)
1732 {
1733 if (result_ && !result_->position.isBowOut())
1734 {
1735 result_->position.bowOut(now_);
1736 adaptor_.propose(result_->position);
1737 }
1738
1739 mode_.set(ConsensusMode::observing, adaptor_);
1740 JLOG(j_.info()) << "Bowing out of consensus";
1741 CLOG(clog) << "Bowing out of consensus. ";
1742 }
1743}
1744
1745template <class Adaptor>
1746void
1748 TxSet_t const& o,
1750{
1751 // Cannot create disputes without our stance
1752 XRPL_ASSERT(result_, "ripple::Consensus::createDisputes : result is set");
1753
1754 // Only create disputes if this is a new set
1755 auto const emplaced = result_->compares.emplace(o.id()).second;
1756 CLOG(clog) << "createDisputes: new set? " << !emplaced << ". ";
1757 if (!emplaced)
1758 return;
1759
1760 // Nothing to dispute if we agree
1761 if (result_->txns.id() == o.id())
1762 {
1763 CLOG(clog) << "both sets are identical. ";
1764 return;
1765 }
1766
1767 CLOG(clog) << "comparing existing with new set: " << result_->txns.id()
1768 << ',' << o.id() << ". ";
1769 JLOG(j_.debug()) << "createDisputes " << result_->txns.id() << " to "
1770 << o.id();
1771
1772 auto differences = result_->txns.compare(o);
1773
1774 int dc = 0;
1775
1776 for (auto const& [txId, inThisSet] : differences)
1777 {
1778 ++dc;
1779 // create disputed transactions (from the ledger that has them)
1780 XRPL_ASSERT(
1781 (inThisSet && result_->txns.find(txId) && !o.find(txId)) ||
1782 (!inThisSet && !result_->txns.find(txId) && o.find(txId)),
1783 "ripple::Consensus::createDisputes : has disputed transactions");
1784
1785 Tx_t tx = inThisSet ? result_->txns.find(txId) : o.find(txId);
1786 auto txID = tx.id();
1787
1788 if (result_->disputes.find(txID) != result_->disputes.end())
1789 continue;
1790
1791 JLOG(j_.debug()) << "Transaction " << txID << " is disputed";
1792
1793 typename Result::Dispute_t dtx{
1794 tx,
1795 result_->txns.exists(txID),
1796 std::max(prevProposers_, currPeerPositions_.size()),
1797 j_};
1798
1799 // Update all of the available peer's votes on the disputed transaction
1800 for (auto const& [nodeId, peerPos] : currPeerPositions_)
1801 {
1802 Proposal_t const& peerProp = peerPos.proposal();
1803 auto const cit = acquired_.find(peerProp.position());
1804 if (cit != acquired_.end())
1805 dtx.setVote(nodeId, cit->second.exists(txID));
1806 }
1807 adaptor_.share(dtx.tx());
1808
1809 result_->disputes.emplace(txID, std::move(dtx));
1810 }
1811 JLOG(j_.debug()) << dc << " differences found";
1812 CLOG(clog) << "disputes: " << dc << ". ";
1813}
1814
1815template <class Adaptor>
1816void
1818{
1819 // Cannot updateDisputes without our stance
1820 XRPL_ASSERT(result_, "ripple::Consensus::updateDisputes : result is set");
1821
1822 // Ensure we have created disputes against this set if we haven't seen
1823 // it before
1824 if (result_->compares.find(other.id()) == result_->compares.end())
1825 createDisputes(other);
1826
1827 for (auto& it : result_->disputes)
1828 {
1829 auto& d = it.second;
1830 d.setVote(node, other.exists(d.tx().id()));
1831 }
1832}
1833
1834template <class Adaptor>
1837{
1838 return roundCloseTime(raw, closeResolution_);
1839}
1840
1841} // namespace ripple
1842
1843#endif
T begin(T... args)
Decorator for streaming out compact json.
Definition: json_writer.h:318
Represents a JSON value.
Definition: json_value.h:148
Value & append(const Value &value)
Append value to array at the end.
Definition: json_value.cpp:897
Json::Int Int
Definition: json_value.h:156
A generic endpoint for log messages.
Definition: Journal.h:60
Stream error() const
Definition: Journal.h:346
Stream debug() const
Definition: Journal.h:328
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
NodeID_t const & nodeID() const
Identifying which peer took this position.
NetClock::time_point const & closeTime() const
The current position on the consensus close time.
Position_t const & position() const
Get the proposed position.
bool isStale(NetClock::time_point cutoff) const
Get whether this position is stale relative to the provided cutoff.
Measures the duration of phases of consensus.
void set(ConsensusMode mode, Adaptor &a)
Definition: Consensus.h:321
MonitoredMode(ConsensusMode m)
Definition: Consensus.h:311
ConsensusMode get() const
Definition: Consensus.h:315
Generic implementation of consensus algorithm.
Definition: Consensus.h:291
void playbackProposals()
If we radically changed our consensus context for some reason, we need to replay recent proposals so ...
Definition: Consensus.h:1133
void timerEntry(NetClock::time_point const &now, std::unique_ptr< std::stringstream > const &clog={})
Call periodically to drive consensus forward.
Definition: Consensus.h:841
ConsensusTimer openTime_
Definition: Consensus.h:572
void startRoundInternal(NetClock::time_point const &now, typename Ledger_t::ID const &prevLedgerID, Ledger_t const &prevLedger, ConsensusMode mode, std::unique_ptr< std::stringstream > const &clog)
Definition: Consensus.h:680
void phaseEstablish(std::unique_ptr< std::stringstream > const &clog)
Handle establish phase.
Definition: Consensus.h:1346
typename Adaptor::PeerPosition_t PeerPosition_t
Definition: Consensus.h:296
ConsensusPhase phase_
Definition: Consensus.h:560
NetClock::time_point prevCloseTime_
Definition: Consensus.h:585
clock_type const & clock_
Definition: Consensus.h:565
void leaveConsensus(std::unique_ptr< std::stringstream > const &clog)
Definition: Consensus.h:1728
void updateDisputes(NodeID_t const &node, TxSet_t const &other)
Definition: Consensus.h:1817
Ledger_t previousLedger_
Definition: Consensus.h:593
typename Adaptor::TxSet_t TxSet_t
Definition: Consensus.h:293
void updateOurPositions(std::unique_ptr< std::stringstream > const &clog)
Definition: Consensus.h:1467
bool haveConsensus(std::unique_ptr< std::stringstream > const &clog)
Definition: Consensus.h:1662
Ledger_t::ID prevLedgerID() const
Get the previous ledger ID.
Definition: Consensus.h:424
void handleWrongLedger(typename Ledger_t::ID const &lgrId, std::unique_ptr< std::stringstream > const &clog)
Definition: Consensus.h:1044
void checkLedger(std::unique_ptr< std::stringstream > const &clog)
Check if our previous ledger matches the network's.
Definition: Consensus.h:1100
hash_map< NodeID_t, std::deque< PeerPosition_t > > recentPeerPositions_
Definition: Consensus.h:609
void simulate(NetClock::time_point const &now, std::optional< std::chrono::milliseconds > consensusDelay)
Simulate the consensus process without any network traffic.
Definition: Consensus.h:922
Json::Value getJson(bool full) const
Get the Json state of the consensus process.
Definition: Consensus.h:946
typename TxSet_t::Tx Tx_t
Definition: Consensus.h:295
void startRound(NetClock::time_point const &now, typename Ledger_t::ID const &prevLedgerID, Ledger_t prevLedger, hash_set< NodeID_t > const &nowUntrusted, bool proposing, std::unique_ptr< std::stringstream > const &clog={})
Kick-off the next round of consensus.
Definition: Consensus.h:633
Consensus(Consensus &&) noexcept=default
NetClock::time_point now_
Definition: Consensus.h:584
std::size_t prevProposers_
Definition: Consensus.h:612
NetClock::time_point asCloseTime(NetClock::time_point raw) const
Definition: Consensus.h:1836
beast::Journal const j_
Definition: Consensus.h:618
void createDisputes(TxSet_t const &o, std::unique_ptr< std::stringstream > const &clog={})
Definition: Consensus.h:1747
void gotTxSet(NetClock::time_point const &now, TxSet_t const &txSet)
Process a transaction set acquired from the network.
Definition: Consensus.h:874
Adaptor & adaptor_
Definition: Consensus.h:558
typename Adaptor::Ledger_t Ledger_t
Definition: Consensus.h:292
ConsensusPhase phase() const
Definition: Consensus.h:430
hash_map< typename TxSet_t::ID, const TxSet_t > acquired_
Definition: Consensus.h:596
typename Adaptor::NodeID_t NodeID_t
Definition: Consensus.h:294
void closeLedger(std::unique_ptr< std::stringstream > const &clog)
Definition: Consensus.h:1411
NetClock::duration closeResolution_
Definition: Consensus.h:574
bool peerProposal(NetClock::time_point const &now, PeerPosition_t const &newProposal)
A peer has proposed a new position, adjust our tracking.
Definition: Consensus.h:725
bool peerProposalInternal(NetClock::time_point const &now, PeerPosition_t const &newProposal)
Handle a replayed or a new peer proposal.
Definition: Consensus.h:746
MonitoredMode mode_
Definition: Consensus.h:561
hash_map< NodeID_t, PeerPosition_t > currPeerPositions_
Definition: Consensus.h:605
bool shouldPause(std::unique_ptr< std::stringstream > const &clog) const
Evaluate whether pausing increases likelihood of validation.
Definition: Consensus.h:1224
void phaseOpen(std::unique_ptr< std::stringstream > const &clog)
Handle pre-close phase.
Definition: Consensus.h:1150
ConsensusCloseTimes rawCloseTimes_
Definition: Consensus.h:599
std::chrono::milliseconds prevRoundTime_
Definition: Consensus.h:577
std::optional< Result > result_
Definition: Consensus.h:598
hash_set< NodeID_t > deadNodes_
Definition: Consensus.h:615
Ledger_t::ID prevLedgerID_
Definition: Consensus.h:591
bool haveCloseTimeConsensus_
Definition: Consensus.h:563
A transaction discovered to be in dispute during consensus.
Definition: DisputedTx.h:48
T emplace(T... args)
T max(T... args)
T min(T... args)
@ arrayValue
array value (ordered list)
Definition: json_value.h:43
@ objectValue
object value (collection of name/value pairs).
Definition: json_value.h:44
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: algorithm.h:26
ConsensusState checkConsensus(std::size_t prevProposers, std::size_t currentProposers, std::size_t currentAgree, std::size_t currentFinished, std::chrono::milliseconds previousAgreeTime, std::chrono::milliseconds currentAgreeTime, ConsensusParms const &parms, bool proposing, beast::Journal j, std::unique_ptr< std::stringstream > const &clog)
Determine whether the network reached consensus and whether we joined.
Definition: Consensus.cpp:163
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.
@ switchedLedger
We switched ledgers since we started this consensus round but are now running on what we believe is t...
@ observing
We are observing peer positions, but not proposing our position.
std::chrono::time_point< Clock, Duration > roundCloseTime(std::chrono::time_point< Clock, Duration > closeTime, std::chrono::duration< Rep, Period > closeResolution)
Calculates the close time for a ledger, given a close time resolution.
Definition: LedgerTiming.h:133
bool set(T &target, std::string const &name, Section const &section)
Set a value from a configuration Section If the named value is not found or doesn't parse as a T,...
Definition: BasicConfig.h:315
auto constexpr ledgerDefaultTimeResolution
Initial resolution of ledger close time.
Definition: LedgerTiming.h:44
ConsensusPhase
Phases of consensus for a single ledger round.
@ accepted
We have accepted a new last closed ledger and are waiting on a call to startRound to begin the next c...
@ open
We haven't closed our ledger yet, but others might have.
@ establish
Establishing consensus by exchanging proposals with our peers.
ConsensusState
Whether we have or don't have a consensus.
@ MovedOn
The network has consensus without us.
@ No
We do not have consensus.
std::string to_string(base_uint< Bits, Tag > const &a)
Definition: base_uint.h:630
bool shouldCloseLedger(bool anyTransactions, std::size_t prevProposers, std::size_t proposersClosed, std::size_t proposersValidated, std::chrono::milliseconds prevRoundTime, std::chrono::milliseconds timeSincePrevClose, std::chrono::milliseconds openTime, std::chrono::milliseconds idleInterval, ConsensusParms const &parms, beast::Journal j, std::unique_ptr< std::stringstream > const &clog)
Determines whether the current ledger should close at this time.
Definition: Consensus.cpp:26
Json::Value getJson(LedgerFill const &fill)
Return a new Json::Value representing the ledger with given options.
int participantsNeeded(int participants, int percent)
How many of the participants must agree to reach a given threshold?
Definition: Consensus.h:1458
std::chrono::duration< Rep, Period > getNextLedgerTimeResolution(std::chrono::duration< Rep, Period > previousResolution, bool previousAgree, Seq ledgerSeq)
Calculates the close time resolution for the specified ledger.
Definition: LedgerTiming.h:80
STL namespace.
T read(T... args)
T size(T... args)
T str(T... args)
Stores the set of initial close times.
Consensus algorithm parameters.
std::size_t avINIT_CONSENSUS_PCT
Percentage of nodes on our UNL that must vote yes.
std::chrono::milliseconds ledgerMIN_CONSENSUS
The number of seconds we wait minimum to ensure participation.
std::chrono::milliseconds avMIN_CONSENSUS_TIME
The minimum amount of time to consider the previous round to have taken.
std::size_t avLATE_CONSENSUS_PCT
Percentage of nodes that most vote yes after advancing.
std::size_t avSTUCK_CONSENSUS_PCT
Percentage of nodes that must vote yes after we are stuck.
std::chrono::seconds proposeFRESHNESS
How long we consider a proposal fresh.
std::size_t avLATE_CONSENSUS_TIME
Percentage of previous round duration before we advance.
std::chrono::seconds proposeINTERVAL
How often we force generating a new proposal to keep ours fresh.
std::size_t avCT_CONSENSUS_PCT
Percentage of nodes required to reach agreement on ledger close time.
std::size_t avMID_CONSENSUS_PCT
Percentage of nodes that most vote yes after advancing.
std::size_t avSTUCK_CONSENSUS_TIME
Percentage of previous round duration before we are stuck.
std::size_t avMID_CONSENSUS_TIME
Percentage of previous round duration before we advance.
Encapsulates the result of consensus.
T time_since_epoch(T... args)
T to_string(T... args)
T value_or(T... args)