Repository navigation
Conversation
yoshidan
force-pushed
the
feature/initial
branch
2 times, most recently
from
May 30, 2026 08:03
0b5a67a to
8729311
Compare
Inserting rlp at field 5 renumbered block_hash/block_hash_branch and broke decoding of payloads encoded with the pre-Gloas schema (block_hash_branch decoded as empty). Append rlp as field 7 instead so existing encodings remain valid. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Align the field order with the proto definition (rlp = 7). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…t for Gloas For Gloas the execution root is the block hash itself, so validate_execution_update requires block_hash to equal it instead of skipping the validation, and the relayer populates block_hash from the execution block hash. This keeps block_hash a verified value across forks for L2 clients that use it as the L1 head (e.g. optimism-elc). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Gloas proves the execution header by hashing its RLP instead of walking SSZ merkle branches, so BuildExecutionUpdateFromBlockHash leaves StateRootBranch and BlockNumberBranch unset. ValidateBasic still required both, so every relayer update on a post-Gloas chain failed with "state root branch cannot be nil". The e2e did not catch this because it builds the request directly and never goes through ValidateBasic, which is only reached from the relayer path (ethereum-ibc-relay-prover: Header.ValidateBasic). Accept an execution update without those branches when Rlp is present, and check BlockHash instead since that is what the verifier compares against the consensus update's finalized execution root.
Post-Gloas the execution block a light client header references is the one `signed_execution_payload_bid.message.parent_block_hash` points at, i.e. the parent of the block produced at the finalized slot. Its timestamp is therefore not `compute_timestamp_at_slot(finalized_slot)`, so every relayer update on a post-Gloas chain failed with UnexpectedTimestamp, off by one slot. It cannot be compared against `finalized_slot - 1` either, since slots may be skipped and the parent may be several slots back. Compare it against the timestamp inside the RLP execution header instead: the consensus verifier authenticates the RLP via `keccak256(rlp) == execution_block_hash`, so this pins the timestamp the same way `state_root` and `block_number` are already pinned, and keeps the check strict rather than relaxing it. `validate_execution_header_timestamp` selects the rule from `fork_spec.is_gloas()` so that every light client on this crate, including optimism-elc, branches the same way. Both timestamp checks now live in validate.rs next to the other execution update validation, leaving time.rs as plain timestamp arithmetic with no ethereum_consensus dependency.
The consensus state records the finalized header's slot and pins its sync committees to that slot's period, but the prover derived the period from the client state height, which is an execution block number. Post-Gloas the light client header references `signed_execution_payload_bid.message.parent_block_hash`, so that block was produced one slot earlier than the finalized slot. Whenever the finalized slot is the first slot of a period the derived period was one behind, and the prover then sent the previous period's committee, failing with InvalidNextSyncCommitteeKeys. GetConsensusStateSlotWithBlockNumber resolves the slot the consensus state actually records: pre-Gloas the execution block's own slot, post-Gloas the next slot that has a block. It searches rather than adding one because slots may be skipped and the parent may be several slots back. GetPeriodWithBlockNumber now takes the minimal fork schedule so it can tell which rule applies. Gloas is detected via `execution_block_hash_gindex`, the same discriminator the Rust verifier uses.
Taking the next slot that has a block assumes it bids on the block being looked up. Compare the bid's parent_block_hash instead so a divergence surfaces as an error rather than a wrong sync committee period.
Fork resolution belongs to ForkParameters rather than a helper that takes a network name and the minimal fork schedule, so the slot helpers now accept the resolved parameters.
yoshidan
commented
Sep 15, 2026
yoshidan
marked this pull request as ready for review
September 15, 2026 00:45
The gloas work was merged and tagged upstream, so stop pinning a commit on the fork. v0.3.0 is the fork's fd7fe9a plus the no_std CI toolchain pin and the gloas verifier tests, so no production code changes. Downstream crates (ethereum-elc, optimism-elc) must move to the same tag in the same step: cargo treats the fork and upstream URLs as distinct sources, so a mixed graph builds two ethereum-consensus crates and fails to typecheck.
eth-clients/sepolia now schedules it at epoch 353024, which is timestamp 1791294816 and matches geth's SepoliaChainConfig AmsterdamTime. Mainnet stays unscheduled.
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.
Summary
Support the Gloas (Glamsterdam) HF. For Gloas and later forks, LightClientHeader carries only execution_block_hash instead of the full ExecutionPayloadHeader (EIP-7732), so the execution update is built from and verified against the RLP-encoded execution block header instead of SSZ merkle proofs.
Requires the Gloas-aware verifier (datachainlab/ethereum-light-client-rs#29).
Changes
Rust (type / proto)
Go (prover)
Notes