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>
69 ConsensusParms
const& parms,
96 ConsensusParms
const& parms,
289template <
class Adaptor>
295 using Tx_t =
typename TxSet_t::Tx;
299 typename Ledger_t::ID,
300 typename TxSet_t::ID>;
323 a.onModeChange(
mode_, mode);
364 std::unique_ptr<
std::stringstream> const& clog = {});
423 typename Ledger_t::ID
457 typename Ledger_t::ID
const& lgrId,
621template <
class Adaptor>
626 : adaptor_(adaptor), clock_(clock), j_{journal}
628 JLOG(
j_.
debug()) <<
"Creating consensus object";
631template <
class Adaptor>
635 typename Ledger_t::ID
const& prevLedgerID,
644 prevRoundTime_ = adaptor_.parms().ledgerIDLE_INTERVAL;
645 prevCloseTime_ = prevLedger.closeTime();
650 prevCloseTime_ = rawCloseTimes_.self;
653 for (
NodeID_t const& n : nowUntrusted)
654 recentPeerPositions_.erase(n);
660 if (prevLedger.id() != prevLedgerID)
663 if (
auto newLedger = adaptor_.acquireLedger(prevLedgerID))
665 prevLedger = *newLedger;
671 <<
"Entering consensus with: " << previousLedger_.id();
672 JLOG(j_.
info()) <<
"Correct LCL is: " << prevLedgerID;
676 startRoundInternal(now, prevLedgerID, prevLedger, startMode, clog);
678template <
class Adaptor>
682 typename Ledger_t::ID
const& prevLedgerID,
688 JLOG(j_.
debug()) <<
"transitioned to ConsensusPhase::open ";
689 CLOG(clog) <<
"startRoundInternal transitioned to ConsensusPhase::open, "
690 "previous ledgerID: "
691 << prevLedgerID <<
", seq: " << prevLedger.seq() <<
". ";
692 mode_.set(mode, adaptor_);
694 prevLedgerID_ = prevLedgerID;
695 previousLedger_ = prevLedger;
697 convergePercent_ = 0;
698 haveCloseTimeConsensus_ =
false;
699 openTime_.reset(clock_.now());
700 currPeerPositions_.clear();
702 rawCloseTimes_.peers.clear();
703 rawCloseTimes_.self = {};
707 previousLedger_.closeTimeResolution(),
708 previousLedger_.closeAgree(),
709 previousLedger_.seq() +
typename Ledger_t::Seq{1});
712 CLOG(clog) <<
"number of peer proposals,previous proposers: "
713 << currPeerPositions_.size() <<
',' << prevProposers_ <<
". ";
714 if (currPeerPositions_.size() > (prevProposers_ / 2))
718 CLOG(clog) <<
"consider closing the ledger immediately. ";
719 timerEntry(now_, clog);
723template <
class Adaptor>
729 JLOG(j_.
debug()) <<
"PROPOSAL " << newPeerPos.render();
730 auto const& peerID = newPeerPos.proposal().nodeID();
734 auto& props = recentPeerPositions_[peerID];
736 if (props.size() >= 10)
739 props.push_back(newPeerPos);
741 return peerProposalInternal(now, newPeerPos);
744template <
class Adaptor>
756 auto const& newPeerProp = newPeerPos.proposal();
758 if (newPeerProp.prevLedger() != prevLedgerID_)
760 JLOG(j_.
debug()) <<
"Got proposal for " << newPeerProp.prevLedger()
761 <<
" but we are on " << prevLedgerID_;
765 auto const& peerID = newPeerProp.nodeID();
767 if (deadNodes_.find(peerID) != deadNodes_.end())
769 JLOG(j_.
info()) <<
"Position from dead node: " << peerID;
775 auto peerPosIt = currPeerPositions_.find(peerID);
777 if (peerPosIt != currPeerPositions_.end())
779 if (newPeerProp.proposeSeq() <=
780 peerPosIt->second.proposal().proposeSeq())
786 if (newPeerProp.isBowOut())
788 JLOG(j_.
info()) <<
"Peer " << peerID <<
" bows out";
791 for (
auto& it : result_->disputes)
792 it.second.unVote(peerID);
794 if (peerPosIt != currPeerPositions_.end())
795 currPeerPositions_.erase(peerID);
796 deadNodes_.insert(peerID);
801 if (peerPosIt != currPeerPositions_.end())
802 peerPosIt->second = newPeerPos;
804 currPeerPositions_.emplace(peerID, newPeerPos);
807 if (newPeerProp.isInitial())
810 JLOG(j_.
trace()) <<
"Peer reports close time as "
811 << newPeerProp.closeTime().time_since_epoch().count();
812 ++rawCloseTimes_.peers[newPeerProp.closeTime()];
815 JLOG(j_.
trace()) <<
"Processing peer proposal " << newPeerProp.proposeSeq()
816 <<
"/" << newPeerProp.position();
819 auto const ait = acquired_.find(newPeerProp.position());
820 if (ait == acquired_.end())
825 if (
auto set = adaptor_.acquireTxSet(newPeerProp.position()))
826 gotTxSet(now_, *
set);
828 JLOG(j_.
debug()) <<
"Don't have tx set for peer";
832 updateDisputes(newPeerProp.nodeID(), ait->second);
839template <
class Adaptor>
845 CLOG(clog) <<
"Consensus<Adaptor>::timerEntry. ";
849 CLOG(clog) <<
"Nothing to do during accepted phase. ";
854 CLOG(clog) <<
"Set network adjusted time to " <<
to_string(now) <<
". ";
857 const auto phaseOrig = phase_;
858 CLOG(clog) <<
"Phase " <<
to_string(phaseOrig) <<
". ";
860 if (phaseOrig != phase_)
862 CLOG(clog) <<
"Changed phase to << " <<
to_string(phase_) <<
". ";
868 phaseEstablish(clog);
869 CLOG(clog) <<
"timerEntry finishing in phase " <<
to_string(phase_) <<
". ";
872template <
class Adaptor>
884 auto id = txSet.id();
888 if (!acquired_.emplace(
id, txSet).second)
893 JLOG(j_.
debug()) <<
"Not creating disputes: no position yet.";
900 id != result_->position.position(),
901 "ripple::Consensus::gotTxSet : updated transaction set");
903 for (
auto const& [nodeId, peerPos] : currPeerPositions_)
905 if (peerPos.proposal().position() ==
id)
907 updateDisputes(nodeId, txSet);
915 <<
"By the time we got " <<
id <<
" no peers were proposing it";
920template <
class Adaptor>
926 using namespace std::chrono_literals;
927 JLOG(j_.
info()) <<
"Simulating consensus";
930 result_->roundTime.tick(consensusDelay.
value_or(100ms));
931 result_->proposers = prevProposers_ = currPeerPositions_.size();
932 prevRoundTime_ = result_->roundTime.read();
934 adaptor_.onForceAccept(
941 JLOG(j_.
info()) <<
"Simulation complete";
944template <
class Adaptor>
954 ret[
"proposers"] =
static_cast<int>(currPeerPositions_.size());
958 ret[
"synched"] =
true;
961 ret[
"close_granularity"] =
static_cast<Int
>(closeResolution_.count());
964 ret[
"synched"] =
false;
968 if (result_ && !result_->disputes.empty() && !full)
969 ret[
"disputes"] =
static_cast<Int
>(result_->disputes.size());
972 ret[
"our_position"] = result_->position.getJson();
978 static_cast<Int
>(result_->roundTime.read().count());
979 ret[
"converge_percent"] = convergePercent_;
980 ret[
"close_resolution"] =
static_cast<Int
>(closeResolution_.count());
981 ret[
"have_time_consensus"] = haveCloseTimeConsensus_;
982 ret[
"previous_proposers"] =
static_cast<Int
>(prevProposers_);
983 ret[
"previous_mseconds"] =
static_cast<Int
>(prevRoundTime_.count());
985 if (!currPeerPositions_.empty())
989 for (
auto const& [nodeId, peerPos] : currPeerPositions_)
991 ppj[
to_string(nodeId)] = peerPos.getJson();
993 ret[
"peer_positions"] = std::move(ppj);
996 if (!acquired_.empty())
999 for (
auto const& at : acquired_)
1003 ret[
"acquired"] = std::move(acq);
1006 if (result_ && !result_->disputes.empty())
1009 for (
auto const& [txId, dispute] : result_->disputes)
1011 dsj[
to_string(txId)] = dispute.getJson();
1013 ret[
"disputes"] = std::move(dsj);
1016 if (!rawCloseTimes_.peers.empty())
1019 for (
auto const& ct : rawCloseTimes_.peers)
1024 ret[
"close_times"] = std::move(ctj);
1027 if (!deadNodes_.empty())
1030 for (
auto const& dn : deadNodes_)
1034 ret[
"dead_nodes"] = std::move(dnj);
1042template <
class Adaptor>
1045 typename Ledger_t::ID
const& lgrId,
1048 CLOG(clog) <<
"handleWrongLedger. ";
1050 lgrId != prevLedgerID_ || previousLedger_.id() != lgrId,
1051 "ripple::Consensus::handleWrongLedger : have wrong ledger");
1054 leaveConsensus(clog);
1057 if (prevLedgerID_ != lgrId)
1059 prevLedgerID_ = lgrId;
1064 result_->disputes.clear();
1065 result_->compares.clear();
1068 currPeerPositions_.clear();
1069 rawCloseTimes_.peers.clear();
1073 playbackProposals();
1076 if (previousLedger_.id() == prevLedgerID_)
1078 CLOG(clog) <<
"previousLedger_.id() == prevLeverID_ " << prevLedgerID_
1084 if (
auto newLedger = adaptor_.acquireLedger(prevLedgerID_))
1086 JLOG(j_.
info()) <<
"Have the consensus ledger " << prevLedgerID_;
1087 CLOG(clog) <<
"Have the consensus ledger " << prevLedgerID_ <<
". ";
1093 CLOG(clog) <<
"Still on wrong ledger. ";
1098template <
class Adaptor>
1102 CLOG(clog) <<
"checkLedger. ";
1105 adaptor_.getPrevLedger(prevLedgerID_, previousLedger_, mode_.get());
1106 CLOG(clog) <<
"network ledgerid " << netLgr <<
", " <<
"previous ledger "
1107 << prevLedgerID_ <<
". ";
1109 if (netLgr != prevLedgerID_)
1112 ss <<
"View of consensus changed during " <<
to_string(phase_)
1113 <<
" mode=" <<
to_string(mode_.get()) <<
", " << prevLedgerID_
1114 <<
" to " << netLgr <<
", "
1117 CLOG(clog) << ss.
str();
1118 CLOG(clog) <<
"State on consensus change "
1120 handleWrongLedger(netLgr, clog);
1122 else if (previousLedger_.id() != prevLedgerID_)
1124 CLOG(clog) <<
"previousLedger_.id() != prevLedgerID_: "
1125 << previousLedger_.id() <<
',' <<
to_string(prevLedgerID_)
1127 handleWrongLedger(netLgr, clog);
1131template <
class Adaptor>
1135 for (
auto const& it : recentPeerPositions_)
1137 for (
auto const& pos : it.second)
1139 if (pos.proposal().prevLedger() == prevLedgerID_)
1141 if (peerProposalInternal(now_, pos))
1142 adaptor_.share(pos);
1148template <
class Adaptor>
1152 CLOG(clog) <<
"phaseOpen. ";
1156 bool anyTransactions = adaptor_.hasOpenTransactions();
1157 auto proposersClosed = currPeerPositions_.size();
1158 auto proposersValidated = adaptor_.proposersValidated(prevLedgerID_);
1160 openTime_.tick(clock_.now());
1165 auto const mode = mode_.get();
1166 bool const closeAgree = previousLedger_.closeAgree();
1167 auto const prevCloseTime = previousLedger_.closeTime();
1168 auto const prevParentCloseTimePlus1 =
1169 previousLedger_.parentCloseTime() + 1s;
1170 bool const previousCloseCorrect =
1172 (prevCloseTime != prevParentCloseTimePlus1);
1174 auto const lastCloseTime = previousCloseCorrect
1178 if (now_ >= lastCloseTime)
1179 sinceClose = duration_cast<milliseconds>(now_ - lastCloseTime);
1181 sinceClose = -duration_cast<milliseconds>(lastCloseTime - now_);
1182 CLOG(
clog) <<
"calculating how long since last ledger's close time "
1184 <<
to_string(mode) <<
", previous closeAgree: " << closeAgree
1185 <<
", previous close time: " <<
to_string(prevCloseTime)
1186 <<
", previous parent close time + 1s: "
1188 <<
", previous close time seen internally: "
1190 <<
", last close time: " <<
to_string(lastCloseTime)
1191 <<
", since close: " << sinceClose.
count() <<
". ";
1194 auto const idleInterval = std::max<milliseconds>(
1195 adaptor_.parms().ledgerIDLE_INTERVAL,
1196 2 * previousLedger_.closeTimeResolution());
1197 CLOG(
clog) <<
"idle interval set to " << idleInterval.
count()
1198 <<
"ms based on " <<
"ledgerIDLE_INTERVAL: "
1199 << adaptor_.parms().ledgerIDLE_INTERVAL.count()
1200 <<
", previous ledger close time resolution: "
1201 << previousLedger_.closeTimeResolution().count() <<
"ms. ";
1217 CLOG(
clog) <<
"closing ledger. ";
1222template <
class Adaptor>
1227 CLOG(
clog) <<
"shouldPause? ";
1228 auto const& parms = adaptor_.parms();
1230 previousLedger_.seq() -
1231 std::min(adaptor_.getValidLedgerIndex(), previousLedger_.seq()));
1232 auto [quorum, trustedKeys] = adaptor_.getQuorumKeys();
1233 std::size_t const totalValidators = trustedKeys.size();
1235 adaptor_.laggards(previousLedger_.seq(), trustedKeys);
1239 vars <<
" consensuslog (working seq: " << previousLedger_.seq() <<
", "
1240 <<
"validated seq: " << adaptor_.getValidLedgerIndex() <<
", "
1241 <<
"am validator: " << adaptor_.validator() <<
", "
1242 <<
"have validated: " << adaptor_.haveValidated() <<
", "
1243 <<
"roundTime: " << result_->roundTime.
read().count() <<
", "
1244 <<
"max consensus time: " << parms.ledgerMAX_CONSENSUS.count() <<
", "
1245 <<
"validators: " << totalValidators <<
", "
1246 <<
"laggards: " << laggards <<
", " <<
"offline: " << offline <<
", "
1247 <<
"quorum: " << quorum <<
")";
1249 if (!ahead || !laggards || !totalValidators || !adaptor_.validator() ||
1250 !adaptor_.haveValidated() ||
1251 result_->roundTime.read() > parms.ledgerMAX_CONSENSUS)
1253 j_.
debug() <<
"not pausing (early)" << vars.
str();
1254 CLOG(
clog) <<
"Not pausing (early). ";
1258 bool willPause =
false;
1294 std::size_t const phase = (ahead - 1) % (maxPausePhase + 1);
1303 if (laggards + offline > totalValidators - quorum)
1318 float const nonLaggards = totalValidators - (laggards + offline);
1319 float const quorumRatio =
1320 static_cast<float>(quorum) / totalValidators;
1321 float const allowedDissent = 1.0f - quorumRatio;
1322 float const phaseFactor =
static_cast<float>(phase) / maxPausePhase;
1324 if (nonLaggards / totalValidators <
1325 quorumRatio + (allowedDissent * phaseFactor))
1333 j_.
warn() <<
"pausing" << vars.
str();
1334 CLOG(
clog) <<
"pausing " << vars.
str() <<
". ";
1338 j_.
debug() <<
"not pausing" << vars.
str();
1339 CLOG(
clog) <<
"not pausing. ";
1344template <
class Adaptor>
1349 CLOG(
clog) <<
"phaseEstablish. ";
1351 XRPL_ASSERT(result_,
"ripple::Consensus::phaseEstablish : result is set");
1356 result_->roundTime.tick(clock_.now());
1357 result_->proposers = currPeerPositions_.size();
1359 convergePercent_ = result_->roundTime.read() * 100 /
1361 CLOG(
clog) <<
"convergePercent_ " << convergePercent_
1362 <<
" is based on round duration so far: "
1363 << result_->roundTime.read().count() <<
"ms, "
1364 <<
"previous round duration: " << prevRoundTime_.count()
1372 CLOG(
clog) <<
"ledgerMIN_CONSENSUS not reached: "
1377 updateOurPositions(
clog);
1380 if (shouldPause(
clog) || !haveConsensus(
clog))
1383 if (!haveCloseTimeConsensus_)
1385 JLOG(j_.
info()) <<
"We have TX consensus but not CT consensus";
1386 CLOG(
clog) <<
"We have TX consensus but not CT consensus. ";
1390 JLOG(j_.
info()) <<
"Converge cutoff (" << currPeerPositions_.size()
1391 <<
" participants)";
1392 CLOG(
clog) <<
"Converge cutoff (" << currPeerPositions_.size()
1393 <<
" participants). Transitioned to ConsensusPhase::accepted. ";
1394 adaptor_.updateOperatingMode(currPeerPositions_.size());
1395 prevProposers_ = currPeerPositions_.size();
1396 prevRoundTime_ = result_->roundTime.read();
1398 JLOG(j_.
debug()) <<
"transitioned to ConsensusPhase::accepted";
1406 adaptor_.validating());
1409template <
class Adaptor>
1414 XRPL_ASSERT(!result_,
"ripple::Consensus::closeLedger : result is not set");
1417 JLOG(j_.
debug()) <<
"transitioned to ConsensusPhase::establish";
1418 rawCloseTimes_.self = now_;
1420 result_.emplace(adaptor_.onClose(previousLedger_, now_, mode_.get()));
1421 result_->roundTime.reset(clock_.now());
1424 if (acquired_.emplace(result_->txns.id(), result_->txns).second)
1425 adaptor_.share(result_->txns);
1427 const auto mode = mode_.get();
1429 <<
"closeLedger transitioned to ConsensusPhase::establish, mode: "
1431 <<
", number of peer positions: " << currPeerPositions_.
size() <<
". ";
1433 adaptor_.propose(result_->position);
1436 for (
auto const& pit : currPeerPositions_)
1438 auto const& pos = pit.second.proposal().position();
1439 auto const it = acquired_.find(pos);
1440 if (it != acquired_.end())
1441 createDisputes(it->second,
clog);
1460 int result = ((participants * percent) + (percent / 2)) / 100;
1462 return (result == 0) ? 1 : result;
1465template <
class Adaptor>
1472 result_,
"ripple::Consensus::updateOurPositions : result is set");
1478 CLOG(
clog) <<
"updateOurPositions. peerCutoff " <<
to_string(peerCutoff)
1479 <<
", ourCutoff " <<
to_string(ourCutoff) <<
". ";
1484 auto it = currPeerPositions_.
begin();
1485 while (it != currPeerPositions_.end())
1487 Proposal_t const& peerProp = it->second.proposal();
1488 if (peerProp.
isStale(peerCutoff))
1492 JLOG(j_.
warn()) <<
"Removing stale proposal from " << peerID;
1493 for (
auto& dt : result_->disputes)
1494 dt.second.unVote(peerID);
1495 it = currPeerPositions_.erase(it);
1500 ++closeTimeVotes[asCloseTime(peerProp.
closeTime())];
1512 for (
auto& [txId, dispute] : result_->disputes)
1516 if (dispute.updateVote(
1522 mutableSet.
emplace(result_->txns);
1524 if (dispute.getOurVote())
1527 mutableSet->insert(dispute.tx());
1532 mutableSet->erase(txId);
1538 ourNewSet.
emplace(std::move(*mutableSet));
1542 haveCloseTimeConsensus_ =
false;
1544 if (currPeerPositions_.empty())
1547 haveCloseTimeConsensus_ =
true;
1548 consensusCloseTime = asCloseTime(result_->position.closeTime());
1562 CLOG(
clog) <<
"neededWeight " << neededWeight <<
". ";
1564 int participants = currPeerPositions_.size();
1567 ++closeTimeVotes[asCloseTime(result_->position.closeTime())];
1575 int const threshConsensus =
1579 ss <<
"Proposers:" << currPeerPositions_.size()
1580 <<
" nw:" << neededWeight <<
" thrV:" << threshVote
1581 <<
" thrC:" << threshConsensus;
1585 for (
auto const& [t, v] : closeTimeVotes)
1589 <<
static_cast<std::uint32_t>(previousLedger_.seq()) + 1 <<
": "
1590 << t.time_since_epoch().count() <<
" has " << v <<
", "
1591 << threshVote <<
" required";
1593 if (v >= threshVote)
1596 consensusCloseTime = t;
1599 if (threshVote >= threshConsensus)
1600 haveCloseTimeConsensus_ =
true;
1604 if (!haveCloseTimeConsensus_)
1607 <<
"No CT consensus:" <<
" Proposers:"
1608 << currPeerPositions_.size()
1610 <<
" Thresh:" << threshConsensus
1612 CLOG(
clog) <<
"No close time consensus. ";
1617 ((consensusCloseTime != asCloseTime(result_->position.closeTime())) ||
1618 result_->position.isStale(ourCutoff)))
1621 ourNewSet.
emplace(result_->txns);
1626 auto newID = ourNewSet->id();
1628 result_->txns = std::move(*ourNewSet);
1631 ss <<
"Position change: CTime "
1636 result_->position.changePosition(newID, consensusCloseTime, now_);
1640 if (acquired_.emplace(newID, result_->txns).second)
1642 if (!result_->position.isBowOut())
1643 adaptor_.share(result_->txns);
1645 for (
auto const& [nodeId, peerPos] : currPeerPositions_)
1649 updateDisputes(nodeId, result_->txns);
1654 if (!result_->position.isBowOut() &&
1656 adaptor_.propose(result_->position);
1660template <
class Adaptor>
1666 XRPL_ASSERT(result_,
"ripple::Consensus::haveConsensus : has result");
1669 int agree = 0, disagree = 0;
1671 auto ourPosition = result_->position.position();
1674 for (
auto const& [nodeId, peerPos] : currPeerPositions_)
1676 Proposal_t const& peerProp = peerPos.proposal();
1677 if (peerProp.
position() == ourPosition)
1683 JLOG(j_.
debug()) << nodeId <<
" has " << peerProp.
position();
1687 auto currentFinished =
1688 adaptor_.proposersFinished(previousLedger_, prevLedgerID_);
1690 JLOG(j_.
debug()) <<
"Checking for TX consensus: agree=" << agree
1691 <<
", disagree=" << disagree;
1700 result_->roundTime.read(),
1708 CLOG(
clog) <<
"No consensus. ";
1716 JLOG(j_.
error()) <<
"Unable to reach consensus";
1718 CLOG(
clog) <<
"Unable to reach consensus "
1722 CLOG(
clog) <<
"Consensus has been reached. ";
1726template <
class Adaptor>
1733 if (result_ && !result_->position.isBowOut())
1735 result_->position.bowOut(now_);
1736 adaptor_.propose(result_->position);
1740 JLOG(j_.
info()) <<
"Bowing out of consensus";
1741 CLOG(
clog) <<
"Bowing out of consensus. ";
1745template <
class Adaptor>
1752 XRPL_ASSERT(result_,
"ripple::Consensus::createDisputes : result is set");
1755 auto const emplaced = result_->compares.emplace(o.id()).second;
1756 CLOG(
clog) <<
"createDisputes: new set? " << !emplaced <<
". ";
1761 if (result_->txns.id() == o.id())
1763 CLOG(
clog) <<
"both sets are identical. ";
1767 CLOG(
clog) <<
"comparing existing with new set: " << result_->txns.id()
1768 <<
',' << o.id() <<
". ";
1769 JLOG(j_.
debug()) <<
"createDisputes " << result_->txns.id() <<
" to "
1772 auto differences = result_->txns.compare(o);
1776 for (
auto const& [txId, inThisSet] : differences)
1781 (inThisSet && result_->txns.find(txId) && !o.find(txId)) ||
1782 (!inThisSet && !result_->txns.find(txId) && o.find(txId)),
1783 "ripple::Consensus::createDisputes : has disputed transactions");
1785 Tx_t tx = inThisSet ? result_->txns.find(txId) : o.find(txId);
1786 auto txID = tx.id();
1788 if (result_->disputes.find(txID) != result_->disputes.end())
1791 JLOG(j_.
debug()) <<
"Transaction " << txID <<
" is disputed";
1795 result_->txns.exists(txID),
1796 std::max(prevProposers_, currPeerPositions_.size()),
1800 for (
auto const& [nodeId, peerPos] : currPeerPositions_)
1802 Proposal_t const& peerProp = peerPos.proposal();
1803 auto const cit = acquired_.find(peerProp.
position());
1804 if (cit != acquired_.end())
1805 dtx.setVote(nodeId, cit->second.exists(txID));
1807 adaptor_.share(dtx.tx());
1809 result_->disputes.emplace(txID, std::move(dtx));
1811 JLOG(j_.
debug()) << dc <<
" differences found";
1812 CLOG(
clog) <<
"disputes: " << dc <<
". ";
1815template <
class Adaptor>
1820 XRPL_ASSERT(result_,
"ripple::Consensus::updateDisputes : result is set");
1824 if (result_->compares.find(other.id()) == result_->compares.end())
1825 createDisputes(other);
1827 for (
auto& it : result_->disputes)
1829 auto& d = it.second;
1830 d.setVote(node, other.exists(d.tx().id()));
1834template <
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, std::unique_ptr< std::stringstream > const &clog={})
Call periodically to drive consensus forward.
void startRoundInternal(NetClock::time_point const &now, typename Ledger_t::ID const &prevLedgerID, Ledger_t const &prevLedger, ConsensusMode mode, std::unique_ptr< std::stringstream > const &clog)
void phaseEstablish(std::unique_ptr< std::stringstream > const &clog)
Handle establish phase.
typename Adaptor::PeerPosition_t PeerPosition_t
NetClock::time_point prevCloseTime_
clock_type const & clock_
void leaveConsensus(std::unique_ptr< std::stringstream > const &clog)
void updateDisputes(NodeID_t const &node, TxSet_t const &other)
typename Adaptor::TxSet_t TxSet_t
void updateOurPositions(std::unique_ptr< std::stringstream > const &clog)
bool haveConsensus(std::unique_ptr< std::stringstream > const &clog)
Ledger_t::ID prevLedgerID() const
Get the previous ledger ID.
void handleWrongLedger(typename Ledger_t::ID const &lgrId, std::unique_ptr< std::stringstream > const &clog)
void checkLedger(std::unique_ptr< std::stringstream > const &clog)
Check if our previous ledger matches the network's.
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 startRound(NetClock::time_point const &now, typename Ledger_t::ID const &prevLedgerID, Ledger_t prevLedger, hash_set< NodeID_t > const &nowUntrusted, bool proposing, std::unique_ptr< std::stringstream > const &clog={})
Kick-off the next round of consensus.
Consensus(Consensus &&) noexcept=default
NetClock::time_point now_
std::size_t prevProposers_
NetClock::time_point asCloseTime(NetClock::time_point raw) const
void createDisputes(TxSet_t const &o, std::unique_ptr< std::stringstream > const &clog={})
void gotTxSet(NetClock::time_point const &now, TxSet_t const &txSet)
Process a transaction set acquired from the network.
typename Adaptor::Ledger_t Ledger_t
ConsensusPhase phase() const
hash_map< typename TxSet_t::ID, const TxSet_t > acquired_
typename Adaptor::NodeID_t NodeID_t
void closeLedger(std::unique_ptr< std::stringstream > const &clog)
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_
bool shouldPause(std::unique_ptr< std::stringstream > const &clog) const
Evaluate whether pausing increases likelihood of validation.
void phaseOpen(std::unique_ptr< std::stringstream > const &clog)
Handle pre-close phase.
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, std::unique_ptr< std::stringstream > const &clog)
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.
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)
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.
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)