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:
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.
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)
Collector.decodeCommitteeVote(archive /ModeArchive) andCommitteeObserver.SaveRoots/saveAttesterRoots(legacy) decodegloas.GloasBeaconVoteand derive the attester root from the payload-status index. Attester + sync-committee traces keep working.spectypes.ProposerConsensusData(from theboole-forkbaseline).The gap
The three new roles from ssvlabs/ssv-spec#632 are not handled by either exporter path:
RolePTCAttesterPTCAttesterPartialSig)RoleProposerPreferencesProposerPreferencesPartialSig), lookahead broadcastRoleEnvelopeBuildergloas.EnvelopeConsensusData+ post-consensus partial-sigFailure mode — graceful, but untraced:
operator/dutytracer/collector.go): non-committee roles route throughtoBNRole(), whosedefaultreturns"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.protocol/v2/ssv/validator/committee_observer.go):getBeaconRolesreturnsnilfor unmapped roles → silently ignored.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:
toBNRole+collect,operator/dutytracer/collector.go): add role handling. Note the envelope'sFullDataisgloas.EnvelopeConsensusData(notProposerConsensusData), so proposal-data extraction needs a Gloas branch; PTC is partial-sig-only with a payload-attestation root that doesn't fit the existingBeaconVote-derived root logic.exporter/model.go): newBeaconRolevalues fit the existingRolefield, but the SSZ encoding is hand-maintained (exporter/model_encoding.go) — any struct change must be hand-edited. Confirm PTC / preferences map ontoValidatorDutyTraceor need a new shape.api/handlers/exporter/*): responses serializeRole.String(); addBeaconRole.String()cases (else"UNDEFINED") and query-param mapping incommon_model.go.nonCommitteeValidatorTTLs): add TTLs for the new roles.Collector.computeAndPersistScheduleForSlot): today only emits Attester / Proposer / SyncCommittee.Related latent issue (independent of Gloas)
On
main,toBNRolehas nodefaultcase and silently returns the zero valueBNRoleAttesterfor any unmapped role (boole-forkfixed this with adefaultthat errors). Worth backporting thedefaultguard tomainindependently, so a future new role can't be silently mis-recorded asATTESTER.