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 auto const& peerID = newPeerPos.proposal().nodeID();
711 auto& props = recentPeerPositions_[peerID];
713 if (props.size() >= 10)
716 props.push_back(newPeerPos);
718 return peerProposalInternal(now, newPeerPos);
721template <
class Adaptor>
733 auto const& newPeerProp = newPeerPos.proposal();
735 if (newPeerProp.prevLedger() != prevLedgerID_)
737 JLOG(j_.
debug()) <<
"Got proposal for " << newPeerProp.prevLedger()
738 <<
" but we are on " << prevLedgerID_;
742 auto const& peerID = newPeerProp.nodeID();
744 if (deadNodes_.find(peerID) != deadNodes_.end())
746 JLOG(j_.
info()) <<
"Position from dead node: " << peerID;
752 auto peerPosIt = currPeerPositions_.find(peerID);
754 if (peerPosIt != currPeerPositions_.end())
756 if (newPeerProp.proposeSeq() <=
757 peerPosIt->second.proposal().proposeSeq())
763 if (newPeerProp.isBowOut())
765 JLOG(j_.
info()) <<
"Peer " << peerID <<
" bows out";
768 for (
auto& it : result_->disputes)
769 it.second.unVote(peerID);
771 if (peerPosIt != currPeerPositions_.end())
772 currPeerPositions_.erase(peerID);
773 deadNodes_.insert(peerID);
778 if (peerPosIt != currPeerPositions_.end())
779 peerPosIt->second = newPeerPos;
781 currPeerPositions_.emplace(peerID, newPeerPos);
784 if (newPeerProp.isInitial())
787 JLOG(j_.
trace()) <<
"Peer reports close time as "
788 << newPeerProp.closeTime().time_since_epoch().count();
789 ++rawCloseTimes_.peers[newPeerProp.closeTime()];
792 JLOG(j_.
trace()) <<
"Processing peer proposal " << newPeerProp.proposeSeq()
793 <<
"/" << newPeerProp.position();
796 auto const ait = acquired_.find(newPeerProp.position());
797 if (ait == acquired_.end())
802 if (
auto set = adaptor_.acquireTxSet(newPeerProp.position()))
803 gotTxSet(now_, *
set);
805 JLOG(j_.
debug()) <<
"Don't have tx set for peer";
809 updateDisputes(newPeerProp.nodeID(), ait->second);
816template <
class Adaptor>
839template <
class Adaptor>
851 auto id = txSet.id();
855 if (!acquired_.emplace(
id, txSet).second)
860 JLOG(j_.
debug()) <<
"Not creating disputes: no position yet.";
867 id != result_->position.position(),
868 "ripple::Consensus::gotTxSet : updated transaction set");
870 for (
auto const& [nodeId, peerPos] : currPeerPositions_)
872 if (peerPos.proposal().position() ==
id)
874 updateDisputes(nodeId, txSet);
882 <<
"By the time we got " <<
id <<
" no peers were proposing it";
887template <
class Adaptor>
893 using namespace std::chrono_literals;
894 JLOG(j_.
info()) <<
"Simulating consensus";
897 result_->roundTime.tick(consensusDelay.
value_or(100ms));
898 result_->proposers = prevProposers_ = currPeerPositions_.size();
899 prevRoundTime_ = result_->roundTime.read();
901 adaptor_.onForceAccept(
908 JLOG(j_.
info()) <<
"Simulation complete";
911template <
class Adaptor>
921 ret[
"proposers"] =
static_cast<int>(currPeerPositions_.size());
925 ret[
"synched"] =
true;
928 ret[
"close_granularity"] =
static_cast<Int
>(closeResolution_.count());
931 ret[
"synched"] =
false;
935 if (result_ && !result_->disputes.empty() && !full)
936 ret[
"disputes"] =
static_cast<Int
>(result_->disputes.size());
939 ret[
"our_position"] = result_->position.getJson();
945 static_cast<Int
>(result_->roundTime.read().count());
946 ret[
"converge_percent"] = convergePercent_;
947 ret[
"close_resolution"] =
static_cast<Int
>(closeResolution_.count());
948 ret[
"have_time_consensus"] = haveCloseTimeConsensus_;
949 ret[
"previous_proposers"] =
static_cast<Int
>(prevProposers_);
950 ret[
"previous_mseconds"] =
static_cast<Int
>(prevRoundTime_.count());
952 if (!currPeerPositions_.empty())
956 for (
auto const& [nodeId, peerPos] : currPeerPositions_)
958 ppj[
to_string(nodeId)] = peerPos.getJson();
960 ret[
"peer_positions"] = std::move(ppj);
963 if (!acquired_.empty())
966 for (
auto const& at : acquired_)
970 ret[
"acquired"] = std::move(acq);
973 if (result_ && !result_->disputes.empty())
976 for (
auto const& [txId, dispute] : result_->disputes)
978 dsj[
to_string(txId)] = dispute.getJson();
980 ret[
"disputes"] = std::move(dsj);
983 if (!rawCloseTimes_.peers.empty())
986 for (
auto const& ct : rawCloseTimes_.peers)
991 ret[
"close_times"] = std::move(ctj);
994 if (!deadNodes_.empty())
997 for (
auto const& dn : deadNodes_)
1001 ret[
"dead_nodes"] = std::move(dnj);
1009template <
class Adaptor>
1014 lgrId != prevLedgerID_ || previousLedger_.id() != lgrId,
1015 "ripple::Consensus::handleWrongLedger : have wrong ledger");
1021 if (prevLedgerID_ != lgrId)
1023 prevLedgerID_ = lgrId;
1028 result_->disputes.clear();
1029 result_->compares.clear();
1032 currPeerPositions_.clear();
1033 rawCloseTimes_.peers.clear();
1037 playbackProposals();
1040 if (previousLedger_.id() == prevLedgerID_)
1044 if (
auto newLedger = adaptor_.acquireLedger(prevLedgerID_))
1046 JLOG(j_.
info()) <<
"Have the consensus ledger " << prevLedgerID_;
1056template <
class Adaptor>
1061 adaptor_.getPrevLedger(prevLedgerID_, previousLedger_, mode_.get());
1063 if (netLgr != prevLedgerID_)
1065 JLOG(j_.
warn()) <<
"View of consensus changed during "
1067 <<
", " <<
" mode=" <<
to_string(mode_.get());
1068 JLOG(j_.
warn()) << prevLedgerID_ <<
" to " << netLgr;
1070 JLOG(j_.
debug()) <<
"State on consensus change "
1072 handleWrongLedger(netLgr);
1074 else if (previousLedger_.id() != prevLedgerID_)
1075 handleWrongLedger(netLgr);
1078template <
class Adaptor>
1082 for (
auto const& it : recentPeerPositions_)
1084 for (
auto const& pos : it.second)
1086 if (pos.proposal().prevLedger() == prevLedgerID_)
1088 if (peerProposalInternal(now_, pos))
1089 adaptor_.share(pos);
1095template <
class Adaptor>
1102 bool anyTransactions = adaptor_.hasOpenTransactions();
1103 auto proposersClosed = currPeerPositions_.size();
1104 auto proposersValidated = adaptor_.proposersValidated(prevLedgerID_);
1106 openTime_.tick(clock_.now());
1111 bool previousCloseCorrect =
1113 previousLedger_.closeAgree() &&
1114 (previousLedger_.closeTime() !=
1115 (previousLedger_.parentCloseTime() + 1s));
1117 auto lastCloseTime = previousCloseCorrect
1118 ? previousLedger_.closeTime()
1121 if (now_ >= lastCloseTime)
1122 sinceClose = duration_cast<milliseconds>(now_ - lastCloseTime);
1124 sinceClose = -duration_cast<milliseconds>(lastCloseTime - now_);
1127 auto const idleInterval = std::max<milliseconds>(
1128 adaptor_.parms().ledgerIDLE_INTERVAL,
1129 2 * previousLedger_.closeTimeResolution());
1148template <
class Adaptor>
1152 auto const& parms = adaptor_.parms();
1154 previousLedger_.seq() -
1155 std::min(adaptor_.getValidLedgerIndex(), previousLedger_.seq()));
1156 auto [quorum, trustedKeys] = adaptor_.getQuorumKeys();
1157 std::size_t const totalValidators = trustedKeys.size();
1159 adaptor_.laggards(previousLedger_.seq(), trustedKeys);
1163 vars <<
" consensuslog (working seq: " << previousLedger_.seq() <<
", "
1164 <<
"validated seq: " << adaptor_.getValidLedgerIndex() <<
", "
1165 <<
"am validator: " << adaptor_.validator() <<
", "
1166 <<
"have validated: " << adaptor_.haveValidated() <<
", "
1167 <<
"roundTime: " << result_->roundTime.
read().count() <<
", "
1168 <<
"max consensus time: " << parms.ledgerMAX_CONSENSUS.count() <<
", "
1169 <<
"validators: " << totalValidators <<
", "
1170 <<
"laggards: " << laggards <<
", " <<
"offline: " << offline <<
", "
1171 <<
"quorum: " << quorum <<
")";
1173 if (!ahead || !laggards || !totalValidators || !adaptor_.validator() ||
1174 !adaptor_.haveValidated() ||
1175 result_->roundTime.read() > parms.ledgerMAX_CONSENSUS)
1177 j_.
debug() <<
"not pausing (early)" << vars.
str();
1181 bool willPause =
false;
1217 std::size_t const phase = (ahead - 1) % (maxPausePhase + 1);
1226 if (laggards + offline > totalValidators - quorum)
1241 float const nonLaggards = totalValidators - (laggards + offline);
1242 float const quorumRatio =
1243 static_cast<float>(quorum) / totalValidators;
1244 float const allowedDissent = 1.0f - quorumRatio;
1245 float const phaseFactor =
static_cast<float>(phase) / maxPausePhase;
1247 if (nonLaggards / totalValidators <
1248 quorumRatio + (allowedDissent * phaseFactor))
1255 j_.
warn() <<
"pausing" << vars.
str();
1257 j_.
debug() <<
"not pausing" << vars.
str();
1261template <
class Adaptor>
1266 XRPL_ASSERT(result_,
"ripple::Consensus::phaseEstablish : result is set");
1271 result_->roundTime.tick(clock_.now());
1272 result_->proposers = currPeerPositions_.size();
1274 convergePercent_ = result_->roundTime.read() * 100 /
1281 updateOurPositions();
1284 if (shouldPause() || !haveConsensus())
1287 if (!haveCloseTimeConsensus_)
1289 JLOG(j_.
info()) <<
"We have TX consensus but not CT consensus";
1293 JLOG(j_.
info()) <<
"Converge cutoff (" << currPeerPositions_.size()
1294 <<
" participants)";
1295 adaptor_.updateOperatingMode(currPeerPositions_.size());
1296 prevProposers_ = currPeerPositions_.size();
1297 prevRoundTime_ = result_->roundTime.read();
1299 JLOG(j_.
debug()) <<
"transitioned to ConsensusPhase::accepted";
1309template <
class Adaptor>
1314 XRPL_ASSERT(!result_,
"ripple::Consensus::closeLedger : result is not set");
1317 JLOG(j_.
debug()) <<
"transitioned to ConsensusPhase::establish";
1318 rawCloseTimes_.self = now_;
1320 result_.emplace(adaptor_.onClose(previousLedger_, now_, mode_.get()));
1321 result_->roundTime.reset(clock_.now());
1324 if (acquired_.emplace(result_->txns.id(), result_->txns).second)
1325 adaptor_.share(result_->txns);
1328 adaptor_.propose(result_->position);
1331 for (
auto const& pit : currPeerPositions_)
1333 auto const& pos = pit.second.proposal().position();
1334 auto const it = acquired_.find(pos);
1335 if (it != acquired_.end())
1337 createDisputes(it->second);
1357 int result = ((participants * percent) + (percent / 2)) / 100;
1359 return (result == 0) ? 1 : result;
1362template <
class Adaptor>
1368 result_,
"ripple::Consensus::updateOurPositions : result is set");
1378 auto it = currPeerPositions_.
begin();
1379 while (it != currPeerPositions_.end())
1381 Proposal_t const& peerProp = it->second.proposal();
1382 if (peerProp.
isStale(peerCutoff))
1386 JLOG(j_.
warn()) <<
"Removing stale proposal from " << peerID;
1387 for (
auto& dt : result_->disputes)
1388 dt.second.unVote(peerID);
1389 it = currPeerPositions_.erase(it);
1394 ++closeTimeVotes[asCloseTime(peerProp.
closeTime())];
1406 for (
auto& [txId, dispute] : result_->disputes)
1410 if (dispute.updateVote(
1416 mutableSet.
emplace(result_->txns);
1418 if (dispute.getOurVote())
1421 mutableSet->insert(dispute.tx());
1426 mutableSet->erase(txId);
1432 ourNewSet.
emplace(std::move(*mutableSet));
1436 haveCloseTimeConsensus_ =
false;
1438 if (currPeerPositions_.empty())
1441 haveCloseTimeConsensus_ =
true;
1442 consensusCloseTime = asCloseTime(result_->position.closeTime());
1457 int participants = currPeerPositions_.size();
1460 ++closeTimeVotes[asCloseTime(result_->position.closeTime())];
1468 int const threshConsensus =
1471 JLOG(j_.
info()) <<
"Proposers:" << currPeerPositions_.size()
1472 <<
" nw:" << neededWeight <<
" thrV:" << threshVote
1473 <<
" thrC:" << threshConsensus;
1475 for (
auto const& [t, v] : closeTimeVotes)
1479 <<
static_cast<std::uint32_t>(previousLedger_.seq()) + 1 <<
": "
1480 << t.time_since_epoch().count() <<
" has " << v <<
", "
1481 << threshVote <<
" required";
1483 if (v >= threshVote)
1486 consensusCloseTime = t;
1489 if (threshVote >= threshConsensus)
1490 haveCloseTimeConsensus_ =
true;
1494 if (!haveCloseTimeConsensus_)
1497 <<
"No CT consensus:" <<
" Proposers:"
1498 << currPeerPositions_.size()
1500 <<
" Thresh:" << threshConsensus
1506 ((consensusCloseTime != asCloseTime(result_->position.closeTime())) ||
1507 result_->position.isStale(ourCutoff)))
1510 ourNewSet.
emplace(result_->txns);
1515 auto newID = ourNewSet->id();
1517 result_->txns = std::move(*ourNewSet);
1519 JLOG(j_.
info()) <<
"Position change: CTime "
1521 <<
", tx " << newID;
1523 result_->position.changePosition(newID, consensusCloseTime, now_);
1527 if (acquired_.emplace(newID, result_->txns).second)
1529 if (!result_->position.isBowOut())
1530 adaptor_.share(result_->txns);
1532 for (
auto const& [nodeId, peerPos] : currPeerPositions_)
1536 updateDisputes(nodeId, result_->txns);
1541 if (!result_->position.isBowOut() &&
1543 adaptor_.propose(result_->position);
1547template <
class Adaptor>
1552 XRPL_ASSERT(result_,
"ripple::Consensus::haveConsensus : has result");
1555 int agree = 0, disagree = 0;
1557 auto ourPosition = result_->position.position();
1560 for (
auto const& [nodeId, peerPos] : currPeerPositions_)
1562 Proposal_t const& peerProp = peerPos.proposal();
1563 if (peerProp.
position() == ourPosition)
1569 JLOG(j_.
debug()) << nodeId <<
" has " << peerProp.
position();
1573 auto currentFinished =
1574 adaptor_.proposersFinished(previousLedger_, prevLedgerID_);
1576 JLOG(j_.
debug()) <<
"Checking for TX consensus: agree=" << agree
1577 <<
", disagree=" << disagree;
1586 result_->roundTime.read(),
1598 JLOG(j_.
error()) <<
"Unable to reach consensus";
1605template <
class Adaptor>
1611 if (result_ && !result_->position.isBowOut())
1613 result_->position.bowOut(now_);
1614 adaptor_.propose(result_->position);
1618 JLOG(j_.
info()) <<
"Bowing out of consensus";
1622template <
class Adaptor>
1627 XRPL_ASSERT(result_,
"ripple::Consensus::createDisputes : result is set");
1630 if (!result_->compares.emplace(o.id()).second)
1634 if (result_->txns.id() == o.id())
1637 JLOG(j_.
debug()) <<
"createDisputes " << result_->txns.id() <<
" to "
1640 auto differences = result_->txns.compare(o);
1644 for (
auto const& [txId, inThisSet] : differences)
1649 (inThisSet && result_->txns.find(txId) && !o.find(txId)) ||
1650 (!inThisSet && !result_->txns.find(txId) && o.find(txId)),
1651 "ripple::Consensus::createDisputes : has disputed transactions");
1653 Tx_t tx = inThisSet ? result_->txns.find(txId) : o.find(txId);
1654 auto txID = tx.id();
1656 if (result_->disputes.find(txID) != result_->disputes.end())
1659 JLOG(j_.
debug()) <<
"Transaction " << txID <<
" is disputed";
1663 result_->txns.exists(txID),
1664 std::max(prevProposers_, currPeerPositions_.size()),
1668 for (
auto const& [nodeId, peerPos] : currPeerPositions_)
1670 Proposal_t const& peerProp = peerPos.proposal();
1671 auto const cit = acquired_.find(peerProp.
position());
1672 if (cit != acquired_.end())
1673 dtx.setVote(nodeId, cit->second.exists(txID));
1675 adaptor_.share(dtx.tx());
1677 result_->disputes.emplace(txID, std::move(dtx));
1679 JLOG(j_.
debug()) << dc <<
" differences found";
1682template <
class Adaptor>
1687 XRPL_ASSERT(result_,
"ripple::Consensus::updateDisputes : result is set");
1691 if (result_->compares.find(other.id()) == result_->compares.end())
1692 createDisputes(other);
1694 for (
auto& it : result_->disputes)
1696 auto& d = it.second;
1697 d.setVote(node, other.exists(d.tx().id()));
1701template <
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)