Skip to content

ePBS (Gloas / SIP-94) reference implementation — epic tracking #642

Description

@iurii-ssv

Tracking issue for the ssv-spec side of ePBS (EIP-7732 / Gloas, Glamsterdam fork), per SIP-94 (ssvlabs/SIPs#94). Node-side counterpart: ssvlabs/ssv#2901, with the direct-builder extension in ssvlabs/ssv#2962.

PR structure: #632 (wire constants, targets main) → #633 (reference implementation, stacked on #632; accumulates the slices below and auto-retargets to main when #632 merges) → #643 (SSZ backend migration to pk910/dynamic-ssz for the progressive Gloas + consensus-data types, stacked on #633).

⚠️ §6 amendment (SIP-94, 2026-09): envelope signing reworked from consensus to dissemination

After the branch tip (2026-08-27), SIP-94 rewrote §6 (2026-09-01→09-03, ssvlabs/SIPs#94 commits "Rewrite envelope signing without consensus""Fold the envelope-source rationale into the SHOULD"): once §4 decides, bid.block_hash pins exactly one valid envelope, so there is nothing to negotiate. Envelope signing is now one dissemination round + one threshold-signing round, no QBFT. This reopens the §6 slice below and supersedes the EnvelopeConsensusData / EnvelopeValueCheckF / consensus+postconsensus-suite parts of the landed work. §1–§5 are unaffected. Detailed plan in the reopened §6 slice below.

Landed

  • Wire constants (types: add ePBS (Gloas) domains, roles, and partial-sig types #632 — green, awaiting review): Gloas domains 0x0B/0x0C/0x0D + builder-specs' DomainBuilderRequestAuth 0x0B000001, beacon/runner roles 7/8/9, partial-sig types 7/8/9, and a golden test pinning the exact values. (§6 rework appends two more wire constants here — see below.)
  • Type layer (on types: ePBS (Gloas / SIP-94) reference implementation — WIP #633): types/gloas package (§3/§4/§5/§6 containers, DataVersionGloas placeholder), flat GloasBeaconVote / EnvelopeConsensusData, fork machinery, six encoding-vector suites in new anchor-safe package prefixes. (§6 rework removes EnvelopeConsensusData + SignedBlindedExecutionPayloadEnvelope and replaces the envelopeconsensusdata suite — see below.)
  • §2 attestations (on types: ePBS (Gloas / SIP-94) reference implementation — WIP #633, activated): fork-aware committee-runner path (Gloas vote carries the BN-supplied 0/1 payload-status index), aggregator-committee Gloas arms (Electra shape under the Gloas version tag), GloasBeaconVoteValueCheckF + six value-check vectors; SupportedAttestationVersions / SupportedAggregatorVersions include Gloas.
  • §4 groundwork (on types: ePBS (Gloas / SIP-94) reference implementation — WIP #633): ProposerValueCheckF decodes gloas.BeaconBlock and pins block slot == duty slot; types stays free of concrete Gloas decoding.

Verification state: full generate + test green on #633; fixture delta vs base: 567 Gloas subtests added, none removed, 3 pre-Gloas changed (details in #633's description). (Superseded once the §6 rework lands and vectors regenerate.)

Process

Remaining implementation slices (landing order)

  • §4 proposer runner — Gloas produce/submit, SupportedBlockVersions += Gloas, TestingGloasBeaconBlock fixtures; valcheckproposer Gloas cases + Gloas full-happy-flow (every SupportedBlockVersions iterator picks up a Gloas case). Closes anchor's spec_tests: fork-gate proposer block decode for Gloas sigp/anchor#1073 acceptance criterion.
  • §3 PTC runner (depends only on the type layer; reorderable) — VR-shaped, no QBFT: partial signatures over PayloadAttestationData under DomainPTCAttester; joins the preconsensus multi-tests; PTC-specific vectors (convergence quorum, minority split no-reconstruct, abstain, wrong root, dutyexe role checks).
  • §5 ProposerPreferences runner + ValidatorRegistration deprecation (reorderable) — per-slot-state runner, gas-limit config plumbing, VR rejected at Gloas slots (unchanged pre-Gloas); vectors include concurrent lookahead slots and re-emission-replaces-slot.
  • §6 EnvelopeProposer runner — REWORKED (consensus → dissemination) — landed across types: add ePBS (Gloas) domains, roles, and partial-sig types #632/types: ePBS (Gloas / SIP-94) reference implementation — WIP #633/types: migrate Gloas + consensus-data SSZ to pk910/dynamic-ssz #643; full suite green (9 packages, 0 failures). The previously-landed QBFT model is removed.
    • Removed: EnvelopeProposerRunner's QBFT decide()/post-consensus path, EnvelopeConsensusData, EnvelopeValueCheckF, SignedBlindedExecutionPayloadEnvelope, the valcheckenvelope suite, and the envelope entries in the consensus/postconsensus suites.
    • Constants (types: add ePBS (Gloas) domains, roles, and partial-sig types #632): SSVEnvelopeDisseminationMsgType MsgType = 3 and EnvelopePartialSig PartialSigMsgType = 10; accepted in SSVMessage.Validate(); golden test extended.
    • Type + runner (types: ePBS (Gloas / SIP-94) reference implementation — WIP #633): EnvelopeDissemination{Slot, Envelope} carrier; non-QBFT runner — the builder operator disseminates the blinded envelope, every operator content-selects the first that binds to its §4 decision (4 checks: block root, parent root, self-build index, hash_tree_root(ExecutionRequests) vs the bid), threshold-signs under DomainBeaconBuilder via the pre-consensus container, and the builder operator publishes.
    • Routing (types: ePBS (Gloas / SIP-94) reference implementation — WIP #633): the dissemination message class runs through SSVMessage.Validate(), the Runner interface (ProcessEnvelopeDissemination), Validator.ProcessMessage, and the reference p2p validator.
    • Vectors (types: ePBS (Gloas / SIP-94) reference implementation — WIP #633): envelopedissemination encoding suite (replacing envelopeconsensusdata); the envelope runner moved from the consensus/postconsensus suites to the preconsensus and non-QBFT newduty suites alongside PTC (full parity); envelopeproposer duty tests rewritten (builder happy-flow, non-builder no-publish, no-decision silent-wait, real §4→§6 root-linkage); valcheckenvelope dropped.
    • dynssz (types: migrate Gloas + consensus-data SSZ to pk910/dynamic-ssz #643): rebased onto the reworked types: ePBS (Gloas / SIP-94) reference implementation — WIP #633; EnvelopeDissemination and the blinded envelope migrated to dynssz (generate_cd.yaml / gloas generate.yaml), replacing the fastssz encoders.
    • Design decisions resolved: (1) linkage store expanded to ProposedBlocks{BlockRoot, ParentRoot, ExecutionRequestsRoot}; (2) ProcessEnvelopeDissemination added to the Runner interface (other runners return an unsupported-message error, mirroring the pre/post-consensus stubs); (3) the mock BeaconNode publishes the blinded envelope + reconstructed signature (the full SignedExecutionPayloadEnvelope/Contents stay node-side). Note: gloas.TestingBeaconBlock now commits a real execution-requests root so §6 binding holds — this changes Gloas §4/§6 fixtures only, pre-Gloas untouched.
  • §5 builder-request-auth signing round — landed across types: ePBS (Gloas / SIP-94) reference implementation — WIP #633/types: migrate Gloas + consensus-data SSZ to pk910/dynamic-ssz #643; full suite green (9 packages, 0 failures). A spec-side distributed signing round riding the proposer-preferences duty (re-scoped from the node-side-only framing previously parked in the watch list).
    • Type (types: ePBS (Gloas / SIP-94) reference implementation — WIP #633): types/gloas.BuilderRequestAuth{Data, Slot} / SignedBuilderRequestAuth. Data is the token agreed with the builder out of band (defaulting to the advertised URL bytes; zero-length invalid), signed byte-for-byte under the chain-independent DomainBuilderRequestAuth (genesis compute_domain, like the deprecated ValidatorRegistrationV1); Slot is the proposal slot and carries no dependent root, so re-emissions reproduce a byte-identical signing root.
    • Runner (types: ePBS (Gloas / SIP-94) reference implementation — WIP #633): BuilderEntry config (per-validator, deduped by data, capped at MaxBuilderEntries = 8); the proposer-preferences slot runner freezes one auth per distinct entry, signs them in one multi-root RequestAuthPartialSig container, and reconstructs + submits each on per-root quorum via SubmitBuilderRequestAuth. The round rides the duty but collects independently of the preference round — neither gates the other.
    • Vectors (types: ePBS (Gloas / SIP-94) reference implementation — WIP #633): happy flow (multi-entry → submit), shared-data dedup, cap at max, wrong-root rejection, and independence from a finished preference round.
    • dynssz (types: migrate Gloas + consensus-data SSZ to pk910/dynamic-ssz #643): BuilderRequestAuth / SignedBuilderRequestAuth migrated to pk910/dynamic-ssz (gloas generate.yaml), replacing the fastssz encoders; the runner's expected-roots helper returns the narrow types.HashRoot.
  • (Optional, post-epic) cleanups: go list -m-based includes for the pre-existing sszgen directives, doc touch-ups.

Wiring checklist for each new runner (§3/§5/§6): DutyRunners construction, testingutils ConstructBaseRunner* case (single-share), message-ID helpers + DutyRunnerForMsgID, per-runner ssv_msgs_*.go, overrideStateComparison + run_test.go mapping. §6 no longer wires a QBFT config/controller (the runner is non-QBFT after the rework); instead it wires the new dissemination message class through the validator and the spectest framework.

Externally gated / watch list

  • Progressive HTR (EIP-7688) — positional roots are shipped deliberately (devnet-6 reality; 7688 is only CFI for Glamsterdam). The §4-block/§6-envelope signing-root goldens and the progressive-container encoding suites stay held until the devnet-7 fork scope settles; prefer go-eth2-client's progressive-SSZ work over hand-rolling the block-tree cascade.
  • go-eth2-client Gloas support — when it ships, swap the DataVersionGloas placeholder for the upstream enum (values match by construction; the (unknown) fixture-name artifact resolves).
  • Consensus-spec pin bump (follow-up) — SIP-94 now pins ethereum/consensus-specs@a5a1bc630 (2026-08-26); the types/gloas containers were vendored against an earlier snapshot. Re-verify field-for-field on the next re-vendor.
  • Node-side lifecycle (ePBS (EIP-7732 / Gloas) — SIP-94 ssv#2901) — the spec model keeps ProposerPreferencesRunner.BySlot and the shared §4→§6 linkage store unbounded (pruning is declared node-side in the code comments); the node implementation owns pruning both and the preferences runner's linear HasRunningDuty() scan.
  • (Optional) index-aware mock slashability store, so the §2 cross-index equivocation rule becomes vector-assertable.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions