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 whenever 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  JLOG(j_.debug()) << "transitioned to ConsensusPhase::open";
670  mode_.set(mode, adaptor_);
671  now_ = now;
672  prevLedgerID_ = prevLedgerID;
673  previousLedger_ = prevLedger;
674  result_.reset();
675  convergePercent_ = 0;
676  haveCloseTimeConsensus_ = false;
677  openTime_.reset(clock_.now());
678  currPeerPositions_.clear();
679  acquired_.clear();
680  rawCloseTimes_.peers.clear();
681  rawCloseTimes_.self = {};
682  deadNodes_.clear();
683 
684  closeResolution_ = getNextLedgerTimeResolution(
685  previousLedger_.closeTimeResolution(),
686  previousLedger_.closeAgree(),
687  previousLedger_.seq() + typename Ledger_t::Seq{1});
688 
689  playbackProposals();
690  if (currPeerPositions_.size() > (prevProposers_ / 2))
691  {
692  // We may be falling behind, don't wait for the timer
693  // consider closing the ledger immediately
694  timerEntry(now_);
695  }
696 }
697 
698 template <class Adaptor>
699 bool
701  NetClock::time_point const& now,
702  PeerPosition_t const& newPeerPos)
703 {
704  auto const& peerID = newPeerPos.proposal().nodeID();
705 
706  // Always need to store recent positions
707  {
708  auto& props = recentPeerPositions_[peerID];
709 
710  if (props.size() >= 10)
711  props.pop_front();
712 
713  props.push_back(newPeerPos);
714  }
715  return peerProposalInternal(now, newPeerPos);
716 }
717 
718 template <class Adaptor>
719 bool
721  NetClock::time_point const& now,
722  PeerPosition_t const& newPeerPos)
723 {
724  // Nothing to do for now if we are currently working on a ledger
725  if (phase_ == ConsensusPhase::accepted)
726  return false;
727 
728  now_ = now;
729 
730  auto const& newPeerProp = newPeerPos.proposal();
731 
732  if (newPeerProp.prevLedger() != prevLedgerID_)
733  {
734  JLOG(j_.debug()) << "Got proposal for " << newPeerProp.prevLedger()
735  << " but we are on " << prevLedgerID_;
736  return false;
737  }
738 
739  auto const& peerID = newPeerProp.nodeID();
740 
741  if (deadNodes_.find(peerID) != deadNodes_.end())
742  {
743  JLOG(j_.info()) << "Position from dead node: " << peerID;
744  return false;
745  }
746 
747  {
748  // update current position
749  auto peerPosIt = currPeerPositions_.find(peerID);
750 
751  if (peerPosIt != currPeerPositions_.end())
752  {
753  if (newPeerProp.proposeSeq() <=
754  peerPosIt->second.proposal().proposeSeq())
755  {
756  return false;
757  }
758  }
759 
760  if (newPeerProp.isBowOut())
761  {
762  JLOG(j_.info()) << "Peer " << peerID << " bows out";
763  if (result_)
764  {
765  for (auto& it : result_->disputes)
766  it.second.unVote(peerID);
767  }
768  if (peerPosIt != currPeerPositions_.end())
769  currPeerPositions_.erase(peerID);
770  deadNodes_.insert(peerID);
771 
772  return true;
773  }
774 
775  if (peerPosIt != currPeerPositions_.end())
776  peerPosIt->second = newPeerPos;
777  else
778  currPeerPositions_.emplace(peerID, newPeerPos);
779  }
780 
781  if (newPeerProp.isInitial())
782  {
783  // Record the close time estimate
784  JLOG(j_.trace()) << "Peer reports close time as "
785  << newPeerProp.closeTime().time_since_epoch().count();
786  ++rawCloseTimes_.peers[newPeerProp.closeTime()];
787  }
788 
789  JLOG(j_.trace()) << "Processing peer proposal " << newPeerProp.proposeSeq()
790  << "/" << newPeerProp.position();
791 
792  {
793  auto const ait = acquired_.find(newPeerProp.position());
794  if (ait == acquired_.end())
795  {
796  // acquireTxSet will return the set if it is available, or
797  // spawn a request for it and return nullopt/nullptr. It will call
798  // gotTxSet once it arrives
799  if (auto set = adaptor_.acquireTxSet(newPeerProp.position()))
800  gotTxSet(now_, *set);
801  else
802  JLOG(j_.debug()) << "Don't have tx set for peer";
803  }
804  else if (result_)
805  {
806  updateDisputes(newPeerProp.nodeID(), ait->second);
807  }
808  }
809 
810  return true;
811 }
812 
813 template <class Adaptor>
814 void
816 {
817  // Nothing to do if we are currently working on a ledger
818  if (phase_ == ConsensusPhase::accepted)
819  return;
820 
821  now_ = now;
822 
823  // Check we are on the proper ledger (this may change phase_)
824  checkLedger();
825 
826  if (phase_ == ConsensusPhase::open)
827  {
828  phaseOpen();
829  }
830  else if (phase_ == ConsensusPhase::establish)
831  {
832  phaseEstablish();
833  }
834 }
835 
836 template <class Adaptor>
837 void
839  NetClock::time_point const& now,
840  TxSet_t const& txSet)
841 {
842  // Nothing to do if we've finished work on a ledger
843  if (phase_ == ConsensusPhase::accepted)
844  return;
845 
846  now_ = now;
847 
848  auto id = txSet.id();
849 
850  // If we've already processed this transaction set since requesting
851  // it from the network, there is nothing to do now
852  if (!acquired_.emplace(id, txSet).second)
853  return;
854 
855  if (!result_)
856  {
857  JLOG(j_.debug()) << "Not creating disputes: no position yet.";
858  }
859  else
860  {
861  // Our position is added to acquired_ as soon as we create it,
862  // so this txSet must differ
863  assert(id != result_->position.position());
864  bool any = false;
865  for (auto const& [nodeId, peerPos] : currPeerPositions_)
866  {
867  if (peerPos.proposal().position() == id)
868  {
869  updateDisputes(nodeId, txSet);
870  any = true;
871  }
872  }
873 
874  if (!any)
875  {
876  JLOG(j_.warn())
877  << "By the time we got " << id << " no peers were proposing it";
878  }
879  }
880 }
881 
882 template <class Adaptor>
883 void
885  NetClock::time_point const& now,
887 {
888  using namespace std::chrono_literals;
889  JLOG(j_.info()) << "Simulating consensus";
890  now_ = now;
891  closeLedger();
892  result_->roundTime.tick(consensusDelay.value_or(100ms));
893  result_->proposers = prevProposers_ = currPeerPositions_.size();
894  prevRoundTime_ = result_->roundTime.read();
895  phase_ = ConsensusPhase::accepted;
896  adaptor_.onForceAccept(
897  *result_,
898  previousLedger_,
899  closeResolution_,
900  rawCloseTimes_,
901  mode_.get(),
902  getJson(true));
903  JLOG(j_.info()) << "Simulation complete";
904 }
905 
906 template <class Adaptor>
909 {
910  using std::to_string;
911  using Int = Json::Value::Int;
912 
914 
915  ret["proposing"] = (mode_.get() == ConsensusMode::proposing);
916  ret["proposers"] = static_cast<int>(currPeerPositions_.size());
917 
918  if (mode_.get() != ConsensusMode::wrongLedger)
919  {
920  ret["synched"] = true;
921  ret["ledger_seq"] =
922  static_cast<std::uint32_t>(previousLedger_.seq()) + 1;
923  ret["close_granularity"] = static_cast<Int>(closeResolution_.count());
924  }
925  else
926  ret["synched"] = false;
927 
928  ret["phase"] = to_string(phase_);
929 
930  if (result_ && !result_->disputes.empty() && !full)
931  ret["disputes"] = static_cast<Int>(result_->disputes.size());
932 
933  if (result_)
934  ret["our_position"] = result_->position.getJson();
935 
936  if (full)
937  {
938  if (result_)
939  ret["current_ms"] =
940  static_cast<Int>(result_->roundTime.read().count());
941  ret["converge_percent"] = convergePercent_;
942  ret["close_resolution"] = static_cast<Int>(closeResolution_.count());
943  ret["have_time_consensus"] = haveCloseTimeConsensus_;
944  ret["previous_proposers"] = static_cast<Int>(prevProposers_);
945  ret["previous_mseconds"] = static_cast<Int>(prevRoundTime_.count());
946 
947  if (!currPeerPositions_.empty())
948  {
950 
951  for (auto const& [nodeId, peerPos] : currPeerPositions_)
952  {
953  ppj[to_string(nodeId)] = peerPos.getJson();
954  }
955  ret["peer_positions"] = std::move(ppj);
956  }
957 
958  if (!acquired_.empty())
959  {
961  for (auto const& at : acquired_)
962  {
963  acq.append(to_string(at.first));
964  }
965  ret["acquired"] = std::move(acq);
966  }
967 
968  if (result_ && !result_->disputes.empty())
969  {
971  for (auto const& [txId, dispute] : result_->disputes)
972  {
973  dsj[to_string(txId)] = dispute.getJson();
974  }
975  ret["disputes"] = std::move(dsj);
976  }
977 
978  if (!rawCloseTimes_.peers.empty())
979  {
981  for (auto const& ct : rawCloseTimes_.peers)
982  {
983  ctj[std::to_string(ct.first.time_since_epoch().count())] =
984  ct.second;
985  }
986  ret["close_times"] = std::move(ctj);
987  }
988 
989  if (!deadNodes_.empty())
990  {
992  for (auto const& dn : deadNodes_)
993  {
994  dnj.append(to_string(dn));
995  }
996  ret["dead_nodes"] = std::move(dnj);
997  }
998  }
999 
1000  return ret;
1001 }
1002 
1003 // Handle a change in the prior ledger during a consensus round
1004 template <class Adaptor>
1005 void
1006 Consensus<Adaptor>::handleWrongLedger(typename Ledger_t::ID const& lgrId)
1007 {
1008  assert(lgrId != prevLedgerID_ || previousLedger_.id() != lgrId);
1009 
1010  // Stop proposing because we are out of sync
1011  leaveConsensus();
1012 
1013  // First time switching to this ledger
1014  if (prevLedgerID_ != lgrId)
1015  {
1016  prevLedgerID_ = lgrId;
1017 
1018  // Clear out state
1019  if (result_)
1020  {
1021  result_->disputes.clear();
1022  result_->compares.clear();
1023  }
1024 
1025  currPeerPositions_.clear();
1026  rawCloseTimes_.peers.clear();
1027  deadNodes_.clear();
1028 
1029  // Get back in sync, this will also recreate disputes
1030  playbackProposals();
1031  }
1032 
1033  if (previousLedger_.id() == prevLedgerID_)
1034  return;
1035 
1036  // we need to switch the ledger we're working from
1037  if (auto newLedger = adaptor_.acquireLedger(prevLedgerID_))
1038  {
1039  JLOG(j_.info()) << "Have the consensus ledger " << prevLedgerID_;
1040  startRoundInternal(
1041  now_, lgrId, *newLedger, ConsensusMode::switchedLedger);
1042  }
1043  else
1044  {
1045  mode_.set(ConsensusMode::wrongLedger, adaptor_);
1046  }
1047 }
1048 
1049 template <class Adaptor>
1050 void
1052 {
1053  auto netLgr =
1054  adaptor_.getPrevLedger(prevLedgerID_, previousLedger_, mode_.get());
1055 
1056  if (netLgr != prevLedgerID_)
1057  {
1058  JLOG(j_.warn()) << "View of consensus changed during "
1059  << to_string(phase_) << " status=" << to_string(phase_)
1060  << ", "
1061  << " mode=" << to_string(mode_.get());
1062  JLOG(j_.warn()) << prevLedgerID_ << " to " << netLgr;
1063  JLOG(j_.warn()) << Json::Compact{previousLedger_.getJson()};
1064  JLOG(j_.debug()) << "State on consensus change "
1065  << Json::Compact{getJson(true)};
1066  handleWrongLedger(netLgr);
1067  }
1068  else if (previousLedger_.id() != prevLedgerID_)
1069  handleWrongLedger(netLgr);
1070 }
1071 
1072 template <class Adaptor>
1073 void
1075 {
1076  for (auto const& it : recentPeerPositions_)
1077  {
1078  for (auto const& pos : it.second)
1079  {
1080  if (pos.proposal().prevLedger() == prevLedgerID_)
1081  {
1082  if (peerProposalInternal(now_, pos))
1083  adaptor_.share(pos);
1084  }
1085  }
1086  }
1087 }
1088 
1089 template <class Adaptor>
1090 void
1092 {
1093  using namespace std::chrono;
1094 
1095  // it is shortly before ledger close time
1096  bool anyTransactions = adaptor_.hasOpenTransactions();
1097  auto proposersClosed = currPeerPositions_.size();
1098  auto proposersValidated = adaptor_.proposersValidated(prevLedgerID_);
1099 
1100  openTime_.tick(clock_.now());
1101 
1102  // This computes how long since last ledger's close time
1103  milliseconds sinceClose;
1104  {
1105  bool previousCloseCorrect =
1106  (mode_.get() != ConsensusMode::wrongLedger) &&
1107  previousLedger_.closeAgree() &&
1108  (previousLedger_.closeTime() !=
1109  (previousLedger_.parentCloseTime() + 1s));
1110 
1111  auto lastCloseTime = previousCloseCorrect
1112  ? previousLedger_.closeTime() // use consensus timing
1113  : prevCloseTime_; // use the time we saw internally
1114 
1115  if (now_ >= lastCloseTime)
1116  sinceClose = duration_cast<milliseconds>(now_ - lastCloseTime);
1117  else
1118  sinceClose = -duration_cast<milliseconds>(lastCloseTime - now_);
1119  }
1120 
1121  auto const idleInterval = std::max<milliseconds>(
1122  adaptor_.parms().ledgerIDLE_INTERVAL,
1123  2 * previousLedger_.closeTimeResolution());
1124 
1125  // Decide if we should close the ledger
1126  if (shouldCloseLedger(
1127  anyTransactions,
1128  prevProposers_,
1129  proposersClosed,
1130  proposersValidated,
1131  prevRoundTime_,
1132  sinceClose,
1133  openTime_.read(),
1134  idleInterval,
1135  adaptor_.parms(),
1136  j_))
1137  {
1138  closeLedger();
1139  }
1140 }
1141 
1142 template <class Adaptor>
1143 bool
1145 {
1146  auto const& parms = adaptor_.parms();
1147  std::uint32_t const ahead(
1148  previousLedger_.seq() -
1149  std::min(adaptor_.getValidLedgerIndex(), previousLedger_.seq()));
1150  auto [quorum, trustedKeys] = adaptor_.getQuorumKeys();
1151  std::size_t const totalValidators = trustedKeys.size();
1152  std::size_t laggards =
1153  adaptor_.laggards(previousLedger_.seq(), trustedKeys);
1154  std::size_t const offline = trustedKeys.size();
1155 
1156  std::stringstream vars;
1157  vars << " (working seq: " << previousLedger_.seq() << ", "
1158  << "validated seq: " << adaptor_.getValidLedgerIndex() << ", "
1159  << "am validator: " << adaptor_.validator() << ", "
1160  << "have validated: " << adaptor_.haveValidated() << ", "
1161  << "roundTime: " << result_->roundTime.read().count() << ", "
1162  << "max consensus time: " << parms.ledgerMAX_CONSENSUS.count() << ", "
1163  << "validators: " << totalValidators << ", "
1164  << "laggards: " << laggards << ", "
1165  << "offline: " << offline << ", "
1166  << "quorum: " << quorum << ")";
1167 
1168  if (!ahead || !laggards || !totalValidators || !adaptor_.validator() ||
1169  !adaptor_.haveValidated() ||
1170  result_->roundTime.read() > parms.ledgerMAX_CONSENSUS)
1171  {
1172  j_.debug() << "not pausing (early)" << vars.str();
1173  return false;
1174  }
1175 
1176  bool willPause = false;
1177 
1191  constexpr static std::size_t maxPausePhase = 4;
1192 
1212  std::size_t const phase = (ahead - 1) % (maxPausePhase + 1);
1213 
1214  // validators that remain after the laggards() function are considered
1215  // offline, and should be considered as laggards for purposes of
1216  // evaluating whether the threshold for non-laggards has been reached.
1217  switch (phase)
1218  {
1219  case 0:
1220  // Laggards and offline shouldn't preclude consensus.
1221  if (laggards + offline > totalValidators - quorum)
1222  willPause = true;
1223  break;
1224  case maxPausePhase:
1225  // No tolerance.
1226  willPause = true;
1227  break;
1228  default:
1229  // Ensure that sufficient validators are known to be not lagging.
1230  // Their sufficiently most recent validation sequence was equal to
1231  // or greater than our own.
1232  //
1233  // The threshold is the amount required for quorum plus
1234  // the proportion of the remainder based on number of intermediate
1235  // phases between 0 and max.
1236  float const nonLaggards = totalValidators - (laggards + offline);
1237  float const quorumRatio =
1238  static_cast<float>(quorum) / totalValidators;
1239  float const allowedDissent = 1.0f - quorumRatio;
1240  float const phaseFactor = static_cast<float>(phase) / maxPausePhase;
1241 
1242  if (nonLaggards / totalValidators <
1243  quorumRatio + (allowedDissent * phaseFactor))
1244  {
1245  willPause = true;
1246  }
1247  }
1248 
1249  if (willPause)
1250  j_.warn() << "pausing" << vars.str();
1251  else
1252  j_.debug() << "not pausing" << vars.str();
1253  return willPause;
1254 }
1255 
1256 template <class Adaptor>
1257 void
1259 {
1260  // can only establish consensus if we already took a stance
1261  assert(result_);
1262 
1263  using namespace std::chrono;
1264  ConsensusParms const& parms = adaptor_.parms();
1265 
1266  result_->roundTime.tick(clock_.now());
1267  result_->proposers = currPeerPositions_.size();
1268 
1269  convergePercent_ = result_->roundTime.read() * 100 /
1270  std::max<milliseconds>(prevRoundTime_, parms.avMIN_CONSENSUS_TIME);
1271 
1272  // Give everyone a chance to take an initial position
1273  if (result_->roundTime.read() < parms.ledgerMIN_CONSENSUS)
1274  return;
1275 
1276  updateOurPositions();
1277 
1278  // Nothing to do if too many laggards or we don't have consensus.
1279  if (shouldPause() || !haveConsensus())
1280  return;
1281 
1282  if (!haveCloseTimeConsensus_)
1283  {
1284  JLOG(j_.info()) << "We have TX consensus but not CT consensus";
1285  return;
1286  }
1287 
1288  JLOG(j_.info()) << "Converge cutoff (" << currPeerPositions_.size()
1289  << " participants)";
1290  adaptor_.updateOperatingMode(currPeerPositions_.size());
1291  prevProposers_ = currPeerPositions_.size();
1292  prevRoundTime_ = result_->roundTime.read();
1293  phase_ = ConsensusPhase::accepted;
1294  JLOG(j_.debug()) << "transitioned to ConsensusPhase::accepted";
1295  adaptor_.onAccept(
1296  *result_,
1297  previousLedger_,
1298  closeResolution_,
1299  rawCloseTimes_,
1300  mode_.get(),
1301  getJson(true));
1302 }
1303 
1304 template <class Adaptor>
1305 void
1307 {
1308  // We should not be closing if we already have a position
1309  assert(!result_);
1310 
1311  phase_ = ConsensusPhase::establish;
1312  JLOG(j_.debug()) << "transitioned to ConsensusPhase::establish";
1313  rawCloseTimes_.self = now_;
1314 
1315  result_.emplace(adaptor_.onClose(previousLedger_, now_, mode_.get()));
1316  result_->roundTime.reset(clock_.now());
1317  // Share the newly created transaction set if we haven't already
1318  // received it from a peer
1319  if (acquired_.emplace(result_->txns.id(), result_->txns).second)
1320  adaptor_.share(result_->txns);
1321 
1322  if (mode_.get() == ConsensusMode::proposing)
1323  adaptor_.propose(result_->position);
1324 
1325  // Create disputes with any peer positions we have transactions for
1326  for (auto const& pit : currPeerPositions_)
1327  {
1328  auto const& pos = pit.second.proposal().position();
1329  auto const it = acquired_.find(pos);
1330  if (it != acquired_.end())
1331  {
1332  createDisputes(it->second);
1333  }
1334  }
1335 }
1336 
1349 inline int
1350 participantsNeeded(int participants, int percent)
1351 {
1352  int result = ((participants * percent) + (percent / 2)) / 100;
1353 
1354  return (result == 0) ? 1 : result;
1355 }
1356 
1357 template <class Adaptor>
1358 void
1360 {
1361  // We must have a position if we are updating it
1362  assert(result_);
1363  ConsensusParms const& parms = adaptor_.parms();
1364 
1365  // Compute a cutoff time
1366  auto const peerCutoff = now_ - parms.proposeFRESHNESS;
1367  auto const ourCutoff = now_ - parms.proposeINTERVAL;
1368 
1369  // Verify freshness of peer positions and compute close times
1370  std::map<NetClock::time_point, int> closeTimeVotes;
1371  {
1372  auto it = currPeerPositions_.begin();
1373  while (it != currPeerPositions_.end())
1374  {
1375  Proposal_t const& peerProp = it->second.proposal();
1376  if (peerProp.isStale(peerCutoff))
1377  {
1378  // peer's proposal is stale, so remove it
1379  NodeID_t const& peerID = peerProp.nodeID();
1380  JLOG(j_.warn()) << "Removing stale proposal from " << peerID;
1381  for (auto& dt : result_->disputes)
1382  dt.second.unVote(peerID);
1383  it = currPeerPositions_.erase(it);
1384  }
1385  else
1386  {
1387  // proposal is still fresh
1388  ++closeTimeVotes[asCloseTime(peerProp.closeTime())];
1389  ++it;
1390  }
1391  }
1392  }
1393 
1394  // This will stay unseated unless there are any changes
1395  std::optional<TxSet_t> ourNewSet;
1396 
1397  // Update votes on disputed transactions
1398  {
1400  for (auto& [txId, dispute] : result_->disputes)
1401  {
1402  // Because the threshold for inclusion increases,
1403  // time can change our position on a dispute
1404  if (dispute.updateVote(
1405  convergePercent_,
1406  mode_.get() == ConsensusMode::proposing,
1407  parms))
1408  {
1409  if (!mutableSet)
1410  mutableSet.emplace(result_->txns);
1411 
1412  if (dispute.getOurVote())
1413  {
1414  // now a yes
1415  mutableSet->insert(dispute.tx());
1416  }
1417  else
1418  {
1419  // now a no
1420  mutableSet->erase(txId);
1421  }
1422  }
1423  }
1424 
1425  if (mutableSet)
1426  ourNewSet.emplace(std::move(*mutableSet));
1427  }
1428 
1429  NetClock::time_point consensusCloseTime = {};
1430  haveCloseTimeConsensus_ = false;
1431 
1432  if (currPeerPositions_.empty())
1433  {
1434  // no other times
1435  haveCloseTimeConsensus_ = true;
1436  consensusCloseTime = asCloseTime(result_->position.closeTime());
1437  }
1438  else
1439  {
1440  int neededWeight;
1441 
1442  if (convergePercent_ < parms.avMID_CONSENSUS_TIME)
1443  neededWeight = parms.avINIT_CONSENSUS_PCT;
1444  else if (convergePercent_ < parms.avLATE_CONSENSUS_TIME)
1445  neededWeight = parms.avMID_CONSENSUS_PCT;
1446  else if (convergePercent_ < parms.avSTUCK_CONSENSUS_TIME)
1447  neededWeight = parms.avLATE_CONSENSUS_PCT;
1448  else
1449  neededWeight = parms.avSTUCK_CONSENSUS_PCT;
1450 
1451  int participants = currPeerPositions_.size();
1452  if (mode_.get() == ConsensusMode::proposing)
1453  {
1454  ++closeTimeVotes[asCloseTime(result_->position.closeTime())];
1455  ++participants;
1456  }
1457 
1458  // Threshold for non-zero vote
1459  int threshVote = participantsNeeded(participants, neededWeight);
1460 
1461  // Threshold to declare consensus
1462  int const threshConsensus =
1463  participantsNeeded(participants, parms.avCT_CONSENSUS_PCT);
1464 
1465  JLOG(j_.info()) << "Proposers:" << currPeerPositions_.size()
1466  << " nw:" << neededWeight << " thrV:" << threshVote
1467  << " thrC:" << threshConsensus;
1468 
1469  for (auto const& [t, v] : closeTimeVotes)
1470  {
1471  JLOG(j_.debug())
1472  << "CCTime: seq "
1473  << static_cast<std::uint32_t>(previousLedger_.seq()) + 1 << ": "
1474  << t.time_since_epoch().count() << " has " << v << ", "
1475  << threshVote << " required";
1476 
1477  if (v >= threshVote)
1478  {
1479  // A close time has enough votes for us to try to agree
1480  consensusCloseTime = t;
1481  threshVote = v;
1482 
1483  if (threshVote >= threshConsensus)
1484  haveCloseTimeConsensus_ = true;
1485  }
1486  }
1487 
1488  if (!haveCloseTimeConsensus_)
1489  {
1490  JLOG(j_.debug())
1491  << "No CT consensus:"
1492  << " Proposers:" << currPeerPositions_.size()
1493  << " Mode:" << to_string(mode_.get())
1494  << " Thresh:" << threshConsensus
1495  << " Pos:" << consensusCloseTime.time_since_epoch().count();
1496  }
1497  }
1498 
1499  if (!ourNewSet &&
1500  ((consensusCloseTime != asCloseTime(result_->position.closeTime())) ||
1501  result_->position.isStale(ourCutoff)))
1502  {
1503  // close time changed or our position is stale
1504  ourNewSet.emplace(result_->txns);
1505  }
1506 
1507  if (ourNewSet)
1508  {
1509  auto newID = ourNewSet->id();
1510 
1511  result_->txns = std::move(*ourNewSet);
1512 
1513  JLOG(j_.info()) << "Position change: CTime "
1514  << consensusCloseTime.time_since_epoch().count()
1515  << ", tx " << newID;
1516 
1517  result_->position.changePosition(newID, consensusCloseTime, now_);
1518 
1519  // Share our new transaction set and update disputes
1520  // if we haven't already received it
1521  if (acquired_.emplace(newID, result_->txns).second)
1522  {
1523  if (!result_->position.isBowOut())
1524  adaptor_.share(result_->txns);
1525 
1526  for (auto const& [nodeId, peerPos] : currPeerPositions_)
1527  {
1528  Proposal_t const& p = peerPos.proposal();
1529  if (p.position() == newID)
1530  updateDisputes(nodeId, result_->txns);
1531  }
1532  }
1533 
1534  // Share our new position if we are still participating this round
1535  if (!result_->position.isBowOut() &&
1536  (mode_.get() == ConsensusMode::proposing))
1537  adaptor_.propose(result_->position);
1538  }
1539 }
1540 
1541 template <class Adaptor>
1542 bool
1544 {
1545  // Must have a stance if we are checking for consensus
1546  assert(result_);
1547 
1548  // CHECKME: should possibly count unacquired TX sets as disagreeing
1549  int agree = 0, disagree = 0;
1550 
1551  auto ourPosition = result_->position.position();
1552 
1553  // Count number of agreements/disagreements with our position
1554  for (auto const& [nodeId, peerPos] : currPeerPositions_)
1555  {
1556  Proposal_t const& peerProp = peerPos.proposal();
1557  if (peerProp.position() == ourPosition)
1558  {
1559  ++agree;
1560  }
1561  else
1562  {
1563  JLOG(j_.debug()) << nodeId << " has " << peerProp.position();
1564  ++disagree;
1565  }
1566  }
1567  auto currentFinished =
1568  adaptor_.proposersFinished(previousLedger_, prevLedgerID_);
1569 
1570  JLOG(j_.debug()) << "Checking for TX consensus: agree=" << agree
1571  << ", disagree=" << disagree;
1572 
1573  // Determine if we actually have consensus or not
1574  result_->state = checkConsensus(
1575  prevProposers_,
1576  agree + disagree,
1577  agree,
1578  currentFinished,
1579  prevRoundTime_,
1580  result_->roundTime.read(),
1581  adaptor_.parms(),
1582  mode_.get() == ConsensusMode::proposing,
1583  j_);
1584 
1585  if (result_->state == ConsensusState::No)
1586  return false;
1587 
1588  // There is consensus, but we need to track if the network moved on
1589  // without us.
1590  if (result_->state == ConsensusState::MovedOn)
1591  {
1592  JLOG(j_.error()) << "Unable to reach consensus";
1593  JLOG(j_.error()) << Json::Compact{getJson(true)};
1594  }
1595 
1596  return true;
1597 }
1598 
1599 template <class Adaptor>
1600 void
1602 {
1603  if (mode_.get() == ConsensusMode::proposing)
1604  {
1605  if (result_ && !result_->position.isBowOut())
1606  {
1607  result_->position.bowOut(now_);
1608  adaptor_.propose(result_->position);
1609  }
1610 
1611  mode_.set(ConsensusMode::observing, adaptor_);
1612  JLOG(j_.info()) << "Bowing out of consensus";
1613  }
1614 }
1615 
1616 template <class Adaptor>
1617 void
1619 {
1620  // Cannot create disputes without our stance
1621  assert(result_);
1622 
1623  // Only create disputes if this is a new set
1624  if (!result_->compares.emplace(o.id()).second)
1625  return;
1626 
1627  // Nothing to dispute if we agree
1628  if (result_->txns.id() == o.id())
1629  return;
1630 
1631  JLOG(j_.debug()) << "createDisputes " << result_->txns.id() << " to "
1632  << o.id();
1633 
1634  auto differences = result_->txns.compare(o);
1635 
1636  int dc = 0;
1637 
1638  for (auto const& [txId, inThisSet] : differences)
1639  {
1640  ++dc;
1641  // create disputed transactions (from the ledger that has them)
1642  assert(
1643  (inThisSet && result_->txns.find(txId) && !o.find(txId)) ||
1644  (!inThisSet && !result_->txns.find(txId) && o.find(txId)));
1645 
1646  Tx_t tx = inThisSet ? result_->txns.find(txId) : o.find(txId);
1647  auto txID = tx.id();
1648 
1649  if (result_->disputes.find(txID) != result_->disputes.end())
1650  continue;
1651 
1652  JLOG(j_.debug()) << "Transaction " << txID << " is disputed";
1653 
1654  typename Result::Dispute_t dtx{
1655  tx,
1656  result_->txns.exists(txID),
1657  std::max(prevProposers_, currPeerPositions_.size()),
1658  j_};
1659 
1660  // Update all of the available peer's votes on the disputed transaction
1661  for (auto const& [nodeId, peerPos] : currPeerPositions_)
1662  {
1663  Proposal_t const& peerProp = peerPos.proposal();
1664  auto const cit = acquired_.find(peerProp.position());
1665  if (cit != acquired_.end())
1666  dtx.setVote(nodeId, cit->second.exists(txID));
1667  }
1668  adaptor_.share(dtx.tx());
1669 
1670  result_->disputes.emplace(txID, std::move(dtx));
1671  }
1672  JLOG(j_.debug()) << dc << " differences found";
1673 }
1674 
1675 template <class Adaptor>
1676 void
1678 {
1679  // Cannot updateDisputes without our stance
1680  assert(result_);
1681 
1682  // Ensure we have created disputes against this set if we haven't seen
1683  // it before
1684  if (result_->compares.find(other.id()) == result_->compares.end())
1685  createDisputes(other);
1686 
1687  for (auto& it : result_->disputes)
1688  {
1689  auto& d = it.second;
1690  d.setVote(node, other.exists(d.tx().id()));
1691  }
1692 }
1693 
1694 template <class Adaptor>
1697 {
1698  return roundCloseTime(raw, closeResolution_);
1699 }
1700 
1701 } // namespace ripple
1702 
1703 #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:1051
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:1074
ripple::Consensus::shouldPause
bool shouldPause() const
Evaluate whether pausing increases likelihood of validation.
Definition: Consensus.h:1144
ripple::ConsensusProposal::isStale
bool isStale(NetClock::time_point cutoff) const
Get whether this position is stale relative to the provided cutoff.
Definition: ConsensusProposal.h:157
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:1696
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:1618
ripple::Consensus::handleWrongLedger
void handleWrongLedger(typename Ledger_t::ID const &lgrId)
Definition: Consensus.h:1006
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:908
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:1601
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:838
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:1091
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:815
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:1677
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:1258
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:296
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:93
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:127
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:720
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:700
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:1543
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:1306
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:1359
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:884
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:100
ripple::participantsNeeded
int participantsNeeded(int participants, int percent)
How many of the participants must agree to reach a given threshold?
Definition: Consensus.h:1350
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:641
std::chrono