[runtime]: Glamsterdam (Gloas) support for the sync committee client - #1056
Draft
dharjeezy wants to merge 20 commits into
Draft
[runtime]: Glamsterdam (Gloas) support for the sync committee client#1056dharjeezy wants to merge 20 commits into
dharjeezy wants to merge 20 commits into
Conversation
Wizdave97
reviewed
Jul 20, 2026
Wizdave97
reviewed
Aug 15, 2026
| @@ -459,4 +492,25 @@ pub struct BeaconState< | |||
| #[cfg(not(feature = "nofulu"))] | |||
Member
There was a problem hiding this comment.
Remove this feature flag no longer useful
Suggested change
| #[cfg(not(feature = "nofulu"))] |
Wizdave97
reviewed
Aug 15, 2026
|
|
||
| if !is_merkle_branch_valid { | ||
| Err(Error::InvalidMerkleBranch("Execution payload branch".into()))?; | ||
| if header.state_root.0 != execution_payload.state_root.0 || |
Member
There was a problem hiding this comment.
This check is not necessary here, if we verify the header hash successfully we don't need this check
Member
|
@dharjeezy track activation on sepolia here: https://forkcast.org/upgrade/glamsterdam/ |
…hthouse ssz stack
…/glamsterdam-upgrade # Conflicts: # Cargo.lock # Cargo.toml # modules/utils/bls-utils/src/ssz/byte_vector.rs # modules/utils/serde/src/lib.rs # tesseract/consensus/bsc/Cargo.toml # tesseract/consensus/bsc/src/host.rs
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.
Prepares the Ethereum sync committee consensus client for Glamsterdam (CL name gloas, EIP-7732 ePBS), and moves the SSZ layer from
ssz-rsto Lighthouse's stack along the way.Closes #1011.
The consensus problem
ePBS removes
BeaconState.latest_execution_payload_header, the field the verifier proves today forstate_root,block_numberandtimestamp. It is replaced bylatest_block_hashplus a builder's bid, and the realExecutionPayloadmoves to a separately gossiped envelope that the sync committee never signs. So after Gloas there is no SSZ path from the signed header to the execution state root.The fix is to SSZ-prove
latest_block_hash, have the relayer supply the EL block header, and checkkeccak256(rlp(header)) == block_hashbefore decoding the three fields out of the header. Same trust assumptions as today. The alternative, verifying the payload envelope through the PTC, was rejected: the PTC attests to payload timeliness, not to the state root, so it still needs the same block-hash-to-state-root binding while adding a second committee path.Note the post-Glamsterdam EL header has 23 RLP fields, not 21. It appends
block_access_list_hash(EIP-7928) andslot_number(ePBS). The 21 and 22 field encodings both produce the wrong hash.Fork dispatch is at runtime, not compile time
ExecutionPayloadProofcarries anExecutionProofenum withLegacyandGloasvariants, both always compiled. The verifier picks the path with a runtime match on the epoch of the signed slot, so an update cannot select its own verifier, and a variant that disagrees with the fork is a hardInvalidUpdate.Why the SSZ layer changed
After devnet-6 was retired, the Gloas spec adopted progressive SSZ merkleization (EIP-7688 and EIP-7916):
BeaconStatebecame aProgressiveContainerand twelve of its lists becameProgressiveList. A progressive container hashes as a right leaning spine rather than a balanced tree, so every generalized index moved:finalized_checkpointnext_sync_committeeBranch depths are no longer uniform, which the old fixed depth scheme assumed. The verifier needed no change for that:
is_valid_merkle_branchwalks the bits of the index and now takes the generalized index alone, deriving the depth from it, so the per field*_INDEX_LOG2constants are gone.ssz-rshas no progressive support, so rather than maintain our own we moved to the crates Sigma Prime maintains for Lighthouse. That stack had gaps, which are filled in five forks underpolytope-labs, each with an open PR:ContainerFields/TreeHashFieldssplit so a balanced container is never handed methods that assume the progressive tree.ProgressiveList<T>, which exists nowhere upstream, plus no_std and SCALE.SCALE is behind a non default
scalefeature and confined to one file per crate. These types cross a runtime boundary inside consensus proofs, and the encodings matchssz-rsexactly so previously encoded proofs still decode. Notably a bitfield encodes asVec<bool>, one byte per bit, which is pinned by a test rather than left implicit.Migration notes for reviewers
Most of the diff is one mechanical change with awkward consequences:
ssz_typestakes its bounds as type level integers wheressz-rsusedconst N: usize.pub typealiases. Rust keeps types and values in separate namespaces, soVariableList<T, MAX_DEPOSITS>picks up the type andvec![0; MAX_DEPOSITS]picks up the constant, and no use site had to change.StateListcould no longer be a type alias and is now a newtype carryingPhantomData<N>, and the containers ePBS empties out carry a phantom field skipped by every derive. Neither reaches the wire.Rootwasssz-rs'sNode, which carried SCALE and wasCopy.Hash256has neither and the orphan rule blocks adding SCALE, soRootis nowBytes32, following theHash32 = Bytes32line beside it. It is no longerCopy, hence some added clones.hash_tree_root()returned aResult;tree_hash_root()is infallible.Validation
All four gloas integration tests pass against live glamsterdam-devnet-8:
The first is the one that matters. It merkleizes the full Gloas
BeaconStatethrough the new stack and compares the root against what Nimbus signed, which is the only check here that validates our merkleization against an independent implementation rather than against itself. It also confirms the phantom fields and theStateListnewtype leave the root untouched.The forks pass their own suites (tree_hash 114, ethereum_ssz 128, ssz_types 78) and build for
wasm32-unknown-unknownwithno_std.Not yet covered: the pre-Gloas prover suite in
test.rs, which needs a local non-Gloas devnet, and the BSC client's tests. BSC is migrated and compiles; it only usedBitvector::deserialize.Merge order
This depends on the five fork PRs above. The
[patch.crates-io]section pins them by revision, and those revisions are branch commits that a squash merge will orphan, so the five need to land first and the pins bumped to the merged SHAs before this can go in.