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