advanced 90 min

Consensus lifecycle & phases

A consensus round in detail — modes, phases (open / establish / accepted) and timing.

Prerequisites

Complete these before starting this module:

What you'll learn

  • Walk a round through the open, establish and accepted phases.
  • Explain ConsensusMode (Proposing / Observing / WrongLedger…).
  • Understand the timing parameters and close conditions.
Complete this module by self-assessment and a quiz. Jump to assessment

Introduction

≈90 min · Advanced · builds on Consensus fundamentals

Now let's watch a single consensus round unfold in detail. In this module you'll walk it through its phases (open, establish, accepted) and learn the ConsensusMode states (proposing, observing, wrong-ledger…) and the timing that governs when a ledger closes. It's consensus in motion, step by step.

High-Level Flow

In brief: one consensus round from start to a validated ledger, at a glance.

The consensus lifecycle: NetworkOPs::beginConsensus opens a round, the open, establish, and accepted phases run on timer ticks inside it, and endConsensus loops straight back into the next round.

Key idea. A round moves through open, establish, and accepted. The ConsensusMode (proposing, observing, wrong-ledger) tells you what part your node is playing at any moment.

Stage 1: Initiating Consensus

Entry Point: Application.cpp

Consensus begins at application startup:

// Application.cpp line 1385
// Start first consensus round
if (!m_networkOPs->beginConsensus(
        m_ledgerMaster->getClosedLedger()->header().hash, {}))
{
    JLOG(m_journal.fatal()) << "Unable to start consensus";
    return false;
}

Key Actions:

  • Passes the hash of the last closed ledger
  • NetworkOPs coordinates the consensus initiation
  • Failure to start consensus is fatal

Stage 2: Pre-Start Preparation

Function: RCLConsensus::Adaptor::preStartRound

Before a round begins, the system prepares:

preStartRound runs before every round: verify the node is in sync, clean the previous round's state, and prepare fresh data structures.

Checks Performed:

  • Is the node synchronized with the network?
  • Is the previous ledger valid and complete?
  • Are all required data structures ready?

Stage 3: Starting the Round

Function: Consensus::startRound

This function establishes the round parameters:

Parameters Established:

  • Previous ledger hash and object
  • Initial mode (proposing or observing)
  • Timer initialization
  • Peer position tracking

Stage 4: Open Phase

In brief: transactions accumulate in the open ledger before it closes.

Function: Consensus::startRoundInternal → ConsensusPhase::Open

Key Timing Parameters:

Parameter Value Purpose
ledgerIdleInterval 15s Max time ledger stays open with no transactions
ledgerMinClose 2s Minimum time ledger must stay open
prevRoundTime/2 Dynamic Must stay open ≥ half of previous consensus time
proposeFRESHNESS 20s How long peer proposals remain valid
proposeINTERVAL 12s How often we must refresh our proposal

The ledger opens to accept transactions:

Inside the open phase: incoming transactions fill the open ledger while the timerEntry loop keeps asking shouldCloseLedger; yes triggers closeLedger, no keeps waiting.

Key Activities:

playbackProposals():

  • Replays peer proposals received for this ledger that arrived early
  • Recovers proposals from up to 10 recent proposals per peer
  • Filters by prevLedgerID to find relevant proposals
  • Allows fast catch-up when switching ledgers

Example:

Proposal playback: proposals that arrive early are saved, then replayed by playbackProposals when the node starts the matching round, so peer positions are known instantly.

timerEntry() in open phase:

  • Called periodically (~1 second via ledgerGRANULARITY)
  • Evaluates close conditions via checkLedger()
  • Calls shouldCloseLedger() to determine if ledger should close

Example:

t=0s:  Open phase starts
t=1s:  timerEntry() → shouldCloseLedger() → NO (too early)
t=2s:  timerEntry() → shouldCloseLedger() → YES (conditions met)
 └─ closeLedger() → phase = ESTABLISH

Close conditions checked:

  • Minimum time elapsed (2s)
  • Has transactions OR idle timeout
  • Not opening too fast (≥ prevRoundTime/2)
  • Enough peers have closed

Stage 5: Close Decision

Function: shouldCloseLedger

Determines if the ledger should close:

Close Decision Process:

Step 1: Safety Checks

  • If timing is unusual (prevRoundTime or timeSincePrevClose out of range) → CLOSE immediately

Step 2: Network Coordination

  • If (proposersClosed + proposersValidated) > prevProposers/2 → CLOSE (follow majority)

