ePBS (EIP-7732) Support - #94
Conversation
iurii-ssv
left a comment
There was a problem hiding this comment.
Appreciate this effort! Doing first pass, just started looking into Gloas, forgive AI-heavy commentary (seems relevant though).
Nit: would be nice to list/organize the duties affected/added by the actual slot timeline (eg. "Proposer Preferences Duty", should come first, then "Modified Proposer Duty", then "Modified Attestation Duty", etc.)
thanks for taking a look! will resolve all the comments this week 😃 |
|
I opened ethereum/EIPs#11684 to update the EIP-7732 Gloas summary against the current consensus-specs Gloas files: Since this SIP depends on those EIP-7732 details, I would appreciate review there as well. |
the current top-level order mirrors upstream's grouping in |
…erences Pin updated from f1371480c4 to upstream master HEAD following PR review feedback from iurii-ssv and diegomrsantos. Net changes in the Proposer Preferences section: ProposerPreferences now carries dependent_root, bid handshake matches on (proposal_slot, dependent_root), gossip rule is first-valid-per-tuple, new Security Considerations entry on too-early publication. PTC paragraph also tightened to distinguish PAYLOAD_ATTESTATION_DUE_BPS from PAYLOAD_DUE_BPS. Slot Timing, Attestation Duty, and Proposer Duty sections unchanged at target pin. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…(§5/§7) §5 (re-)emission is triggered by the very validator-index change that invalidates peers' cached proposer-duty views. Because a preference partial is a one-shot deterministic broadcast, a wrongly-dropped first copy is unrecoverable under gossip dedup, starving that slot's quorum. §7: a duty-assignment view predating the node's latest validator-set change is treated as unknown (skip the check, don't IGNORE) until it refreshes, extending the not-yet-fetched-epoch tolerance. Applies to the RoleProposerPreferences (8) and RoleEnvelopeProposer (9) checks. §5: emitters SHOULD delay (re-)emission a couple slots after a validator-set change so peers converge before the one-shot partials go out.
|
Thanks @iurii-ssv, great find! Folded both parts into the SIP (0a8de9e): §7 now skips (doesn't IGNORE) the duty-assignment check when the cached view predates the latest validator-set change, until it refreshes; §5 gets the non-normative "delay re-emission a couple slots after a validator-set change" note. Anti-spam bound (committee membership + distinct-root cap + duty-count cap) kept explicit. Confirmed Anchor would hit the same wrong-drop without it, so we'll match. |
The summary said the SIP activates at the Gloas-aligned SSV fork, but the ePBS SIP (ssvlabs#94, checked at d5197bc) defines no SSV-network fork; its validation rules gate on epoch(msg.slot) >= GLOAS_FORK_EPOCH. This SIP now matches that model explicitly: activation is Ethereum-gated by the epoch(S) >= GLOAS_FORK_EPOCH conjunct, the SSV fork-equality conjunct is window-boundary protection only (it keeps the 2-slot emission window from spanning any scheduled SSV fork), and a new S >= EARLY_RANDAO_LEAD conjunct resolves the genesis underflow normatively, surfaced by the slot-zero unit test in the Anchor implementation. Vectors spell out the boundary outcomes.
| |---|---|---|---|---|---|---|---| | ||
| | `RolePTCAttester` (7) | REJECT | `PTCAttesterPartialSig` (7); 1 | PTC attestation slot | none | 3 slots | PTC assignment at slot (IGNORE) | 2 (IGNORE) | | ||
| | `RoleProposerPreferences` (8) | REJECT | `ProposerPreferencesPartialSig` (8); up to 4 distinct signing roots, repeat of a seen root = duplicate | `proposal_slot` | `(1 + MIN_SEED_LOOKAHEAD) * SLOTS_PER_EPOCH` slots | 2 slots | proposer assignment at `proposal_slot` (IGNORE) | `SLOTS_PER_EPOCH` (IGNORE) | | ||
| | `RoleEnvelopeProposer` (9) | QBFT: 1 proposal / prepare / commit / round-change per (signer, slot, round); round cut-off 2 | `PostConsensusPartialSig` (0); 1 | proposal slot | none | 3 slots | proposer assignment at slot (IGNORE) | `SLOTS_PER_EPOCH` (IGNORE) | |
There was a problem hiding this comment.
just a reminder that this and other message validation rules should be fixed in https://github.com/ssvlabs/knowledge-base/blob/main/p2p/MessageValidation/Rules.md and spec
There was a problem hiding this comment.
@GalRogozinski not sure what you mean, you want us to update https://github.com/ssvlabs/knowledge-base/blob/main/p2p/MessageValidation/Rules.md to align it with the SIP ? I can create PR for it, I guess. Just maybe lets approve/merge the SIP first, so we know it is final.
And I also think we plan/want to add the "trusted bids flow/duty" to this SIP (once it's finalized on the Ethereum side), right @shane-moore ?
There was a problem hiding this comment.
Yes, we'll add a duty for RequestAuth once the beacon api/builder-spec pr's are a bit more solid:
ethereum/builder-specs#165
ethereum/beacon-APIs#630
ethereum/beacon-APIs#625
similarly, it shows EIP-7688 Progressive Containers as still CFI status in the glamsterdam meta EIP, but they verbally SFI'd it on last ACDC call, so once that's documented, I'll update the SIP accordingly
Mechanical re-pin of every consensus-specs permalink from 801a38e15 (reviewed 2026-07-06) to master head 46d3d3513 (reviewed 2026-08-04), plus citation and context accuracy fixes for upstream changes since the old pin. No SSV implementation impact in this commit. - Re-anchor the PTC security citation from a p2p-interface.md line anchor to the stable payload_attestation_message gossip-validation heading (the file was rewritten as executable functions upstream). - Update the proposer_preferences gossip dedup key to (dependent_root, proposal_slot): upstream dropped validator_index, which the proposer check already determines. Matching key-language updates in the two related security entries. - State the REJECT severity of dependent_root sourcing and the network-wide IGNORE of re-emissions that change only fee_recipient or target_gas_limit under an unchanged key. - Flatten PR-history narration (#5414, #5429) into plain statements of current spec behavior.
EIP-7688 was ratified Scheduled for Inclusion for Glamsterdam at ACDC 183, so the SIP drops its describe-positional-until-SFI stance and specifies the progressive Gloas roots. Serialization, the QBFT values, and all SSV containers are unchanged; only signing-root derivation moves. - Summary/Motivation/Rationale: state the same-bytes different-roots boundary and enumerate exactly which SSV signing paths change (aggregate, block, self-build envelope) and which do not. - §2: the aggregate serialization stays Electra-shaped, but the Gloas Attestation is progressive, so the AggregateAndProof signing root requires the Gloas type in both aggregation paths. - §4: the decided block bytes are unchanged while the block root commits to the progressive BeaconBlockBody; positional merkleization is invalid at Gloas slots. - §6: the blinded envelope is a five-field progressive container with an explicit mix_in_active_fields root derivation, progressive payload/request roots, ProgressiveList bounds guidance, and a fixed-expected-root test requirement for blinded-to-full parity. - Security: mixed positional/progressive merkleization splits threshold signatures on identical decided bytes. - Watchlist: track the EIP-7773 status update and the client type implementations instead of waiting on SFI.
- §3: payload_attestation_data moved its slot from path to query parameter after v5.0.0-alpha.2; re-pin that one link to beacon-APIs master 4b4d89a2 and note the 204 no-block response as the explicit abstain signal. - Watchlist: beacon-APIs #625 closed 2026-08-04, superseded by #630 (BuilderEntry preferences, max_execution_payment, submitBuilderPreferences, request_auth types); reserve DOMAIN_REQUEST_AUTH 0x0B000001 and partial-sig type 9 for the request-auth amendment pending builder-specs #165.
…nale - §4: point the GetBlockData permalink at ssv-spec ac6b4233, which contains the roles and partial-sig types this SIP defines; the old 85ee4f32 tree predates them and contradicted the SIP for readers following the link. Same file and line range, verified unchanged. - §6: state the settled design rule that only payload is blinded (the SSV form deviates from the consensus-spec envelope in exactly one field; the request lists are tiny relative to the payload), and link EIP-8282, previously named with no link.
Node-side surface for Gloas (ePBS) Payload Timeliness Committee duties, per SIP ssvlabs/SIPs#94: - types/gloas: PayloadAttestationData (42B SSZ) and PayloadAttestationMessage (146B SSZ), plus JSON-only PTCDuty, matching the beacon-APIs Gloas schemas; SSZ round-trip and golden JSON wire-format tests. - goclient: hand-rolled HTTP for the three PTC endpoints (get duties, produce data, submit messages). go-eth2-client has no Gloas provider yet, so these are direct requests until it is rebased. - beacon: standalone PTCCalls interface (folded into BeaconNode with the PTC runner later) plus regenerated MockPTCCalls; GoClient conformance asserted in tests.
EIP-7773 now lists EIP-7688 as Scheduled for Inclusion, so the watch trigger is gone; the normative progressive-root text stands on its own.
|
@iurii-ssv, I updated the proposer-preferences deduplication rule. A previously accepted signing root now receives An honest retry or sender restart can repeat the same root after the gossip duplicate cache expires. A same-peer repeat therefore does not prove peer fault or justify |
| - *Earliness:* the message slot is `proposal_slot` ([§5](#5-proposer-preferences-duty)), up to the proposer lookahead in the future at emission, hence the allowance. | ||
| - *Lateness:* the tight TTL drops stale preferences as replays. | ||
| - *Slot-advance exempt:* a signer holds its whole lookahead at once, so a lower-slot message is a concurrent duty, not a stale one (lateness is its past bound). | ||
| - *Dedup:* The base one-per-(signer, slot) rule gains a bounded exception for distinct signing roots at one proposal slot. Each of the first four distinct roots per (`MessageID`, signer, `proposal_slot`) is eligible for ACCEPT, subject to all other validation rules. When a message with a new root receives ACCEPT, the implementation MUST record the root. It MUST NOT record roots from messages that receive IGNORE or REJECT. If this dedup rule is the only failing condition, implementations MUST classify the message as IGNORE in either case: |
There was a problem hiding this comment.
Fix: add a retention requirement — validation state backing this rule (recorded roots, duty counts) MUST be kept for the message's full validity window: earliness + lateness = (1 + MIN_SEED_LOOKAHEAD) * SLOTS_PER_EPOCH + 2 ≈ 66 slots for RoleProposerPreferences.
Why: the rule says the implementation MUST record accepted roots, but current validation state spans only SLOTS_PER_EPOCH + LateSlotAllowance = 34 slots (a slot % 34 ring buffer, sized for roles whose messages live ~3 slots). Role-8 messages are valid across 66 slots — wider than the store — so entries for slots 34 apart overwrite each other and the recorded roots are forgotten while their slot is still live, meaning the 4-root cap silently never binds.
Worst attack if unfixed: a malicious operator (only committee members pass validation at all) picks two proposal slots 34 apart in the lookahead, and alternates messages between them, each with a fresh fabricated signing root (roots are unverified claims). Each message evicts the other slot's recorded roots, so every message lands on empty state, gets a fresh "first of 4" budget, and is ACCEPT'd and re-forwarded by every honest node on the cluster subnet — an unbounded, network-amplified spam flood, sustained indefinitely. No safety impact (fail-open: honest partials still flow, junk is dropped by a cheap root comparison before any BLS work), but it defeats exactly the DoS bound this cap exists to provide. With 66-slot retention the same attacker gets 4 accepted roots per slot and everything after is IGNORE'd at its direct peers.
This SIP describes the ssv spec changes needed to keep operators performing validator duties correctly after ePBS (EIP-7732) lands in the consensus layer Gloas fork. Covers earlier slot deadlines,
AttestationData.Indexpropagation throughBeaconVote, the new PTC committee duty, theproduceBlockV4proposer flow (self-build vs external-builder variants), and the newSignedProposerPreferencesbroadcast.