Replace the BLS-signature-based Random Decentralized Autonomous Organization (RANDAO) reveal with a hash-chain commit-reveal scheme. RANDAO's resistance to grinding currently relies on BLS signatures being unique, so that a proposer cannot bias its contribution. Since BLS relies on pre-quantum hardness assumptions, a quantum computer can recover a validator's key and predict its future reveals.
A hash chain relies only on standard hash-function security: collision resistance to prevent grinding (the property that replaces BLS's uniqueness) and preimage resistance for unpredictability. Both are believed to hold against quantum attack, and this removes the dependency on the signature scheme entirely. Each validator commits to the tip of a generated hash chain. When proposing a block, the validator reveals the preimage of its currently stored commitment; the protocol verifies that the revealed value is indeed the preimage to the current stored commitment for that validator, folds the preimage into the RANDAO accumulator, and stores the preimage as the validator's new commitment.
A commitment is registered once, via a new per-block-capped beacon operation (similar to BLSToExecutionChange); it cannot be updated in place, so a validator that ever needs a new chain exits and re-enters. Validators that have not yet registered a commitment continue to use the legacy BLS reveal; this is a transitional path intended for removal in a later fork.
We note the simplicity: the protocol holds only the current commitment (32 bytes) and walks one link back per proposal for that validator.
The primary driver is post-quantum readiness.
Today's RANDAO contribution is a BLS signature over the epoch number. Its security rests on the uniqueness of BLS signatures: given the message and public key, exactly one valid signature exists, so a proposer cannot grind their own contribution to bias the randomness.
BLS is not post-quantum safe, so a cryptographically relevant quantum computer (CRQC) can recover a validator's secret key from its public key. Uniqueness still holds, but the attacker can compute the validator's reveals itself, predicting the chain's future randomness, and thus the proposer schedule, far in advance.
Prediction also amplifies bias, beyond merely leaking the schedule. The standard reveal-or-withhold attack requires the attacker to control k consecutive proposer slots at the tail of an epoch to choose among 2**k candidate mixes, because it cannot evaluate a candidate without knowing the contributions that land after its own. An attacker that can predict every honest contribution evaluates each candidate outright, so (assuming honest proposers always reveal) any k of its slots in the mixing period (the span of slots whose reveals feed the target seed) work; they need not be consecutive or at the end of the epoch.
Most uses of signatures, such as block proposals and attestations, can move to any secure post-quantum scheme. RANDAO, however, additionally depends on signature uniqueness, and most post-quantum schemes do not provide it, allowing one to grind for a favorable RANDAO contribution. The hash-based signature scheme currently planned for the consensus layer (a generalized eXtended Merkle Signature Scheme (XMSS) (RFC 8391), commonly referred to as the lean signature scheme) is grindable in exactly this way, since it includes a salt component.
A hash chain sidesteps both problems in a simple way. There is no signature, it is just chaining hashes together. Grinding would require building a chain around a hash collision: if a proposer could find two values that hash to the same word, it could place that word in its chain and later choose which of the two preimages to reveal, biasing its contribution. This is exactly the freedom BLS's uniqueness denies, and collision resistance denies it here. Preimage resistance separately keeps each reveal unpredictable to others until it is published. Both properties are believed to hold even against a quantum attacker, with reduced but adequate security.
The commit-reveal structure also preserves RANDAO's existing security model, since the whole chain is fixed at commitment time, long before the validator knows its proposal slots or the mixes it might want to bias, so the proposer's only remaining lever is the same as the one it has today: reveal, or withhold and forfeit the block.
Note: This EIP does not make beacon chain randomness post-quantum secure end to end; block signatures and the registration operation's signature still use BLS. It incrementally introduces a post-quantum version of RANDAO, so that a later post-quantum fork which changes the signature-scheme and sets the initial hash-chain commitment at deposit time can complete the transition.
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 and RFC 8174.
These changes are applied to the consensus specifications (ethereum/consensus-specs) at a fork to be scheduled; functions and constants not defined here retain their meaning from those specifications.
blake3(data: bytes) -> Bytes32 is the BLAKE3 hash function (version 1) in its default unkeyed hash mode, with no derive-key context, restricted to its default 32-byte output. All hashing introduced by this EIP uses blake3 (see Rationale); the consensus specifications' hash helper continues to serve the legacy reveal path.
| Name | Value | Description |
|---|---|---|
DOMAIN_RANDAO_COMMITMENT_REGISTRATION | DomainType('0x0F000000') | Domain for signed commitment registrations |
HASH_CHAIN_RANDAO_DST | b'HASH_CHAIN_RANDAO' | Domain-separation tag prefixed to each hash-chain link |
| Name | Value | Description |
|---|---|---|
COMMITMENT_REGISTRATION_DELAY | Epoch(3) | Epochs before a new commitment becomes active; MUST be at least MIN_SEED_LOOKAHEAD + 2 (see Rationale) |
MAX_RANDAO_COMMITMENT_REGISTRATIONS | uint64(128) | Maximum commitment registration operations per block |
A validator generates a uniformly random 32-byte chain secret c_0 and computes a chain of length n; every chain value c_i is a Bytes32, matching the BLAKE3 output width:
where blake3 is defined in the Cryptographic Functions section above. HASH_CHAIN_RANDAO_DST is the fixed byte-string domain-separation prefix defined in the Constants section above, so hash-chain links cannot collide with hashes used elsewhere in the protocol. It carries no per-link index, so a validator still needs to store only chain values, not their positions.
Nothing in the construction is specific to BLAKE3; any collision- and preimage-resistant hash with a 32-byte output serves (see Rationale for why BLAKE3 over the consensus specifications' hash helper). Note that a commitment binds the hash function it was generated with, so a later fork that migrates to a different hash either keeps verifying previously registered chains under BLAKE3 or has validators re-register under the new hash.
Chain values MUST NOT be the zero word: process_randao rejects a zero reveal (the zero word marks an unregistered validator), and a zero commitment cannot be registered. The seed c_0 is chosen non-zero, and every other c_i is a BLAKE3 output, so a zero link occurs only with negligible probability (about n / 2**256). Even so, a validator MUST check that no c_i is the zero word and regenerate the chain if any link is zero.
The validator publishes c_n as their commitment and stores the chain (or the chain secret plus periodic checkpoints if the chain is large). Reveals are consumed in reverse order: the first reveal is c_{n-1}, the next is c_{n-2}, and so on. Each revealed value becomes the new on-chain commitment, so the protocol requires no knowledge of n or of the validator's position in the chain.
Validators SHOULD choose n large enough that the chain outlasts the validator, since it cannot be extended in place. n >= 2**16 (~65,000 links) is RECOMMENDED. The whole chain can be generated in milliseconds, and storing every link takes ~2 MB. This exact value is of course arbitrary: the protocol never learns or enforces n, and since generation and storage stay cheap, most operators lose nothing by choosing a larger n.
This bounded lifetime is the one structural difference from BLS, where a public key is a commitment that never expires, a hash chain instead lasts for its length. It is not a practical constraint though since we can generate a large enough chain that will last for centuries.
A commitment cannot be updated in place, so a chain cannot be extended once registered. The chain is exhausted only when its stored commitment reaches the secret seed c_0, whose preimage the validator does not hold. A validator whose chain runs out can no longer propose on the hash-chain path and must exit and re-enter as a new validator to obtain a fresh chain.
With the recommended n this never happens in practice. A validator proposes on the order of 100 times per year even in aggressive futures, so a chain of 2**16 links lasts for centuries; sizing n generously (it is cheap) makes exhaustion a non-issue. The same applies to a lost or mis-generated chain secret: there is no in-place recovery, so the remedy is exit and re-entry, and the chain secret SHOULD be guarded like the signing key (see Security Considerations).
BeaconBlockBody gains two fields and retains randao_reveal transitionally:
The new fields are appended after all existing fields, following the convention since Capella of adding fields at the end.
BeaconState gains a commitment registry and a pending queue. randao_commitments is indexed by validator index, holding one entry per registry member; a zero entry means no commitment is registered and the legacy BLS reveal path applies. The Validator container is unchanged.
The pending queue is a ProgressiveList (EIP-7916): it is almost always near-empty (worst-case steady state is ~12,300 entries, see Security Considerations), so a progressive shape avoids both an arbitrary capacity constant and the hashing overhead of a large fixed-limit list. randao_commitments stays a fixed-limit List because it holds one entry per registry member and must track the length of the other per-validator lists, which share the VALIDATOR_REGISTRY_LIMIT bound.
process_randaoNote the structure of the hash-chain path:
blake3(HASH_CHAIN_RANDAO_DST + reveal) == commitment).mix = blake3(mix + reveal).G2_POINT_AT_INFINITY is the existing BLS point-at-infinity signature constant already defined in the consensus specs.The hash accumulator has no efficiently computable inverse, so a validator that copies another's commitment cannot cancel the victim's contribution: re-injecting the same revealed value produces a fresh, unrelated mix rather than undoing it. This is why the hash-chain path can fold in the raw reveal directly, with no assumption on the mixed-in value being unique per validator. The legacy BLS path keeps its existing xor accumulator; the two coexist only until the BLS path is sunset.
The non-zero assert protects the sentinel: a zero entry in randao_commitments means "unregistered", and the reveal is stored as the next commitment. Without the guard, a validator that committed to the zero word as a chain value would, upon revealing it, silently store the sentinel and flip onto the legacy branch, while its client, still believing itself registered, produced invalid blocks indefinitely. With the guard, the zero-revealing block is itself invalid, so a validator that committed the zero word simply cannot propose (hence the rule above that no chain value may be the zero word). So "zero means unregistered" is an enforced invariant.
process_operationsprocess_operations gains a loop over the new operation, appended after the existing operations so that existing processing is unchanged:
process_randao_commitment_registrationCalled from process_operations for each element of body.randao_commitment_registrations. This is the one-time registration path: it moves a validator from the legacy BLS reveal onto its hash chain, and works regardless of proposal schedule. It is valid only while the validator is unregistered (its stored commitment is zero). The signing domain is computed against the genesis fork version (similar to BLSToExecutionChange) so that messages remain valid across forks.
Registration is single-use by construction, covering two windows with two mechanisms. Once a registration has activated, randao_commitments[validator_index] is non-zero, so any replay fails the unregistered check in this handler. While it is still pending (queued but not yet activated), the stored entry is still zero, but the one-pending rule in queue_randao_commitment rejects a second submission. Together these leave no gap from inclusion onward. This mirrors the replay-safety of BLSToExecutionChange, whose handler likewise asserts the credential has not already been changed.
queue_randao_commitmentAt most one registration per validator may be pending at any time; a block containing a registration for a validator with an in-flight one is invalid.
The resulting lifecycle, worked through to be more explicit:
N is queued with activation epoch N + COMMITMENT_REGISTRATION_DELAY (N + 3 with the preset value).N to N + 2; its proposals in that window are BLS reveals as before.N + 3, the pending entry is consumed and the hash chain becomes active; from then on the validator reveals from its chain.Activation is a property of the canonical state, not of the validator's broadcast history. A validator MUST continue producing legacy BLS reveals until randao_commitments[validator_index] is non-zero in the state it proposes against, even if it has observed its registration included in some block: if that block is orphaned, the pending entry never enters the canonical queue. The message stays valid in that case (the stored commitment is still zero) and remains includable: it persists in operation pools, as with bls_to_execution_change.
process_pending_randao_commitmentsCalled from process_epoch, immediately after process_registry_updates. Pending commitments are applied in queue order once their activation epoch is reached. The one-pending-registration-per-validator rule enforced in queue_randao_commitment guarantees entries never conflict.
A new global gossip topic randao_commitment_registration carries SignedRandaoCommitmentRegistration messages. Because a validator registers at most once, first-seen-per-validator deduplication suffices, exactly as for bls_to_execution_change. The rules are evaluated in order, so the cheap checks and the seen check precede signature verification.
commitment is zero, or validator_index is unknown.SignedRandaoCommitmentRegistration for validator_index has already been seen, or a pending registration for it already exists in the node's view of the state.randao_commitments entry in the node's view of the head state).At the fork epoch, the upgrade_to_* function initializes randao_commitments with one zero entry per registry member and pending_randao_commitments as empty:
This establishes the invariant that len(state.randao_commitments) == len(state.validators), which process_randao and process_randao_commitment_registration rely on when indexing by validator index. To preserve the invariant for validators onboarded after the fork, add_validator_to_registry is modified to append a zero entry alongside the other per-validator lists:
A validator added this way starts unregistered and uses the legacy path until it registers. All validators start on the legacy BLS reveal path and migrate by broadcasting a SignedRandaoCommitmentRegistration. At MAX_RANDAO_COMMITMENT_REGISTRATIONS = 128 per block, the full current validator set (~1M) can register in under two days of full blocks; there is no deadline, and unregistered validators simply continue on the legacy path.
The BLS reveal branch in process_randao, and the randao_reveal field itself, are transitional and SHOULD be removed in a later fork after registration has saturated, naturally the fork that reworks deposits for post-quantum signatures, at which point initial hash chain commitments move into the validator onboarding flow and the RandaoCommitmentRegistration signature migrates to the post-quantum scheme.
Note: the RandaoCommitmentRegistration operation registers a validator's initial hash chain only. A validator that loses its chain secret must exit and re-enter. Once the post-quantum fork sets initial commitments at deposit time, the operation is no longer needed and can be deprecated alongside the BLS reveal path.
The mixed-in contribution must be unpredictable before the reveal: the mix must never absorb a value derivable from pre-reveal public state. This rules out folding in blake3(HASH_CHAIN_RANDAO_DST + reveal), which for a hash chain is by definition the validator's stored commitment, already public on-chain. The raw reveal is the opposite: it is the secret preimage, unknown to everyone until the proposer publishes it, so folding it in is safe.
The complication is that hash-chain commitments carry no identity and are trivially copyable. A validator can register a value it did not generate by including a copy of another validator's current commitment, via a perfectly valid, freshly signed registration. Under a linear XOR accumulator this enables a cancellation attack.
A hash accumulator, mix = blake3(mix + reveal), closes this at the root: the hash has no efficiently computable inverse, so re-injecting a copied reveal produces a fresh, unrelated mix rather than undoing anything.
Because the accumulator itself neutralizes copyability, the hash-chain path folds in the raw reveal with no per-validator personalization.
hashThe scheme needs nothing beyond standard collision and preimage resistance; the choice of BLAKE3 is forward-looking rather than security-driven. BLAKE3 is fast in software, and recent results on proving it efficiently in binary-field proof systems make it the leading candidate hash for the post-quantum consensus layer.
The legacy reveal is a signature over the epoch, so two proposals by the same validator in one epoch reveal the same value. Consuming one link per block is simpler (no per-epoch bookkeeping), makes every block contribute fresh entropy, and costs nothing, since chains are cheap to generate at any reasonable length.
We also note that with consolidations, the probability of a validator proposing multiple times in the same epoch increases, effectively cancelling out their RANDAO contribution.
Storing only the current commitment and requiring a preimage per proposal makes the chain length a purely private, off-chain parameter. The protocol state cost is one 32-byte entry per validator regardless of chain length, and validators can size chains so that the chain outlasts the validator. The only reason to store the chain length would be to mandate a specific length for every validator, which seems unnecessarily restrictive.
MIN_SEED_LOOKAHEAD + 2The scheme's grinding resistance comes entirely from the commitment predating the validator's knowledge of what it would want to bias. If a validator could register a chain just before proposing and it became active instantly, it could grind: generate many candidate chains, compute the mix each would produce at the upcoming slot, and register the most favourable one. We add a delay, sized so that the registrant cannot even know whether it will propose in the activation epoch at the time the registration is included.
Proposer duties for epoch E are drawn from the RANDAO mix as of the end of epoch E - MIN_SEED_LOOKAHEAD - 1, i.e. E - 2 with the current consensus-specs preset MIN_SEED_LOOKAHEAD = 1. For a registration included in epoch N:
N + 1 fixed at the end of N - 1 or earlier: already known at inclusion.N + 2 fix at the end of N, which is nearly over at inclusion: still grindable.N + 3 fix at the end of N + 1: entirely after inclusion.The earliest safe activation epoch is therefore N + MIN_SEED_LOOKAHEAD + 2 = N + 3, met exactly by COMMITMENT_REGISTRATION_DELAY = 3; the new commitment is first usable from the validator's first proposal in that epoch. The constants table states the bound symbolically so it survives a future change to the preset.
Without the one-pending rule, a validator's stored entry stays zero until activation, so multiple registrations for it would all pass the unregistered check and could be queued at once. That enables queue stuffing: the entire MAX_RANDAO_COMMITMENT_REGISTRATIONS budget could be filled with registrations for a single validator, costing the network 128 signature verifications and 128 queue entries for a single state effect, and crowding legitimate registrations out of the shared per-block budget.
The one-pending rule also keeps gossip sound: the "IGNORE if a pending registration exists" rule is only meaningful if a further registration for that validator is guaranteed redundant. With the rule, every included registration corresponds to a distinct validator making its one-time transition, and duplicates are invalid rather than merely wasteful.
A commitment can be registered but never updated in place. This is a deliberate simplification: because a chain can be sized to outlast the validator (see Chain Exhaustion), in-place rotation is never needed for exhaustion, and a lost chain secret is treated like a lost signing key, recovered by exiting and re-entering rather than by a protocol operation.
Replay-safety falls out of the unregistered check. A registration is valid only while randao_commitments[validator_index] is zero. Once it activates, the entry is non-zero, so any later replay of the message is invalid; and while the registration is merely pending, the one-pending rule blocks a second submission. This is the analogue of the idempotence predicate that makes BLSToExecutionChange replay-safe, whose handler asserts the credentials are still BLS-prefixed, which the first application falsifies.
There is no urgent need for validators to be fully post-quantum yet, so a hard deadline buys little; unregistered validators keep functioning on the BLS path in the meantime. There will also likely be a long tail of validators who never submit the message because they run custom software. We therefore defer the forced cutover to a fork that must touch these code paths anyway, the post-quantum switchover, which also lets us exercise the new code paths first.
This EIP requires a scheduled consensus-layer hard fork. Within the fork, the change is backwards compatible from the validator's perspective: unregistered validators continue proposing exactly as today. Downstream consumers of randao_mixes (including the execution layer's EIP-4399's PREVRANDAO) are unaffected; the mix remains a 32-byte accumulator updated once per block, only the provenance of contributions changes.
State-transition test vectors to be provided in the consensus-specs test suite.
The proposer's only degree of freedom is still withholding: reveal and propose, or withhold and forfeit the block plus its rewards. This is the same one-bit-per-proposer bias RANDAO has today, with the same economic cost. The validator freely choosing its own chain values (whereas a BLS reveal is uniquely determined by the key and epoch, leaving no freedom) does not add grinding power, because the entire chain is fixed before the validator knows its proposal slots or the co-contributions to any future mix.
A validator that sees a proposal duty approaching cannot register a favourable chain to influence the outcome. Activation is derived from the inclusion epoch, the only event the protocol observes: registration activates COMMITMENT_REGISTRATION_DELAY epochs after inclusion, strictly after every epoch whose proposer-duty seed was fixed (or partially accumulated) at inclusion, so the chain is always fixed before the seed that draws any duty it could serve. Signing earlier than inclusion only means committing with less information.
Note: This argument assumes the registrant cannot predict the contributions that land between inclusion and the fixing of the target seed. That holds today (a BLS reveal is computable only by its key holder) and after migration (a hash-chain reveal is protected by preimage resistance), but an attacker that already holds a CRQC during the transition could predict every remaining legacy contribution and grind its one-time registration against them.
Timing games around inclusion add nothing: an unregistered validator stays on the BLS path until activation, and once registered it cannot re-register, so there is no second chain to time.
Nor can registrations be replayed by third parties: once a validator is registered its stored entry is non-zero, so any replay fails the unregistered check.
A validator may choose pathological values for its own chain, for example deriving the chain secret from a publicly known constant like the genesis hash, so that all its reveals are predictable in advance and contribute no effective entropy.
This cannot be exhaustively restricted, and we do not try to, because RANDAO's security does not assume that every contribution is honest. If this were the case, then a validator choosing to add nothing by withholding would also break the security; one honest contributor per mixing period suffices.
We do forbid the pathological case of using the zero word, since this would collide with the unregistered sentinel when revealed, so process_randao rejects zero reveals.
Losing the chain secret makes a validator unable to produce valid blocks on the hash-chain path, and there is no in-place recovery: a commitment cannot be re-registered. The remedy is to exit and re-enter as a new validator. Funds are unaffected (withdrawal does not depend on the chain), and the failure mode is missed proposals, not slashing risk.
Theft of the chain secret alone lets an attacker predict (not choose) the validator's future contributions. In effect this is equivalent to leaking the validator's entire list of future BLS-RANDAO reveals: the attacker learns every future contribution in advance, but can neither choose them nor sign anything else on the validator's behalf. It is therefore strictly less power than theft of the signing key today, which gives the attacker that same prediction plus the ability to sign the validator's blocks and attestations. Validator clients should treat the chain secret with the same custody standards as signing keys. As a safety measure, it should not be derived in a way that links it to the signing key: a hash chain, taken to its limit, eventually reveals its seed, whereas a secret key must never be revealed.
The legacy BLS reveal signs the epoch number, so a reveal exposed in a block that never makes the canonical chain is worthless outside that epoch. A hash-chain reveal has no such time binding: a reveal published in an orphaned block remains the validator's mandatory next contribution at whatever future slot it next proposes. An observer who collects reveals from orphaned blocks (or losing forks) accumulates known future co-contributions.
Because a chain cannot be retired in place, there is no cheap remediation, but none is needed: the exposure is harmless. A published-but-not-yet-consumed reveal only makes that one future contribution predictable rather than secret, which is no worse than the validator withholding it, and RANDAO tolerates predictable contributions (one honest contributor per mixing period suffices). A validator sufficiently concerned about a specific exposed chain can exit and re-enter, but this is unwarranted in practice.
Preimage possession is unverifiable at registration: the protocol cannot distinguish a commitment whose chain the validator holds from a typo or the output of a buggy key-derivation.
Because registration is one-time, a bad commitment cannot be corrected in place: the validator can never propose on the hash-chain path, and the only remedy is to exit and re-enter. This makes an erroneous registration as costly as losing the chain secret, and it does not exist under BLS reveals, where there is nothing to misconfigure beyond the signing key itself. Validator clients should therefore verify a commitment by walking its full chain before registering, and treat the chain secret with the same care as the signing key.
The scheme rests on two properties of BLAKE3. Grinding resistance rests on collision resistance: a proposer that could find a collision could give a chain link two preimages and choose between them at reveal time to bias its contribution.
Unpredictability rests on preimage resistance: a reveal cannot be derived from the public commitment before it is published. The post-quantum security of the scheme is therefore comparable to the collision resistance of BLAKE3, which is believed to remain adequate against a quantum adversary. The transitional BLS reveal path and the BLS signature on RandaoCommitmentRegistration remain quantum-vulnerable, as do block signatures generally; this EIP removes RANDAO's structural dependence on signature uniqueness so that the eventual post-quantum fork is a signature-scheme swap rather than a randomness redesign.
Registration confers no randomness advantage, so spamming registrations is a pure-cost DoS vector. It is bounded on every surface:
MAX_RANDAO_COMMITMENT_REGISTRATIONS per block (at most 128 additional BLS verifications).bls_to_execution_change) bounds gossip to one signature verification per validator, ever. Non-validators cannot participate: their messages fail signature validation and incur gossipsub peer penalties. Messages for an already-registered validator fail the head-state REJECT check, also penalized.queue_randao_commitment enforces at most one pending registration per validator, and the queue is additionally bounded by inclusion rates: at most MAX_RANDAO_COMMITMENT_REGISTRATIONS entries per block, each resident for COMMITMENT_REGISTRATION_DELAY epochs, giving a worst-case steady state of ~12,300 entries.Copyright and related rights waived via CC0.