Step 3: Transaction-Based Decision

Path A: No Transactions

  • Close only if: timeSincePrevClose ≥ ledgerIdleInterval (15s)
  • Purpose: Maintain regular ledger cadence even when idle

Path B: Has Transactions

  • Must satisfy: openTime ≥ ledgerMinClose (2s)
  • Must satisfy: openTime ≥ prevRoundTime/2
  • If both pass → CLOSE

Key Parameters:

Parameter Value Purpose
ledgerMinClose 2s Minimum time ledger must stay open
ledgerIdleInterval 15s Maximum idle time before forcing close
prevRoundTime/2 Dynamic Throttle: ledger must stay open ≥ half of previous round time

Stage 6: Closing the Ledger

Function: Consensus::closeLedger

Transitions from open to establish phase:

closeLedger ends the open phase: the open ledger is closed, the node takes and proposes its initial position, builds the disputes set, and enters establish.

Actions:

  • Finalizes transaction set for the ledger
  • Creates initial consensus proposal
  • Broadcasts closure to peers
  • Transitions to establish phase

Stage 7: Establish Phase

In brief: nodes converge their transaction sets until they agree.

Function: Consensus::phaseEstablish

Key Timing Parameters:

Parameter Value Purpose
ledgerMinConsensus 1.95s Minimum time before consensus can be declared
ledgerMaxConsensus 15s Maximum time to wait for lagging validators
ledgerABANDON_CONSENSUS 120s Absolute maximum round time
ledgerABANDON_CONSENSUS_FACTOR 10× Dynamic abandonment: min(prevTime × 10, 120s)
avMIN_CONSENSUS_TIME 5s Minimum time used for avalanche calculations

Avalanche Threshold Progression:

convergePercent = (currentRoundTime × 100) / max(prevRoundTime, avMIN_CONSENSUS_TIME)

Thresholds by state:
  init  (0% time):   50% agreement needed
  mid   (50% time):  65% agreement needed
  late  (85% time):  70% agreement needed
  stuck (200% time): 95% agreement needed

Example: If previous round took 4s, use avMIN_CONSENSUS_TIME (5s)
  At 2.5s: convergePercent = (2500 × 100) / 5000 = 50%
  → Transition from 'init' to 'mid'
  → Threshold increases from 50% to 65%

The core of consensus, exchanging proposals and resolving disputes:

The establish phase loop: each timer tick updates the node's position from peer proposals and asks checkConsensus whether the network has converged; yes moves to accepted, no loops again.

Key Functions:

updateOurPositions():

  • Processes peer proposals
  • Adjusts votes on disputed transactions
  • Updates local candidate transaction set

shouldPause():

  • Checks if consensus should pause
  • Handles lagging or non-participating peers
  • Prevents premature advancement

haveConsensus() → checkConsensus() → checkConsensusReached():

  • Evaluates if agreement threshold met
  • Considers timing constraints
  • Returns consensus state (Yes/No/MovedOn/Expired)

Stage 8: Consensus Checking

Consensus Evaluation Logic:

Agreement Threshold:

    checkConsensusReached():
      agreement_pct = (agreeing / total) * 100

Required: 80% (fixed - minCONSENSUS_PCT)

This checks if 80% of validators have the SAME complete transaction set (same hash).

Two checks performed:

  1. Do 80% agree with OUR position? → Yes
  2. Did 80% move to next ledger? → MovedOn

Important Distinction:

The 80% threshold here is for final consensus (comparing complete transaction set hashes).

This is separate from avalanche voting (50%→65%→70%→95%) which determines which individual transactions to include during updateOurPositions().

Consensus States:

State Meaning Action
No Not enough agreement yet Continue voting
Yes 80%+ agree with us Accept consensus!
MovedOn 80%+ moved to next ledger We're behind, catch up
Expired Took too long Bow out of consensus

Stage 9: Acceptance

In brief: the agreed ledger is closed and accepted.

Function: RCLConsensus::Adaptor::onAccept

When consensus is reached:

onAccept: once consensus is declared, the accept work is scheduled on the job queue so the heavy ledger building never blocks the consensus thread.

Sub-Functions:

buildLCL():

  • Constructs Last Closed Ledger from agreed transactions
  • Applies transactions in canonical order
  • Produces validated ledger object

LedgerMaster::consensusBuilt():

  • Updates system's view of current ledger
  • Triggers downstream processing

Apply Disputed Transactions:

  • Attempts to apply transactions that weren't included
  • Queues for next round if applicable

Stage 10: Final Acceptance

Function: RCLConsensus::Adaptor::doAccept

Finalizes the acceptance process:

doAccept builds the consensus ledger, applies the agreed transaction set, validates the result, and publishes the new last closed ledger.

Key Actions:

BuildLedger::buildLedger:

  • Final ledger construction
  • State persistence

app_.getTxQ().processClosedLedger:

  • Updates fee metrics
  • Removes included transactions
  • Manages deferred transactions

app_.openLedger().accept:

  • Applies local transactions not in closed ledger
  • Prepares open ledger for next round

Stage 11: End Consensus

Function: NetworkOPsImp::endConsensus

Completes the round and prepares for the next:

endConsensus closes the round: NetworkOPs checks how the node tracked the network, processes the outcome, and immediately begins the next round.

Stage 12: Begin Next Round

Function: NetworkOPsImp::beginConsensus

The cycle continues:

// Begin next consensus round
reportConsensusStateChange();  // Log state change
RCLConsensus::startRound();    // Initialize next round
// Loop back to Stage 2

Complete Lifecycle Diagram

The twelve stages above chain into one continuous cycle: begin consensus, run the open, establish, and accepted phases on timer ticks, end consensus, and immediately begin again. The lifecycle diagram at the top of this module shows the full loop in one picture.

Consensus Modes and Phases


Introduction

