Repository navigation
fix(rpc): bound liveness by the wall clock - #670
MegaRedHand wants to merge 3 commits into
Conversation
…eposit_contract and POST /eth/v1/validator/duties/sync/{epoch} from ethlambda beacon, three of the four Beacon API endpoints validator clients call that were missing. fork returns the state's own Fork through the same state_id resolution as the other state endpoints, so a state root is the same 404. deposit_contract returns the Config's deposit chain id and contract address. duties/sync reads the head state's current_sync_committee for an epoch in the head's own sync committee period and next_sync_committee for the next one, matches each requested validator by pubkey and returns every seat it holds (the committee is drawn with replacement), leaves out validators with no seat, and answers 400 for an unknown index or any other period and 503 while syncing. An earlier period is refused rather than answered from a historical state, recorded in docs/spec_deviations.md. compute_sync_committee_period is added to the altair helpers as validator.md defines it.
…the endpoint a validator client's doppelganger protection calls before signing. A validator is live in an epoch if the head state credits it for that epoch (a non-zero participation byte, covering what blocks have already included) or if this node observed it act in that epoch, which covers what no block has included yet. The observations live in a new ObservedLiveness on the Store, one bitset per epoch for the newest three, because three places write them and all three already hold a Store clone: P2P when it accepts a gossip aggregate (its aggregator and every attester its signature verified) or subnet attestation, the chain actor when it imports a beacon block (its proposer, so range-synced and self-published blocks count), and the RPC for attestations and aggregates submitted through pool/attestations and aggregate_and_proofs, since gossip never delivers a node its own messages. The endpoint answers the store clock's previous, current and next epoch and is a 400 for any other epoch or an unknown index, and a 503 while syncing.
The previous/current/next epoch window used the store's tick-driven clock, which still reads the old epoch until the slot tick runs. A validator client asking for the next epoch right after a boundary got a 400. Use the wall-clock epoch, as the duty endpoints do.
🤖 Kimi Code ReviewI'll review this PR focusing on the changes to the validator liveness endpoint and its tests. SummaryThis PR changes the liveness endpoint's epoch validation from using the store's tick-driven epoch ( Issues Found1. Potential panic in
|
| Priority | Item | Action |
|---|---|---|
| High | Test determinism | Refactor wall_epoch() to use shared store; avoid creating multiple stores |
| Medium | Underflow safety | Add saturating_sub or preconditions in tests |
| Medium | Test validity | Add explicit test simulating tick/wall clock divergence |
| Low | Comment precision | Clarify "previous epoch" wording |
The core logic change is correct and addresses a real consensus-layer issue. The test infrastructure needs hardening to be reliable and to actually validate the boundary condition that motivated the fix.
Automated review by Kimi (Moonshot AI) · kimi-k2.6 · custom prompt
🤖 Codex Code ReviewLooks good overall. The change is narrowly scoped to RPC liveness windowing and does not touch fork choice, attestation validation, justification/finalization, STF, XMSS, or SSZ logic.
No obvious security or memory-safety concerns:
Minor note:
If you want, I can also sanity-check the full PR against the base branch with Automated review by OpenAI Codex · gpt-5.4 · custom prompt |
🤖 Claude Code ReviewReview of PR 670: bound liveness by the wall clock The change is small and correct. I only read the diff and the The old check used the store's tick-driven epoch, which lags the real epoch until the slot tick runs. That would refuse a validator client asking for the next epoch just after a boundary. Notes
Otherwise this looks good to merge. Automated review by Claude (Anthropic) · sonnet · custom prompt |
…3-636-638-gloas-live Liveness now bounds its epoch by the wall clock; tmp's get_head_node import is kept next to checkpoint_state.
d973505 to
54adf35
Compare
Motivation
POST /eth/v1/validator/liveness/{epoch}bounds the epoch by the store's tick-driven clock (get_current_store_epoch). At the first moments of an epoch, before the boundary slot's tick has run, that clock still reads the previous epoch, so a validator client asking for the next epoch right at the boundary (doppelganger protection polls at every epoch start) gets a 400. beacon-APIs bounds it by the current (wall-clock) epoch. Same bug class as the duties fixes in #663, found while auditing for it.Change
node::wall_slot, the clockattestation_dataand the duties endpoints already use.cargo test -p ethlambda-rpc --lib --profile release-fast, clippy and fmt clean.