Skip to content

feat(validator_store): implement sign_proposer_preferences (SingleValidator partial-sig) - #1125

Merged
mergify[bot] merged 9 commits into
sigp:epbsfrom
petarjuki7:feat/proposer-preferences-sign
Jul 21, 2026
Merged

feat(validator_store): implement sign_proposer_preferences (SingleValidator partial-sig)#1125
mergify[bot] merged 9 commits into
sigp:epbsfrom
petarjuki7:feat/proposer-preferences-sign

Conversation

@petarjuki7

Copy link
Copy Markdown
Member

Closes #1063 (ePBS Proposer Preferences duty milestone, SIP-94 §5/§7).

Implements sign_proposer_preferences on AnchorValidatorStore: a single-validator partial-signature collection round over ProposerPreferences, domain keyed on proposal_slot's epoch, envelope slot stamped with the duty's proposal_slot (not the send slot), and no slashing-DB interaction. Adds a reconstruction-failure reporter + metric and generalizes the shared collect_signature failure classifier.

Stacked on #1106 — only the final commit (implement sign_proposer_preferences) is in scope here; the ssv_types / message-validator commits belong to #1106 and drop out of this diff once it merges.

@codecov-commenter

codecov-commenter commented Jul 9, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 98.30986% with 6 lines in your changes missing coverage. Please review.
⚠️ Please upload report for BASE (epbs@078dd90). Learn more about missing BASE report.

Files with missing lines Patch % Lines
...alidator_store/src/testing/proposer_preferences.rs 98.51% 4 Missing ⚠️
anchor/validator_store/src/instrumentation.rs 75.00% 1 Missing ⚠️
anchor/validator_store/src/lib.rs 98.24% 1 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##             epbs    #1125   +/-   ##
=======================================
  Coverage        ?   68.03%           
=======================================
  Files           ?      164           
  Lines           ?    30942           
  Branches        ?        0           
=======================================
  Hits            ?    21050           
  Misses          ?     9892           
  Partials        ?        0           
Flag Coverage Δ
rust 68.03% <98.30%> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Comment thread anchor/validator_store/src/lib.rs Outdated
petarjuki7 added a commit to petarjuki7/anchor that referenced this pull request Jul 15, 2026
…ure telemetry

The NoSignature arm labelled every failed reconstruction signing_root_divergence
and warned that operators had likely diverged on the signing root. That class is
reached only via QueueClosedError, which conflates threshold-not-reached, too-few
operators, and delivery loss, and the wire carries no preference fields, so the
collector cannot know a signing-root split occurred. The sibling PTC path already
labels the identical class honestly.

Rename the label to insufficient_partial_signatures, reword the warn to list
signing-root divergence as one of several possible causes, and update the two
metric tests. Document CollectionTimeout in the classifier as reserved and not
currently produced by the collector.

Addresses review feedback on sigp#1125.
@petarjuki7
petarjuki7 force-pushed the feat/proposer-preferences-sign branch from 417c2f3 to 2deb940 Compare July 15, 2026 21:39
@petarjuki7
petarjuki7 requested a review from shane-moore July 15, 2026 21:40
@shane-moore

Copy link
Copy Markdown
Member

think CI will be fixed if rebase this branch from the epbs branch

Sign ProposerPreferences via a single-validator partial-signature
collection round: domain keyed on proposal_slot's epoch, envelope slot
stamped with the duty's proposal_slot (SIP-94 §5/§7, not the send slot),
no slashing-DB interaction. Add a reconstruction-failure reporter and
metric, and generalize the shared collect_signature failure classifier.

Part of sigp#1063.
…ure telemetry

The NoSignature arm labelled every failed reconstruction signing_root_divergence
and warned that operators had likely diverged on the signing root. That class is
reached only via QueueClosedError, which conflates threshold-not-reached, too-few
operators, and delivery loss, and the wire carries no preference fields, so the
collector cannot know a signing-root split occurred. The sibling PTC path already
labels the identical class honestly.

Rename the label to insufficient_partial_signatures, reword the warn to list
signing-root divergence as one of several possible causes, and update the two
metric tests. Document CollectionTimeout in the classifier as reserved and not
currently produced by the collector.

Addresses review feedback on sigp#1125.
…ions

