Skip to content

Commit

Permalink
Drop out of consensus if the round takes too long
Browse files Browse the repository at this point in the history
  • Loading branch information
ximinez committed Feb 11, 2025
1 parent fa5a854 commit a201bfe
Show file tree
Hide file tree
Showing 6 changed files with 39 additions and 3 deletions.
2 changes: 1 addition & 1 deletion docs/consensus.md
Original file line number Diff line number Diff line change
Expand Up @@ -558,7 +558,7 @@ struct ConsensusResult
ConsensusTimer roundTime;
// Indicates state in which consensus ended. Once in the accept phase
// will be either Yes or MovedOn
// will be either Yes or MovedOn or Expired
ConsensusState state = ConsensusState::No;
};
Expand Down
5 changes: 5 additions & 0 deletions src/test/consensus/Consensus_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,11 @@ class Consensus_test : public beast::unit_test::suite
BEAST_EXPECT(
ConsensusState::Yes ==
checkConsensus(0, 0, 0, 0, 3s, 16s, p, true, journal_));

// Expire if too much time has passed without agreement
BEAST_EXPECT(
ConsensusState::Expired ==
checkConsensus(10, 8, 1, 0, 1s, 19s, p, true, journal_));
}

void
Expand Down
13 changes: 12 additions & 1 deletion src/xrpld/consensus/Consensus.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ checkConsensus(
<< " minimum duration to reach consensus: "
<< parms.ledgerMIN_CONSENSUS.count() << "ms"
<< " max consensus time "
<< parms.ledgerMAX_CONSENSUS.count() << "s"
<< parms.ledgerMAX_CONSENSUS.count() << "ms"
<< " minimum consensus percentage: "
<< parms.minCONSENSUS_PCT;

Expand Down Expand Up @@ -181,6 +181,17 @@ checkConsensus(
return ConsensusState::MovedOn;
}

std::chrono::milliseconds const maxAgreeTime =
previousAgreeTime * parms.ledgerABANDON_CONSENSUS_FACTOR;
if (currentAgreeTime > std::clamp(
maxAgreeTime,
parms.ledgerMAX_CONSENSUS,
parms.ledgerABANDON_CONSENSUS))
{
JLOG(j.warn()) << "consensus taken too long";
return ConsensusState::Expired;
}

// no consensus yet
JLOG(j.trace()) << "no consensus";
return ConsensusState::No;
Expand Down
7 changes: 7 additions & 0 deletions src/xrpld/consensus/Consensus.h
Original file line number Diff line number Diff line change
Expand Up @@ -1591,6 +1591,13 @@ Consensus<Adaptor>::haveConsensus()
if (result_->state == ConsensusState::No)
return false;

// Consensus has taken far too long. Drop out of the round.
if (result_->state == ConsensusState::Expired)
{
JLOG(j_.error()) << "Nobody can reach consensus";
JLOG(j_.error()) << Json::Compact{getJson(true)};
leaveConsensus();
}
// There is consensus, but we need to track if the network moved on
// without us.
if (result_->state == ConsensusState::MovedOn)
Expand Down
12 changes: 12 additions & 0 deletions src/xrpld/consensus/ConsensusParms.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,18 @@ struct ConsensusParms
//! How often we check state or change positions
std::chrono::milliseconds ledgerGRANULARITY = std::chrono::seconds{1};

//! How long to wait before completely abandoning consensus
std::size_t ledgerABANDON_CONSENSUS_FACTOR = 10;

/**
* Maximum amount of time to give a consensus round
*
* Does not include the time to build the LCL, so there is no reason for a
* round to go this long, regardless of how big the ledger is.
*/
std::chrono::milliseconds ledgerABANDON_CONSENSUS =
std::chrono::seconds{60};

/** The minimum amount of time to consider the previous round
to have taken.
Expand Down
3 changes: 2 additions & 1 deletion src/xrpld/consensus/ConsensusTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ struct ConsensusCloseTimes
enum class ConsensusState {
No, //!< We do not have consensus
MovedOn, //!< The network has consensus without us
Expired, //!< Consensus time limit has hard-expired
Yes //!< We have consensus along with the network
};

Expand Down Expand Up @@ -235,7 +236,7 @@ struct ConsensusResult
ConsensusTimer roundTime;

// Indicates state in which consensus ended. Once in the accept phase
// will be either Yes or MovedOn
// will be either Yes or MovedOn or Expired
ConsensusState state = ConsensusState::No;

// The number of peers proposing during the round
Expand Down

0 comments on commit a201bfe

Please sign in to comment.