feat: add heze fork definition and boilerplate - #9505
Conversation
Scaffold the heze fork (post-gloas) as a gloas-copy plus the EIP-7805 inclusion-list data model: IL containers (InclusionList, SignedInclusionList, committee/request), ExecutionPayloadBid extended with inclusionListBits, and the modified BeaconState/Body/PayloadAttributes. Wires fork registration (params, config, forkConfig, ssz/type maps), upgradeStateToHeze, IBeaconStateViewHeze, and fork-specific bid signing root/storage and genesis fork versions so the bid type round-trips correctly. Enables heze spec tests (skipping the FOCIL fork-choice scenarios shared with gloas pending #9233) and validates heze config fields against the spec. Excludes FOCIL feature logic (inclusion-list pools, validation, gossip, reqresp, duties) which remains on the focil branch.
Move heze constants/presets after gloas to match fork sequence, validate the heze fork version, inclusion-list config and committee-size values against the remote spec (ensure-config-is-synced), and wire ethspecify sources for the heze constant/preset/config references so they are no longer skipped.
There was a problem hiding this comment.
Code Review
This pull request introduces the initial types, configurations, and state transition logic for the upcoming 'Heze' hard fork (EIP-7805), which implements inclusion lists. It adds Heze-specific parameters to the mainnet and minimal configurations, defines SSZ types (such as InclusionList and modified BeaconState), implements the state upgrade logic (upgradeStateToHeze), and updates spec references. However, two issues were identified: first, removing unimplemented Heze fields (VIEW_FREEZE_CUTOFF_BPS, INCLUSION_LIST_SUBMISSION_DUE_BPS, and PROPOSER_INCLUSION_LIST_CUTOFF_BPS) from the ignored remote config list in ensure-config-is-synced.test.ts will cause configuration sync tests to fail; second, a biome ignore comment is required directly preceding case ForkName.heze: in utils.ts to prevent lint/build failures from the switch-case fallthrough.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
Performance Report✔️ no performance regression detected Full benchmark results
|
…snapshot Add DOMAIN_INCLUSION_LIST_COMMITTEE to the config API specConstants so the e2e config test (which scans heze spec docs) sees it exposed, and refresh the forkName snapshot to include heze (shared by the browser test via the test/browser -> ./unit symlink).
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9f70a38ca9
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
|
@ensi321 can you address bot comments please |
- getGossipSSZType: dispatch SignedExecutionPayloadBid via sszTypesFor(fork) so heze bids include inclusionListBits instead of being decoded as gloas and rejected as invalid serialized bytes. - preparePayloadAttributes: set inclusionListTransactions to an empty list post-heze so the heze SSEPayloadAttributes container can be serialized. Aggregation pool wiring lands with the focil feature (TODO HEZE). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Resolve conflicts: - params/index.ts: keep both DOMAIN_BUILDER_DEPOSIT (gloas, 0x0E) and DOMAIN_INCLUSION_LIST_COMMITTEE (heze); fix heze domain 14 -> 16 (0x10) to match spec (both had collided on 14). Keep both request-type + committee-size consts. - api config constants.ts: export both gloas builder-deposit consts and heze DOMAIN_INCLUSION_LIST_COMMITTEE. - processExecutionPayloadBid.ts: merge imports; drop unused ForkPostGloas and BeaconBlock.
|
seems like spec tests are failing |
nflaig
left a comment
There was a problem hiding this comment.
we might wanna wait until alpha.12 since inclusion_list_bits will be there again
This reverts commit bdafb91. inclusion_list_bits is re-added to the bid post-alpha.11 (consensus-specs #5410), so removing it was a regression against the latest spec. Add the Bitvector[INCLUSION_LIST_COMMITTEE_SIZE] field back and wait for the alpha.12 spec-tests, where the field is present again. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Drop spurious `(EIP-7805)` from HEZE config comments to match the style of other forks - Remove heze redefinitions of ExecutionPayload, ExecutionPayloadHeader and the blinded block types: heze does not modify them per the alpha.12 spec, so inherit them from gloas/deneb/electra via the fork type registry (mirrors gloas, which defines none of these) - Type getExecutionPayloadBidSigningRoot's bid param with the top-level ExecutionPayloadBid instead of a gloas|heze union - Drop the `as unknown` double cast in processExecutionPayloadBid by widening through CachedBeaconStateAllForks Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
wait for #9606 to be merged. |
looks ready now |
- heze SSZ types now use ProgressiveContainerType (ExecutionPayloadBid 13 active fields, BeaconState 46) and ProgressiveList for inclusion list transactions - move INCLUSION_LIST_COMMITTEE_SIZE from ChainConfig to BeaconPreset and add MAX_SIGNED_EXECUTION_PAYLOAD_BID_SIZE_HEZE / MAX_SIGNED_INCLUSION_LIST_SIZE - align heze TypesByFork entries with gloas (light client + attestation types) - run heze spec test suites; skip eip8148 fork vector, heze FCR and PTC/FOCIL fork choice handlers matching existing gloas skips - check name-based spec test skips before input deserialization so cross-fork vectors that cannot be parsed can be skipped Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Note: the eip8148 upgrade vector (fork_base_state) is generated under heze/fork because eip8148 is not a testgen fork, so it is not covered by skippedForks and currently fails. To be investigated separately. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: aa4505cc19
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
# Conflicts: # packages/beacon-node/test/spec/presets/fast_confirmation.test.ts
Gloas block production (#9595) returns `BlockContents` containing the execution payload envelope for the stateless self-build flow. Heze was still mapped to `fulu.BlockContents`, which has no envelope and carries a fulu block, so produceBlockV4 would have serialized the wrong shape at the heze boundary.
The heze preset raises MAX_SIGNED_EXECUTION_PAYLOAD_BID_SIZE from 196932 to 196934 to cover the new inclusion_list_bits field. Without selecting the heze bound, valid bids in those last two bytes are rejected before deserialization.
The heze block body requires a heze SignedExecutionPayloadBid, so the self-build path must initialize inclusionListBits or hashing/serializing the produced block reads an undefined bitvector.
Conflict resolutions: - specTestIterator: keep the heze on_payload_attestation_message skip alongside unstable's new gloas should_apply_proposer_boost skip - ensure-config-is-synced: this branch implements the EIP-7805 inclusion list params in ChainConfig, so they no longer need to be ignored - processExecutionPayloadBid: read parentSlot before overwriting the bid (#5473) while keeping the heze ExecutionPayloadBid type branch - .ethspecify.yml: heze configs are wired to sources on this branch, so drop them from the "not implemented" ignore list
Most of the alpha.12 -> alpha.13 heze delta is cosmetic in the spec (uint64 -> Uint64, Bitvector -> BitVector). The functional changes: - Add MIN_SLOTS_FOR_INCLUSION_LISTS_REQUESTS (= 1), a new inclusion list req/resp serving window param. Marked non-critical in assertEqualParams to match the other MIN_EPOCHS_FOR_*_REQUESTS / MAX_REQUEST_* params. - Rename InclusionListByCommitteeIndices -> InclusionListsByIndices and add the inclusion_list_committee_root field to the request, renaming committee_indices to indices. - Point heze spec reference URLs at v1.7.0-alpha.13. - Unskip heze/fork/fork/pyspec_tests: the broken eip8148 upgrade vector was removed upstream in consensus-specs#5440 and alpha.13 adds real heze fork test vectors, which pass. - Skip heze/fork_choice/should_apply_proposer_boost alongside the gloas suite; the generator emits heze vectors too and Lodestar still applies the pre-gloas proposer boost.
lodekeeper
left a comment
There was a problem hiding this comment.
Reviewed this and opened a stacked fix in #9763.
What I found/fixed there:
- Heze
PayloadAttributesandupgrade_to_hezewere implemented but still listed as unimplemented in specrefs. - The generated mainnet
gloas/heze sanity/slots/historical_accumulatorcases pass but exceed the current 30s timeout, which reproduces the failing CI job. - Heze
inclusionListTransactionswas populated by block production but dropped during engine payload-attributes RPC serialization. - The stale
/heze_forkskip is removed; the current Heze fork-upgrade vectors pass withSPEC_FILTER_FORK=heze.
I do not think the remaining zeroed FOCIL placeholders need to block this boilerplate PR while Heze is disabled by default and FOCIL suites are explicitly skipped. They should block any PR that actually enables Heze on a live/test network before inclusion-list pool/gossip/fork-choice handling is wired.
gloas and heze take ~23-24s on the mainnet preset versus ~1-6s for every pre-gloas fork, which is 76-81% of the 30s sanity/slots timeout and a flake risk on slower runners. The suite steps SLOTS_PER_HISTORICAL_ROOT (8192) slots and every post-gloas slot writes into the equally wide executionPayloadAvailability bitvector. Skipped rather than raising the timeout so the regression stays visible. Tracked in #9771
- getExecutionPayloadBidSigningRoot: select the bid ssz type via fork-aware sszTypesFor() instead of a manual heze/gloas ternary, matching the pattern in getGossipSSZType. Keeps the pre-gloas fallback so the "different fork" negative test still compares roots rather than throwing. - IBeaconStateViewHeze: drop the latestExecutionPayloadBid redeclaration. IBeaconStateViewGloas already declares it as ExecutionPayloadBid, which defaults to the ForkPostGloas union and therefore already covers heze. This follows latestExecutionPayloadHeader, declared once at bellatrix as a union and never re-narrowed in capella or deneb. Dropping it also removes the Omit/re-add of the field in IBeaconStateViewLatestFork, which only existed to undo the narrowing. The interface and isStatePostHeze guard stay: every fork from altair onwards has both, even when it adds no fields (IBeaconStateViewDeneb is forkName-only and isStatePostDeneb has no callers).
Match the convention in the other fork type modules: gloas, fulu, electra, capella and altair carry no consensus-specs URLs in sszTypes.ts. The [New in Heze:EIP7805] / [Modified in Heze:EIP7805] field markers are kept, since those mirror the spec annotations and mark what heze actually changes.
|
|
||
| // Verify that the builder has funds to cover the bid | ||
| if (!canBuilderCoverBid(state, builderIndex, amount)) { | ||
| if (!canBuilderCoverBid(state as CachedBeaconStateGloas, builderIndex, amount)) { |
There was a problem hiding this comment.
this is a bit annoying but it's the cleanest and safe type cast
There was a problem hiding this comment.
Handled in #9773 now. I refreshed the branch onto current #9505 head d388ac1f, so it preserves your later type-cast cleanup and only keeps the intended change here: widen canBuilderCoverBid / the pending-balance helper to CachedBeaconStateGloas | CachedBeaconStateHeze, then remove this stale Gloas-only cast at the call site.
|
🎉 This PR is included in v1.46.0 🎉 |
No description provided.