You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
In Gloas, on-chain PTC votes are recorded in block bodies as PayloadAttestation aggregates whose aggregation_bits: Bitvector[PTC_SIZE] are indexed by the PTC ordering — the proposer constructs them "with respect to the PTC that is obtained from get_ptc(state, Slot(block_slot - 1))" (validator.md).
No API surface exposes that ordering — and none would expose PTC membership to observers either, once the duties endpoint rework lands:
The current duties endpoint (POST /eth/v1/validator/duties/ptc/{epoch}, Standardized Epbs Beacon Api #552) returns membership for the requested validators only, with no position information (unlike AttesterDuty.validator_committee_index).
Update to handle non canonical ptc duties #619 replaces it with a per-slot GET returning {data, validator_indices} for validators the beacon node tracks — the right call for the VC flow (duties must exist per branch), but it removes the last way to query PTC membership in advance, historically, or for arbitrary validators.
GET /eth/v1/beacon/pool/payload_attestations (Standardized Epbs Beacon Api #552) exposes individual messages, but the pool is ephemeral — it cannot answer historical questions, while the durable on-chain record (the aggregates) is exactly what cannot be interpreted.
Both analogous on-chain participation bitfields have ordering endpoints today: attestation aggregation_bits → GET /eth/v1/beacon/states/{state_id}/committees, and sync_aggregate.sync_committee_bits → GET /eth/v1/beacon/states/{state_id}/sync_committees. The PTC is the one participation bitfield without one.
As a result, an external observer (staking operators' monitoring, block explorers, analytics, PTC-liveness research) cannot attribute on-chain payload attestations to validators — i.e. per-validator PTC participation cannot be computed the way attestation and sync participation are computed today.
Note that a position field on the duty object alone would not suffice: compute_ptc returns the committee "with possible duplicates", so one validator can hold several seats (bits). Sync committees share this property, and sync_committees solves it by returning the full ordered list.
Proposal
Mirror the existing committee endpoints:
GET /eth/v1/beacon/states/{state_id}/ptc?slot={slot}
slot optional, defaulting to the state's slot; must be within the range computable from the requested state (same semantics as committees?slot=).
Serving cost is minimal for recent slots: state.ptc_window already caches the PTC for (2 + MIN_SEED_LOOKAHEAD) * SLOTS_PER_EPOCH slots, so this is a cache read; historical slots can be served from historical states exactly like /committees. And because it is state-scoped, it composes with the non-canonical-branch concern motivating #619: the PTC for a branch is queryable via that branch's state.
Alternative considered
A positions array on the duty object would be a smaller change, but it only helps callers that already know which validators to ask about, cannot answer historical queries, and #619's direction (BN-tracked validators, in-slot fetch) moves the duties endpoint further away from being an observer surface. Happy to go either way — and happy to follow up with the spec PR once there's agreement on the shape.
Problem
In Gloas, on-chain PTC votes are recorded in block bodies as
PayloadAttestationaggregates whoseaggregation_bits: Bitvector[PTC_SIZE]are indexed by the PTC ordering — the proposer constructs them "with respect to the PTC that is obtained fromget_ptc(state, Slot(block_slot - 1))" (validator.md).No API surface exposes that ordering — and none would expose PTC membership to observers either, once the duties endpoint rework lands:
POST /eth/v1/validator/duties/ptc/{epoch}, Standardized Epbs Beacon Api #552) returns membership for the requested validators only, with no position information (unlikeAttesterDuty.validator_committee_index).GETreturning{data, validator_indices}for validators the beacon node tracks — the right call for the VC flow (duties must exist per branch), but it removes the last way to query PTC membership in advance, historically, or for arbitrary validators.GET /eth/v1/beacon/pool/payload_attestations(Standardized Epbs Beacon Api #552) exposes individual messages, but the pool is ephemeral — it cannot answer historical questions, while the durable on-chain record (the aggregates) is exactly what cannot be interpreted.aggregation_bits→GET /eth/v1/beacon/states/{state_id}/committees, andsync_aggregate.sync_committee_bits→GET /eth/v1/beacon/states/{state_id}/sync_committees. The PTC is the one participation bitfield without one.As a result, an external observer (staking operators' monitoring, block explorers, analytics, PTC-liveness research) cannot attribute on-chain payload attestations to validators — i.e. per-validator PTC participation cannot be computed the way attestation and sync participation are computed today.
Note that a
positionfield on the duty object alone would not suffice:compute_ptcreturns the committee "with possible duplicates", so one validator can hold several seats (bits). Sync committees share this property, andsync_committeessolves it by returning the full ordered list.Proposal
Mirror the existing committee endpoints:
slotoptional, defaulting to the state's slot; must be within the range computable from the requested state (same semantics ascommittees?slot=).data.validators: exactlyPTC_SIZEvalidator indices, inget_ptc(state, slot)order, duplicates preserved.{ "execution_optimistic": false, "finalized": false, "data": { "validators": ["1021", "994", "1021", "..."] } }Serving cost is minimal for recent slots:
state.ptc_windowalready caches the PTC for(2 + MIN_SEED_LOOKAHEAD) * SLOTS_PER_EPOCHslots, so this is a cache read; historical slots can be served from historical states exactly like/committees. And because it is state-scoped, it composes with the non-canonical-branch concern motivating #619: the PTC for a branch is queryable via that branch's state.Alternative considered
A
positionsarray on the duty object would be a smaller change, but it only helps callers that already know which validators to ask about, cannot answer historical queries, and #619's direction (BN-tracked validators, in-slot fetch) moves the duties endpoint further away from being an observer surface. Happy to go either way — and happy to follow up with the spec PR once there's agreement on the shape.References
compute_ptc/get_ptc/ptc_windowPayloadAttestationapis/beacon/states/sync_committees.yaml