Repository navigation
fix(rpc): bound duties by the wall clock, advance the head for later epochs - #663
MegaRedHand wants to merge 2 commits into
Conversation
…epochs The attester duties endpoint refused any epoch more than one past the head state's. The Beacon API bounds it by the current epoch instead, and at the first slot of an epoch the head is still in the previous one until that slot's block arrives, so every validator client's next-epoch lookahead got a 400 at each epoch boundary (and for as long as the boundary slot stayed empty). Duties now accept any epoch up to one past the later of the wall clock's epoch and the head's. An epoch the head state cannot derive as it is (committees need a state at epoch - MIN_SEED_LOOKAHEAD) is read from the head advanced through fork choice's checkpoint-state cache, on a blocking thread, so the epoch processing neither stalls the runtime nor repeats for a second request. dependent_root still comes from the head's own chain. Proposer duties had the same head-relative limit on their lookahead window and get the same treatment. PTC duties already bounded by the clock and advance the head, so they are unchanged.
🤖 Claude Code ReviewReview: PR 663, bound duties by the wall clock and advance the head for later epochsI read the diff only. I did not build it or run the tests. The change is well reasoned. Bounding by
Findings
Verdict The logic is correct and the test coverage is good. I would address Item 1 before merge, since it adds a new expensive path that a lagging node can hit repeatedly. Item 2 is worth fixing at the same time. Automated review by Claude (Anthropic) · sonnet · custom prompt |
🤖 Codex Code ReviewLooks good overall. The change is thoughtful about Beacon API timing semantics, moves expensive
One thing to tighten:
If you want, I can turn that into a tiny follow-up patch suggestion. Automated review by OpenAI Codex · gpt-5.4 · custom prompt |
🤖 Kimi Code ReviewI'll review this PR which extends validator duty endpoints to serve epochs beyond the head state's immediate lookahead, using Overall AssessmentThe PR is well-structured and addresses a real consensus-layer need (serving next-epoch duties at epoch boundaries). However, there are several correctness, safety, and maintainability issues. Critical Issues1. Incorrect
|
| Item | Severity | File | Line | Issue |
|---|---|---|---|---|
| 1 | High | validator.rs | 265-268 | Synthetic checkpoint with future epoch may break checkpoint_state cache semantics |
| 5 | High | validator.rs | 241-242, 248 | TOCTOU race between head() and checkpoint_state |
| 6 | Medium | validator.rs | 241-242 | Unbounded spawn_blocking tasks |
| 7 | Medium | validator.rs | 248 | Thundering herd on process_slots at epoch boundaries |
| 2 | Medium | validator.rs | 248-251 | Error information loss |
| 13 | Low | lib.rs | 635-642 | Test helper panics on bad input |
| 14 | Trivial | validator.rs | 236 | Comment typo |
The core logic for epoch bounds and state advancement is correct, but the interaction with checkpoint_state cache semantics and potential races need careful attention.
Automated review by Kimi (Moonshot AI) · kimi-k2.6 · custom prompt
…-64-633-636-638-gloas-live
…653 line #663 landed on tmp while #660 was being reverted here, and was written against #660's v2 helpers. The merge keeps #663's wall-clock bound, head advance and blocking thread, with #653's DependentRoot in place of #660's last_slot_before helpers and #653's 503-while-syncing v2 handler. The lookahead paragraph moves from the v1 handler's doc to proposer_duties, as #663 has it. The tests' get helper now layers a default SyncStatusController, as the production router always does: #663's v2 assertions went through routes() without it, which #653's v2 handler answers with a 500.
PTC duties bounded the epoch by the store's tick-driven clock, which still reads the previous epoch until the boundary slot's tick runs. A validator client asking for the next epoch's duties in the first milliseconds of an epoch (lighthouse does) got a 400. Share epoch_upper_bound with attester and proposer duties so all three bound by the wall clock.
…tmp/bci-626-63-64-633-636-638-gloas-live
Motivation
On the gloas devnet (#662), the validator client's next-epoch attester-duty request got a 400 ("epoch is not within one epoch of the head state's") at every epoch boundary. At the first slot of epoch N+1 the head is still in epoch N until that slot's block arrives, and the validator client asks for epoch N+2 right then. The node bounded the epoch by the head state; the Beacon API bounds it by the current epoch (up to
current_epoch + 1). Other validator clients ask for next-epoch duties early in the epoch too, so a late or missed boundary block hits them the same way.Changes
max(wall-clock epoch, head epoch) + 1. The wall clock isnode::wall_slot, the clockattestation_dataalready uses; themaxkeeps a head ahead of a lagging clock from being refused, as PTC duties already do.checkpoint_statecache (the start ofepoch - 1for attester duties,epoch - MIN_SEED_LOOKAHEADfor proposer duties), so a repeated request does no epoch processing.dependent_rootis still read from the head's chain.spawn_blocking, like PTC duties.attestation_dataalready bound by the clock; nothing else bounds by the head's epoch.Tests
process_slots-advanced state; dependent root is the head); two past the clock is a 400; proposer duties before the head's epoch are a 400.attester_duties_two_epochs_ahead_are_a_400becameattester_duties_past_the_clocks_next_epoch_are_a_400, andan_epoch_outside_the_lookahead_is_a_400becamean_epoch_past_the_clocks_next_is_a_400.cargo test --profile release-fast --lib: rpc 175, validator 269; fmt and clippy-D warningsclean.