Skip to content

Exporter: accommodate Glamsterdam / ePBS (Gloas) duties — PTC attester, proposer preferences, envelope builder #2917

Description

@iurii-ssv

Context

#2901 (ePBS / EIP-7732 / Gloas) introduces three new validator-scoped duties and wires them into SetupRunners, but the exporter only partially accommodates Gloas. This issue tracks the exporter follow-ups so an exporter node on a Glamsterdam network actually observes the new duties — today it silently drops them.

Scoped intentionally as a follow-up to #2901 (draft / devnet), not a blocker for it.

What already works (no action needed)

  • Committee attestation on Gloas is handled in both exporter paths: Collector.decodeCommitteeVote (archive / ModeArchive) and CommitteeObserver.SaveRoots / saveAttesterRoots (legacy) decode gloas.GloasBeaconVote and derive the attester root from the payload-status index. Attester + sync-committee traces keep working.
  • Proposer traces decode spectypes.ProposerConsensusData (from the boole-fork baseline).

The gap

The three new roles from ssvlabs/ssv-spec#632 are not handled by either exporter path:

Role Message shape
RolePTCAttester partial-sig only (PTCAttesterPartialSig)
RoleProposerPreferences pre-consensus partial-sig (ProposerPreferencesPartialSig), lookahead broadcast
RoleEnvelopeBuilder QBFT consensus over gloas.EnvelopeConsensusData + post-consensus partial-sig

Failure mode — graceful, but untraced:

  • Archive path (operator/dutytracer/collector.go): non-committee roles route through toBNRole(), whose default returns "unexpected runner role". All three hit it → collect() returns the error → the message worker logs at Debug and drops the message (no custom error handler is registered; defaultErrHandler). The duties are not traced.
  • Legacy path (protocol/v2/ssv/validator/committee_observer.go): getBeaconRoles returns nil for unmapped roles → silently ignored.
  • Routing (operator/validator/controller.go, nonCommitteeValidatorTTLs) has no entries for the new roles either.

Scope / checklist

Whether to surface each of these in the exporter is partly a product decision (PTC and proposer-preferences are new concepts). Once decided:

  • Collector (toBNRole + collect, operator/dutytracer/collector.go): add role handling. Note the envelope's FullData is gloas.EnvelopeConsensusData (not ProposerConsensusData), so proposal-data extraction needs a Gloas branch; PTC is partial-sig-only with a payload-attestation root that doesn't fit the existing BeaconVote-derived root logic.
  • Model (exporter/model.go): new BeaconRole values fit the existing Role field, but the SSZ encoding is hand-maintained (exporter/model_encoding.go) — any struct change must be hand-edited. Confirm PTC / preferences map onto ValidatorDutyTrace or need a new shape.
  • API (api/handlers/exporter/*): responses serialize Role.String(); add BeaconRole.String() cases (else "UNDEFINED") and query-param mapping in common_model.go.
  • Routing (nonCommitteeValidatorTTLs): add TTLs for the new roles.
  • Schedule filler (Collector.computeAndPersistScheduleForSlot): today only emits Attester / Proposer / SyncCommittee.

Related latent issue (independent of Gloas)

On main, toBNRole has no default case and silently returns the zero value BNRoleAttester for any unmapped role (boole-fork fixed this with a default that errors). Worth backporting the default guard to main independently, so a future new role can't be silently mis-recorded as ATTESTER.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestexporterRelated to exporter

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions