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