fix(validator_store): publish VotingAssignments 50ms past slot boundary - #1235
fix(validator_store): publish VotingAssignments 50ms past slot boundary#1235shane-moore wants to merge 1 commit into
Conversation
…undary The Phase 1 publisher slept exactly to the slot boundary and re-read the wall clock on wake. A timer wake a few milliseconds early read the previous slot, republished it, and the next iteration slept past the new slot, failing its waiters with MetadataSlotPassed (sigp#1223). Sleep 50ms past the boundary instead, mirroring Phase 3's offset idiom and the message validator's CLOCK_ERROR_TOLERANCE. Nothing consumes the assignments earlier than the selection-proof flows (2/3 slot deadline) and the voting-context build (head event or 1/3 slot).
|
@claude, please take a look |
|
Claude finished @shane-moore's task in 4m 10s —— View job Review: PR #1235 — publish
|
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## unstable #1235 +/- ##
============================================
+ Coverage 65.42% 65.47% +0.05%
============================================
Files 164 164
Lines 28075 28125 +50
============================================
+ Hits 18367 18415 +48
- Misses 9708 9710 +2
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:
|
|
@claude On the error-branch clock source: the two durations are equal by construction. The production clock is built at |
|
Claude finished @shane-moore's task in 37s —— View job Re: error-branch clock sourceAgreed — verified and correct. Grepping the whole workspace, let slot_clock = SystemTimeSlotClock::new(
spec.genesis_slot,
Duration::from_secs(genesis_time),
spec.get_slot_duration(),
);Every other That was a non-blocking "source-of-truth swap" note in the first place, and your reasoning resolves it. No change needed. My review stands as approving. |
|
ssv-mini validation results Setup: 4 Anchor operators, 100 managed validators on a full 4-operator committee, Boole active, 12s slots, image built from 42bf00b. Scored window of 128 slots (736-863):
|
Problem, Evidence, and Context
MetadataServicePhase 1 sleeps to the slot boundary and then reads the wall clock. A timer wake a few milliseconds early reads the previous slot, republishes it, and the next iteration sleeps past the new slot. Waiters fail withMetadataSlotPassed, and the Phase 2/3 cascade costs the operator three slots of committee participation.Change Overview
duration_to_next_slot() + 50ms, the same idiom Phase 3 already uses (its 2/3-slot offset makes it immune to this race) and the same shape Lighthouse uses for slot-keyed work. 50 ms mirrors the message validator'sCLOCK_ERROR_TOLERANCE, the inter-node clock error the network already budgets for, and is 10-50x the measured drift.run_slot_start_publisherhelper so the race is unit-testable.VOTING_ASSIGNMENTS_PUBLISH_DELAYdoc comment, then the helper, then the two tests.update_voting_assignmentsitself, the waiter contract, and Phase 2/3 scheduling.Risks, Trade-offs, and Mitigations
Validation
make test(including ssv-spec tests): 622 passed, 0 failed. fmt and clippy clean.MetadataSlotPassed, and 120 of 123 produced blocks carried sync participation of 500+/512 on a finalizing chain. The two slots without publications coincided with a measured 34-second host-VM stall (all containers silent), not publisher behavior.Rollback