Introduction
The Consensus Manager is a native blueprint that exposes the network’s consensus state – current epoch, round, validator set, and emissions schedule – to applications as ordinary engine objects. It is the on-ledger surface of the Cerberus consensus protocol: validators register and unregister through it, epochs advance through it, and staking-related blueprints (Validator, LSUs) interact with it natively.
There is exactly one instance of it on each network, at a fixed address baked into the protocol. On mainnet that is consensusmanager_rdx1scxxxxxxxxxxcnsmgrxxxxxxxxx000999665565xxxxxxxxxcnsmgr, returned as the consensus_manager well-known address by the Gateway’s network configuration endpoint. It is a system component: only the protocol itself may call the methods that advance a round or start the network, and everything else it exposes is read-only.
What the blueprint stores
The Consensus Manager keeps consensus bookkeeping in a handful of separate fields rather than one blob, so that a round update touches as little state as possible. The fields are declared in consensus_manager.rs:
- Configuration – the
ConsensusManagerConfigdescribed below. Written at genesis and thereafter only by a protocol update. - State – whether consensus has started, the current epoch and round, the current leader, and two epoch start timestamps. The effective start is the one epoch length is measured against; the actual start is recorded alongside it as a check on how far the two have diverged.
- Current validator set – the active validators for this epoch, each with its public key and stake, ordered by stake descending.
- Current proposal statistic – per validator, a count of proposals made and proposals missed, in the same order as the validator set. Missed covers both gap rounds and fallback rounds.
- Validator rewards – a vault holding the proposer share of network fees, plus the per-validator claim on it.
- Proposer timestamps – the consensus clock, held twice: to the millisecond for internal use, and rounded to whole minutes as the value transactions read when they ask the network for the time.
Two sorted indexes sit beside the fields: every registered validator keyed by stake, and every validator that has signalled readiness for a protocol update. The first is what the epoch boundary reads to pick the next active set.
The configuration
Nine values govern the whole of it. They are fixed in ConsensusManagerConfig at genesis and can only be changed by a protocol update, never by a transaction. The mainnet genesis values come from the node’s production defaults and are mirrored in ConsensusManagerConfig::mainnet_genesis():
| Field | Mainnet value | What it does |
|---|---|---|
max_validators | 100 | Size of the active set |
min_round_count | 100 (500 at genesis) | Rounds that must pass before the clock can end an epoch |
max_round_count | 3,000 | Rounds after which the epoch ends regardless of the clock |
target_duration_millis | 300,000 | Intended epoch length, five minutes |
num_unstake_epochs | 2,016 | Unstaking delay, about one week |
total_emission_xrd_per_epoch | 2,853.881278538812785388 | XRD minted per epoch for emissions |
min_validator_reliability | 1 | Proposal success ratio required to earn any emission |
num_owner_stake_units_unlock_epochs | 8,064 | Owner stake lock, about four weeks |
num_fee_increase_delay_epochs | 4,032 | Notice a validator must give before raising its fee, about two weeks |
validator_creation_usd_cost | 1,000 USD | Fee to create a validator component |
Three of these are worth reading twice. The emission figure is not a round number because it is a division: 300,000,000 XRD a year over the 105,120 five-minute epochs in a 365-day year. The unstaking, owner-lock and fee-notice delays are all counted in epochs rather than days, so they stretch or shrink with the network’s actual epoch length.
The validator creation fee is quoted in dollars but charged in XRD, converted at USD_PRICE_IN_XRD, a hard-coded engine constant of 16.666666666666666666 – one XRD at six cents, the price assumed when the engine was designed. Creating a validator therefore costs 16,666.67 XRD whatever XRD is trading at.
When an epoch ends
Every round update asks one question: has this epoch run long enough? The answer is a three-branch rule in EpochChangeCondition::is_change_criterion_met, evaluated in order:
- Round number has reached
max_round_count– end the epoch now, whatever the clock says. - Round number is below
min_round_count– do not end the epoch, whatever the clock says. - Otherwise – end the epoch once the elapsed time reaches
target_duration_millis.
The two round bounds exist to cap the damage from a validator lying about the time. Rounds are the only quantity the protocol can count for itself; the clock is reported by proposers, so an epoch is never allowed to be arbitrarily short or arbitrarily long on the strength of a timestamp alone.
The floor was set at 500 rounds at genesis, on an estimate carried over from Olympia that an epoch would run about 1,800 rounds. Cuttlefish lowered that floor to 100 in December 2024, and did it conditionally: the update writes 100 only where the stored value is still 500, so a network that had already changed it is left alone. Since a healthy epoch ends on the clock at several hundred rounds, the floor is invisible in normal operation and binds only when round production stalls – which is exactly the case where a 500-round floor would have held an epoch open far past five minutes.
Read live on 12 August 2026, mainnet epoch 334,398 opened at 19:04:01 UTC and was at round 669 five minutes later, running about 2.4 rounds a second. At that rate the five-minute target arrives around round 700: comfortably above the 100-round floor, comfortably below the 3,000-round ceiling, so mainnet epochs end on the clock.
What the epoch boundary does
When the condition is met, epoch_change runs four steps in one transaction, before the first round of the new epoch.
Emissions are applied to the outgoing set. Each validator’s stake is multiplied by its reliability factor for the epoch just ended, and the epoch’s XRD is divided across those effective stakes. This is settled against the set that did the work, not the set about to start.
The next active set is selected. The engine scans the registered-validators-by-stake index and takes the top max_validators. It deliberately reads more than it needs – max_validators + max_validators/10 + 10, so 120 entries for a 100-seat set – because that index buckets stake into 16-bit keys and several validators can share a bucket at the cut-off. The over-read is then re-sorted exactly. The code notes the residual risk plainly: a very large bucket could still hide a validator who belonged in the set, and the ones affected would be at the bottom of the list where few proposals land.
Protocol update readiness is tallied. Each incoming validator is asked which protocol version it has signalled for, and the stake behind each answer is summed. Signals carrying less than 10% of the new set’s total stake are dropped before the tally is published, so a lone validator cannot put a version name on the ledger.
The books are reset. Proposal statistics are zeroed for the incoming set, the new validator set is written, and an EpochChangeEvent is emitted carrying the epoch number, the full set, and the surviving readiness tallies. That event is what indexers and the Gateway read to track the validator set over time.
At epoch 334,399 the mainnet ledger carried 287 validator components of which 100 were active, holding 4,737,835,621 XRD of stake between them. The distribution across those seats is steep: the largest active validator held 246,042,541 XRD and the hundredth, Encrypta.xyz, held 19,005 XRD. The hundredth seat is cheap because the cap is a fixed count, not a stake threshold.
Reliability is all or nothing on mainnet
Emissions are weighted by performance through a reliability factor, computed in ValidatorInfo::to_reliability_factor. A validator’s absolute reliability is its proposal success ratio for the epoch: proposals made over proposals made plus missed. That ratio is then rescaled against the configured minimum, so the factor is (reliability − minimum) / (1 − minimum), clamped to zero below the minimum.
On a network configured with a minimum of 0.6, a validator that made 7 of 10 rounds scores 0.7, rescales to 0.25, and receives a quarter of its share. Mainnet is not configured that way. Its minimum is 1, which collapses the formula to a single step: the factor is 1 for a validator that missed nothing in the epoch and 0 for a validator that missed one round. There is no partial credit, and no separate 98% threshold – missing a single proposal costs the validator and all of its delegators the whole epoch’s emission. The node’s genesis builder states the reasoning in a comment: epochs are much shorter than they were on Olympia, so full reliability is demanded across each short window. Radix Docs confirms the mainnet setting and the binary outcome it produces.
The XRD a validator forfeits this way is not paid to anyone else, and it is not burned. The engine sums the effective stakes first and mints only the emission those stakes earn, so the forfeited share is never created. This is measurable at the supply level: the twelve months to 9 August 2026 minted 297.2 million XRD against a scheduled 300 million, and that 0.9% gap is reliability forfeit that never entered supply. See Network Emissions for the full reading.
