Skip to content

feat(client): spawn LH PayloadAttestationService with AnchorValidatorStore backend #1078

@shane-moore

Description

@shane-moore

Goal

Spawn Lighthouse's PayloadAttestationService with AnchorValidatorStore as the backend in anchor/client/src/lib.rs (the service is absent from client/ today). This is the final wiring step for the PTC duty. Reworks the not-yet-implemented #1038.

Context

The LH service drives the per-slot PTC loop: at the 75% cutoff it fetches PayloadAttestationData, abstains on no-block, and calls sign_payload_attestation per validator (implemented in #1077). PTC duties are populated by LH's poll_beacon_ptc_attesters under the existing duties_service::start_update_service (Gloas-gated), so no extra duty wiring is needed. Dep: #1077. Part of milestone #6.

Construction shape (verified against the pin): there is no PayloadAttestationServiceBuilder. Construct with PayloadAttestationService::new(...) (6 positional args, the 6th being chain_spec, which SyncCommitteeService::new does NOT take), and start with start_update_service() which takes no args (consumes self), NOT start_update_service(&spec). Mirror SyncCommitteeService::new (client/src/lib.rs:682-687) for the constructor only. Gate the start on spec.is_gloas_scheduled(), matching LH's own VC (validator_client/src/lib.rs:657), which starts payload_attestation_service (and proposer_preferences_service) only when Gloas is scheduled. Do NOT mirror the ungated sync_committee_service start: the per-slot gloas_enabled() self-gate makes an ungated start harmless, but it spawns a perpetual idle task pre-Gloas, which diverges from LH.

Suggested approach

anchor/client/src/lib.rs (lead with the symbol; line hints drift):

Symbol (line hint) change
validator_services::{...} use block (~54-63) add payload_attestation_service::PayloadAttestationService
after the sync_committee_service construction (~687) construct (below)
after metadata_service.start_update_service() (~730) start (below)
let payload_attestation_service = PayloadAttestationService::new(
    duties_service.clone(),
    validator_store.clone(),
    slot_clock.clone(),
    beacon_nodes.clone(),
    executor.clone(),
    spec.clone(),
);

// Match LH's VC (validator_client/src/lib.rs:657): start only when Gloas is scheduled.
if spec.is_gloas_scheduled() {
    payload_attestation_service
        .start_update_service()
        .map_err(|e| format!("Unable to start payload attestation service: {e}"))?;
}

(Confirm the spec binding/type in scope here; is_gloas_scheduled() is on ChainSpec at the pin, called as …spec.is_gloas_scheduled() in LH's VC.)

LH constructor (verified, …/validator_services/src/payload_attestation_service.rs:42-49): new(duties_service: Arc<DutiesService<S,T>>, validator_store: Arc<S>, slot_clock: T, beacon_nodes: Arc<BeaconNodeFallback<T>>, executor: TaskExecutor, chain_spec: Arc<ChainSpec>). Start: start_update_service(self) -> Result<(), String> (:63).

Acceptance criteria

  • PayloadAttestationService imported and constructed with 6 positional args ending in spec.clone(); started via .start_update_service() (no &spec); start error wrapped into the String error chain like sibling services.
  • Start wrapped in if spec.is_gloas_scheduled() { … }, matching LH's VC (validator_client/src/lib.rs:657); not started ungated.
  • cargo build --workspace green; the ssv-mini smoke test shows PTC duties attempted under a Gloas-scheduled spec.

Tests

Client startup is wired/integration-tested, not unit-tested per service (no per-service spawn unit test exists for SyncCommitteeService). Mirror that convention: rely on cargo check / build + existing client integration tests; if a startup smoke test exists, extend it to assert the service spawns under a Gloas spec.

Notes

Dep #1077 (the service drives sign_payload_attestation; the trait is satisfied by the stub so it technically compiles on the role/kind change in #1075 alone, but should land after #1077). The LH types are already in the pin, so no LH bump. Issues are directionally correct, not prescriptive; verify symbols at PR time.

Metadata

Metadata

Assignees

No one assigned

    Labels

    epbsePBS / EIP-7732 / Gloas implementation

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions