feat(blockchain): warn instead of failing on unclosed justified divergence#480
Conversation
…gence When a produced block's justified slot lagged the store's justified checkpoint, `produce_block_with_signatures` returned `JustifiedDivergenceNotClosed`, which aborted block production entirely. In practice this halts the chain: the proposer cannot re-justify the head because validators source from the store-justified checkpoint, so every subsequent proposal hits the same divergence and freezes block production. Halting production is worse than publishing a block whose justified slot lags by one fork: the latter still lets the chain make progress and gives pool attestations a chance to close the divergence on later slots. Replace the hard error with a warn-level log and publish the block anyway.
🤖 Kimi Code ReviewReview: PR #480 - Liveness fix for justified checkpoint divergence The change correctly prioritizes chain liveness over strict adherence to the ideal justified checkpoint invariant. Allowing block production to continue with a warning—rather than halting with an error—is the correct consensus-layer behavior. crates/blockchain/src/store.rs
crates/blockchain/state_transition/src/lib.rs
Consensus considerations
Verdict: LGTM. Safe liveness improvement with proper observability. Automated review by Kimi (Moonshot AI) · kimi-k2.5 · custom prompt |
🤖 Codex Code ReviewNo findings. The behavioral change in crates/blockchain/src/store.rs is narrow: it stops treating a lagging Residual risk: there is no store-level regression test for the new “warn and continue” path at store.rs, so if Automated review by OpenAI Codex · gpt-5.4 · custom prompt |
Greptile SummaryThis PR changes justified-divergence handling during block production. The main changes are:
Confidence Score: 5/5This looks safe to merge after considering the warning-only behavior.
|
| Filename | Overview |
|---|---|
| crates/blockchain/src/store.rs | Changes block production from hard-failing on lagging justified checkpoints to warning and continuing, and removes the old error variant. |
| crates/blockchain/state_transition/src/lib.rs | Updates a test comment to describe the freeze scenario without naming the removed error variant. |
Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.
---
### Issue 1 of 1
crates/blockchain/src/store.rs:835-845
**Lagging Justification Still Publishes**
When `post_checkpoints.justified.slot` stays behind `store.latest_justified().slot`, this path now publishes the block without changing the block or surfacing the divergence to the caller. The local import path only advances the store checkpoint on a higher justified slot, so repeated proposals in this state can keep producing blocks that do not close justification and leave finality stalled instead of making the failure actionable.
Reviews (1): Last reviewed commit: "feat(blockchain): warn instead of failin..." | Re-trigger Greptile
| let store_justified_slot = store.latest_justified().slot; | ||
| if post_checkpoints.justified.slot < store_justified_slot { | ||
| return Err(StoreError::JustifiedDivergenceNotClosed { | ||
| block_justified_slot: post_checkpoints.justified.slot, | ||
| warn!( | ||
| %slot, | ||
| proposer = validator_index, | ||
| block_justified_slot = post_checkpoints.justified.slot, | ||
| store_justified_slot, | ||
| }); | ||
| "Produced block justified slot is behind store justified slot; \ | ||
| fixed-point attestation loop did not converge" | ||
| ); | ||
| } |
There was a problem hiding this comment.
Lagging Justification Still Publishes
When post_checkpoints.justified.slot stays behind store.latest_justified().slot, this path now publishes the block without changing the block or surfacing the divergence to the caller. The local import path only advances the store checkpoint on a higher justified slot, so repeated proposals in this state can keep producing blocks that do not close justification and leave finality stalled instead of making the failure actionable.
Prompt To Fix With AI
This is a comment left during a code review.
Path: crates/blockchain/src/store.rs
Line: 835-845
Comment:
**Lagging Justification Still Publishes**
When `post_checkpoints.justified.slot` stays behind `store.latest_justified().slot`, this path now publishes the block without changing the block or surfacing the divergence to the caller. The local import path only advances the store checkpoint on a higher justified slot, so repeated proposals in this state can keep producing blocks that do not close justification and leave finality stalled instead of making the failure actionable.
How can I resolve this? If you propose a fix, please make it concise.
🤖 Claude Code ReviewNow I have everything I need. Let me write up the review. Review: feat(blockchain): warn instead of failing on unclosed justified divergenceThis PR converts a hard block-production abort (
|
What
produce_block_with_signatureschecks that a produced block's justifiedslot does not lag the store's justified checkpoint (leanSpec #595). When it
did, the function returned
StoreError::JustifiedDivergenceNotClosed, whichaborted block production for that slot.
This PR removes that error variant and replaces the hard failure with a
warn-level log, then publishes the block anyway.
Why
Aborting production on this condition halts the chain rather than recovering
from it. The proposer cannot re-justify the head because validators source
from the store-justified checkpoint, so every subsequent proposal hits the
same divergence and block production freezes indefinitely (observed as a
"did not converge" stall).
Publishing a block whose justified slot lags by one fork is the lesser evil:
the chain keeps making progress and pool attestations get a chance to close
the divergence on later slots. The divergence is now surfaced as a warning
instead of silently halting the node.
This mirrors the upstream spec change in
leanEthereum/leanSpec#1168.
Changes
crates/blockchain/src/store.rs: replace thereturn Err(JustifiedDivergenceNotClosed)with awarn!log; remove the now-unusedStoreError::JustifiedDivergenceNotClosedvariant.crates/blockchain/state_transition/src/lib.rs: reword a test doc comment that referenced the removed error type by name.Testing
cargo build -p ethlambda-blockchain✅cargo clippy -p ethlambda-blockchain --all-targets✅ (no warnings)References