20#ifndef RIPPLE_CONSENSUS_CONSENSUS_H_INCLUDED
21#define RIPPLE_CONSENSUS_CONSENSUS_H_INCLUDED
23#include <xrpld/consensus/ConsensusParms.h>
24#include <xrpld/consensus/ConsensusProposal.h>
25#include <xrpld/consensus/ConsensusTypes.h>
26#include <xrpld/consensus/DisputedTx.h>
27#include <xrpld/consensus/LedgerTiming.h>
28#include <xrpl/basics/Log.h>
29#include <xrpl/basics/chrono.h>
30#include <xrpl/beast/utility/Journal.h>
31#include <xrpl/json/json_writer.h>
32#include <boost/logic/tribool.hpp>
69 ConsensusParms
const& parms,
94 ConsensusParms
const& parms,
286template <
class Adaptor>
292 using Tx_t =
typename TxSet_t::Tx;
296 typename Ledger_t::ID,
297 typename TxSet_t::ID>;
320 a.onModeChange(
mode_, mode);
405 std::optional<
std::chrono::milliseconds> consensusDelay);
607template <
class Adaptor>
612 : adaptor_(adaptor), clock_(clock), j_{journal}
614 JLOG(
j_.
debug()) <<
"Creating consensus object";
617template <
class Adaptor>
621 typename Ledger_t::ID
const& prevLedgerID,
629 prevRoundTime_ = adaptor_.parms().ledgerIDLE_INTERVAL;
630 prevCloseTime_ = prevLedger.closeTime();
635 prevCloseTime_ = rawCloseTimes_.self;
638 for (
NodeID_t const& n : nowUntrusted)
639 recentPeerPositions_.erase(n);
645 if (prevLedger.id() != prevLedgerID)
648 if (
auto newLedger = adaptor_.acquireLedger(prevLedgerID))
650 prevLedger = *newLedger;
656 <<
"Entering consensus with: " << previousLedger_.id();
657 JLOG(j_.
info()) <<
"Correct LCL is: " << prevLedgerID;
661 startRoundInternal(now, prevLedgerID, prevLedger, startMode);
663template <
class Adaptor>
667 typename Ledger_t::ID
const& prevLedgerID,
672 JLOG(j_.
debug()) <<
"transitioned to ConsensusPhase::open";
673 mode_.set(mode, adaptor_);
675 prevLedgerID_ = prevLedgerID;
676 previousLedger_ = prevLedger;
678 convergePercent_ = 0;
679 haveCloseTimeConsensus_ =
false;
680 openTime_.reset(clock_.now());
681 currPeerPositions_.clear();
683 rawCloseTimes_.peers.clear();
684 rawCloseTimes_.self = {};
688 previousLedger_.closeTimeResolution(),
689 previousLedger_.closeAgree(),
690 previousLedger_.seq() +
typename Ledger_t::Seq{1});
693 if (currPeerPositions_.size() > (prevProposers_ / 2))
701template <
class Adaptor>
707 JLOG(j_.
debug()) <<
"PROPOSAL " << newPeerPos.render();
708 auto const& peerID = newPeerPos.proposal().nodeID();
712 auto& props = recentPeerPositions_[peerID];
714 if (props.size() >= 10)
717 props.push_back(newPeerPos);
719 return peerProposalInternal(now, newPeerPos);
722template <
class Adaptor>
734 auto const& newPeerProp = newPeerPos.proposal();
736 if (newPeerProp.prevLedger() != prevLedgerID_)
738 JLOG(j_.
debug()) <<
"Got proposal for " << newPeerProp.prevLedger()
739 <<
" but we are on " << prevLedgerID_;
743 auto const& peerID = newPeerProp.nodeID();
745 if (deadNodes_.find(peerID) != deadNodes_.end())
747 JLOG(j_.
info()) <<
"Position from dead node: " << peerID;
753 auto peerPosIt = currPeerPositions_.find(peerID);
755 if (peerPosIt != currPeerPositions_.end())
757 if (newPeerProp.proposeSeq() <=
758 peerPosIt->second.proposal().proposeSeq())
764 if (newPeerProp.isBowOut())
766 JLOG(j_.
info()) <<
"Peer " << peerID <<
" bows out";
769 for (
auto& it : result_->disputes)
770 it.second.unVote(peerID);
772 if (peerPosIt != currPeerPositions_.end())
773 currPeerPositions_.erase(peerID);
774 deadNodes_.insert(peerID);
779 if (peerPosIt != currPeerPositions_.end())
780 peerPosIt->second = newPeerPos;
782 currPeerPositions_.emplace(peerID, newPeerPos);
785 if (newPeerProp.isInitial())
788 JLOG(j_.
trace()) <<
"Peer reports close time as "
789 << newPeerProp.closeTime().time_since_epoch().count();
790 ++rawCloseTimes_.peers[newPeerProp.closeTime()];
793 JLOG(j_.
trace()) <<
"Processing peer proposal " << newPeerProp.proposeSeq()
794 <<
"/" << newPeerProp.position();
797 auto const ait = acquired_.find(newPeerProp.position());
798 if (ait == acquired_.end())
803 if (
auto set = adaptor_.acquireTxSet(newPeerProp.position()))
804 gotTxSet(now_, *
set);
806 JLOG(j_.
debug()) <<
"Don't have tx set for peer";
810 updateDisputes(newPeerProp.nodeID(), ait->second);
817template <
class Adaptor>
840template <
class Adaptor>
852 auto id = txSet.id();
856 if (!acquired_.emplace(
id, txSet).second)
861 JLOG(j_.
debug()) <<
"Not creating disputes: no position yet.";
868 id != result_->position.position(),
869 "ripple::Consensus::gotTxSet : updated transaction set");
871 for (
auto const& [nodeId, peerPos] : currPeerPositions_)
873 if (peerPos.proposal().position() ==
id)
875 updateDisputes(nodeId, txSet);
883 <<
"By the time we got " <<
id <<
" no peers were proposing it";
888template <
class Adaptor>
894 using namespace std::chrono_literals;
895 JLOG(j_.
info()) <<
"Simulating consensus";
898 result_->roundTime.tick(consensusDelay.
value_or(100ms));
899 result_->proposers = prevProposers_ = currPeerPositions_.size();
900 prevRoundTime_ = result_->roundTime.read();
902 adaptor_.onForceAccept(
909 JLOG(j_.
info()) <<
"Simulation complete";
912template <
class Adaptor>
922 ret[
"proposers"] =
static_cast<int>(currPeerPositions_.size());
926 ret[
"synched"] =
true;
929 ret[
"close_granularity"] =
static_cast<Int
>(closeResolution_.count());
932 ret[
"synched"] =
false;
936 if (result_ && !result_->disputes.empty() && !full)
937 ret[
"disputes"] =
static_cast<Int
>(result_->disputes.size());
940 ret[
"our_position"] = result_->position.getJson();
946 static_cast<Int
>(result_->roundTime.read().count());
947 ret[
"converge_percent"] = convergePercent_;
948 ret[
"close_resolution"] =
static_cast<Int
>(closeResolution_.count());
949 ret[
"have_time_consensus"] = haveCloseTimeConsensus_;
950 ret[
"previous_proposers"] =
static_cast<Int
>(prevProposers_);
951 ret[
"previous_mseconds"] =
static_cast<Int
>(prevRoundTime_.count());
953 if (!currPeerPositions_.empty())
957 for (
auto const& [nodeId, peerPos] : currPeerPositions_)
959 ppj[
to_string(nodeId)] = peerPos.getJson();
961 ret[
"peer_positions"] = std::move(ppj);
964 if (!acquired_.empty())
967 for (
auto const& at : acquired_)
971 ret[
"acquired"] = std::move(acq);
974 if (result_ && !result_->disputes.empty())
977 for (
auto const& [txId, dispute] : result_->disputes)
979 dsj[
to_string(txId)] = dispute.getJson();
981 ret[
"disputes"] = std::move(dsj);
984 if (!rawCloseTimes_.peers.empty())
987 for (
auto const& ct : rawCloseTimes_.peers)
992 ret[
"close_times"] = std::move(ctj);
995 if (!deadNodes_.empty())
998 for (
auto const& dn : deadNodes_)
1002 ret[
"dead_nodes"] = std::move(dnj);
1010template <
class Adaptor>
1015 lgrId != prevLedgerID_ || previousLedger_.id() != lgrId,
1016 "ripple::Consensus::handleWrongLedger : have wrong ledger");
1022 if (prevLedgerID_ != lgrId)
1024 prevLedgerID_ = lgrId;
1029 result_->disputes.clear();
1030 result_->compares.clear();
1033 currPeerPositions_.clear();
1034 rawCloseTimes_.peers.clear();
1038 playbackProposals();
1041 if (previousLedger_.id() == prevLedgerID_)
1045 if (
auto newLedger = adaptor_.acquireLedger(prevLedgerID_))
1047 JLOG(j_.
info()) <<
"Have the consensus ledger " << prevLedgerID_;
1057template <
class Adaptor>
1062 adaptor_.getPrevLedger(prevLedgerID_, previousLedger_, mode_.get());
1064 if (netLgr != prevLedgerID_)
1066 JLOG(j_.
warn()) <<
"View of consensus changed during "
1068 <<
", " <<
" mode=" <<
to_string(mode_.get());
1069 JLOG(j_.
warn()) << prevLedgerID_ <<
" to " << netLgr;
1071 JLOG(j_.
debug()) <<
"State on consensus change "
1073 handleWrongLedger(netLgr);
1075 else if (previousLedger_.id() != prevLedgerID_)
1076 handleWrongLedger(netLgr);
1079template <
class Adaptor>
1083 for (
auto const& it : recentPeerPositions_)
1085 for (
auto const& pos : it.second)
1087 if (pos.proposal().prevLedger() == prevLedgerID_)
1089 if (peerProposalInternal(now_, pos))
1090 adaptor_.share(pos);
1096template <
class Adaptor>
1103 bool anyTransactions = adaptor_.hasOpenTransactions();
1104 auto proposersClosed = currPeerPositions_.size();
1105 auto proposersValidated = adaptor_.proposersValidated(prevLedgerID_);
1107 openTime_.tick(clock_.now());
1112 bool previousCloseCorrect =
1114 previousLedger_.closeAgree() &&
1115 (previousLedger_.closeTime() !=
1116 (previousLedger_.parentCloseTime() + 1s));
1118 auto lastCloseTime = previousCloseCorrect
1119 ? previousLedger_.closeTime()
1122 if (now_ >= lastCloseTime)
1123 sinceClose = duration_cast<milliseconds>(now_ - lastCloseTime);
1125 sinceClose = -duration_cast<milliseconds>(lastCloseTime - now_);
1128 auto const idleInterval = std::max<milliseconds>(
1129 adaptor_.parms().ledgerIDLE_INTERVAL,
1130 2 * previousLedger_.closeTimeResolution());
1149template <
class Adaptor>
1153 auto const& parms = adaptor_.parms();
1155 previousLedger_.seq() -
1156 std::min(adaptor_.getValidLedgerIndex(), previousLedger_.seq()));
1157 auto [quorum, trustedKeys] = adaptor_.getQuorumKeys();
1158 std::size_t const totalValidators = trustedKeys.size();
1160 adaptor_.laggards(previousLedger_.seq(), trustedKeys);
1164 vars <<
" consensuslog (working seq: " << previousLedger_.seq() <<
", "
1165 <<
"validated seq: " << adaptor_.getValidLedgerIndex() <<
", "
1166 <<
"am validator: " << adaptor_.validator() <<
", "
1167 <<
"have validated: " << adaptor_.haveValidated() <<
", "
1168 <<
"roundTime: " << result_->roundTime.
read().count() <<
", "
1169 <<
"max consensus time: " << parms.ledgerMAX_CONSENSUS.count() <<
", "
1170 <<
"validators: " << totalValidators <<
", "
1171 <<
"laggards: " << laggards <<
", " <<
"offline: " << offline <<
", "
1172 <<
"quorum: " << quorum <<
")";
1174 if (!ahead || !laggards || !totalValidators || !adaptor_.validator() ||
1175 !adaptor_.haveValidated() ||
1176 result_->roundTime.read() > parms.ledgerMAX_CONSENSUS)
1178 j_.
debug() <<
"not pausing (early)" << vars.
str();
1182 bool willPause =
false;
1218 std::size_t const phase = (ahead - 1) % (maxPausePhase + 1);
1227 if (laggards + offline > totalValidators - quorum)
1242 float const nonLaggards = totalValidators - (laggards + offline);
1243 float const quorumRatio =
1244 static_cast<float>(quorum) / totalValidators;
1245 float const allowedDissent = 1.0f - quorumRatio;
1246 float const phaseFactor =
static_cast<float>(phase) / maxPausePhase;
1248 if (nonLaggards / totalValidators <
1249 quorumRatio + (allowedDissent * phaseFactor))
1256 j_.
warn() <<
"pausing" << vars.
str();
1258 j_.
debug() <<
"not pausing" << vars.
str();
1262template <
class Adaptor>
1267 XRPL_ASSERT(result_,
"ripple::Consensus::phaseEstablish : result is set");
1272 result_->roundTime.tick(clock_.now());
1273 result_->proposers = currPeerPositions_.size();
1275 convergePercent_ = result_->roundTime.read() * 100 /
1282 updateOurPositions();
1285 if (shouldPause() || !haveConsensus())
1288 if (!haveCloseTimeConsensus_)
1290 JLOG(j_.
info()) <<
"We have TX consensus but not CT consensus";
1294 JLOG(j_.
info()) <<
"Converge cutoff (" << currPeerPositions_.size()
1295 <<
" participants)";
1296 adaptor_.updateOperatingMode(currPeerPositions_.size());
1297 prevProposers_ = currPeerPositions_.size();
1298 prevRoundTime_ = result_->roundTime.read();
1300 JLOG(j_.
debug()) <<
"transitioned to ConsensusPhase::accepted";
1310template <
class Adaptor>
1315 XRPL_ASSERT(!result_,
"ripple::Consensus::closeLedger : result is not set");
1318 JLOG(j_.
debug()) <<
"transitioned to ConsensusPhase::establish";
1319 rawCloseTimes_.self = now_;
1321 result_.emplace(adaptor_.onClose(previousLedger_, now_, mode_.get()));
1322 result_->roundTime.reset(clock_.now());
1325 if (acquired_.emplace(result_->txns.id(), result_->txns).second)
1326 adaptor_.share(result_->txns);
1329 adaptor_.propose(result_->position);
1332 for (
auto const& pit : currPeerPositions_)
1334 auto const& pos = pit.second.proposal().position();
1335 auto const it = acquired_.find(pos);
1336 if (it != acquired_.end())
1338 createDisputes(it->second);
1358 int result = ((participants * percent) + (percent / 2)) / 100;
1360 return (result == 0) ? 1 : result;
1363template <
class Adaptor>
1369 result_,
"ripple::Consensus::updateOurPositions : result is set");
1379 auto it = currPeerPositions_.
begin();
1380 while (it != currPeerPositions_.end())
1382 Proposal_t const& peerProp = it->second.proposal();
1383 if (peerProp.
isStale(peerCutoff))
1387 JLOG(j_.
warn()) <<
"Removing stale proposal from " << peerID;
1388 for (
auto& dt : result_->disputes)
1389 dt.second.unVote(peerID);
1390 it = currPeerPositions_.erase(it);
1395 ++closeTimeVotes[asCloseTime(peerProp.
closeTime())];
1407 for (
auto& [txId, dispute] : result_->disputes)
1411 if (dispute.updateVote(
1417 mutableSet.
emplace(result_->txns);
1419 if (dispute.getOurVote())
1422 mutableSet->insert(dispute.tx());
1427 mutableSet->erase(txId);
1433 ourNewSet.
emplace(std::move(*mutableSet));
1437 haveCloseTimeConsensus_ =
false;
1439 if (currPeerPositions_.empty())
1442 haveCloseTimeConsensus_ =
true;
1443 consensusCloseTime = asCloseTime(result_->position.closeTime());
1458 int participants = currPeerPositions_.size();
1461 ++closeTimeVotes[asCloseTime(result_->position.closeTime())];
1469 int const threshConsensus =
1472 JLOG(j_.
info()) <<
"Proposers:" << currPeerPositions_.size()
1473 <<
" nw:" << neededWeight <<
" thrV:" << threshVote
1474 <<
" thrC:" << threshConsensus;
1476 for (
auto const& [t, v] : closeTimeVotes)
1480 <<
static_cast<std::uint32_t>(previousLedger_.seq()) + 1 <<
": "
1481 << t.time_since_epoch().count() <<
" has " << v <<
", "
1482 << threshVote <<
" required";
1484 if (v >= threshVote)
1487 consensusCloseTime = t;
1490 if (threshVote >= threshConsensus)
1491 haveCloseTimeConsensus_ =
true;
1495 if (!haveCloseTimeConsensus_)
1498 <<
"No CT consensus:" <<
" Proposers:"
1499 << currPeerPositions_.size()
1501 <<
" Thresh:" << threshConsensus
1507 ((consensusCloseTime != asCloseTime(result_->position.closeTime())) ||
1508 result_->position.isStale(ourCutoff)))
1511 ourNewSet.
emplace(result_->txns);
1516 auto newID = ourNewSet->id();
1518 result_->txns = std::move(*ourNewSet);
1520 JLOG(j_.
info()) <<
"Position change: CTime "
1522 <<
", tx " << newID;
1524 result_->position.changePosition(newID, consensusCloseTime, now_);
1528 if (acquired_.emplace(newID, result_->txns).second)
1530 if (!result_->position.isBowOut())
1531 adaptor_.share(result_->txns);
1533 for (
auto const& [nodeId, peerPos] : currPeerPositions_)
1537 updateDisputes(nodeId, result_->txns);
1542 if (!result_->position.isBowOut() &&
1544 adaptor_.propose(result_->position);
1548template <
class Adaptor>
1553 XRPL_ASSERT(result_,
"ripple::Consensus::haveConsensus : has result");
1556 int agree = 0, disagree = 0;
1558 auto ourPosition = result_->position.position();
1561 for (
auto const& [nodeId, peerPos] : currPeerPositions_)
1563 Proposal_t const& peerProp = peerPos.proposal();
1564 if (peerProp.
position() == ourPosition)
1570 JLOG(j_.
debug()) << nodeId <<
" has " << peerProp.
position();
1574 auto currentFinished =
1575 adaptor_.proposersFinished(previousLedger_, prevLedgerID_);
1577 JLOG(j_.
debug()) <<
"Checking for TX consensus: agree=" << agree
1578 <<
", disagree=" << disagree;
1587 result_->roundTime.read(),
1599 JLOG(j_.
error()) <<
"Unable to reach consensus";
1606template <
class Adaptor>
1612 if (result_ && !result_->position.isBowOut())
1614 result_->position.bowOut(now_);
1615 adaptor_.propose(result_->position);
1619 JLOG(j_.
info()) <<
"Bowing out of consensus";
1623template <
class Adaptor>
1628 XRPL_ASSERT(result_,
"ripple::Consensus::createDisputes : result is set");
1631 if (!result_->compares.emplace(o.id()).second)
1635 if (result_->txns.id() == o.id())
1638 JLOG(j_.
debug()) <<
"createDisputes " << result_->txns.id() <<
" to "
1641 auto differences = result_->txns.compare(o);
1645 for (
auto const& [txId, inThisSet] : differences)
1650 (inThisSet && result_->txns.find(txId) && !o.find(txId)) ||
1651 (!inThisSet && !result_->txns.find(txId) && o.find(txId)),
1652 "ripple::Consensus::createDisputes : has disputed transactions");
1654 Tx_t tx = inThisSet ? result_->txns.find(txId) : o.find(txId);
1655 auto txID = tx.id();
1657 if (result_->disputes.find(txID) != result_->disputes.end())
1660 JLOG(j_.
debug()) <<
"Transaction " << txID <<
" is disputed";
1664 result_->txns.exists(txID),
1665 std::max(prevProposers_, currPeerPositions_.size()),
1669 for (
auto const& [nodeId, peerPos] : currPeerPositions_)
1671 Proposal_t const& peerProp = peerPos.proposal();
1672 auto const cit = acquired_.find(peerProp.
position());
1673 if (cit != acquired_.end())
1674 dtx.setVote(nodeId, cit->second.exists(txID));
1676 adaptor_.share(dtx.tx());
1678 result_->disputes.emplace(txID, std::move(dtx));
1680 JLOG(j_.
debug()) << dc <<
" differences found";
1683template <
class Adaptor>
1688 XRPL_ASSERT(result_,
"ripple::Consensus::updateDisputes : result is set");
1692 if (result_->compares.find(other.id()) == result_->compares.end())
1693 createDisputes(other);
1695 for (
auto& it : result_->disputes)
1697 auto& d = it.second;
1698 d.setVote(node, other.exists(d.tx().id()));
1702template <
class Adaptor>
Decorator for streaming out compact json.
Value & append(const Value &value)
Append value to array at the end.
A generic endpoint for log messages.
Stream trace() const
Severity stream access functions.
NodeID_t const & nodeID() const
Identifying which peer took this position.
NetClock::time_point const & closeTime() const
The current position on the consensus close time.
Position_t const & position() const
Get the proposed position.
bool isStale(NetClock::time_point cutoff) const
Get whether this position is stale relative to the provided cutoff.
Measures the duration of phases of consensus.
void set(ConsensusMode mode, Adaptor &a)
MonitoredMode(ConsensusMode m)
ConsensusMode get() const
Generic implementation of consensus algorithm.
void playbackProposals()
If we radically changed our consensus context for some reason, we need to replay recent proposals so ...
void timerEntry(NetClock::time_point const &now)
Call periodically to drive consensus forward.
void updateOurPositions()
typename Adaptor::PeerPosition_t PeerPosition_t
NetClock::time_point prevCloseTime_
clock_type const & clock_
void updateDisputes(NodeID_t const &node, TxSet_t const &other)
typename Adaptor::TxSet_t TxSet_t
Ledger_t::ID prevLedgerID() const
Get the previous ledger ID.
hash_map< NodeID_t, std::deque< PeerPosition_t > > recentPeerPositions_
void simulate(NetClock::time_point const &now, std::optional< std::chrono::milliseconds > consensusDelay)
Simulate the consensus process without any network traffic.
Json::Value getJson(bool full) const
Get the Json state of the consensus process.
typename TxSet_t::Tx Tx_t
void createDisputes(TxSet_t const &o)
Consensus(Consensus &&) noexcept=default
void phaseOpen()
Handle pre-close phase.
void handleWrongLedger(typename Ledger_t::ID const &lgrId)
NetClock::time_point now_
std::size_t prevProposers_
NetClock::time_point asCloseTime(NetClock::time_point raw) const
void gotTxSet(NetClock::time_point const &now, TxSet_t const &txSet)
Process a transaction set acquired from the network.
void checkLedger()
Check if our previous ledger matches the network's.
bool shouldPause() const
Evaluate whether pausing increases likelihood of validation.
void startRoundInternal(NetClock::time_point const &now, typename Ledger_t::ID const &prevLedgerID, Ledger_t const &prevLedger, ConsensusMode mode)
typename Adaptor::Ledger_t Ledger_t
ConsensusPhase phase() const
hash_map< typename TxSet_t::ID, const TxSet_t > acquired_
void phaseEstablish()
Handle establish phase.
typename Adaptor::NodeID_t NodeID_t
NetClock::duration closeResolution_
bool peerProposal(NetClock::time_point const &now, PeerPosition_t const &newProposal)
A peer has proposed a new position, adjust our tracking.
bool peerProposalInternal(NetClock::time_point const &now, PeerPosition_t const &newProposal)
Handle a replayed or a new peer proposal.
hash_map< NodeID_t, PeerPosition_t > currPeerPositions_
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.
ConsensusCloseTimes rawCloseTimes_
std::chrono::milliseconds prevRoundTime_
std::optional< Result > result_
hash_set< NodeID_t > deadNodes_
Ledger_t::ID prevLedgerID_
bool haveCloseTimeConsensus_
A transaction discovered to be in dispute during consensus.
@ arrayValue
array value (ordered list)
@ objectValue
object value (collection of name/value pairs).
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
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.
ConsensusMode
Represents how a node currently participates in Consensus.
@ wrongLedger
We have the wrong ledger and are attempting to acquire it.
@ proposing
We are normal participant in consensus and propose our position.
@ switchedLedger
We switched ledgers since we started this consensus round but are now running on what we believe is t...
@ observing
We are observing peer positions, but not proposing our position.
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.
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.
bool set(T &target, std::string const &name, Section const §ion)
Set a value from a configuration Section If the named value is not found or doesn't parse as a T,...
auto constexpr ledgerDefaultTimeResolution
Initial resolution of ledger close time.
ConsensusPhase
Phases of consensus for a single ledger round.
@ accepted
We have accepted a new last closed ledger and are waiting on a call to startRound to begin the next c...
@ open
We haven't closed our ledger yet, but others might have.
@ establish
Establishing consensus by exchanging proposals with our peers.
ConsensusState
Whether we have or don't have a consensus.
@ MovedOn
The network has consensus without us.
@ No
We do not have consensus.
std::string to_string(base_uint< Bits, Tag > const &a)
Json::Value getJson(LedgerFill const &fill)
Return a new Json::Value representing the ledger with given options.
int participantsNeeded(int participants, int percent)
How many of the participants must agree to reach a given threshold?
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.
Stores the set of initial close times.
Consensus algorithm parameters.
std::size_t avINIT_CONSENSUS_PCT
Percentage of nodes on our UNL that must vote yes.
std::chrono::milliseconds ledgerMIN_CONSENSUS
The number of seconds we wait minimum to ensure participation.
std::chrono::milliseconds avMIN_CONSENSUS_TIME
The minimum amount of time to consider the previous round to have taken.
std::size_t avLATE_CONSENSUS_PCT
Percentage of nodes that most vote yes after advancing.
std::size_t avSTUCK_CONSENSUS_PCT
Percentage of nodes that must vote yes after we are stuck.
std::chrono::seconds proposeFRESHNESS
How long we consider a proposal fresh.
std::size_t avLATE_CONSENSUS_TIME
Percentage of previous round duration before we advance.
std::chrono::seconds proposeINTERVAL
How often we force generating a new proposal to keep ours fresh.
std::size_t avCT_CONSENSUS_PCT
Percentage of nodes required to reach agreement on ledger close time.
std::size_t avMID_CONSENSUS_PCT
Percentage of nodes that most vote yes after advancing.
std::size_t avSTUCK_CONSENSUS_TIME
Percentage of previous round duration before we are stuck.
std::size_t avMID_CONSENSUS_TIME
Percentage of previous round duration before we advance.
Encapsulates the result of consensus.
T time_since_epoch(T... args)