fix: skip validator duties while syncing#418
Conversation
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Greptile SummaryThis PR gates block proposals and attestations behind a new
Confidence Score: 4/5Safe to merge; the gating logic is straightforward and the happy/recovery/stall paths are all tested. A minor arithmetic expression needs hardening before constants evolve. The sync-gating logic is correct end-to-end: proposals and attestations are blocked at the right intervals, crates/blockchain/src/lib.rs — the recovery expression and the threshold boundary test
|
| Filename | Overview |
|---|---|
| crates/blockchain/src/lib.rs | Introduces SyncStatusTracker with threshold + hysteresis logic, gates proposals and attestations via gate_proposer/duties_allowed, moves sync-status refresh to every tick, and adds 10 unit tests. One minor runtime-underflow risk in the recovery expression; test boundary coverage for the threshold transition is incomplete. |
| crates/blockchain/src/metrics.rs | Adds Debug, Clone, Copy, PartialEq, Eq derives to SyncStatus — required by the new unit tests that assert equality on the enum. Purely additive and correct. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[on_tick: interval fires] --> B[update_sync_status]
B --> C{network_lag >\nNETWORK_STALL_THRESHOLD?}
C -- yes --> D[syncing = false\nnetwork stall, help recover]
C -- no --> E{currently syncing?}
E -- yes --> F{head_lag >\nTHRESHOLD - BAND?}
F -- yes --> G[syncing = true\nstay syncing]
F -- no --> H[syncing = false\nrecovered]
E -- no --> I{head_lag >\nSYNC_LAG_THRESHOLD?}
I -- yes --> J[syncing = true\nenter syncing]
I -- no --> K[syncing = false\nremains synced]
D & G & H & J & K --> L[metrics::set_node_sync_status]
L --> M{interval == 0 and slot > 0?}
M -- yes --> N[get_our_proposer]
N --> O[gate_proposer: syncing?]
O -- syncing --> P[proposer_validator_id = None\nlog skip\nhas_proposal=false to store]
O -- synced --> Q[proposer_validator_id = Some\npropose_block]
M -- no --> R{interval == 1?}
R -- yes --> S{duties_allowed?}
S -- yes --> T[produce_attestations]
S -- no --> U[log skip attestations]
Prompt To Fix All With AI
Fix the following 2 code review issues. Work through them one at a time, proposing concise fixes.
---
### Issue 1 of 2
crates/blockchain/src/lib.rs:89
The recovery-threshold expression `SYNC_LAG_THRESHOLD - SYNC_HYSTERESIS_BAND` is `u64` arithmetic evaluated at runtime. If a future change makes `SYNC_HYSTERESIS_BAND > SYNC_LAG_THRESHOLD`, this underflows — panicking in debug mode and wrapping to a huge number in release, which would permanently lock the node in the syncing state and disable all validator duties. Using `saturating_sub` makes the expression safe against such accidental changes.
```suggestion
self.syncing = head_lag > SYNC_LAG_THRESHOLD.saturating_sub(SYNC_HYSTERESIS_BAND);
```
### Issue 2 of 2
crates/blockchain/src/lib.rs:898-916
**Test doesn't verify the syncing boundary at `SYNC_LAG_THRESHOLD + 1`**
`sync_status_allows_lag_through_threshold` loops `0..=SYNC_LAG_THRESHOLD` and asserts `Synced`, but never checks that `lag == SYNC_LAG_THRESHOLD + 1` produces `Syncing`. Without that assertion the test would still pass if the `>` comparison were accidentally changed to `>=` (which would break the boundary by one slot). `sync_status_detects_local_lag_when_fresh_blocks_are_known` covers the "is syncing" case but does so with a separate tracker; adding one extra call inside this test would make the off-by-one boundary self-contained.
Reviews (1): Last reviewed commit: "Merge branch 'fix/sync-status-heuristic'..." | Re-trigger Greptile
🗒️ Description / Motivation
What Changed
has_proposal = falseto the store when a proposal is skipped.Correctness / Behavior Guarantees
Tests Added / Run
cargo test -p ethlambda-blockchain --lib --offline.cargo clippy -p ethlambda-blockchain --lib --offline -- -D warnings.cargo fmt --all -- --check.git diff --check.Related Issues / PRs
✅ Verification Checklist
make fmt— cleanmake lint(clippy with-D warnings) — cleancargo test --workspace --release— all passing