epbs (sigp#1082/sigp#1103/sigp#1128) added `spec` and `forced_gloas_index` to the
shared test HarnessOptions. Append `..Default::default()` to the two
ProposerPreferences failure-test constructions, matching the sibling
payload_attestation tests, so the suite compiles on the rebased base.

Part of sigp#1063.
Comment thread anchor/validator_store/src/metrics.rs Outdated
Comment thread anchor/validator_store/src/testing/proposer_preferences.rs
Comment thread anchor/validator_store/src/lib.rs Outdated
Comment thread anchor/validator_store/src/testing/proposer_preferences.rs Outdated
Comment thread anchor/validator_store/src/testing/proposer_preferences.rs Outdated
Comment thread anchor/validator_store/src/lib.rs Outdated
Rename signed_proposer_preferences_total to
anchor_signed_proposer_preferences_total so the top-line signing counter
carries the same anchor_ prefix as its paired failure counter
(anchor_proposer_preferences_reconstruction_failures_total) and the rest of
the file. Choosing the prefix before ship avoids a later rename breaking
dashboards.

Addresses review feedback on sigp#1125.
Wrap the per-validator signature collection in a slot-derived tokio timeout
(2 slots) mapped to CollectionError::CollectionTimeout. Without a deadline the
await resolves only when the collector is reaped at proposal_slot + 2 (up to
~13 min for a next-epoch lookahead emission); because the LH
ProposerPreferencesService awaits each validator sequentially in one task, a
single no-quorum validator (an expected SIP-94 §5 state such as a
target_gas_limit config mismatch or a dependent_root observation split) blocks
both epochs' emissions for that duration. The collector outlives the deadline,
so a later per-slot retry still reconstructs if quorum forms.

Also drop the run_and_update_metrics wrapper, whose catch-all arm error-logged
a no-quorum failure and miscounted it as other_error on top of the reporter's
correct warn and insufficient_partial_signatures. Match sign_payload_attestation:
the reporter owns failure telemetry, and success is counted explicitly on the
Ok path.

Addresses review feedback on sigp#1125; closes the bounded-timeout AC of sigp#1063.
Three tests asserted conditions that held regardless of the behavior they
named, so none could catch its target regression:
- the signing-domain recompute ran under ChainSpec::mainnet(), where the send
  and proposal epochs share the genesis fork version, so keying the domain on
  the send epoch would produce an identical root;
- one test asserted attribution labels the production code never writes, so the
  zero deltas held even if the whole reporting arm were deleted;
- comments claimed the on-wire PartialSignatureMessages.slot was asserted, but
  the mock captures metadata.slot at the trait boundary before create_message
  runs.

Fix by mirroring the payload_attestation sibling suite:
- run the success test on a spec with Gloas activated at the lookahead epoch, so
  a fork boundary sits between the send and proposal epochs, and assert the root
  matches the proposal-epoch domain and differs from the send-epoch domain;
- drop the vacuous attribution-label test; add an infra-class test
  (EmptySignature) that gives the Infra classification arm its first coverage,
  and a zero-delta infra check on the QueueClosedError test;
- add a slashing-protection tripwire;
- reword the slot comments to state metadata.slot is what is asserted.

Addresses review feedback on sigp#1125.
sign_proposer_preferences now synthesizes CollectionTimeout when its bounded
collection-wait deadline elapses, so the note claiming the variant is not
produced and has no deadline path is stale. Reword it to describe the caller
that produces it.
…path

Exercise issue sigp#1063 AC7: a no-quorum collection must fail per-validator with a
bounded timeout and never hang the caller. Add a mock-collector hang mode that
returns a never-resolving future, and a test that drives the production
tokio::time::timeout to elapse under paused virtual time (start_paused),
asserting the result is SignatureCollectionFailed(CollectionTimeout) and that it
lands in the insufficient_partial_signatures bucket, not infra.

Without the production timeout the test would hang on the pending collector
future rather than return, which is the "never hangs the caller" property AC7
requires.
@shane-moore

Copy link
Copy Markdown
Member

looks good! just got small check-fmt CI fixes then should be all good

@shane-moore

shane-moore commented Jul 21, 2026

Copy link
Copy Markdown
Member

pushed the small check-fmt fix && approved, so we can merge if you feel good about it 🙌

@mergify

mergify Bot commented Jul 21, 2026

Copy link
Copy Markdown

Queued — the merge queue status continues in this comment ↓.

@mergify

mergify Bot commented Jul 21, 2026

Copy link
Copy Markdown

Merge Queue Status

This pull request spent 12 minutes 4 seconds in the queue, including 10 minutes 13 seconds running CI.

Required conditions to merge
  • check-success=test-suite-success

@mergify mergify Bot added the queued label Jul 21, 2026
@mergify
mergify Bot merged commit 2bf9ec4 into sigp:epbs Jul 21, 2026
22 checks passed
@mergify mergify Bot removed the queued label Jul 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants