Skip to content

Milestones

List view

  • Tracks the remaining correctness, interoperability, and rollout work required for Anchor to activate and operate safely across the Boole SSV fork. The original Boole implementation is tracked in #768. This milestone captures residual activation-readiness work discovered through current-code review, mixed-client testing, restart testing, and fork-boundary analysis. Work in this milestone should: - preserve slot-derived message routing and validation; - align lifecycle-driven subscriptions, scoring, ENR, discovery, handshake, and peer state with scheduled fork boundaries; - cover continuous transitions, restart-at-boundary behavior, clock anomalies, and mixed-version operation; - include focused regression tests and activation canary evidence where appropriate. The first issue aligns lifecycle transitions with exact slots and reconciles downstream scoring and ENR state. Issues are directionally correct, not prescriptive; verify symbols at PR time.

    No due date
    9/10 issues closed
  • Implements [SIP-101 at 8279853](https://github.com/ssvlabs/SIPs/blob/82798537fea271d1a31d02fdc000d87b37fa3d98/sips/early_randao_preconsensus.md) for Anchor. Activation is derived from the stamped proposal slot `S`, not the current wall-clock epoch. Let `F` be the first slot of a non-genesis `GLOAS_FORK_EPOCH`. The receiver rules must be available by receiver-local `slot_start(F - 2) - EARLY_RANDAO_CLOCK_TOLERANCE`, because a conforming message targeting `F` can arrive then. Anchor's recommended producer attempt for `F` is at `slot_start(F - 1)`. This does not narrow the mandatory receiver window. There is no warm-up epoch and the SSV fork schedule is not an input. **Protocol classification and Anchor choices:** - #1177 implements the mandatory receiver rules: qualifying-message structure, the maximum two-slot receive window, timing tolerance, message-kind-scoped ordering exemption, complete-schedule duty authority, allocation-free non-accepting candidate paths, and the initial IGNORE verdict for Known-unassigned and Unknown views. - #1178 implements Anchor's Unknown-only subset of optional receiver retention. SIP-101 permits local retention when either Unknown or Known-unassigned is the sole non-passing check. Anchor initially retains only Unknown candidates to recover missing-view delivery, and treats Known-unassigned discard as local policy. When retention is enabled, its admission isolation and full-promotion-revalidation rules are mandatory. Cache capacity, keying, eviction, expiry, and persistence remain Anchor-local policy. - #1179 implements optional producer emission. Producers `MAY` choose any emission moment inside the maximum window and `MUST NOT` emit before it. Anchor follows the recommended first-attempt policy at exact `slot_start(S - 1)`, with no 500ms offset and no scheduled second pre-slot attempt. Its scheduler consumes #1184's watch so a locally owned duty first installed between `S - 1` and `S` can receive at most one immediate attempt without waiting for another slot tick. Anchor still invokes the existing in-slot path at `S` regardless of its recorded early outcome. A completed reconstruction can serve the in-slot consumer without waiting for repeated signing or publication, while the best-effort in-slot emission proceeds independently. - #1196 supplies the SIP-required testnet measurement before mainnet activation guidance, including retention-enabled and retention-disabled strata plus promotion timing and outcome. It does not block implementation merge. **Dependency arc:** 1. #1182 absorbed the current and next epoch fetch work from closed #1176. 2. #1183, in its existing ePBS milestone, retains and validates complete proposer-duty v2 responses. 3. #1184, also in its existing ePBS milestone, owns atomic complete-schedule authority transitions and the watch consumed by #1178 and #1179. Unknown means no retained complete schedule. Failed or malformed refreshes, local registry changes, optimistic metadata, and reorg detection do not revoke a prior complete view before replacement. 4. #1190 migrates general proposer validation to the pubkey-keyed tri-state and prevents non-Assigned network traffic from spawning proposer QBFT instances. 5. #1192 makes outbound sanity validation stateless so failed local publication attempts do not poison sender-side duplicate state. It does not guarantee receiver redelivery after a successful earlier publish. 6. #1177, #1178, and #1179 add the Early RANDAO receiver, optional retention, and optional producer behavior described above. #933 remains a hard producer-path safety dependency because reconstructed RANDAO must verify BLS shares and fall back from an invalid combination. If #1192 is deployed while the active SSV fork is Alan, include PR #1052 or an equivalent pre-Boole sync-contribution batching fix. The recommended `S - 1` lead coordinates normal producer behavior, but cross-client conformance does not require identical producer lead times or retention algorithms. It requires receivers to support the protocol window and verdicts. Implementations that never emit early or never retain any duty-assignment-ignored candidate remain conformant.

    No due date
    2/8 issues closed
  • Covers the envelope-signing duty for the Gloas / EIP-7732 / ePBS fork (SSV-side §6 in [SIP-94](https://github.com/ssvlabs/SIPs/pull/94), "New Duty: Envelope Signing (Self-Build Path)"). Supersedes the "envelope signing out of scope" note in milestone #2, which predates the SIP re-adding §6. Under Gloas, on the self-build path (`bid.builder_index == BUILDER_INDEX_SELF_BUILD`) the proposer must sign `SignedExecutionPayloadEnvelope` after the §4 block is published; without it, honest PTC validators vote `payload_present = FALSE` and the slot forfeits the payload reward. SIP-94 §6 specifies this as a second, companion QBFT duty: a new validator-scoped `Role::EnvelopeProposer` (wire value 9, matching ssv-spec [#632](https://github.com/ssvlabs/ssv-spec/pull/632)) runs QBFT over an internal `BlindedExecutionPayloadEnvelope` value (the `payload` field replaced by `payload_root = hash_tree_root(payload)`), keeping consensus messages bounded. By SSZ root substitution the blinded root equals the full envelope's root, so one BLS signature covers both forms. Post-consensus signs under `DOMAIN_BEACON_BUILDER` (`0x0B`), domain epoch from the duty slot, and reuses `PostConsensusPartialSig` (no new kind; the runner role discriminates routing, a wire-compat requirement). Publication is by content-match: only the operator whose local envelope blinds to the decided value returns a publishable full signed envelope; which operator led the deciding round is irrelevant. **Trigger is already wired.** Unlike PTC (§3) and Proposer Preferences (§5), this duty needs no new client service spawn. Lighthouse's `BlockService` drives the whole loop inline after block publish (BN GET, `sign_execution_payload_envelope(pubkey, envelope)`, BN POST). Anchor's only seam is the `Unsupported` stub in `validator_store`. **Trust model.** The value check is deliberately structural: it confirms the decoded blinded envelope's duty fields, that `builder_index == BUILDER_INDEX_SELF_BUILD`, and that `beacon_block_root` equals the root the §4 block QBFT decided for the slot. No envelope-content validation and no slashing-protection call. `payload_root` is leader-decided and trusted, matching the existing blinded-block trust model where operators do no field-level validation against their local BN view. Envelope signing is not in the slashing predicate. **Timing.** `PAYLOAD_DUE_BPS = 50%` since [consensus-specs #5414](https://github.com/ethereum/consensus-specs/pull/5414): with the §4 block out by the ~25% attestation deadline, roughly a quarter slot remains for the envelope QBFT, post-consensus collection, POST, and gossip. This pins `max_round = Some(2)` (round 1 plus exactly one leader failover; Anchor quick rounds are 2s), now normative in SIP-94 §7. **Critical path:** No. Block production completes without envelope signing; this milestone recovers the self-build payload reward. Ships after the modified proposer duty (milestone #2). **Issue arc:** 1. **Role + message validation:** add validator-scoped QBFT `Role::EnvelopeProposer` (wire 9, `max_round = Some(2)`, `PostConsensusPartialSig` reuse) built to the SIP-94 §7 validation rules (round cut-off 2, one packet per post-consensus, 3-slot lateness TTL, epoch-guarded proposer-assignment check, `SLOTS_PER_EPOCH` duty limit, Gloas fork gate), with the message-validator wiring and a transient `qbft_manager` reject. Depends on the already-open [#1118](https://github.com/sigp/anchor/issues/1118) for the duty-limit IGNORE classification (a pre-existing repo-wide defect affecting all count-capped roles, not envelope-specific). 2. **Types + value check:** internal `BlindedExecutionPayloadEnvelope` + `EnvelopeConsensusData` (wire-identical to `ProposerConsensusData`) + the structural value check, with the load-bearing root-equivalence test. This is an SSV/QBFT value, not a beacon-APIs transport type. 3. **Decided-root hand-off:** record the block-QBFT-decided root at its single success point, keyed `(validator, slot)`, for the envelope value check. 4. **QBFT wiring:** per-proposer envelope instances in `qbft_manager` (fifth instance map), replacing the transient reject. 5. **Sign path:** implement `sign_execution_payload_envelope` against the current Lighthouse full-envelope trait (blind internally, decide, collect under `Domain::BeaconBuilder`, content-match publish gate, metrics). **API drift, not worked around.** The current Lighthouse pin already uses the Gloas five-list `ExecutionRequestsGloas<E>` for the envelope, so Anchor should not implement the older Electra three-list shape. The same pin still has a slot-only envelope GET and a bare full-envelope POST, so it is reliable for single-BN-per-operator and guarded by the content-match gate for wrong-BN fetches. beacon-APIs transport is still moving: [ethereum/beacon-APIs#624](https://github.com/ethereum/beacon-APIs/pull/624) currently removes the public `BlindedExecutionPayloadEnvelope` API object, keeps the `{slot}/{beacon_block_root}` lookup, returns a full `ExecutionPayloadEnvelope`, and switches publish selection to `Eth-Blob-Data-Included` (`SignedExecutionPayloadEnvelope` for stateful, `SignedExecutionPayloadEnvelopeContents` for stateless). When Lighthouse adopts a final shape, Anchor should adapt the Lighthouse-facing seam to that trait; this milestone's internal QBFT core remains over the blinded value unless SIP-94 or ssv-spec changes. Issues under this milestone are directionally correct, not prescriptive. The implementer should verify against the current SIP, ssv-spec, consensus-specs Gloas docs, beacon-APIs envelope PRs, go-ssv, and the latest Lighthouse VC behavior, and deviate from the suggested approach if a better one emerges. Acceptance criteria are the contract; the sketch is a starting point.

    No due date
    6/7 issues closed
  • Build a committee-duty observability baseline for Anchor before making further committee QBFT, attestation, sync-message, or post-consensus batching changes. Why this exists: - Committee duties are the critical path for attestation and sync-message production, and they have different timing and batching behavior from proposer duties. - Anchor already has useful local metrics and tracing, but not one coherent committee-duty view from duty trigger through QBFT, post-consensus partial signatures, signature reconstruction, and submission. - Recent ePBS / Gloas timing work makes this urgent, but the observability model should be fork-agnostic and useful for today's attestation failures as well. - We should measure the committee duty path end to end before changing committee QBFT start timing, sync triggering, or partial-signature batching behavior. How this relates to SSV: - SSV has already shipped relevant duty-flow tracing and runner instrumentation: `ssvlabs/ssv#2076` and `ssvlabs/ssv#2272`. - SSV has shipped QBFT stage duration logging that applies to committee-duty consensus analysis: `ssvlabs/ssv#2609`. - SSV's committee runner gives a useful reference shape for measuring scheduler delay, duty execution, QBFT duration, post-consensus duration, total duty duration, submissions, and failed submissions. - Anchor does not need a literal port of SSV's OTel rollout. It needs a committee-focused observability slice that lets us decide whether the next optimization belongs in duty triggering, pre-QBFT readiness, QBFT round behavior, post-consensus signing, partial-signature batching, reconstruction, or submission. Desired outcome: - one canonical committee-duty root span - stable event / checkpoint vocabulary - low-cardinality metrics suitable for dashboards and alerting - committee QBFT lifecycle visibility comparable to the proposer baseline - post-consensus and submission visibility for attestations and sync messages - enough data to attribute missed or slow committee duties to the dominant phase - deadline-relative summaries where useful, without making the metric taxonomy ePBS-specific Non-goals: - protocol redesign - changing committee QBFT timeout logic in the same PRs as instrumentation - changing Lighthouse duty scheduling in the same PRs as instrumentation - changing partial-signature batching semantics in the same PRs as instrumentation - a repo-wide OpenTelemetry rollout on day one

    No due date
    0/6 issues closed
  • Covers PTC (Payload Timeliness Committee) attestation duty for the Gloas / EIP-7732 / ePBS fork (SSV-side §3 in [SIP-94](https://github.com/ssvlabs/SIPs/pull/94)). This is the **no-QBFT redesign** of §3 and supersedes the QBFT-era PTC milestone (#4): SIP-94 §3 was rewritten to a validator-scoped, leaderless design. PTC is a new per-slot duty. The consensus layer selects 512 validators per slot to attest to whether the proposer published its `SignedExecutionPayloadEnvelope` on time (`payload_present`) and whether the block's blob data is locally available (`blob_data_available`). The duty fires at the 75% slot mark; validators sign `PayloadAttestationData { beacon_block_root, slot, payload_present, blob_data_available }` under `DOMAIN_PTC_ATTESTER` (`0x0C`). See [consensus-specs Gloas validator.md](https://github.com/ethereum/consensus-specs/blob/dev/specs/gloas/validator.md) and [beacon-chain.md containers](https://github.com/ethereum/consensus-specs/blob/dev/specs/gloas/beacon-chain.md). **No QBFT, validator-scoped.** Unlike §2 attestation, PTC runs no consensus round and has no leader. A PTC vote is one beacon node's observation, not a value to negotiate, and QBFT would only add round-trips that risk the late-slot deadline. SSV models PTC like `ProposerPreferences` (§5): a validator-scoped runner role `Role::PTCAttester` and a dedicated `PartialSignatureKind::PTCAttester = 7` (next free after `AggregatorCommitteePartialSig = 6`), with one runner instance per locally-assigned PTC validator (keyed by pubkey). Each operator evaluates `PayloadAttestationData` from its own beacon node at the 75% cutoff, pins that snapshot, and signs it directly with its BLS share. Partial signatures group by signing root; when a threshold (2f+1) of operators converge on byte-identical data, any operator reconstructs and submits one `PayloadAttestationMessage` per validator to the beacon node within the [75%, 100%] window. Lighthouse's `PayloadAttestationService` owns the data fetch (once at 75%) and the no-block abstain, then calls `sign_payload_attestation(pubkey, data)` per validator; Anchor signs the `data` it is handed and never fetches from the BN or writes slashing protection (CR-1). **Trust model: honest-convergence, not consensus.** There is no QBFT value check because there is no leader-proposed value: each operator signs only the observation its own BN made (one that saw no block abstains, matching the Gloas validator spec). So there is no Byzantine-leader wrong-value surface (the concern §2 accepts). The cost is liveness rather than safety: when operators' beacon nodes split across observations near the cutoff, no signing root reaches threshold and the cluster's vote for that validator is a **non-slashable, penalty-free silent miss**, bounded by SSV's PTC seat share. PTC attestations are not in the beacon-chain slashing predicate, so no slashability call is made. Tracking how often this silent miss happens is useful operational telemetry (the divergence-rate question was raised in the SIP-94 thread; it is not a normative §3 requirement); see the telemetry note in issue 3. **Critical path:** No. PTC sits on the proposer-reward path (75% slot deadline), not the 3-second attestation deadline. Ships after §2 attestation lands. **Migration from the merged QBFT code.** Two QBFT-era PRs merged on the `epbs` branch and are reworked here: PR #1033 (committee-scoped `Role::PTCCommittee` + message-validator wiring + a `qbft_manager` stub) and PR #1047 (`PayloadAttestationVote` + `QbftData` value object, now dead). The QBFT-design milestone (#4) is retained as a fallback but superseded by this one. **Issue arc:** 1. **Remove dead QBFT value object:** delete the merged `PayloadAttestationVote` struct + `QbftData` impl + value checker + tests (reverts PR #1047). Standalone. 2. **Retarget the role:** rename committee-scoped `Role::PTCCommittee` to validator-scoped `Role::PTCAttester` (`is_committee_role = false`, `duty_executor = Validator`, `max_round = None`) and add a dedicated `PartialSignatureKind::PTCAttester = 7` (no longer reusing `PostConsensus`); message-validator fork-gate (`Fork::CStar`), long TTL, duty-limit, per-validator packet bound (one message per packet), partial-sig binding, and message-counts bucketing. No `qbft_manager` routing (the role is non-QBFT; relocate it into the `InconsistentMessageId` arms). Reworks PR #1033. `PTCAttester = 7` keeps value-parity with the SIP's `RunnerRole` / `PartialSigMsgType` / `BeaconRole`; `ProposerPreferences` (§5) renumbers to `8`. 3. **Sign path:** `sign_payload_attestation` runs `collect_signature` in `SingleValidator` mode over the passed-in `data` under `Domain::PTCAttester`, mirroring `collect_voluntary_exit_partial_signatures`. No BN fetch, no slashing-DB writes. Reconstruction-failure telemetry: increment `anchor_ptc_reconstruction_failures_total`, but note that at the current signature-collector granularity a no-threshold miss is indistinguishable from a genuine channel close (both surface as `QueueClosedError`), so the divergence count is a coarse upper bound. A separate `research`-tagged follow-up (collector divergence-error split) can refine it; it is tracked outside this milestone. 4. **Service wiring:** spawn Lighthouse's `PayloadAttestationService` with `AnchorValidatorStore` as the backend (`::new(...)`, no builder); gate the start on `spec.is_gloas_scheduled()`, mirroring LH's own VC. A separate, non-blocking observability follow-up (`signature_collector` divergence-error split, tagged `epbs` + `research`) is tracked outside this milestone: it would let the issue 3 metric report a clean `observation_divergence` rate instead of the coarse `no_signature` upper bound. **Issues are directionally correct, not prescriptive.** The implementer should verify against the current SIP, consensus-specs Gloas `validator.md` / `beacon-chain.md`, and the latest Lighthouse VC behavior, and deviate from the suggested approach if a better one emerges. Acceptance criteria are the contract; surface tables are starting points.

    No due date
    6/8 issues closed
  • Covers the new ProposerPreferences duty for the Gloas / EIP-7732 / ePBS fork (SSV-side §5 in [SIP-94](https://github.com/ssvlabs/SIPs/pull/94)). Under Gloas, the pre-fork relay-based builder registration is gone along with blinded blocks. Proposers instead broadcast `SignedProposerPreferences` on a new `proposer_preferences` p2p topic; builders listen and use the preferences to construct matching `execution_payload_bid` objects. Without this broadcast, gossip IGNOREs unmatched bids and the proposer's BN has no trustless external builder options for that slot. Each `SignedProposerPreferences` carries `dependent_root`, `proposal_slot`, `validator_index`, `fee_recipient`, and `target_gas_limit`, signed under a new `DOMAIN_PROPOSER_PREFERENCES` with the validator's BLS share. SSV runs this as a validator-scoped, non-QBFT duty: each operator signs locally with its share, partial signatures go through one collection round, and reconstruction succeeds when ≥ 2f+1 operators converge on the same signing root. Same shape as `ValidatorRegistration` / `VoluntaryExit`. LH provides the `ProposerPreferencesService` that drives the polling and publication loop. **Trust model:** no QBFT and no shared decided value. Each operator's signing root is determined by its `target_gas_limit` (per-operator config) and `dependent_root` (per-operator BN observation); `fee_recipient` is cluster-consistent in practice. Divergence on either input splits signing roots and silently fails reconstruction, leaving the validator without trustless external builder bids for that slot. The wire carries only the signing root, so a failed reconstruction cannot be attributed to a specific input; Anchor surfaces collection failure via metrics + warn-logs. **Critical path:** No. Required for external-builder bid availability under Gloas; self-build still works without it. The cluster's block-signing path (§4) does not require seeing its own preferences before producing a block, because BN-side selection from `execution_payload_bid` gossip filters non-matching bids before the proposer is asked. **Issue arc:** 1. **Wire surface** ([#1062](https://github.com/sigp/anchor/issues/1062), [PR #1106](https://github.com/sigp/anchor/pull/1106)): introduce `Role::ProposerPreferences` (byte `[8, 0, 0, 0]`) + `PartialSignatureKind::ProposerPreferences = 8` (next free after `PTCAttester = 7`); message-validator fork-gate on Ethereum `ForkName::Gloas` read from the consensus fork schedule (no SSV-side fork gate); timing split into an earliness allowance of `(1 + MIN_SEED_LOOKAHEAD) * SLOTS_PER_EPOCH` (the envelope slot is the duty's future `proposal_slot`, so the whole proposer lookahead must be accepted) and a lateness TTL of `LATE_SLOT_ALLOWANCE` (2 slots) past `proposal_slot`, plus the usual clock margins; per-role bounds, partial-sig binding, and message-counts bucketing. Non-QBFT, so no `qbft_manager` stub. 2. **Sign path** ([#1063](https://github.com/sigp/anchor/issues/1063), [PR #1125](https://github.com/sigp/anchor/pull/1125)): `sign_proposer_preferences` runs `collect_signature` in `SingleValidator` mode, mirroring `sign_validator_registration_data`. No slashing-DB writes (CR-1). 3. **Service wiring** ([#1064](https://github.com/sigp/anchor/issues/1064)): spawn LH's `ProposerPreferencesService` with `AnchorValidatorStore` as the backend; the service drives the polling and publication loop. 4. **`RegistrationService` fork-gate** ([#1065](https://github.com/sigp/anchor/issues/1065)): short-circuit `register_validators` post-Gloas (the relay-builder mechanism is gone). Service stays spawned; pre-Gloas testnets and transition-window operators continue unchanged. 5. **Related**: reject inbound `ValidatorRegistration` partial-sigs at/after Gloas ([#1115](https://github.com/sigp/anchor/issues/1115)); make `ValidatorIndex` SSZ encoding architecture-independent ([#1132](https://github.com/sigp/anchor/issues/1132)). **Upstream LH status** (at [PR #1125](https://github.com/sigp/anchor/pull/1125)'s pin `81d576943`): the service polls every slot and re-emits when `dependent_root` changes; preferences for the first Gloas epoch publish during the pre-fork epoch (the fork gate is per preference epoch, current and next); the duties/preferences race is fixed ([sigp/lighthouse#9309](https://github.com/sigp/lighthouse/pull/9309)); the publish endpoint is locked in ([ethereum/beacon-APIs#608](https://github.com/ethereum/beacon-APIs/pull/608)). Still open upstream: [sigp/lighthouse#9617](https://github.com/sigp/lighthouse/pull/9617) makes signing and publication concurrent so one pending share collection cannot delay sibling validators' publications (relevant to Anchor's distributed signature collection; picked up on a future pin bump). Note `unstable` currently pins an older LH (`4b3a9d3`); PR #1125 carries the bump. **Known verification gap:** byte layouts were audited as identical to ssv-spec/go-ssv on 64-bit targets (76-byte `ProposerPreferences`, 172-byte signed, 164-byte one-entry partial-sig payload), but no shared cross-client fixture yet proves each side decodes the other's bytes. Issues under this milestone are directionally correct, not prescriptive. The implementer should verify against the current SIP, consensus-specs Gloas `validator.md`, beacon-APIs PR #563 (proposer-duties `dependent_root`), and the latest Lighthouse VC behavior, and deviate from the suggested approach if a better one emerges. Acceptance criteria are the contract; the sketch is a starting point.

    No due date
    9/10 issues closed
  • Covers attestation duty updates required for the Gloas / EIP-7732 / ePBS fork (SSV-side §2 in SIP-94). Under Gloas, `AttestationData.index` is repurposed as a payload-presence flag set by the beacon node: - **`index = 0`** = payload `EMPTY` (or attesting to a same-slot block, which is unconditionally `0`). - **`index = 1`** = payload `FULL` (non-same-slot, payload envelope observed for the attested block). The BN owns the determination; the validator client carries the value unchanged. SSV must thread it through QBFT consensus so that all cluster operators sign over the same `AttestationData.index` and produce identical signing roots. Wire format: a new `GloasBeaconVote { block_root, source, target, attestation_data_index }` SSZ struct activates on Gloas slots, alongside today's 3-field `BeaconVote` (which stays unchanged for pre-Gloas slots). Cross-fork wire-incompatibility falls out of SSZ length mismatch by design (SIP-94 §2). After Gloas has activated on all networks, a follow-up SIP retires `BeaconVote` and renames `GloasBeaconVote` back to `BeaconVote`. Value-check trust model: per SIP-94 SC-2, the SSV value check does **not** compare the proposed `attestation_data_index` against each operator's local BN view. Operators' BNs may legitimately disagree on payload-arrival timing near the 25% slot deadline; requiring local agreement would fail QBFT rounds. Accepted tradeoff: a malicious QBFT leader can push a payload-status value contrary to the cluster's BN-majority observation; worst case is a missed attestation when the gossip REJECT layer drops it. Issues under this milestone are directionally correct, not prescriptive. The implementer should verify against the current SIP, beacon-APIs `attestation_data` schema, consensus-specs Gloas `validator.md`, and the latest Lighthouse VC behavior, and deviate from the suggested approach if a better one emerges. Acceptance criteria are the contract; the sketch is a starting point.

    No due date
    7/9 issues closed
  • Covers block proposing updates required for the Gloas / EIP-7732 / ePBS fork (SSV-side §4 in SIP-94). Under Gloas, the `ExecutionPayloadEnvelope` reaches the network through one of three paths: - **External builder:** the builder wins the on-chain bid market and broadcasts a `SignedExecutionPayloadEnvelope` under `DOMAIN_BEACON_BUILDER`. The proposer's `BeaconBlock` commits to the bid via `signed_execution_payload_bid`; the VC never signs the envelope. - **Stateless self-build:** `produceBlockV4` with `include_payload=true` returns `Gloas.BlockContents` (block + envelope + blobs + KZG proofs) as one bundle. Multi-BN friendly: any BN can publish the envelope on behalf of the proposer. - **Stateful self-build:** `produceBlockV4` with `include_payload=false` returns `Gloas.BeaconBlock` only; the BN caches the envelope and blobs internally. The client fetches them later via `GET /eth/v1/validator/execution_payload_envelope/{slot}/{root}` against the same BN, signs the envelope, and publishes in a second step. Single-BN coupling per slot. The response header `execution_payload_included` tells the client whether the BN has an envelope to offer (`true` for self-build, `false` for external-builder slots where the builder will broadcast independently). Lighthouse's VC currently fetches blocks via v4 in stateful self-build mode; PR ethereum/beacon-APIs#580 is still evolving. The SSV cluster runs QBFT over `Gloas.BeaconBlock` only on every path; blobs and KZG proofs never pass through consensus. Distributed signing of `SignedExecutionPayloadEnvelope` is out of scope per SIP-94 (commit `cfb672e`), so on self-build slots the cluster does not republish the envelope and PTC may attest `payload_present = FALSE`. This is a known consequence. External-builder slots are unaffected. Issues under this milestone are directionally correct, not prescriptive. The implementer should verify the proposed solution against the current SIP, beacon-APIs PR #580, consensus-specs Gloas validator doc, and the latest Lighthouse VC behavior, and deviate from the suggested approach if a better one emerges. Acceptance criteria are the contract; the sketch is a starting point.

    No due date
    4/6 issues closed
  • Build a proposer-duty observability baseline for Anchor before making further QBFT or proposer-path changes. Why this exists: - Anchor has clear evidence that proposer timing is fragile under a tight slot budget, including `#758` and the broader fetch-before-consensus concern. - We currently have useful `tracing` and Prometheus coverage, but not one coherent proposer-duty view from pre-QBFT fetch/build through QBFT, post-consensus threshold/reconstruction, and publish. - We should measure the duty path end to end before changing consensus behavior. How this relates to SSV: - SSV has already shipped relevant observability and proposer-path work: duty-flow tracing (`ssvlabs/ssv#2076`, `ssvlabs/ssv#2272`), QBFT stage duration logging (`ssvlabs/ssv#2609`), smart proposal selection (`ssvlabs/ssv#2648`), and proposer timing budget documentation (`ssvlabs/ssv#2416`). - SSV still has open proposer-liveness and timing work: fetch-before-consensus / async fetch (`ssvlabs/ssv#1825`, `ssvlabs/ssv-spec#371`) and timeout alignment (`ssvlabs/SIPs#37`, `ssvlabs/ssv-spec#352`). - Anchor does not need a literal port of `ssvlabs/ssv#2076`. It needs a proposer-focused observability slice that lets us decide whether the next optimization belongs in pre-QBFT fetch/build, QBFT round behavior, post-consensus signing, or publish. Desired outcome: - one canonical proposer-duty root span - stable event / checkpoint vocabulary - low-cardinality metrics suitable for dashboards and alerting - enough data to attribute missed or slow proposer duties to the dominant phase Non-goals: - protocol redesign - leaderless consensus work - changing timeout logic in the same PRs as instrumentation - a repo-wide OpenTelemetry rollout on day one

    No due date
    1/8 issues closed