feat: reject same-slot FULL votes the head event already rules out (SIP-94 §2) - #1301
Conversation
…IP-94 §2) A Gloas block proposed in the current slot has no payload yet, so an attestation with index 1 (FULL) on it is invalid and the beacon node gossip-rejects every attestation the committee signs over it. SSV Labs QA measured this on ssv-mini with a leader flipping the index. The metadata service already learns the fact needed to catch it: when a head event for the current slot triggers the voting context, that event names a block whose slot is the current slot, and a reorg never changes a block's slot. Record that root on VotingContext (only when the event's slot matches the slot the context is built for, since the two clock reads can straddle a boundary) and hand it to the Gloas vote validator at instance start, so proposals, round-change justifications and decided messages in that instance all evaluate the same knowledge. Index 1 on that root is rejected; everything else is unchanged, and a timer-triggered context or a disabled head monitor carries no root and skips the check.
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## epbs #1301 +/- ##
=======================================
Coverage ? 79.96%
=======================================
Files ? 179
Lines ? 40081
Branches ? 0
=======================================
Hits ? 32052
Misses ? 8029
Partials ? 0
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
shane-moore
left a comment
There was a problem hiding this comment.
Reviewed bbe6fc46. No correctness or design findings. The same-slot check uses a fixed instance snapshot and preserves the permitted behavior for unknown roots.
Formatting is fixed. CI passed all 965 tests in both debug and release, including the eight new cases; formatting and Clippy also passed. Still running: coverage, local-testnet.
Reviewed by gpt-6-astra xhigh.
Problem, Evidence, and Context
SIP-94 section 2 now says an operator should reject a Gloas beacon vote with
AttestationDataIndex = 1when its own view already establishes that the block atBlockRoothas slotduty.slot, evaluated against knowledge fixed at instance start. A block proposed in the current slot has no payload yet, so a FULL vote on it is invalid and the beacon node gossip-rejects every attestation the committee signs over it. SSV Labs QA measured exactly that on ssv-mini with a leader flipping the index (SIPs#94 comment); go-ssv tracks its side as ssvlabs/ssv#3035. Anchor today range-checks the index to{0, 1}and nothing more.Change Overview
VotingContextgainssame_slot_head_root, set from that event only when the event's slot equals the slot the context is built for (the two clock reads can straddle a boundary). Timer-triggered contexts carryNone.SameSlotFullIndexerror; every other vote is unchanged.--disable-beacon-head-monitorthe root is alwaysNoneand behavior is unchanged. Pre-Gloas validation is untouched.Risks, Trade-offs, and Mitigations
Coverage is a deliberately partial snapshot: a second same-slot head arriving after the first event, or a head event landing after the attestation-due timer, is unknown and the check is skipped, which the SIP permits. A rejection has no false positive, since an honest beacon node never yields index 1 for a same-slot block; it returns from
receiveand the shared committee instance (attestation and sync signing) waits for the round timer or another valid proposal, which is existing behavior for any value-check rejection. Reusing the block-header slot that weighted attestation data fetches was considered and left out: it is opt-in and only helps the timer path.Validation
cargo test -p ssv_types --lib -- gloas: 30 passed, including four new cases (reject on the known root, index 0 accepted, other block accepted, no knowledge accepted).cargo test -p anchor_validator_store --lib: 174 passed, including three cases for the slot guard and one test through the production constructor path, because the committee tests' mock decider discards the validator.cargo fmt --check,cargo clippy -p ssv_types -p anchor_validator_store --tests -- -D warnings,git diff --check: clean.Rollback
Revert this PR. The field is in memory; no configuration or database change.
🤖 Generated with Claude Code
https://claude.ai/code/session_019jwWeHCBBac5Ma7Vrfonaw