rippled
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 <ripple/basics/Log.h>
24 #include <ripple/basics/chrono.h>
25 #include <ripple/beast/utility/Journal.h>
26 #include <ripple/consensus/ConsensusParms.h>
27 #include <ripple/consensus/ConsensusProposal.h>
28 #include <ripple/consensus/ConsensusTypes.h>
29 #include <ripple/consensus/DisputedTx.h>
30 #include <ripple/consensus/LedgerTiming.h>
31 #include <ripple/json/json_writer.h>
32 #include <boost/logic/tribool.hpp>
33 #include <optional>
34 #include <sstream>
35 
36 namespace ripple {
37 
56 bool
58  bool anyTransactions,
59  std::size_t prevProposers,
60  std::size_t proposersClosed,
61  std::size_t proposersValidated,
62  std::chrono::milliseconds prevRoundTime,
63  std::chrono::milliseconds timeSincePrevClose,
65  std::chrono::milliseconds idleInterval,
66  ConsensusParms const& parms,
67  beast::Journal j);
68 
85  std::size_t prevProposers,
86  std::size_t currentProposers,
87  std::size_t currentAgree,
88  std::size_t currentFinished,
89  std::chrono::milliseconds previousAgreeTime,
90  std::chrono::milliseconds currentAgreeTime,
91  ConsensusParms const& parms,
92  bool proposing,
93  beast::Journal j);
94 
283 template <class Adaptor>
285 {
286  using Ledger_t = typename Adaptor::Ledger_t;
287  using TxSet_t = typename Adaptor::TxSet_t;
288  using NodeID_t = typename Adaptor::NodeID_t;
289  using Tx_t = typename TxSet_t::Tx;
290  using PeerPosition_t = typename Adaptor::PeerPosition_t;
292  NodeID_t,
293  typename Ledger_t::ID,
294  typename TxSet_t::ID>;
295 
297 
298  // Helper class to ensure adaptor is notified whenver the ConsensusMode
299  // changes
301  {
303 
304  public:
306  {
307  }
309  get() const
310  {
311  return mode_;
312  }
313 
314  void
315  set(ConsensusMode mode, Adaptor& a)
316  {
317  a.onModeChange(mode_, mode);
318  mode_ = mode;
319  }
320  };
321 
322 public:
325 
326  Consensus(Consensus&&) noexcept = default;
327 
334  Consensus(clock_type const& clock, Adaptor& adaptor, beast::Journal j);
335 
349  void
350  startRound(
351  NetClock::time_point const& now,
352  typename Ledger_t::ID const& prevLedgerID,
353  Ledger_t prevLedger,
354  hash_set<NodeID_t> const& nowUntrusted,
355  bool proposing);
356 
363  bool
364  peerProposal(
365  NetClock::time_point const& now,
366  PeerPosition_t const& newProposal);
367 
372  void
373  timerEntry(NetClock::time_point const& now);
374 
380  void
381  gotTxSet(NetClock::time_point const& now, TxSet_t const& txSet);
382 
399  void
400  simulate(
401  NetClock::time_point const& now,
402  std::optional<std::chrono::milliseconds> consensusDelay);
403 
411  typename Ledger_t::ID
412  prevLedgerID() const
413  {
414  return prevLedgerID_;
415  }
416 
418  phase() const
419  {
420  return phase_;
421  }
422 
431  getJson(bool full) const;
432 
433 private:
434  void
436  NetClock::time_point const& now,
437  typename Ledger_t::ID const& prevLedgerID,
438  Ledger_t const& prevLedger,
439  ConsensusMode mode);
440 
441  // Change our view of the previous ledger
442  void
443  handleWrongLedger(typename Ledger_t::ID const& lgrId);
444 
450  void
451  checkLedger();
452 
456  void
458 
461  bool
463  NetClock::time_point const& now,
464  PeerPosition_t const& newProposal);
465 
472  void
473  phaseOpen();
474 
483  void
484  phaseEstablish();
485 
508  bool
509  shouldPause() const;
510 
511  // Close the open ledger and establish initial position.
512  void
513  closeLedger();
514 
515  // Adjust our positions to try to agree with other validators.
516  void
518 
519  bool
520  haveConsensus();
521 
522  // Create disputes between our position and the provided one.
523  void
524  createDisputes(TxSet_t const& o);
525 
526  // Update our disputes given that this node has adopted a new position.
527  // Will call createDisputes as needed.
528  void
529  updateDisputes(NodeID_t const& node, TxSet_t const& other);
530 
531  // Revoke our outstanding proposal, if any, and cease proposing
532  // until this round ends.
533  void
534  leaveConsensus();
535 
536  // The rounded or effective close time estimate from a proposer
539 
540 private:
541  Adaptor& adaptor_;
542 
545  bool firstRound_ = true;
547 
549 
550  // How long the consensus convergence has taken, expressed as
551  // a percentage of the time that we expected it to take.
553 
554  // How long has this round been open
556 
558 
559  // Time it took for the last consensus round to converge
561 
562  //-------------------------------------------------------------------------
563  // Network time measurements of consensus progress
564 
565  // The current network adjusted time. This is the network time the
566  // ledger would close if it closed now
569 
570  //-------------------------------------------------------------------------
571  // Non-peer (self) consensus data
572 
573  // Last validated ledger ID provided to consensus
574  typename Ledger_t::ID prevLedgerID_;
575  // Last validated ledger seen by consensus
577 
578  // Transaction Sets, indexed by hash of transaction tree
580 
583 
584  //-------------------------------------------------------------------------
585  // Peer related consensus data
586 
587  // Peer proposed positions for the current round
589 
590  // Recently received peer positions, available when transitioning between
591  // ledgers or rounds
593 
594  // The number of proposers who participated in the last consensus round
596 
597  // nodes that have bowed out of this consensus process
599 
600  // Journal for debugging
602 };
603 
604 template <class Adaptor>
606  clock_type const& clock,
607  Adaptor& adaptor,
608  beast::Journal journal)
609  : adaptor_(adaptor), clock_(clock), j_{journal}
610 {
611  JLOG(j_.debug()) << "Creating consensus object";
612 }
613 
614 template <class Adaptor>
615 void
617  NetClock::time_point const& now,
618  typename Ledger_t::ID const& prevLedgerID,
619  Ledger_t prevLedger,
620  hash_set<NodeID_t> const& nowUntrusted,
621  bool proposing)
622 {
623  if (firstRound_)
624  {
625  // take our initial view of closeTime_ from the seed ledger
626  prevRoundTime_ = adaptor_.parms().ledgerIDLE_INTERVAL;
627  prevCloseTime_ = prevLedger.closeTime();
628  firstRound_ = false;
629  }
630  else
631  {
632  prevCloseTime_ = rawCloseTimes_.self;
633  }
634 
635  for (NodeID_t const& n : nowUntrusted)
636  recentPeerPositions_.erase(n);
637 
638  ConsensusMode startMode =
639  proposing ? ConsensusMode::proposing : ConsensusMode::observing;
640 
641  // We were handed the wrong ledger
642  if (prevLedger.id() != prevLedgerID)
643  {
644  // try to acquire the correct one
645  if (auto newLedger = adaptor_.acquireLedger(prevLedgerID))
646  {
647  prevLedger = *newLedger;
648  }
649  else // Unable to acquire the correct ledger
650  {
651  startMode = ConsensusMode::wrongLedger;
652  JLOG(j_.info())
653  << "Entering consensus with: " << previousLedger_.id();
654  JLOG(j_.info()) << "Correct LCL is: " << prevLedgerID;
655  }
656  }
657 
658  startRoundInternal(now, prevLedgerID, prevLedger, startMode);
659 }
660 template <class Adaptor>
661 void
663  NetClock::time_point const& now,
664  typename Ledger_t::ID const& prevLedgerID,
665  Ledger_t const& prevLedger,
666  ConsensusMode mode)
667 {
668  phase_ = ConsensusPhase::open;
669  mode_.set(mode, adaptor_);
670  now_ = now;
671  prevLedgerID_ = prevLedgerID;
672  previousLedger_ = prevLedger;
673  result_.reset();
674  convergePercent_ = 0;
675  haveCloseTimeConsensus_ = false;
676  openTime_.reset(clock_.now());
677  currPeerPositions_.clear();
678  acquired_.clear();
679  rawCloseTimes_.peers.clear();
680  rawCloseTimes_.self = {};
681  deadNodes_.clear();
682 
683  closeResolution_ = getNextLedgerTimeResolution(
684  previousLedger_.closeTimeResolution(),
685  previousLedger_.closeAgree(),
686  previousLedger_.seq() + typename Ledger_t::Seq{1});
687 
688  playbackProposals();
689  if (currPeerPositions_.size() > (prevProposers_ / 2))
690  {
691  // We may be falling behind, don't wait for the timer
692  // consider closing the ledger immediately
693  timerEntry(now_);
694  }
695 }
696 
697 template <class Adaptor>
698 bool
700  NetClock::time_point const& now,
701  PeerPosition_t const& newPeerPos)
702 {
703  auto const& peerID = newPeerPos.proposal().nodeID();
704 
705  // Always need to store recent positions
706  {
707  auto& props = recentPeerPositions_[peerID];
708 
709  if (props.size() >= 10)
710  props.pop_front();
711 
712  props.push_back(newPeerPos);
713  }
714  return peerProposalInternal(now, newPeerPos);
715 }
716 
717 template <class Adaptor>
718 bool
720  NetClock::time_point const& now,
721  PeerPosition_t const& newPeerPos)
722 {
723  // Nothing to do for now if we are currently working on a ledger
724  if (phase_ == ConsensusPhase::accepted)
725  return false;
726 
727  now_ = now;
728 
729  auto const& newPeerProp = newPeerPos.proposal();
730 
731  if (newPeerProp.prevLedger() != prevLedgerID_)
732  {
733  JLOG(j_.debug()) << "Got proposal for " << newPeerProp.prevLedger()
734  << " but we are on " << prevLedgerID_;
735  return false;
736  }
737 
738  auto const& peerID = newPeerProp.nodeID();
739 
740  if (deadNodes_.find(peerID) != deadNodes_.end())
741  {
742  JLOG(j_.info()) << "Position from dead node: " << peerID;
743  return false;
744  }
745 
746  {
747  // update current position
748  auto peerPosIt = currPeerPositions_.find(peerID);
749 
750  if (peerPosIt != currPeerPositions_.end())
751  {
752  if (newPeerProp.proposeSeq() <=
753  peerPosIt->second.proposal().proposeSeq())
754  {
755  return false;
756  }
757  }
758 
759  if (newPeerProp.isBowOut())
760  {
761  JLOG(j_.info()) << "Peer " << peerID << " bows out";
762  if (result_)
763  {
764  for (auto& it : result_->disputes)
765  it.second.unVote(peerID);
766  }
767  if (peerPosIt != currPeerPositions_.end())
768  currPeerPositions_.erase(peerID);
769  deadNodes_.insert(peerID);
770 
771  return true;
772  }
773 
774  if (peerPosIt != currPeerPositions_.end())
775  peerPosIt->second = newPeerPos;
776  else
777  currPeerPositions_.emplace(peerID, newPeerPos);
778  }
779 
780  if (newPeerProp.isInitial())
781  {
782  // Record the close time estimate
783  JLOG(j_.trace()) << "Peer reports close time as "
784  << newPeerProp.closeTime().time_since_epoch().count();
785  ++rawCloseTimes_.peers[newPeerProp.closeTime()];
786  }
787 
788  JLOG(j_.trace()) << "Processing peer proposal " << newPeerProp.proposeSeq()
789  << "/" << newPeerProp.position();
790 
791  {
792  auto const ait = acquired_.find(newPeerProp.position());
793  if (ait == acquired_.end())
794  {
795  // acquireTxSet will return the set if it is available, or
796  // spawn a request for it and return nullopt/nullptr. It will call
797  // gotTxSet once it arrives
798  if (auto set = adaptor_.acquireTxSet(newPeerProp.position()))
799  gotTxSet(now_, *set);
800  else
801  JLOG(j_.debug()) << "Don't have tx set for peer";
802  }
803  else if (result_)
804  {
805  updateDisputes(newPeerProp.nodeID(), ait->second);
806  }
807  }
808 
809  return true;
810 }
811 
812 template <class Adaptor>
813 void
815 {
816  // Nothing to do if we are currently working on a ledger
817  if (phase_ == ConsensusPhase::accepted)
818  return;
819 
820  now_ = now;
821 
822  // Check we are on the proper ledger (this may change phase_)
823  checkLedger();
824 
825  if (phase_ == ConsensusPhase::open)
826  {
827  phaseOpen();
828  }
829  else if (phase_ == ConsensusPhase::establish)
830  {
831  phaseEstablish();
832  }
833 }
834 
835 template <class Adaptor>
836 void
838  NetClock::time_point const& now,
839  TxSet_t const& txSet)
840 {
841  // Nothing to do if we've finished work on a ledger
842  if (phase_ == ConsensusPhase::accepted)
843  return;
844 
845  now_ = now;
846 
847  auto id = txSet.id();
848 
849  // If we've already processed this transaction set since requesting
850  // it from the network, there is nothing to do now
851  if (!acquired_.emplace(id, txSet).second)
852  return;
853 
854  if (!result_)
855  {
856  JLOG(j_.debug()) << "Not creating disputes: no position yet.";
857  }
858  else
859  {
860  // Our position is added to acquired_ as soon as we create it,
861  // so this txSet must differ
862  assert(id != result_->position.position());
863  bool any = false;
864  for (auto const& [nodeId, peerPos] : currPeerPositions_)
865  {
866  if (peerPos.proposal().position() == id)
867  {
868  updateDisputes(nodeId, txSet);
869  any = true;
870  }
871  }
872 
873  if (!any)
874  {
875  JLOG(j_.warn())
876  << "By the time we got " << id << " no peers were proposing it";
877  }
878  }
879 }
880 
881 template <class Adaptor>
882 void
884  NetClock::time_point const& now,
886 {
887  using namespace std::chrono_literals;
888  JLOG(j_.info()) << "Simulating consensus";
889  now_ = now;
890  closeLedger();
891  result_->roundTime.tick(consensusDelay.value_or(100ms));
892  result_->proposers = prevProposers_ = currPeerPositions_.size();
893  prevRoundTime_ = result_->roundTime.read();
894  phase_ = ConsensusPhase::accepted;
895  adaptor_.onForceAccept(
896  *result_,
897  previousLedger_,
898  closeResolution_,
899  rawCloseTimes_,
900  mode_.get(),
901  getJson(true));
902  JLOG(j_.info()) << "Simulation complete";
903 }
904 
905 template <class Adaptor>
908 {
909  using std::to_string;
910  using Int = Json::Value::Int;
911 
913 
914  ret["proposing"] = (mode_.get() == ConsensusMode::proposing);
915  ret["proposers"] = static_cast<int>(currPeerPositions_.size());
916 
917  if (mode_.get() != ConsensusMode::wrongLedger)
918  {
919  ret["synched"] = true;
920  ret["ledger_seq"] =
921  static_cast<std::uint32_t>(previousLedger_.seq()) + 1;
922  ret["close_granularity"] = static_cast<Int>(closeResolution_.count());
923  }
924  else
925  ret["synched"] = false;
926 
927  ret["phase"] = to_string(phase_);
928 
929  if (result_ && !result_->disputes.empty() && !full)
930  ret["disputes"] = static_cast<Int>(result_->disputes.size());
931 
932  if (result_)
933  ret["our_position"] = result_->position.getJson();
934 
935  if (full)
936  {
937  if (result_)
938  ret["current_ms"] =
939  static_cast<Int>(result_->roundTime.read().count());
940  ret["converge_percent"] = convergePercent_;
941  ret["close_resolution"] = static_cast<Int>(closeResolution_.count());
942  ret["have_time_consensus"] = haveCloseTimeConsensus_;
943  ret["previous_proposers"] = static_cast<Int>(prevProposers_);
944  ret["previous_mseconds"] = static_cast<Int>(prevRoundTime_.count());
945 
946  if (!currPeerPositions_.empty())
947  {
949 
950  for (auto const& [nodeId, peerPos] : currPeerPositions_)
951  {
952  ppj[to_string(nodeId)] = peerPos.getJson();
953  }
954  ret["peer_positions"] = std::move(ppj);
955  }
956 
957  if (!acquired_.empty())
958  {
960  for (auto const& at : acquired_)
961  {
962  acq.append(to_string(at.first));
963  }
964  ret["acquired"] = std::move(acq);
965  }
966 
967  if (result_ && !result_->disputes.empty())
968  {
970  for (auto const& [txId, dispute] : result_->disputes)
971  {
972  dsj[to_string(txId)] = dispute.getJson();
973  }
974  ret["disputes"] = std::move(dsj);
975  }
976 
977  if (!rawCloseTimes_.peers.empty())
978  {
980  for (auto const& ct : rawCloseTimes_.peers)
981  {
982  ctj[std::to_string(ct.first.time_since_epoch().count())] =
983  ct.second;
984  }
985  ret["close_times"] = std::move(ctj);
986  }
987 
988  if (!deadNodes_.empty())
989  {
991  for (auto const& dn : deadNodes_)
992  {
993  dnj.append(to_string(dn));
994  }
995  ret["dead_nodes"] = std::move(dnj);
996  }
997  }
998 
999  return ret;
1000 }
1001 
1002 // Handle a change in the prior ledger during a consensus round
1003 template <class Adaptor>
1004 void
1005 Consensus<Adaptor>::handleWrongLedger(typename Ledger_t::ID const& lgrId)
1006 {
1007  assert(lgrId != prevLedgerID_ || previousLedger_.id() != lgrId);
1008 
1009  // Stop proposing because we are out of sync
1010  leaveConsensus();
1011 
1012  // First time switching to this ledger
1013  if (prevLedgerID_ != lgrId)
1014  {
1015  prevLedgerID_ = lgrId;
1016 
1017  // Clear out state
1018  if (result_)
1019  {
1020  result_->disputes.clear();
1021  result_->compares.clear();
1022  }
1023 
1024  currPeerPositions_.clear();
1025  rawCloseTimes_.peers.clear();
1026  deadNodes_.clear();
1027 
1028  // Get back in sync, this will also recreate disputes
1029  playbackProposals();
1030  }
1031 
1032  if (previousLedger_.id() == prevLedgerID_)
1033  return;
1034 
1035  // we need to switch the ledger we're working from
1036  if (auto newLedger = adaptor_.acquireLedger(prevLedgerID_))
1037  {
1038  JLOG(j_.info()) << "Have the consensus ledger " << prevLedgerID_;
1039  startRoundInternal(
1040  now_, lgrId, *newLedger, ConsensusMode::switchedLedger);
1041  }
1042  else
1043  {
1044  mode_.set(ConsensusMode::wrongLedger, adaptor_);
1045  }
1046 }
1047 
1048 template <class Adaptor>
1049 void
1051 {
1052  auto netLgr =
1053  adaptor_.getPrevLedger(prevLedgerID_, previousLedger_, mode_.get());
1054 
1055  if (netLgr != prevLedgerID_)
1056  {
1057  JLOG(j_.warn()) << "View of consensus changed during "
1058  << to_string(phase_) << " status=" << to_string(phase_)
1059  << ", "
1060  << " mode=" << to_string(mode_.get());
1061  JLOG(j_.warn()) << prevLedgerID_ << " to " << netLgr;
1062  JLOG(j_.warn()) << Json::Compact{previousLedger_.getJson()};
1063  JLOG(j_.debug()) << "State on consensus change "
1064  << Json::Compact{getJson(true)};
1065  handleWrongLedger(netLgr);
1066  }
1067  else if (previousLedger_.id() != prevLedgerID_)
1068  handleWrongLedger(netLgr);
1069 }
1070 
1071 template <class Adaptor>
1072 void
1074 {
1075  for (auto const& it : recentPeerPositions_)
1076  {
1077  for (auto const& pos : it.second)
1078  {
1079  if (pos.proposal().prevLedger() == prevLedgerID_)
1080  {
1081  if (peerProposalInternal(now_, pos))
1082  adaptor_.share(pos);
1083  }
1084  }
1085  }
1086 }
1087 
1088 template <class Adaptor>
1089 void
1091 {
1092  using namespace std::chrono;
1093 
1094  // it is shortly before ledger close time
1095  bool anyTransactions = adaptor_.hasOpenTransactions();
1096  auto proposersClosed = currPeerPositions_.size();
1097  auto proposersValidated = adaptor_.proposersValidated(prevLedgerID_);
1098 
1099  openTime_.tick(clock_.now());
1100 
1101  // This computes how long since last ledger's close time
1102  milliseconds sinceClose;
1103  {
1104  bool previousCloseCorrect =
1105  (mode_.get() != ConsensusMode::wrongLedger) &&
1106  previousLedger_.closeAgree() &&
1107  (previousLedger_.closeTime() !=
1108  (previousLedger_.parentCloseTime() + 1s));
1109 
1110  auto lastCloseTime = previousCloseCorrect
1111  ? previousLedger_.closeTime() // use consensus timing
1112  : prevCloseTime_; // use the time we saw internally
1113 
1114  if (now_ >= lastCloseTime)
1115  sinceClose = duration_cast<milliseconds>(now_ - lastCloseTime);
1116  else
1117  sinceClose = -duration_cast<milliseconds>(lastCloseTime - now_);
1118  }
1119 
1120  auto const idleInterval = std::max<milliseconds>(
1121  adaptor_.parms().ledgerIDLE_INTERVAL,
1122  2 * previousLedger_.closeTimeResolution());
1123 
1124  // Decide if we should close the ledger
1125  if (shouldCloseLedger(
1126  anyTransactions,
1127  prevProposers_,
1128  proposersClosed,
1129  proposersValidated,
1130  prevRoundTime_,
1131  sinceClose,
1132  openTime_.read(),
1133  idleInterval,
1134  adaptor_.parms(),
1135  j_))
1136  {
1137  closeLedger();
1138  }
1139 }
1140 
1141 template <class Adaptor>
1142 bool
1144 {
1145  auto const& parms = adaptor_.parms();
1146  std::uint32_t const ahead(
1147  previousLedger_.seq() -
1148  std::min(adaptor_.getValidLedgerIndex(), previousLedger_.seq()));
1149  auto [quorum, trustedKeys] = adaptor_.getQuorumKeys();
1150  std::size_t const totalValidators = trustedKeys.size();
1151  std::size_t laggards =
1152  adaptor_.laggards(previousLedger_.seq(), trustedKeys);
1153  std::size_t const offline = trustedKeys.size();
1154 
1155  std::stringstream vars;
1156  vars << " (working seq: " << previousLedger_.seq() << ", "
1157  << "validated seq: " << adaptor_.getValidLedgerIndex() << ", "
1158  << "am validator: " << adaptor_.validator() << ", "
1159  << "have validated: " << adaptor_.haveValidated() << ", "
1160  << "roundTime: " << result_->roundTime.read().count() << ", "
1161  << "max consensus time: " << parms.ledgerMAX_CONSENSUS.count() << ", "
1162  << "validators: " << totalValidators << ", "
1163  << "laggards: " << laggards << ", "
1164  << "offline: " << offline << ", "
1165  << "quorum: " << quorum << ")";
1166 
1167  if (!ahead || !laggards || !totalValidators || !adaptor_.validator() ||
1168  !adaptor_.haveValidated() ||
1169  result_->roundTime.read() > parms.ledgerMAX_CONSENSUS)
1170  {
1171  j_.debug() << "not pausing (early)" << vars.str();
1172  return false;
1173  }
1174 
1175  bool willPause = false;
1176 
1190  constexpr static std::size_t maxPausePhase = 4;
1191 
1211  std::size_t const phase = (ahead - 1) % (maxPausePhase + 1);
1212 
1213  // validators that remain after the laggards() function are considered
1214  // offline, and should be considered as laggards for purposes of
1215  // evaluating whether the threshold for non-laggards has been reached.
1216  switch (phase)
1217  {
1218  case 0:
1219  // Laggards and offline shouldn't preclude consensus.
1220  if (laggards + offline > totalValidators - quorum)
1221  willPause = true;
1222  break;
1223  case maxPausePhase:
1224  // No tolerance.
1225  willPause = true;
1226  break;
1227  default:
1228  // Ensure that sufficient validators are known to be not lagging.
1229  // Their sufficiently most recent validation sequence was equal to
1230  // or greater than our own.
1231  //
1232  // The threshold is the amount required for quorum plus
1233  // the proportion of the remainder based on number of intermediate
1234  // phases between 0 and max.
1235  float const nonLaggards = totalValidators - (laggards + offline);
1236  float const quorumRatio =
1237  static_cast<float>(quorum) / totalValidators;
1238  float const allowedDissent = 1.0f - quorumRatio;
1239  float const phaseFactor = static_cast<float>(phase) / maxPausePhase;
1240 
1241  if (nonLaggards / totalValidators <
1242  quorumRatio + (allowedDissent * phaseFactor))
1243  {
1244  willPause = true;
1245  }
1246  }
1247 
1248  if (willPause)
1249  j_.warn() << "pausing" << vars.str();
1250  else
1251  j_.debug() << "not pausing" << vars.str();
1252  return willPause;
1253 }
1254 
1255 template <class Adaptor>
1256 void
1258 {
1259  // can only establish consensus if we already took a stance
1260  assert(result_);
1261 
1262  using namespace std::chrono;
1263  ConsensusParms const& parms = adaptor_.parms();
1264 
1265  result_->roundTime.tick(clock_.now());
1266  result_->proposers = currPeerPositions_.size();
1267 
1268  convergePercent_ = result_->roundTime.read() * 100 /
1269  std::max<milliseconds>(prevRoundTime_, parms.avMIN_CONSENSUS_TIME);
1270 
1271  // Give everyone a chance to take an initial position
1272  if (result_->roundTime.read() < parms.ledgerMIN_CONSENSUS)
1273  return;
1274 
1275  updateOurPositions();
1276 
1277  // Nothing to do if too many laggards or we don't have consensus.
1278  if (shouldPause() || !haveConsensus())
1279  return;
1280 
1281  if (!haveCloseTimeConsensus_)
1282  {
1283  JLOG(j_.info()) << "We have TX consensus but not CT consensus";
1284  return;
1285  }
1286 
1287  JLOG(j_.info()) << "Converge cutoff (" << currPeerPositions_.size()
1288  << " participants)";
1289  adaptor_.updateOperatingMode(currPeerPositions_.size());
1290  prevProposers_ = currPeerPositions_.size();
1291  prevRoundTime_ = result_->roundTime.read();
1292  phase_ = ConsensusPhase::accepted;
1293  adaptor_.onAccept(
1294  *result_,
1295  previousLedger_,
1296  closeResolution_,
1297  rawCloseTimes_,
1298  mode_.get(),
1299  getJson(true));
1300 }
1301 
1302 template <class Adaptor>
1303 void
1305 {
1306  // We should not be closing if we already have a position
1307  assert(!result_);
1308 
1309  phase_ = ConsensusPhase::establish;
1310  rawCloseTimes_.self = now_;
1311 
1312  result_.emplace(adaptor_.onClose(previousLedger_, now_, mode_.get()));
1313  result_->roundTime.reset(clock_.now());
1314  // Share the newly created transaction set if we haven't already
1315  // received it from a peer
1316  if (acquired_.emplace(result_->txns.id(), result_->txns).second)
1317  adaptor_.share(result_->txns);
1318 
1319  if (mode_.get() == ConsensusMode::proposing)
1320  adaptor_.propose(result_->position);
1321 
1322  // Create disputes with any peer positions we have transactions for
1323  for (auto const& pit : currPeerPositions_)
1324  {
1325  auto const& pos = pit.second.proposal().position();
1326  auto const it = acquired_.find(pos);
1327  if (it != acquired_.end())
1328  {
1329  createDisputes(it->second);
1330  }
1331  }
1332 }
1333 
1346 inline int
1347 participantsNeeded(int participants, int percent)
1348 {
1349  int result = ((participants * percent) + (percent / 2)) / 100;
1350 
1351  return (result == 0) ? 1 : result;
1352 }
1353 
1354 template <class Adaptor>
1355 void
1357 {
1358  // We must have a position if we are updating it
1359  assert(result_);
1360  ConsensusParms const& parms = adaptor_.parms();
1361 
1362  // Compute a cutoff time
1363  auto const peerCutoff = now_ - parms.proposeFRESHNESS;
1364  auto const ourCutoff = now_ - parms.proposeINTERVAL;
1365 
1366  // Verify freshness of peer positions and compute close times
1367  std::map<NetClock::time_point, int> closeTimeVotes;
1368  {
1369  auto it = currPeerPositions_.begin();
1370  while (it != currPeerPositions_.end())
1371  {
1372  Proposal_t const& peerProp = it->second.proposal();
1373  if (peerProp.isStale(peerCutoff))
1374  {
1375  // peer's proposal is stale, so remove it
1376  NodeID_t const& peerID = peerProp.nodeID();
1377  JLOG(j_.warn()) << "Removing stale proposal from " << peerID;
1378  for (auto& dt : result_->disputes)
1379  dt.second.unVote(peerID);
1380  it = currPeerPositions_.erase(it);
1381  }
1382  else
1383  {
1384  // proposal is still fresh
1385  ++closeTimeVotes[asCloseTime(peerProp.closeTime())];
1386  ++it;
1387  }
1388  }
1389  }
1390 
1391  // This will stay unseated unless there are any changes
1392  std::optional<TxSet_t> ourNewSet;
1393 
1394  // Update votes on disputed transactions
1395  {
1397  for (auto& [txId, dispute] : result_->disputes)
1398  {
1399  // Because the threshold for inclusion increases,
1400  // time can change our position on a dispute
1401  if (dispute.updateVote(
1402  convergePercent_,
1403  mode_.get() == ConsensusMode::proposing,
1404  parms))
1405  {
1406  if (!mutableSet)
1407  mutableSet.emplace(result_->txns);
1408 
1409  if (dispute.getOurVote())
1410  {
1411  // now a yes
1412  mutableSet->insert(dispute.tx());
1413  }
1414  else
1415  {
1416  // now a no
1417  mutableSet->erase(txId);
1418  }
1419  }
1420  }
1421 
1422  if (mutableSet)
1423  ourNewSet.emplace(std::move(*mutableSet));
1424  }
1425 
1426  NetClock::time_point consensusCloseTime = {};
1427  haveCloseTimeConsensus_ = false;
1428 
1429  if (currPeerPositions_.empty())
1430  {
1431  // no other times
1432  haveCloseTimeConsensus_ = true;
1433  consensusCloseTime = asCloseTime(result_->position.closeTime());
1434  }
1435  else
1436  {
1437  int neededWeight;
1438 
1439  if (convergePercent_ < parms.avMID_CONSENSUS_TIME)
1440  neededWeight = parms.avINIT_CONSENSUS_PCT;
1441  else if (convergePercent_ < parms.avLATE_CONSENSUS_TIME)
1442  neededWeight = parms.avMID_CONSENSUS_PCT;
1443  else if (convergePercent_ < parms.avSTUCK_CONSENSUS_TIME)
1444  neededWeight = parms.avLATE_CONSENSUS_PCT;
1445  else
1446  neededWeight = parms.avSTUCK_CONSENSUS_PCT;
1447 
1448  int participants = currPeerPositions_.size();
1449  if (mode_.get() == ConsensusMode::proposing)
1450  {
1451  ++closeTimeVotes[asCloseTime(result_->position.closeTime())];
1452  ++participants;
1453  }
1454 
1455  // Threshold for non-zero vote
1456  int threshVote = participantsNeeded(participants, neededWeight);
1457 
1458  // Threshold to declare consensus
1459  int const threshConsensus =
1460  participantsNeeded(participants, parms.avCT_CONSENSUS_PCT);
1461 
1462  JLOG(j_.info()) << "Proposers:" << currPeerPositions_.size()
1463  << " nw:" << neededWeight << " thrV:" << threshVote
1464  << " thrC:" << threshConsensus;
1465 
1466  for (auto const& [t, v] : closeTimeVotes)
1467  {
1468  JLOG(j_.debug())
1469  << "CCTime: seq "
1470  << static_cast<std::uint32_t>(previousLedger_.seq()) + 1 << ": "
1471  << t.time_since_epoch().count() << " has " << v << ", "
1472  << threshVote << " required";
1473 
1474  if (v >= threshVote)
1475  {
1476  // A close time has enough votes for us to try to agree
1477  consensusCloseTime = t;
1478  threshVote = v;
1479 
1480  if (threshVote >= threshConsensus)
1481  haveCloseTimeConsensus_ = true;
1482  }
1483  }
1484 
1485  if (!haveCloseTimeConsensus_)
1486  {
1487  JLOG(j_.debug())
1488  << "No CT consensus:"
1489  << " Proposers:" << currPeerPositions_.size()
1490  << " Mode:" << to_string(mode_.get())
1491  << " Thresh:" << threshConsensus
1492  << " Pos:" << consensusCloseTime.time_since_epoch().count();
1493  }
1494  }
1495 
1496  if (!ourNewSet &&
1497  ((consensusCloseTime != asCloseTime(result_->position.closeTime())) ||
1498  result_->position.isStale(ourCutoff)))
1499  {
1500  // close time changed or our position is stale
1501  ourNewSet.emplace(result_->txns);
1502  }
1503 
1504  if (ourNewSet)
1505  {
1506  auto newID = ourNewSet->id();
1507 
1508  result_->txns = std::move(*ourNewSet);
1509 
1510  JLOG(j_.info()) << "Position change: CTime "
1511  << consensusCloseTime.time_since_epoch().count()
1512  << ", tx " << newID;
1513 
1514  result_->position.changePosition(newID, consensusCloseTime, now_);
1515 
1516  // Share our new transaction set and update disputes
1517  // if we haven't already received it
1518  if (acquired_.emplace(newID, result_->txns).second)
1519  {
1520  if (!result_->position.isBowOut())
1521  adaptor_.share(result_->txns);
1522 
1523  for (auto const& [nodeId, peerPos] : currPeerPositions_)
1524  {
1525  Proposal_t const& p = peerPos.proposal();
1526  if (p.position() == newID)
1527  updateDisputes(nodeId, result_->txns);
1528  }
1529  }
1530 
1531  // Share our new position if we are still participating this round
1532  if (!result_->position.isBowOut() &&
1533  (mode_.get() == ConsensusMode::proposing))
1534  adaptor_.propose(result_->position);
1535  }
1536 }
1537 
1538 template <class Adaptor>
1539 bool
1541 {
1542  // Must have a stance if we are checking for consensus
1543  assert(result_);
1544 
1545  // CHECKME: should possibly count unacquired TX sets as disagreeing
1546  int agree = 0, disagree = 0;
1547 
1548  auto ourPosition = result_->position.position();
1549 
1550  // Count number of agreements/disagreements with our position
1551  for (auto const& [nodeId, peerPos] : currPeerPositions_)
1552  {
1553  Proposal_t const& peerProp = peerPos.proposal();
1554  if (peerProp.position() == ourPosition)
1555  {
1556  ++agree;
1557  }
1558  else
1559  {
1560  JLOG(j_.debug()) << nodeId << " has " << peerProp.position();
1561  ++disagree;
1562  }
1563  }
1564  auto currentFinished =
1565  adaptor_.proposersFinished(previousLedger_, prevLedgerID_);
1566 
1567  JLOG(j_.debug()) << "Checking for TX consensus: agree=" << agree
1568  << ", disagree=" << disagree;
1569 
1570  // Determine if we actually have consensus or not
1571  result_->state = checkConsensus(
1572  prevProposers_,
1573  agree + disagree,
1574  agree,
1575  currentFinished,
1576  prevRoundTime_,
1577  result_->roundTime.read(),
1578  adaptor_.parms(),
1579  mode_.get() == ConsensusMode::proposing,
1580  j_);
1581 
1582  if (result_->state == ConsensusState::No)
1583  return false;
1584 
1585  // There is consensus, but we need to track if the network moved on
1586  // without us.
1587  if (result_->state == ConsensusState::MovedOn)
1588  {
1589  JLOG(j_.error()) << "Unable to reach consensus";
1590  JLOG(j_.error()) << Json::Compact{getJson(true)};
1591  }
1592 
1593  return true;
1594 }
1595 
1596 template <class Adaptor>
1597 void
1599 {
1600  if (mode_.get() == ConsensusMode::proposing)
1601  {
1602  if (result_ && !result_->position.isBowOut())
1603  {
1604  result_->position.bowOut(now_);
1605  adaptor_.propose(result_->position);
1606  }
1607 
1608  mode_.set(ConsensusMode::observing, adaptor_);
1609  JLOG(j_.info()) << "Bowing out of consensus";
1610  }
1611 }
1612 
1613 template <class Adaptor>
1614 void
1616 {
1617  // Cannot create disputes without our stance
1618  assert(result_);
1619 
1620  // Only create disputes if this is a new set
1621  if (!result_->compares.emplace(o.id()).second)
1622  return;
1623 
1624  // Nothing to dispute if we agree
1625  if (result_->txns.id() == o.id())
1626  return;
1627 
1628  JLOG(j_.debug()) << "createDisputes " << result_->txns.id() << " to "
1629  << o.id();
1630 
1631  auto differences = result_->txns.compare(o);
1632 
1633  int dc = 0;
1634 
1635  for (auto const& [txId, inThisSet] : differences)
1636  {
1637  ++dc;
1638  // create disputed transactions (from the ledger that has them)
1639  assert(
1640  (inThisSet && result_->txns.find(txId) && !o.find(txId)) ||
1641  (!inThisSet && !result_->txns.find(txId) && o.find(txId)));
1642 
1643  Tx_t tx = inThisSet ? *result_->txns.find(txId) : *o.find(txId);
1644  auto txID = tx.id();
1645 
1646  if (result_->disputes.find(txID) != result_->disputes.end())
1647  continue;
1648 
1649  JLOG(j_.debug()) << "Transaction " << txID << " is disputed";
1650 
1651  typename Result::Dispute_t dtx{
1652  tx,
1653  result_->txns.exists(txID),
1654  std::max(prevProposers_, currPeerPositions_.size()),
1655  j_};
1656 
1657  // Update all of the available peer's votes on the disputed transaction
1658  for (auto const& [nodeId, peerPos] : currPeerPositions_)
1659  {
1660  Proposal_t const& peerProp = peerPos.proposal();
1661  auto const cit = acquired_.find(peerProp.position());
1662  if (cit != acquired_.end())
1663  dtx.setVote(nodeId, cit->second.exists(txID));
1664  }
1665  adaptor_.share(dtx.tx());
1666 
1667  result_->disputes.emplace(txID, std::move(dtx));
1668  }
1669  JLOG(j_.debug()) << dc << " differences found";
1670 }
1671 
1672 template <class Adaptor>
1673 void
1675 {
1676  // Cannot updateDisputes without our stance
1677  assert(result_);
1678 
1679  // Ensure we have created disputes against this set if we haven't seen
1680  // it before
1681  if (result_->compares.find(other.id()) == result_->compares.end())
1682  createDisputes(other);
1683 
1684  for (auto& it : result_->disputes)
1685  {
1686  auto& d = it.second;
1687  d.setVote(node, other.exists(d.tx().id()));
1688  }
1689 }
1690 
1691 template <class Adaptor>
1694 {
1695  return roundCloseTime(raw, closeResolution_);
1696 }
1697 
1698 } // namespace ripple
1699 
1700 #endif
ripple::ConsensusParms::avCT_CONSENSUS_PCT
std::size_t avCT_CONSENSUS_PCT
Percentage of nodes required to reach agreement on ledger close time.
Definition: ConsensusParms.h:137
Json::Value::Int
Json::Int Int
Definition: json_value.h:154
ripple::Consensus::deadNodes_
hash_set< NodeID_t > deadNodes_
Definition: Consensus.h:598
ripple::checkConsensus
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)
Determine whether the network reached consensus and whether we joined.
Definition: Consensus.cpp:108
sstream
ripple::Consensus::checkLedger
void checkLedger()
Check if our previous ledger matches the network's.
Definition: Consensus.h:1050
ripple::ConsensusState
ConsensusState
Whether we have or don't have a consensus.
Definition: ConsensusTypes.h:186
ripple::Consensus::playbackProposals
void playbackProposals()
If we radically changed our consensus context for some reason, we need to replay recent proposals so ...
Definition: Consensus.h:1073
ripple::Consensus::shouldPause
bool shouldPause() const
Evaluate whether pausing increases likelihood of validation.
Definition: Consensus.h:1143
ripple::ConsensusProposal::isStale
bool isStale(NetClock::time_point cutoff) const
Get whether this position is stale relative to the provided cutoff.
Definition: ConsensusProposal.h:154
ripple::ConsensusMode::proposing
@ proposing
We are normal participant in consensus and propose our position.
ripple::Consensus::result_
std::optional< Result > result_
Definition: Consensus.h:581
ripple::ConsensusTimer
Measures the duration of phases of consensus.
Definition: ConsensusTypes.h:134
ripple::Consensus::asCloseTime
NetClock::time_point asCloseTime(NetClock::time_point raw) const
Definition: Consensus.h:1693
beast::Journal::trace
Stream trace() const
Severity stream access functions.
Definition: Journal.h:309
ripple::shouldCloseLedger
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)
Determines whether the current ledger should close at this time.
Definition: Consensus.cpp:26
ripple::Consensus::createDisputes
void createDisputes(TxSet_t const &o)
Definition: Consensus.h:1615
ripple::Consensus::handleWrongLedger
void handleWrongLedger(typename Ledger_t::ID const &lgrId)
Definition: Consensus.h:1005
ripple::ConsensusParms::avMID_CONSENSUS_TIME
std::size_t avMID_CONSENSUS_TIME
Percentage of previous round duration before we advance.
Definition: ConsensusParms.h:119
ripple::Consensus::closeResolution_
NetClock::duration closeResolution_
Definition: Consensus.h:557
std::unordered_set
STL class.
ripple::Consensus< ripple::test::csf::Peer >::Tx_t
typename TxSet_t::Tx Tx_t
Definition: Consensus.h:289
Json::arrayValue
@ arrayValue
array value (ordered list)
Definition: json_value.h:42
ripple::ConsensusParms::avLATE_CONSENSUS_PCT
std::size_t avLATE_CONSENSUS_PCT
Percentage of nodes that most vote yes after advancing.
Definition: ConsensusParms.h:128
ripple::Consensus::MonitoredMode::set
void set(ConsensusMode mode, Adaptor &a)
Definition: Consensus.h:315
ripple::Consensus::MonitoredMode::MonitoredMode
MonitoredMode(ConsensusMode m)
Definition: Consensus.h:305
Json::Compact
Decorator for streaming out compact json.
Definition: json_writer.h:316
ripple::Consensus::getJson
Json::Value getJson(bool full) const
Get the Json state of the consensus process.
Definition: Consensus.h:907
ripple::ConsensusParms::avSTUCK_CONSENSUS_PCT
std::size_t avSTUCK_CONSENSUS_PCT
Percentage of nodes that must vote yes after we are stuck.
Definition: ConsensusParms.h:134
ripple::Consensus
Generic implementation of consensus algorithm.
Definition: Consensus.h:284
ripple::Consensus::recentPeerPositions_
hash_map< NodeID_t, std::deque< PeerPosition_t > > recentPeerPositions_
Definition: Consensus.h:592
std::optional::value_or
T value_or(T... args)
std::chrono::milliseconds
std::optional::emplace
T emplace(T... args)
std::stringstream
STL class.
beast::Journal::warn
Stream warn() const
Definition: Journal.h:327
ripple::Consensus::leaveConsensus
void leaveConsensus()
Definition: Consensus.h:1598
ripple::Consensus::phase
ConsensusPhase phase() const
Definition: Consensus.h:418
ripple::Consensus::j_
const beast::Journal j_
Definition: Consensus.h:601
ripple::roundCloseTime
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:129
ripple::Consensus::currPeerPositions_
hash_map< NodeID_t, PeerPosition_t > currPeerPositions_
Definition: Consensus.h:588
ripple::ConsensusParms::proposeINTERVAL
std::chrono::seconds proposeINTERVAL
How often we force generating a new proposal to keep ours fresh.
Definition: ConsensusParms.h:67
ripple::Consensus::prevProposers_
std::size_t prevProposers_
Definition: Consensus.h:595
ripple::Consensus::now_
NetClock::time_point now_
Definition: Consensus.h:567
std::stringstream::read
T read(T... args)
ripple::ConsensusPhase::accepted
@ accepted
We have accepted a new last closed ledger and are waiting on a call to startRound to begin the next c...
ripple::Consensus::gotTxSet
void gotTxSet(NetClock::time_point const &now, TxSet_t const &txSet)
Process a transaction set acquired from the network.
Definition: Consensus.h:837
ripple::ConsensusResult
Encapsulates the result of consensus.
Definition: ConsensusTypes.h:201
ripple::Consensus::acquired_
hash_map< typename TxSet_t::ID, const TxSet_t > acquired_
Definition: Consensus.h:579
ripple::Consensus::prevRoundTime_
std::chrono::milliseconds prevRoundTime_
Definition: Consensus.h:560
ripple::Consensus::clock_
clock_type const & clock_
Definition: Consensus.h:548
ripple::ConsensusMode::observing
@ observing
We are observing peer positions, but not proposing our position.
ripple::Consensus::adaptor_
Adaptor & adaptor_
Definition: Consensus.h:541
ripple::Consensus::phaseOpen
void phaseOpen()
Handle pre-close phase.
Definition: Consensus.h:1090
std::chrono::time_point::time_since_epoch
T time_since_epoch(T... args)
Json::Value::append
Value & append(const Value &value)
Append value to array at the end.
Definition: json_value.cpp:882
Json::objectValue
@ objectValue
object value (collection of name/value pairs).
Definition: json_value.h:43
ripple::Consensus::timerEntry
void timerEntry(NetClock::time_point const &now)
Call periodically to drive consensus forward.
Definition: Consensus.h:814
ripple::Consensus::convergePercent_
int convergePercent_
Definition: Consensus.h:552
ripple::Consensus::phase_
ConsensusPhase phase_
Definition: Consensus.h:543
ripple::Consensus::startRoundInternal
void startRoundInternal(NetClock::time_point const &now, typename Ledger_t::ID const &prevLedgerID, Ledger_t const &prevLedger, ConsensusMode mode)
Definition: Consensus.h:662
ripple::Consensus::Consensus
Consensus(Consensus &&) noexcept=default
std::to_string
T to_string(T... args)
ripple::ConsensusParms::proposeFRESHNESS
std::chrono::seconds proposeFRESHNESS
How long we consider a proposal fresh.
Definition: ConsensusParms.h:64
ripple::Consensus::prevLedgerID
Ledger_t::ID prevLedgerID() const
Get the previous ledger ID.
Definition: Consensus.h:412
ripple::set
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:313
ripple::ConsensusPhase
ConsensusPhase
Phases of consensus for a single ledger round.
Definition: ConsensusTypes.h:103
beast::Journal::error
Stream error() const
Definition: Journal.h:333
beast::Journal::info
Stream info() const
Definition: Journal.h:321
std::chrono::time_point
ripple::Consensus::updateDisputes
void updateDisputes(NodeID_t const &node, TxSet_t const &other)
Definition: Consensus.h:1674
ripple::Consensus< ripple::test::csf::Peer >::NodeID_t
typename ripple::test::csf::Peer ::NodeID_t NodeID_t
Definition: Consensus.h:288
ripple::Consensus::haveCloseTimeConsensus_
bool haveCloseTimeConsensus_
Definition: Consensus.h:546
ripple::ConsensusParms::avINIT_CONSENSUS_PCT
std::size_t avINIT_CONSENSUS_PCT
Percentage of nodes on our UNL that must vote yes.
Definition: ConsensusParms.h:116
beast::Journal
A generic endpoint for log messages.
Definition: Journal.h:58
std::uint32_t
ripple::Consensus::firstRound_
bool firstRound_
Definition: Consensus.h:545
std::map
STL class.
ripple::Consensus::mode_
MonitoredMode mode_
Definition: Consensus.h:544
ripple::Consensus< ripple::test::csf::Peer >::Ledger_t
typename ripple::test::csf::Peer ::Ledger_t Ledger_t
Definition: Consensus.h:286
ripple::ConsensusParms::avMID_CONSENSUS_PCT
std::size_t avMID_CONSENSUS_PCT
Percentage of nodes that most vote yes after advancing.
Definition: ConsensusParms.h:122
ripple::Consensus< ripple::test::csf::Peer >::PeerPosition_t
typename ripple::test::csf::Peer ::PeerPosition_t PeerPosition_t
Definition: Consensus.h:290
beast::abstract_clock< std::chrono::steady_clock >
ripple::Consensus::phaseEstablish
void phaseEstablish()
Handle establish phase.
Definition: Consensus.h:1257
std::min
T min(T... args)
ripple::getJson
Json::Value getJson(LedgerFill const &fill)
Return a new Json::Value representing the ledger with given options.
Definition: LedgerToJson.cpp:291
ripple::Consensus::MonitoredMode::get
ConsensusMode get() const
Definition: Consensus.h:309
ripple::ConsensusProposal::nodeID
NodeID_t const & nodeID() const
Identifying which peer took this position.
Definition: ConsensusProposal.h:90
ripple::Consensus::prevCloseTime_
NetClock::time_point prevCloseTime_
Definition: Consensus.h:568
ripple::ConsensusProposal::closeTime
NetClock::time_point const & closeTime() const
The current position on the consensus close time.
Definition: ConsensusProposal.h:124
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::ConsensusParms::ledgerMIN_CONSENSUS
std::chrono::milliseconds ledgerMIN_CONSENSUS
The number of seconds we wait minimum to ensure participation.
Definition: ConsensusParms.h:80
ripple::ConsensusMode
ConsensusMode
Represents how a node currently participates in Consensus.
Definition: ConsensusTypes.h:55
ripple::Consensus::peerProposalInternal
bool peerProposalInternal(NetClock::time_point const &now, PeerPosition_t const &newProposal)
Handle a replayed or a new peer proposal.
Definition: Consensus.h:719
ripple::ConsensusParms
Consensus algorithm parameters.
Definition: ConsensusParms.h:33
std::map::begin
T begin(T... args)
ripple::Consensus::MonitoredMode
Definition: Consensus.h:300
std
STL namespace.
ripple::ConsensusParms::avSTUCK_CONSENSUS_TIME
std::size_t avSTUCK_CONSENSUS_TIME
Percentage of previous round duration before we are stuck.
Definition: ConsensusParms.h:131
ripple::Consensus::prevLedgerID_
Ledger_t::ID prevLedgerID_
Definition: Consensus.h:574
ripple::Consensus::openTime_
ConsensusTimer openTime_
Definition: Consensus.h:555
ripple::Consensus::peerProposal
bool peerProposal(NetClock::time_point const &now, PeerPosition_t const &newProposal)
A peer has proposed a new position, adjust our tracking.
Definition: Consensus.h:699
optional
std::stringstream::str
T str(T... args)
ripple::DisputedTx
A transaction discovered to be in dispute during consensus.
Definition: DisputedTx.h:50
beast::Journal::debug
Stream debug() const
Definition: Journal.h:315
std::size_t
ripple::ConsensusParms::avLATE_CONSENSUS_TIME
std::size_t avLATE_CONSENSUS_TIME
Percentage of previous round duration before we advance.
Definition: ConsensusParms.h:125
ripple::Consensus< ripple::test::csf::Peer >::TxSet_t
typename ripple::test::csf::Peer ::TxSet_t TxSet_t
Definition: Consensus.h:287
ripple::Consensus::haveConsensus
bool haveConsensus()
Definition: Consensus.h:1540
std::max
T max(T... args)
ripple::getNextLedgerTimeResolution
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
ripple::Consensus::closeLedger
void closeLedger()
Definition: Consensus.h:1304
ripple::ledgerDefaultTimeResolution
constexpr auto ledgerDefaultTimeResolution
Initial resolution of ledger close time.
Definition: LedgerTiming.h:44
ripple::NetClock
Clock for measuring the network time.
Definition: chrono.h:48
ripple::Consensus::updateOurPositions
void updateOurPositions()
Definition: Consensus.h:1356
ripple::Consensus::simulate
void simulate(NetClock::time_point const &now, std::optional< std::chrono::milliseconds > consensusDelay)
Simulate the consensus process without any network traffic.
Definition: Consensus.h:883
ripple::Consensus::MonitoredMode::mode_
ConsensusMode mode_
Definition: Consensus.h:302
ripple::ConsensusParms::avMIN_CONSENSUS_TIME
std::chrono::milliseconds avMIN_CONSENSUS_TIME
The minimum amount of time to consider the previous round to have taken.
Definition: ConsensusParms.h:107
std::unordered_map
STL class.
ripple::ConsensusProposal::position
Position_t const & position() const
Get the proposed position.
Definition: ConsensusProposal.h:97
ripple::participantsNeeded
int participantsNeeded(int participants, int percent)
How many of the participants must agree to reach a given threshold?
Definition: Consensus.h:1347
ripple::Consensus::startRound
void startRound(NetClock::time_point const &now, typename Ledger_t::ID const &prevLedgerID, Ledger_t prevLedger, hash_set< NodeID_t > const &nowUntrusted, bool proposing)
Kick-off the next round of consensus.
Definition: Consensus.h:616
ripple::ConsensusCloseTimes
Stores the set of initial close times.
Definition: ConsensusTypes.h:174
Json::Value
Represents a JSON value.
Definition: json_value.h:145
ripple::Consensus::rawCloseTimes_
ConsensusCloseTimes rawCloseTimes_
Definition: Consensus.h:582
ripple::Consensus::previousLedger_
Ledger_t previousLedger_
Definition: Consensus.h:576
ripple::ConsensusProposal< NodeID_t, typename Ledger_t::ID, typename TxSet_t::ID >
beast
Definition: base_uint.h:657
std::chrono