3#include <xrpld/consensus/ConsensusParms.h>
4#include <xrpld/consensus/ConsensusProposal.h>
5#include <xrpld/consensus/ConsensusTypes.h>
6#include <xrpld/consensus/DisputedTx.h>
8#include <xrpl/basics/Log.h>
9#include <xrpl/basics/chrono.h>
10#include <xrpl/beast/utility/Journal.h>
11#include <xrpl/json/json_writer.h>
12#include <xrpl/ledger/LedgerTiming.h>
44 std::size_t prevProposers,
45 std::size_t proposersClosed,
46 std::size_t proposersValidated,
47 std::chrono::milliseconds prevRoundTime,
48 std::chrono::milliseconds timeSincePrevClose,
49 std::chrono::milliseconds openTime,
50 std::chrono::milliseconds idleInterval,
53 std::unique_ptr<std::stringstream>
const& clog = {});
76 std::size_t prevProposers,
77 std::size_t currentProposers,
78 std::size_t currentAgree,
79 std::size_t currentFinished,
80 std::chrono::milliseconds previousAgreeTime,
81 std::chrono::milliseconds currentAgreeTime,
86 std::unique_ptr<std::stringstream>
const& clog = {});
276template <
class Adaptor>
307 a.onModeChange(
mode_, mode);
348 std::unique_ptr<
std::stringstream> const& clog = {});
606template <
class Adaptor>
610 JLOG(
j_.debug()) <<
"Creating consensus object";
613template <
class Adaptor>
635 for (
NodeID_t const& n : nowUntrusted)
646 prevLedger = *newLedger;
658template <
class Adaptor>
668 JLOG(
j_.debug()) <<
"transitioned to ConsensusPhase::Open ";
669 CLOG(clog) <<
"startRoundInternal transitioned to ConsensusPhase::Open, "
670 "previous ledgerID: "
671 <<
prevLedgerID <<
", seq: " << prevLedger.seq() <<
". ";
693 CLOG(clog) <<
"number of peer proposals,previous proposers: " <<
currPeerPositions_.size()
699 CLOG(clog) <<
"consider closing the ledger immediately. ";
704template <
class Adaptor>
708 JLOG(
j_.debug()) <<
"PROPOSAL " << newPeerPos.render();
709 auto const& peerID = newPeerPos.proposal().nodeID();
715 if (props.size() >= 10)
718 props.push_back(newPeerPos);
723template <
class Adaptor>
735 auto const& newPeerProp = newPeerPos.proposal();
739 JLOG(
j_.debug()) <<
"Got proposal for " << newPeerProp.prevLedger() <<
" but we are on "
744 auto const& peerID = newPeerProp.nodeID();
748 JLOG(
j_.info()) <<
"Position from dead node: " << peerID;
758 if (newPeerProp.proposeSeq() <= 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);
781 peerPosIt->second = newPeerPos;
789 if (newPeerProp.isInitial())
792 JLOG(
j_.trace()) <<
"Peer reports close time as "
793 << newPeerProp.closeTime().time_since_epoch().count();
797 JLOG(
j_.trace()) <<
"Processing peer proposal " << newPeerProp.proposeSeq() <<
"/"
798 << newPeerProp.position();
801 auto const ait =
acquired_.find(newPeerProp.position());
807 if (
auto set =
adaptor_.acquireTxSet(newPeerProp.position()))
813 JLOG(
j_.debug()) <<
"Don't have tx set for peer";
825template <
class Adaptor>
831 CLOG(clog) <<
"Consensus<Adaptor>::timerEntry. ";
835 CLOG(clog) <<
"Nothing to do during accepted phase. ";
840 CLOG(clog) <<
"Set network adjusted time to " <<
to_string(now) <<
". ";
843 auto const phaseOrig =
phase_;
844 CLOG(clog) <<
"Phase " <<
to_string(phaseOrig) <<
". ";
859 CLOG(clog) <<
"timerEntry finishing in phase " <<
to_string(
phase_) <<
". ";
862template <
class Adaptor>
872 auto id = txSet.id();
876 if (!
acquired_.emplace(
id, txSet).second)
881 JLOG(
j_.debug()) <<
"Not creating disputes: no position yet.";
888 id !=
result_->position.position(),
889 "xrpl::Consensus::gotTxSet : updated transaction set");
893 if (peerPos.proposal().position() ==
id)
902 JLOG(
j_.warn()) <<
"By the time we got " <<
id <<
" no peers were proposing it";
907template <
class Adaptor>
913 using namespace std::chrono_literals;
914 JLOG(
j_.info()) <<
"Simulating consensus";
925 JLOG(
j_.info()) <<
"Simulation complete";
928template <
class Adaptor>
942 ret[
"synched"] =
true;
948 ret[
"synched"] =
false;
954 ret[
"disputes"] =
static_cast<Int
>(
result_->disputes.size());
957 ret[
"our_position"] =
result_->position.getJson();
962 ret[
"current_ms"] =
static_cast<Int
>(
result_->roundTime.read().count());
967 ret[
"previous_mseconds"] =
static_cast<Int
>(
prevRoundTime_.count());
975 ppj[
to_string(nodeId)] = peerPos.getJson();
977 ret[
"peer_positions"] = std::move(ppj);
987 ret[
"acquired"] = std::move(acq);
993 for (
auto const& [txId, dispute] :
result_->disputes)
995 dsj[
to_string(txId)] = dispute.getJson();
997 ret[
"disputes"] = std::move(dsj);
1005 ctj[
std::to_string(ct.first.time_since_epoch().count())] = ct.second;
1007 ret[
"close_times"] = std::move(ctj);
1017 ret[
"dead_nodes"] = std::move(dnj);
1025template <
class Adaptor>
1028 Ledger_t::ID
const& lgrId,
1031 CLOG(clog) <<
"handleWrongLedger. ";
1034 "xrpl::Consensus::handleWrongLedger : have wrong ledger");
1061 CLOG(clog) <<
"previousLedger_.id() == prevLeverID_ " <<
prevLedgerID_ <<
". ";
1069 CLOG(clog) <<
"Have the consensus ledger " <<
prevLedgerID_ <<
". ";
1074 CLOG(clog) <<
"Still on wrong ledger. ";
1079template <
class Adaptor>
1083 CLOG(clog) <<
"checkLedger. ";
1086 CLOG(clog) <<
"network ledgerid " << netLgr <<
", "
1095 JLOG(
j_.warn()) << ss.
str();
1096 CLOG(clog) << ss.
str();
1102 CLOG(clog) <<
"previousLedger_.id() != prevLedgerID_: " <<
previousLedger_.id() <<
','
1108template <
class Adaptor>
1114 for (
auto const& pos : it.second)
1125template <
class Adaptor>
1129 CLOG(clog) <<
"phaseOpen. ";
1133 bool const anyTransactions =
adaptor_.hasOpenTransactions();
1142 auto const mode =
mode_.get();
1145 auto const prevParentCloseTimePlus1 =
previousLedger_.parentCloseTime() + 1s;
1147 (prevCloseTime != prevParentCloseTimePlus1);
1149 auto const lastCloseTime = previousCloseCorrect
1153 if (
now_ >= lastCloseTime)
1161 CLOG(
clog) <<
"calculating how long since last ledger's close time "
1163 <<
to_string(mode) <<
", previous closeAgree: " << closeAgree
1164 <<
", previous close time: " <<
to_string(prevCloseTime)
1165 <<
", previous parent close time + 1s: " <<
to_string(prevParentCloseTimePlus1)
1167 <<
", last close time: " <<
to_string(lastCloseTime)
1168 <<
", since close: " << sinceClose.
count() <<
". ";
1173 CLOG(
clog) <<
"idle interval set to " << idleInterval.
count() <<
"ms based on "
1174 <<
"ledgerIDLE_INTERVAL: " <<
adaptor_.parms().ledgerIdleInterval.count()
1175 <<
", previous ledger close time resolution: "
1192 CLOG(
clog) <<
"closing ledger. ";
1197template <
class Adaptor>
1201 CLOG(
clog) <<
"shouldPause? ";
1202 auto const& parms =
adaptor_.parms();
1205 auto [quorum, trustedKeys] =
adaptor_.getQuorumKeys();
1206 std::size_t const totalValidators = trustedKeys.size();
1211 vars <<
" consensuslog (working seq: " <<
previousLedger_.seq() <<
", "
1212 <<
"validated seq: " <<
adaptor_.getValidLedgerIndex() <<
", "
1213 <<
"am validator: " <<
adaptor_.validator() <<
", "
1214 <<
"have validated: " <<
adaptor_.haveValidated()
1218 <<
"roundTime: " <<
result_->roundTime.read().count()
1221 <<
"max consensus time: " << parms.ledgerMaxConsensus.count() <<
", "
1222 <<
"validators: " << totalValidators <<
", "
1223 <<
"laggards: " << laggards <<
", "
1224 <<
"offline: " << offline <<
", "
1225 <<
"quorum: " << quorum <<
")";
1227 if ((ahead == 0u) || (laggards == 0u) || (totalValidators == 0u) || !
adaptor_.validator() ||
1230 result_->roundTime.read() > parms.ledgerMaxConsensus)
1232 j_.debug() <<
"not pausing (early)" << vars.
str();
1233 CLOG(
clog) <<
"Not pausing (early). ";
1237 bool willPause =
false;
1282 if (laggards + offline > totalValidators - quorum)
1285 case kMaxPausePhase:
1297 float const nonLaggards = totalValidators - (laggards + offline);
1298 float const quorumRatio =
static_cast<float>(quorum) / totalValidators;
1299 float const allowedDissent = 1.0f - quorumRatio;
1300 float const phaseFactor =
static_cast<float>(
phase) / kMaxPausePhase;
1302 if (nonLaggards / totalValidators < quorumRatio + (allowedDissent * phaseFactor))
1310 j_.warn() <<
"pausing" << vars.
str();
1311 CLOG(
clog) <<
"pausing " << vars.
str() <<
". ";
1315 j_.debug() <<
"not pausing" << vars.
str();
1316 CLOG(
clog) <<
"not pausing. ";
1321template <
class Adaptor>
1325 CLOG(
clog) <<
"phaseEstablish. ";
1327 XRPL_ASSERT(
result_,
"xrpl::Consensus::phaseEstablish : result is set");
1342 <<
" is based on round duration so far: " <<
result_->roundTime.read().count()
1344 <<
"previous round duration: " <<
prevRoundTime_.count() <<
"ms, "
1363 JLOG(
j_.info()) <<
"We have TX consensus but not CT consensus";
1364 CLOG(
clog) <<
"We have TX consensus but not CT consensus. ";
1370 <<
" participants). Transitioned to ConsensusPhase::Accepted. ";
1375 JLOG(
j_.debug()) <<
"transitioned to ConsensusPhase::Accepted";
1387template <
class Adaptor>
1392 XRPL_ASSERT(!
result_,
"xrpl::Consensus::closeLedger : result is not set");
1395 JLOG(
j_.debug()) <<
"transitioned to ConsensusPhase::Establish";
1407 auto const mode =
mode_.get();
1408 CLOG(
clog) <<
"closeLedger transitioned to ConsensusPhase::Establish, mode: " <<
to_string(mode)
1416 auto const& pos = pit.second.proposal().position();
1438 int const result = ((participants * percent) + (percent / 2)) / 100;
1440 return (result == 0) ? 1 : result;
1443template <
class Adaptor>
1448 XRPL_ASSERT(
result_,
"xrpl::Consensus::updateOurPositions : result is set");
1455 CLOG(
clog) <<
"updateOurPositions. peerCutoff " <<
to_string(peerCutoff) <<
", ourCutoff "
1464 Proposal_t const& peerProp = it->second.proposal();
1465 if (peerProp.
isStale(peerCutoff))
1469 JLOG(
j_.warn()) <<
"Removing stale proposal from " << peerID;
1470 for (
auto& dt :
result_->disputes)
1471 dt.second.unVote(peerID);
1489 for (
auto& [txId, dispute] :
result_->disputes)
1493 if (dispute.updateVote(
1499 if (dispute.getOurVote())
1502 mutableSet->insert(dispute.tx());
1507 mutableSet->erase(txId);
1513 ourNewSet.
emplace(std::move(*mutableSet));
1528 auto const [neededWeight, newState] =
1532 CLOG(
clog) <<
"neededWeight " << neededWeight <<
". ";
1549 <<
" thrV:" << threshVote <<
" thrC:" << threshConsensus;
1550 JLOG(
j_.info()) << ss.
str();
1553 for (
auto const& [t, v] : closeTimeVotes)
1555 JLOG(
j_.debug()) <<
"CCTime: seq "
1557 << t.time_since_epoch().count() <<
" has " << v <<
", " << threshVote
1560 if (v >= threshVote)
1563 consensusCloseTime = t;
1566 if (threshVote >= threshConsensus)
1573 JLOG(
j_.debug()) <<
"No CT consensus:"
1575 <<
" Mode:" <<
to_string(
mode_.get()) <<
" Thresh:" << threshConsensus
1577 CLOG(
clog) <<
"No close time consensus. ";
1583 result_->position.isStale(ourCutoff)))
1591 auto newID = ourNewSet->id();
1593 result_->txns = std::move(*ourNewSet);
1596 ss <<
"Position change: CTime " << consensusCloseTime.
time_since_epoch().count() <<
", tx "
1598 JLOG(
j_.info()) << ss.
str();
1601 result_->position.changePosition(newID, consensusCloseTime,
now_);
1607 if (!
result_->position.isBowOut())
1625template <
class Adaptor>
1630 XRPL_ASSERT(
result_,
"xrpl::Consensus::haveConsensus : has result");
1634 int agree = 0, disagree = 0;
1636 auto ourPosition =
result_->position.position();
1641 Proposal_t const& peerProp = peerPos.proposal();
1642 if (peerProp.
position() == ourPosition)
1648 JLOG(
j_.debug()) <<
"Proposal disagreement: Peer " << nodeId <<
" has "
1655 JLOG(
j_.debug()) <<
"Checking for TX consensus: agree=" << agree <<
", disagree=" << disagree;
1661 bool const stalled =
1664 return dispute.second.stalled(
1665 parms, mode_.get() == ConsensusMode::Proposing, peerUnchangedCounter_, j_, clog);
1670 ss <<
"Consensus detects as stalled with " << (agree + disagree) <<
"/" <<
prevProposers_
1671 <<
" proposers, and " <<
result_->disputes.size() <<
" stalled disputed transactions.";
1672 JLOG(
j_.error()) << ss.
str();
1692 CLOG(
clog) <<
"No consensus. ";
1711 <<
"; continue until round " << kMinimumCounter <<
". "
1713 JLOG(
j_.error()) << ss.
str();
1714 CLOG(
clog) << ss.
str() <<
". ";
1718 JLOG(
j_.error()) << ss.
str();
1719 CLOG(
clog) << ss.
str() <<
". ";
1726 JLOG(
j_.error()) <<
"Unable to reach consensus";
1731 CLOG(
clog) <<
"Consensus has been reached. ";
1736template <
class Adaptor>
1749 JLOG(
j_.info()) <<
"Bowing out of consensus";
1750 CLOG(
clog) <<
"Bowing out of consensus. ";
1754template <
class Adaptor>
1759 XRPL_ASSERT(
result_,
"xrpl::Consensus::createDisputes : result is set");
1763 auto const emplaced =
result_->compares.emplace(o.id()).second;
1764 CLOG(
clog) <<
"createDisputes: new set? " << !emplaced <<
". ";
1769 if (
result_->txns.id() == o.id())
1771 CLOG(
clog) <<
"both sets are identical. ";
1775 CLOG(
clog) <<
"comparing existing with new set: " <<
result_->txns.id() <<
',' << o.id()
1777 JLOG(
j_.debug()) <<
"createDisputes " <<
result_->txns.id() <<
" to " << o.id();
1779 auto differences =
result_->txns.compare(o);
1783 for (
auto const& [txId, inThisSet] : differences)
1788 (inThisSet &&
result_->txns.find(txId) && !o.find(txId)) ||
1789 (!inThisSet && !
result_->txns.find(txId) && o.find(txId)),
1790 "xrpl::Consensus::createDisputes : has disputed transactions");
1792 Tx_t const tx = inThisSet ?
result_->txns.find(txId) : o.find(txId);
1793 auto txID = tx.id();
1798 JLOG(
j_.debug()) <<
"Transaction " << txID <<
" is disputed";
1809 Proposal_t const& peerProp = peerPos.proposal();
1811 if (cit !=
acquired_.end() && dtx.
setVote(nodeId, cit->second.exists(txID)))
1816 result_->disputes.emplace(txID, std::move(dtx));
1818 JLOG(
j_.debug()) << dc <<
" differences found";
1819 CLOG(
clog) <<
"disputes: " << dc <<
". ";
1823template <
class Adaptor>
1828 XRPL_ASSERT(
result_,
"xrpl::Consensus::updateDisputes : result is set");
1833 if (
result_->compares.find(other.id()) ==
result_->compares.end())
1836 for (
auto& it :
result_->disputes)
1838 auto& d = it.second;
1839 if (d.setVote(node, other.exists(d.tx().id())))
1845template <
class Adaptor>
Abstract interface to a clock.
A generic endpoint for log messages.
Decorator for streaming out compact json.
Value & append(Value const &value)
Append value to array at the end.
Represents a proposed position taken during a round of consensus.
Position const & position() const
Get the proposed position.
NodeId const & nodeID() const
Identifying which peer took this position.
NetClock::time_point const & closeTime() const
The current position on the consensus close time.
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.
ConsensusMode get() const
MonitoredMode(ConsensusMode m)
void set(ConsensusMode mode, Adaptor &a)
bool peerProposalInternal(NetClock::time_point const &now, PeerPosition_t const &newProposal)
Handle a replayed or a new peer proposal.
void updateDisputes(NodeID_t const &node, TxSet_t const &other)
bool haveCloseTimeConsensus_
ConsensusProposal< NodeID_t, typename Ledger_t::ID, typename TxSet_t::ID > Proposal_t
hash_map< NodeID_t, std::deque< PeerPosition_t > > recentPeerPositions_
NetClock::duration closeResolution_
void simulate(NetClock::time_point const &now, std::optional< std::chrono::milliseconds > consensusDelay)
Simulate the consensus process without any network traffic.
void gotTxSet(NetClock::time_point const &now, TxSet_t const &txSet)
Process a transaction set acquired from the network.
bool shouldPause(std::unique_ptr< std::stringstream > const &clog) const
Evaluate whether pausing increases likelihood of validation.
void phaseEstablish(std::unique_ptr< std::stringstream > const &clog)
Handle establish phase.
hash_set< NodeID_t > deadNodes_
Adaptor::Ledger_t Ledger_t
std::size_t peerUnchangedCounter_
void leaveConsensus(std::unique_ptr< std::stringstream > const &clog)
beast::AbstractClock< std::chrono::steady_clock > clock_type
Clock type for measuring time within the consensus code.
NetClock::time_point prevCloseTime_
json::Value getJson(bool full) const
Get the Json state of the consensus process.
Ledger_t::ID prevLedgerID() const
ConsensusResult< Adaptor > Result
hash_map< NodeID_t, PeerPosition_t > currPeerPositions_
std::chrono::milliseconds prevRoundTime_
void checkLedger(std::unique_ptr< std::stringstream > const &clog)
Check if our previous ledger matches the network's.
Consensus(Consensus &&) noexcept=default
void startRound(NetClock::time_point const &now, Ledger_t::ID const &prevLedgerID, Ledger_t prevLedger, hash_set< NodeID_t > const &nowUntrusted, bool proposing, std::unique_ptr< std::stringstream > const &clog={})
NetClock::time_point asCloseTime(NetClock::time_point raw) const
ConsensusParms::AvalancheState closeTimeAvalancheState_
ConsensusPhase phase() const
Adaptor::PeerPosition_t PeerPosition_t
void playbackProposals()
If we radically changed our consensus context for some reason, we need to replay recent proposals so ...
void handleWrongLedger(Ledger_t::ID const &lgrId, std::unique_ptr< std::stringstream > const &clog)
Ledger_t::ID prevLedgerID_
hash_map< typename TxSet_t::ID, TxSet_t const > acquired_
void startRoundInternal(NetClock::time_point const &now, Ledger_t::ID const &prevLedgerID, Ledger_t const &prevLedger, ConsensusMode mode, std::unique_ptr< std::stringstream > const &clog)
void phaseOpen(std::unique_ptr< std::stringstream > const &clog)
Handle pre-close phase.
void closeLedger(std::unique_ptr< std::stringstream > const &clog)
std::size_t prevProposers_
std::size_t establishCounter_
NetClock::time_point now_
clock_type const & clock_
void createDisputes(TxSet_t const &o, std::unique_ptr< std::stringstream > const &clog={})
bool peerProposal(NetClock::time_point const &now, PeerPosition_t const &newProposal)
A peer has proposed a new position, adjust our tracking.
std::optional< Result > result_
void timerEntry(NetClock::time_point const &now, std::unique_ptr< std::stringstream > const &clog={})
Call periodically to drive consensus forward.
bool haveConsensus(std::unique_ptr< std::stringstream > const &clog)
void updateOurPositions(std::unique_ptr< std::stringstream > const &clog)
ConsensusCloseTimes rawCloseTimes_
Adaptor::NodeID_t NodeID_t
bool setVote(NodeId const &peer, bool votesYes)
Change a peer's vote.
Tx const & tx() const
The disputed transaction.
std::chrono::time_point< NetClock > time_point
std::chrono::duration< rep, period > duration
T duration_cast(T... args)
@ Array
array value (ordered list)
@ Object
object value (collection of name/value pairs).
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
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,...
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.
@ Observing
We are observing peer positions, but not proposing our position.
std::unordered_set< Value, Hash, Pred, Allocator > hash_set
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.
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, bool stalled, ConsensusParms const &parms, bool proposing, beast::Journal j, std::unique_ptr< std::stringstream > const &clog)
Determine whether the network reached consensus and whether we joined.
std::string to_string(BaseUInt< Bits, Tag > const &a)
ConsensusState
Whether we have or don't have a consensus.
@ Expired
Consensus time limit has hard-expired.
@ MovedOn
The network has consensus without us.
@ No
We do not have consensus.
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.
ConsensusPhase
Phases of consensus for a single ledger round.
@ Establish
Establishing consensus by exchanging proposals with our peers.
@ Open
We haven't closed our ledger yet, but others might have.
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::unordered_map< Key, Value, Hash, Pred, Allocator > hash_map
std::pair< std::size_t, std::optional< ConsensusParms::AvalancheState > > getNeededWeight(ConsensusParms const &p, ConsensusParms::AvalancheState currentState, int percentTime, std::size_t currentRounds, std::size_t minimumRounds)
constexpr auto kLedgerDefaultTimeResolution
Initial resolution of ledger close time.
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, std::unique_ptr< std::stringstream > const &clog)
Determines whether the current ledger should close at this time.
constexpr bool any(HashRouterFlags flags)
Stores the set of initial close times.
Consensus algorithm parameters.
std::size_t const avCtConsensusPct
Percentage of nodes required to reach agreement on ledger close time.
std::chrono::milliseconds const ledgerMinConsensus
The number of seconds we wait minimum to ensure participation.
std::size_t const avMinRounds
Number of rounds before certain actions can happen.
std::chrono::seconds const proposeFRESHNESS
How long we consider a proposal fresh.
std::chrono::seconds const proposeINTERVAL
How often we force generating a new proposal to keep ours fresh.
std::map< AvalancheState, AvalancheCutoff > const avalancheCutoffs
std::chrono::milliseconds const avMinConsensusTime
The minimum amount of time to consider the previous round to have taken.
Encapsulates the result of consensus.
DisputedTx< Tx_t, NodeID_t > Dispute_t
T time_since_epoch(T... args)