The consensus process operates as a state machine with two orthogonal dimensions: the mode (the node's relationship with the network) and the phase (the current stage of the consensus round). Understanding these states is crucial for debugging, monitoring, and developing consensus-related functionality.

This chapter provides a detailed breakdown of each mode and phase, their transitions, and how they affect node behavior.

ConsensusMode: Node Operating States

The ConsensusMode enum defines how a node participates in consensus:

enum class ConsensusMode {
    Proposing,       // Actively proposing positions
    Observing,       // Watching but not proposing
    WrongLedger,     // Out of sync with network
    SwitchedLedger   // Recently switched to different ledger
};

The four consensus modes side by side: proposing, observing, wrongLedger, and switchedLedger, with what each means for the node's participation in the round.

Mode: proposing

Definition: The node is actively participating in consensus by broadcasting proposals.

Requirements for Proposing:

  • Node is synchronized with network
  • Has valid signing keys configured
  • Connected to sufficient peers
  • Running on correct ledger chain

Behavior:

  • Creates and signs proposals
  • Broadcasts position changes
  • Participates in dispute resolution
  • Contributes to the quorum

Mode: observing

Definition: The node monitors consensus but doesn't submit proposals.

When Observing:

  • Node is configured as non-validator
  • Insufficient peers for meaningful participation
  • Deliberately passive (tracking node)
  • Testing or development scenarios

Behavior:

  • Collects peer proposals
  • Builds local view of consensus
  • Applies final ledger when consensus reached
  • Does not influence outcome

Mode: WrongLedger

Definition: The node is operating on a different ledger than the network consensus.

Causes:

  • Network partition recovery
  • Missed ledger closes
  • Database corruption or gaps
  • Slow synchronization

Recovery Actions:

  • Acquire correct ledger from peers
  • Switch to network's preferred LCL
  • Resume normal operation

Mode: SwitchedLedger

Definition: The node recently switched to a different ledger chain.

Behavior:

  • Temporary state after correction
  • Clears stale proposal data
  • Rebuilds peer position tracking
  • Transitions to appropriate mode

Mode Transitions

Mode transitions: how a node moves between proposing, observing, wrongLedger, and switchedLedger as it gains or loses sync with the network.

ConsensusPhase: Round Stages

The ConsensusPhase enum defines the current stage within a consensus round:

enum class ConsensusPhase {
    Open,      // Collecting transactions
    Establish, // Exchanging proposals, resolving disputes
    Accepted   // Consensus reached, ledger closed
};

Phase: open

Definition: The ledger is open and accepting new transactions.

Phase open at a glance: transactions accumulate in the open ledger until the close conditions are met.

Close Conditions:

The open phase continues until specific conditions signal it's time to close the ledger and begin consensus. Multiple conditions are evaluated:

1. Minimum Time Requirement (ledgerMinClose = 2s)

  • The ledger must remain open for at least 2 seconds
  • This ensures there's sufficient time for transaction propagation across the network
  • Prevents ledgers from closing too rapidly, which could exclude valid transactions
  • Even if all other conditions are met, the system waits for this minimum duration

2. Transaction Activity or Idle Timeout

  • With Transactions: If at least one transaction is present in the open ledger, closure can proceed after minimum time
  • Without Transactions: If no transactions arrive, the ledger waits up to 15 seconds (ledgerIdleInterval) before closing
  • This dual approach balances responsiveness (closing when there's work) with liveness (preventing indefinite waiting)
  • The idle timeout ensures the network continues to produce ledgers even during periods of low activity

3. Speed Limiting (openTime ≥ prevRoundTime/2)

  • The current ledger must stay open for at least half the duration of the previous consensus round
  • This prevents the network from accelerating too quickly
  • Allows slower validators time to participate and stay synchronized
  • Example: If the previous round took 6 seconds, the current ledger must stay open for at least 3 seconds

4. Network Coordination (>50% validators closed)

  • Alternatively, if more than half of the trusted validators have already closed their ledgers
  • The local node will also close to maintain synchronization with the network majority
  • This "follow the network" behavior prevents nodes from falling behind
  • Helps the network reach consensus even if individual timing conditions vary

Key Functions:

playbackProposals()

  • Called at the start of the open phase when beginning a new consensus round
  • Handles proposals that arrived early (for future ledgers) before the node was ready
  • When the node advances to a new ledger, it replays stored proposals that are now relevant
  • Ensures consistency by processing proposals in the correct ledger context
  • Prevents loss of consensus information due to timing variations between nodes
  • Example: If peer proposals for ledger N+1 arrived while still working on ledger N, they are stored and replayed when entering the open phase for N+1

shouldCloseLedger()

  • Called repeatedly during the open phase to check if it's time to close
  • Central decision function that evaluates all closure conditions
  • Located in rippled/src/xrpld/consensus/Consensus.cpp
  • Takes parameters including transaction count, proposer states, and timing information
  • Returns true when conditions are met, triggering the transition to establish phase
  • Implements the logic for all four conditions described above

Timing Characteristics:

The open phase duration varies based on network conditions:

Typical Duration: 2-15 seconds

  • In normal operation with steady transaction flow: 2-5 seconds
  • During low activity periods: can extend to the full 15-second idle timeout
  • The actual duration depends on transaction arrival patterns and network consensus

Minimum Duration: 2 seconds (ledgerMinClose)

  • Absolute floor for any ledger's open phase
  • Cannot be shortened regardless of other conditions
  • Ensures network-wide coordination time

Maximum Idle Duration: 15 seconds (ledgerIdleInterval)

  • When no transactions are present, the ledger will close after this timeout
  • Prevents the network from stalling during quiet periods
  • Guarantees regular ledger progression for time-sensitive operations

Phase: establish

Definition: Proposals are being exchanged and disputes are being resolved.

Phase establish at a glance: the node exchanges proposals with peers, resolves disputes, and converges on a single transaction set.

Activities:

  • Exchange positions with peers
  • Identify disputed transactions
  • Vote on inclusion/exclusion
  • Update local position based on peer input
  • Check for consensus achievement

Timing:

  • Minimum duration: ledgerMinConsensus (1.95s) - must wait at least this long before consensus can be declared
  • Maximum duration: ledgerMaxConsensus (15s) - max time to pause for laggards
  • Check interval: ledgerGRANULARITY (1s) - how often state is checked and positions updated
  • Abandonment: ledgerABANDON_CONSENSUS (120s) - absolute maximum before giving up
  • Typical duration: 2-10 seconds in healthy network conditions

Avalanche Thresholds:

As time progresses through the establish phase, the threshold for including disputed transactions increases:

State Time Threshold Agreement Required Purpose
init 0% of prevRoundTime 50% Initial voting - easy to add transactions
mid 50% of prevRoundTime 65% Mid-consensus - slightly harder
late 85% of prevRoundTime 70% Late consensus - harder still
stuck 200% of prevRoundTime 95% Stuck - very hard to change

The time percentage is calculated as:

convergePercent = (currentRoundTime × 100) / max(prevRoundTime, avMIN_CONSENSUS_TIME)

currentRoundTime

  • How long the current consensus round has been running (in milliseconds)
  • Starts at 0 when entering establish phase
  • Increases continuously as the round progresses

prevRoundTime

  • How long the previous consensus round took to complete
  • Used as the baseline for "expected" duration
  • Assumption: current round should take similar time

avMIN_CONSENSUS_TIME

  • Minimum consensus time = 5 seconds (from ConsensusParms.h)
  • Safety floor to prevent using very short previous rounds as baseline
  • Ensures all avalanche states have time to activate

max(prevRoundTime, avMIN_CONSENSUS_TIME)

  • Use whichever is larger: previous round time OR 5 seconds
  • Prevents baseline from being too short

This rising threshold forces the network to converge on a stable transaction set.

Key Functions:

  • updateOurPositions(): Adjusts local votes based on peer input
  • haveConsensus(): Checks if agreement threshold reached
  • createDisputes(): Identifies transactions with disagreement

Phase: accepted

Definition: Consensus has been reached and the ledger is being finalized.

Phase accepted at a glance: consensus is reached, doAccept builds the last closed ledger, and the node moves straight into the next round.

Actions:

  • Build new ledger from agreed transactions
  • Update ledger master with new LCL
  • Process remaining transaction queue
  • Begin next consensus round

Key Functions:

  • onAccept(): Handles consensus acceptance
  • buildLCL(): Constructs the Last Closed Ledger
  • doAccept(): Finalizes acceptance and prepares next round

Phase Transitions

The phase cycle: open leads to establish, establish loops on position updates until consensus, accepted follows, and the next round's open phase begins immediately.

Timer-Driven Progression

The consensus process is driven by periodic timer events through the timerEntry() function.

Location: rippled/src/xrpld/consensus/Consensus.h:840-869

Actual Implementation:

Key Steps:

  1. Early Exit for Accepted Phase
  • If already in accepted phase, nothing to do
  • Prevents unnecessary processing while building the ledger
  1. Update Network Time
  • Sets now_ to the current network-adjusted time
  • Ensures all timing calculations use consistent time
  1. Ledger Verification (checkLedger())
  • Verifies we're working on the correct ledger
  • May detect we're on the wrong ledger and switch to WrongLedger mode
  • Can change the phase if ledger issues are detected
  • If phase changed, exit early to handle the new phase next timer tick
  1. Phase-Specific Processing
  • Open Phase: Calls phaseOpen() to check if ledger should close
  • Establish Phase: Calls phaseEstablish() to process consensus round

Timer Responsibilities:

  • Called periodically (every ledgerGRANULARITY = 1 second)
  • Advances phase when conditions met
  • Detects and handles ledger synchronization issues
  • Updates proposal positions during establish phase
  • Checks for consensus achievement or timeouts

Consensus State Outcomes

The ConsensusState enum captures the result of consensus attempts:

enum class ConsensusState {
    No,       // No consensus reached
    MovedOn,  // Network moved on without this node
    Expired,  // Consensus timed out
    Yes       // Consensus successfully reached
};

State Implications:

State Meaning Action
No Insufficient agreement Continue voting
MovedOn Network ahead of this node Resync required
Expired Timeout reached Build best-effort ledger
Yes Agreement achieved Finalize and close

Monitoring and Debugging

JSON Serialization:

Consensus state can be serialized for monitoring:

Json::Value getJson() const {
    Json::Value ret;
    ret["phase"] = to_string(phase_);
    ret["mode"] = to_string(mode_);
    ret["proposers"] = proposerCount_;
    ret["agreements"] = agreementCount_;
    // ... additional state
    return ret;
}

RPC Access:

The consensus_info RPC provides real-time consensus status:

{
    "result": {
        "info": {
            "phase": "establish",
            "mode": "proposing",
            "proposers": 35,
            "current_ms": 2100
        }
    }
}

Summary

This module walked a single consensus round in detail, through its open, establish, and accepted phases, from collecting transactions in the open ledger to converging on a set and accepting the closed ledger. You also learned the ConsensusMode states (proposing, observing, wrong-ledger) that tell you what part your node is playing at any moment.

To remember:

  • Phases of a round: open (collect transactions), establish (converge on the set), accepted (close the ledger)
  • ConsensusMode: proposing, observing, wrongLedger, switchedLedger
  • Mainnet closes a ledger roughly every 3 to 5 seconds
  • During establish, agreement thresholds on disputed transactions rise round by round (avalanche to convergence)
  • consensus_info (admin) shows the live phase and proposer count
  • Timing knobs live in ConsensusParms
  • Code: src/xrpld/consensus/Consensus.h
  • Watch out: wrongLedger means you are building on a different parent than the network; check connectivity and UNL before blaming consensus

Next up. Rounds assume every node has the ledgers; reality disagrees. Next: ledger acquisition, or how a node that fell behind catches up.

Assignments

0 of 2 complete

Unlocks

Finishing this module opens up:

XRPL Academy © 2026