Repository navigation
Add post-quantum consensus key registry (EIP-8450) - #5782
Draft
kevaundray wants to merge 17 commits into
Draft
kevaundray wants to merge 17 commits into
kevaundray wants to merge 17 commits into
Conversation
Add EIP-8450 as a feature on top of Heze. Validators register a leanXMSS public key and a VRF root, authorized by their BLS signing key and proven by a leanXMSS signature at leaf index 0. Registered keys are dormant and can be replaced at most once every MIN_PQ_KEY_CHANGE_DELAY epochs. Registrations are a new block operation and gossip topic, and the keys live in lists parallel to the validator registry. leanXMSS verification and signing are backed by py-lean-pq in tests and follow the BLS setting, so tests that run without BLS also skip leanXMSS checks. Public keys are always derived since they are written to the state. The feature is added to the CI test matrix.
Contributor
Author
|
Blocked on ethereum/EIPs#12592 |
Once the clock reaches the EIP-8450 fork epoch, the head state can still be a Heze state until the first block of the fork epoch is imported. That state has no `pq_public_keys` or `pq_key_change_epochs`, so gossip validation would fail while reading them. Ignore registrations until the head state is at or after the fork, as the EIP now specifies.
Replay protection relies on each change being applied in a later epoch than the change it names. With a delay of zero, a change applied in the epoch it names stores that epoch again, so the same registration could be included twice. State the constraint next to the preset, matching the EIP. Current presets use 256 and 8, so neither is affected.
Registry entries no longer carry a version byte, so `PQPublicKeys` is the leanXMSS key and the VRF root (64 bytes), and a registration is 2580 bytes. Whether a validator has registered is read from its change epoch, which is `FAR_FUTURE_EPOCH` until the first registration. The hash-based primitives are now specified by their interface only, as in the EIP, which no longer depends on EIP-CRYPTO: `xmss.Verify`, `xmss_signature_index`, and `vrf.Verify`, which this upgrade does not call. This matches ethereum/EIPs#12592.
Stateful signing is not assumed to be deterministic, so signing the same message twice at one leaf index is also a leaf reuse. A signer therefore signs at `PQ_POSSESSION_XMSS_INDEX` only once per key, keeps the proof, and resubmits the same bytes in every registration of the key. The crypto section also drops construction details, since the constructions are specified outside this upgrade. This matches ethereum/EIPs#12592.
jtraglia
reviewed
Oct 28, 2026
Comment on lines
+280
to
+282
| pq_public_keys: ValidatorPQPublicKeys | ||
| # [New in EIP8450] | ||
| pq_key_change_epochs: ValidatorPQKeyChangeEpochs |
Member
There was a problem hiding this comment.
Also (with the exception of ptc_window ugh) we try not to use acronyms in structure fields. It would be a bit verbose, but we might want to expand "pq" to "post_quantum" here.
A registration is now rejected if any registered validator holds its leanXMSS key, including the validator that registers. A change never reuses the current key or its proof, so each proof of possession is signed once and only resubmitted unchanged when its registration was not included. Changing only the VRF root therefore requires a new leanXMSS key. This matches ethereum/EIPs#12592.
Consensus only rejects a leanXMSS key that a validator currently holds, so returning to an earlier key, or registering a key that another validator released, is valid on chain. The honest validator guide now states the separate signer rule: each key is used in at most one registration, for one validator, so its proof is signed once. A registration that is not included stays in the operation pools and is included later with the same bytes. This matches ethereum/EIPs#12592.
Returning to one's own earlier key could reuse the stored first proof, since its message is unchanged, while taking another validator's released key needs a proof of a different message at leaf 0. The signer rule still forbids both, so a signer never has to keep old proofs. This matches ethereum/EIPs#12592.
Builders register a leanXMSS key through the same operation, message, proof of possession, and gossip topic as validators, naming their index with `BUILDER_INDEX_FLAG` set. Their keys are stored in lists parallel to `builders`. Builder indices are reused once a builder has withdrawn, so a builder's entry starts at its deposit epoch instead of `FAR_FUTURE_EPOCH`, and a registration signed for an earlier builder at the same index never applies. Builders register a zero VRF root and must be active. Key uniqueness spans validators and builders. `PQKeyRegistration.validator_index` is renamed to `index` and `PQKeyPossession.validator_pubkey` to `pubkey`, with the SSZ layout unchanged. This matches ethereum/EIPs#12592.
The `beacon_block` topic now rejects a block with more than `MAX_PQ_KEY_REGISTRATIONS` registrations before it is processed. The `pq_key_registration` topic checks key uniqueness before the BLS signature, cheapest first. The validator guide notes that a validator and a builder may share a BLS public key, so a key released by one can be registered by the other with the same proof bytes. This matches ethereum/EIPs#12592.
An entry holds keys exactly when its leanXMSS key is not all-zero, since a registration writes a nonzero key and the current epoch together and onboarding writes the all-zero key and the never-registered marker together. Registration therefore reads and writes the selected keys and change epoch lists directly, gates the change delay on the stored key, and rejects the all-zero key explicitly. Only `is_pq_public_key_in_use` and `is_valid_pq_possession_proof` remain as helpers. Outcomes are unchanged except that an all-zero key now fails the explicit check. This matches ethereum/EIPs#12592.
Gossip now ignores a builder registration whose deposit epoch is not finalized in the head state, since that depends on the node's view of finality, and still rejects one for a builder that has initiated an exit. Notes state that builder index reuse relies on `MIN_BUILDER_WITHDRAWABILITY_DELAY` being at least 1, and that reading the builder's BLS public key after `parent_execution_requests` cannot diverge from batch signature verification on a valid block. Random `ssz_static` block bodies are capped at `MAX_PQ_KEY_REGISTRATIONS` registrations, as for the other block operations.
`PQPublicKeys` becomes `PQKeyMaterial`, since it holds a VRF root as well as a public key, with the state fields, list types, and the registration field renamed to match. Names that refer to actual public keys are unchanged. SSZ layout and hash roots are unchanged. This matches ethereum/EIPs#12592.
Field comments on `PQKeyRegistration` and `SignedPQKeyRegistration` become notes above the containers. The registration and possession domains now pass `GENESIS_FORK_VERSION` explicitly instead of relying on the default, which replaces the comments and notes saying so.
The four new `BeaconState` fields spell out "post_quantum" instead of the "pq" abbreviation, since container fields avoid acronyms. Type, function, and block body names are unchanged. SSZ layout and hash roots are unchanged.
`xmss_signature_index` becomes `get_xmss_signature_index` and decodes with `ENDIANNESS`. `process_pq_key_registration` reads and writes the validator or builder lists directly in each branch instead of aliasing them. Gossip checks that depend on the node's head state, namely an index not yet in the head state, a stale previous change epoch, and a key already in use, now IGNORE instead of REJECT. Notes cover how a key map is maintained and how a faulty first-seen registration is replaced. Tests now cover each BLS signature failure separately, a replay after a later change released the key, an exact registration count limit in the state transition, two registrations for one index in a block, a builder registering soon after its deposit, the all-zero key when every entry holds a key, and a key released by builder index reuse. This matches ethereum/EIPs#12599.
2 of 3 tasks
tcoratger
added a commit
to tcoratger/ethlambda
that referenced
this pull request
Oct 30, 2026
EIP-8450 gives every validator and builder a post-quantum key ahead of a signature switch. This puts it in heze, where consensus-specs builds it as the `eip8450` feature on top of heze (ethereum/consensus-specs#5782), following the EIP as revised by ethereum/EIPs#12592 and #12599: - Heze's state appends four lists parallel to the validator and builder registries: each index's key material (a leanXMSS key and a VRF root, zero for builders) and the epoch of its last change. Heze's body appends `pq_key_registrations`. The gloas upgrade into heze fills the lists, and new validators and builders, a reused builder index included, get unset entries. - A registration names a validator or a builder, is authorized by the registrant's BLS key, and carries a leanXMSS proof of possession of the key for that BLS key, at the reserved leaf 0. It may replace registered keys once per MIN_PQ_KEY_CHANGE_DELAY epochs (256, 8 in minimal), and names the change it replaces, which blocks replays. A key any index holds is refused. - Registrations travel as a new operation on the `pq_key_registration` topic, validated in the specification's order on a heze head, pooled per (index, change epoch), packed by heze block production when they apply after the other operations, and submitted through `/eth/v1/beacon/pool/pq_key_registrations` (not yet a Beacon API route). The beacon suite runs the `eip8450` vectors as heze, and lists `heze` (without the registry) as unmodeled. No release ships those vectors yet: `make eip8450-spec-tests` generates them from #5782's pyspec, pinned by commit. DB_VERSION goes to 8, since heze rows change shape. New dependency: lean-pq, the leanXMSS bindings of ethereum/lean-pq-bindings (v0.8.0, pinned by commit). The consensus-specs vectors are generated with py-lean-pq, the Python side of the same bindings, and proofs must verify under exactly that scheme. The `leanvm` pin here carries the lean chain's own XMSS (BLAKE2s, 42 chains, one tree), a different scheme, so it cannot stand in. thiserror moves to 2.0.21, which lean-pq requires, resolved under the 14-day cooldown. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
tcoratger
added a commit
to tcoratger/ethlambda
that referenced
this pull request
Nov 1, 2026
EIP-8450 gives every validator and builder a post-quantum key ahead of a signature switch. This puts it in heze, where consensus-specs builds it as the `eip8450` feature on top of heze (ethereum/consensus-specs#5782), following the EIP as revised by ethereum/EIPs#12592 and #12599: - Heze's state appends four lists parallel to the validator and builder registries: each index's key material (a leanXMSS key and a VRF root, zero for builders) and the epoch of its last change. Heze's body appends `pq_key_registrations`. The gloas upgrade into heze fills the lists, and new validators and builders, a reused builder index included, get unset entries. - A registration names a validator or a builder, is authorized by the registrant's BLS key, and carries a leanXMSS proof of possession of the key for that BLS key, at the reserved leaf 0. It may replace registered keys once per MIN_PQ_KEY_CHANGE_DELAY epochs (256, 8 in minimal), and names the change it replaces, which blocks replays. A key any index holds is refused. - Registrations travel as a new operation on the `pq_key_registration` topic, validated in the specification's order on a heze head, pooled per (index, change epoch), packed by heze block production when they apply after the other operations, and submitted through `/eth/v1/beacon/pool/pq_key_registrations` (not yet a Beacon API route). The beacon suite runs the `eip8450` vectors as heze, and lists `heze` (without the registry) as unmodeled. No release ships those vectors yet: `make eip8450-spec-tests` generates them from #5782's pyspec, pinned by commit. DB_VERSION goes to 9, since heze rows change shape. New dependency: lean-pq, the leanXMSS bindings of ethereum/lean-pq-bindings (v0.8.0, pinned by commit). The consensus-specs vectors are generated with py-lean-pq, the Python side of the same bindings, and proofs must verify under exactly that scheme. The `leanvm` pin here carries the lean chain's own XMSS (BLAKE2s, 42 chains, one tree), a different scheme, so it cannot stand in. thiserror moves to 2.0.21, which lean-pq requires, resolved under the 14-day cooldown. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
jtraglia
reviewed
Nov 2, 2026
Comment on lines
+12
to
+15
| - [New `ValidatorPQKeyMaterial`](#new-validatorpqkeymaterial) | ||
| - [New `ValidatorPQKeyChangeEpochs`](#new-validatorpqkeychangeepochs) | ||
| - [New `BuilderPQKeyMaterial`](#new-builderpqkeymaterial) | ||
| - [New `BuilderPQKeyChangeEpochs`](#new-builderpqkeychangeepochs) |
Member
There was a problem hiding this comment.
We don't need separate types for validators & builders.
Let's halve these and name them PQKeyMaterial and PQKeyChangeEpochs.
Member
There was a problem hiding this comment.
Err correction, PQKeyMaterial should be PQKeyMaterials.
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Add new EIP
This adds the pq public key registry ontop of Heze.
TLDR:
New Library
py-lean-pqcurrently