-
Notifications
You must be signed in to change notification settings - Fork 8
feat: add checkpoint-sync #93
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
🤖 Kimi Code ReviewReview SummaryThis PR adds checkpoint sync functionality to ethlambda, allowing nodes to sync from a recent finalized state instead of genesis. The implementation is well-structured and follows Rust best practices, but there are several important issues to address: Critical Issues
Important Issues
Minor Issues
Line-specific Feedback
The PR is well-structured overall, but the security and consensus issues must be addressed before merging. Automated review by Kimi (Moonshot AI) · custom prompt |
🤖 Codex Code ReviewReview Findings (ordered by severity)
Consensus-layer considerations
Rust/maintainability notes
Open questions
If you address the anchoring and validation gaps above, the rest of the changes look clean and well-structured. Automated review by OpenAI Codex · custom prompt |
🤖 Claude Code ReviewPerfect! Now I have a complete picture of the PR. Let me compile my review findings. Code Review: PR #93 - Add Checkpoint Sync SupportSummaryThis PR adds checkpoint sync functionality to allow nodes to sync from a trusted checkpoint state instead of starting from genesis. The implementation is well-structured with proper validation and error handling. Below are my findings. ✅ Strengths
🔴 Critical Issues1. State Root Mismatch Not Validated (Security)Location: The Issue: pub fn construct_anchor_block(state: &State) -> Block {
Block {
slot: state.latest_block_header.slot,
parent_root: state.latest_block_header.parent_root,
proposer_index: state.latest_block_header.proposer_index,
state_root: state.latest_block_header.state_root,
body: BlockBody::default(), // ⚠️ Default body - root may not match!
}
}The block's Recommendation:
2. No HTTPS Enforcement (Security)Location: Checkpoint sync downloads consensus state over HTTP without enforcing HTTPS. An attacker performing a MITM attack could provide a malicious state that passes validation checks. Current code: let url = format!("{base_url}/lean/v0/states/finalized");
let client = Client::builder()
.connect_timeout(CHECKPOINT_CONNECT_TIMEOUT)
.read_timeout(CHECKPOINT_READ_TIMEOUT)
.build()?;Recommendation:
3. Potential Type Confusion in Checkpoint ValidationLocation: The validation checks if let block_root = state.latest_block_header.tree_hash_root(); // Header root
if state.latest_block_header.slot == state.latest_finalized.slot
&& block_root != state.latest_finalized.root.0 // Comparing to block root?
{
return Err(CheckpointSyncError::BlockHeaderFinalizedRootMismatch);
}Question: Does Investigation needed: Check the consensus spec to confirm what
|
| Category | Status |
|---|---|
| Correctness | |
| Security | 🔴 Issues #1, #2 are critical |
| Performance | ✅ Good timeout strategy |
| Memory Safety | |
| Error Handling | ✅ Well-structured |
| Testing | ✅ Comprehensive unit tests |
| Code Quality | ✅ Clean, readable |
Recommendations Priority
- MUST FIX: State root validation (docs: add initial roadmap #1)
- MUST FIX: HTTPS enforcement (chore: initial repo setup #2)
- MUST INVESTIGATE: Checkpoint root type confusion (docs: improve readme #3)
- SHOULD FIX: Download size limit (feat: load genesis network configuration and state #5)
- SHOULD FIX: Future slot check (docs: add philosophy and design principles #4)
- CONSIDER: Anchor block root mismatch implications (feat: listen for gossipsub blocks #9)
The core implementation is solid, but the consensus-critical validation needs strengthening before merge.
Automated review by Claude (Anthropic) · custom prompt
Required for #93 This PR streamlines block storage by splitting block headers from bodies. This simplifies the handling of the anchor state, since the block header is readily available from it, but the body is not, even if it's unnecessary for child block processing.
460575a to
8a27971
Compare
🤖 Kimi Code ReviewReview SummaryOverall Assessment: The checkpoint sync implementation is well-structured with comprehensive validation. However, there are several critical security and correctness issues that need addressing. Critical Issues
High Priority Issues
Medium Priority Issues
Minor Issues
Security Recommendations
The implementation is solid but needs the above security and correctness fixes before production use. Automated review by Kimi (Moonshot AI) · custom prompt |
🤖 Codex Code ReviewFindings
Questions / assumptions
If you address the two high‑severity items, the rest looks reasonable for the minimal checkpoint‑sync flow. Automated review by OpenAI Codex · custom prompt |
🤖 Claude Code ReviewNow let me compile my findings into a comprehensive review. PR #93 Review: Checkpoint Sync ImplementationSummaryThis PR adds checkpoint sync functionality via Critical Issues🔴 State Root Comparison Bug (Lines 172-181)Location: let block_root = state.latest_block_header.tree_hash_root();
if state.latest_block_header.slot == state.latest_finalized.slot
&& block_root != state.latest_finalized.root.0
{
return Err(CheckpointSyncError::BlockHeaderFinalizedRootMismatch);
}
if state.latest_block_header.slot == state.latest_justified.slot
&& block_root != state.latest_justified.root.0
{
return Err(CheckpointSyncError::BlockHeaderJustifiedRootMismatch);
}Problem: Computing
Why this matters: If a malicious peer sends a checkpoint state with an incorrect Fix: // Save original state_root for proper validation
let original_state_root = state.latest_block_header.state_root;
// Compute block root with zeroed state_root (matches what init_store does)
let mut header_for_root = state.latest_block_header.clone();
header_for_root.state_root = H256::ZERO;
let block_root = header_for_root.tree_hash_root();
// Verify state_root is either zero (shouldn't be for checkpoint) or computed value
// This prevents accepting states with arbitrary state_root values
if original_state_root != H256::ZERO {
let mut state_for_root = state.clone();
state_for_root.latest_block_header.state_root = H256::ZERO;
let computed_state_root = state_for_root.tree_hash_root();
if original_state_root != computed_state_root {
return Err(CheckpointSyncError::StateRootMismatch {
expected: computed_state_root,
got: original_state_root
});
}
}
// Now verify block root matches checkpoints
if state.latest_block_header.slot == state.latest_finalized.slot
&& block_root != state.latest_finalized.root.0
{ ... }Impact: Medium-High security risk. While Medium Priority Issues
|
Greptile OverviewGreptile SummaryImplements checkpoint sync functionality allowing nodes to start from a recent finalized state instead of syncing from genesis. When Key changes:
Confidence Score: 5/5
|
| Filename | Overview |
|---|---|
| bin/ethlambda/src/checkpoint_sync.rs | New checkpoint sync module with HTTP fetch, SSZ deserialization, and comprehensive validation logic with extensive test coverage |
| bin/ethlambda/src/main.rs | Added checkpoint sync CLI flag and fetch_initial_state function to support downloading state from remote peer or genesis initialization |
| Cargo.toml | Added reqwest HTTP client dependency with rustls-tls feature for checkpoint sync functionality |
| bin/ethlambda/Cargo.toml | Added reqwest and thiserror dependencies for checkpoint sync module |
Sequence Diagram
sequenceDiagram
participant Main as main()
participant FIS as fetch_initial_state()
participant CS as checkpoint_sync
participant HTTP as Remote Peer
participant Store as Store
Main->>FIS: checkpoint_sync_url provided?
alt Checkpoint Sync Path
FIS->>CS: fetch_checkpoint_state(url)
CS->>HTTP: GET /lean/v0/states/finalized
HTTP-->>CS: SSZ-encoded State bytes
CS->>CS: Deserialize State from SSZ
CS-->>FIS: Return State
FIS->>CS: verify_checkpoint_state(state, genesis_time, validators)
CS->>CS: Validate slot > 0
CS->>CS: Validate validators exist
CS->>CS: Check genesis_time matches
CS->>CS: Check validator count matches
CS->>CS: Verify validator indices sequential
CS->>CS: Verify validator pubkeys match
CS->>CS: Validate checkpoint consistency
CS->>CS: Validate block header consistency
CS-->>FIS: Validation OK
FIS->>Store: from_anchor_state(backend, state)
Store-->>FIS: Initialized Store
else Genesis Path
FIS->>FIS: State::from_genesis(genesis_time, validators)
FIS->>Store: from_anchor_state(backend, genesis_state)
Store-->>FIS: Initialized Store
end
FIS-->>Main: Return Store
Backfill is too slow to sync after a checkpoint-sync. This should make it fast enough.
Also tunes parameters so added retry events fall between the old ones.
Closes #80
Closes #89
This PR adds checkpoint-sync functionality when
--checkpoint-sync-urlis specified. This means the node will fetch the latest finalized state from the URL specified in the flag.NOTE: has some changes from #96, they'll be gone once that's merged.Other changes in this PR
2162947: includes an optimization to the unknown block fetch mechanism in
BlockChainServer. While testing checkpoint-sync we found the backfill never ended, so we sped it up a bit by storing the oldest missing ancestor of each block, jumping to it directly instead of going block by block and resetting after a failed request like now.ebcc1a7: fixes block fetch logic in the P2P module. Before we considered empty responses as succeded, so we didn't retry. Now we follow the same logic as for error responses: we retry after a backoff.
89b5c3f: increases the number of retries to 10, with 5ms initial backoff and an exponent of 2.