Document historical_block_hashes bounds check as defensive guard#217
Conversation
The spec relies on the SSZ list limit (HISTORICAL_ROOTS_LIMIT) to enforce the bound implicitly during serialization. We check explicitly before allocating to prevent OOM from a crafted block with a large slot gap.
🤖 Kimi Code ReviewSecurity & Correctness ReviewFile: Critical Issue: Integer UnderflowLine 154-155: The calculation Fix: let num_empty_slots = block.slot.saturating_sub(parent_header.slot).saturating_sub(1) as usize;Logic Issue: Incorrect Historical Block Hash CountLines 155-158: The guard checks against Fix: Remove the let new_total = current_len + num_empty_slots;Missing ValidationLine 154: No validation that if block.slot <= parent_header.slot {
return Err(Error::BlockSlotNotLaterThanParent);
}Minor: Comment AccuracyLine 152: The comment mentions "historical_block_hashes" but the actual field is These issues could lead to consensus failures or DoS vulnerabilities. Automated review by Kimi (Moonshot AI) · custom prompt |
🤖 Codex Code Review
Automated review by OpenAI Codex · custom prompt |
🤖 Claude Code ReviewThis is a documentation-only PR — 4 lines added, 0 deleted — adding a comment explaining an existing defensive bounds check. The review is straightforward. PR 217 Review: Document
|
Greptile SummaryThis PR is a documentation-only change that adds a 4-line comment to
Confidence Score: 5/5
|
| Filename | Overview |
|---|---|
| crates/blockchain/state_transition/src/lib.rs | Adds a 4-line comment above the existing historical_block_hashes bounds check in process_block_header, accurately explaining it as a defensive OOM-prevention guard that goes beyond the spec's implicit SSZ serialization enforcement. No logic changes. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[process_block_header] --> B{block.slot == state.slot?}
B -- No --> C[Err: SlotMismatch]
B -- Yes --> D{block.slot > parent.slot?}
D -- No --> E[Err: ParentSlotIsNewer]
D -- Yes --> F{Valid proposer?}
F -- No --> G[Err: InvalidProposer]
F -- Yes --> H{Parent root matches?}
H -- No --> I[Err: InvalidParent]
H -- Yes --> J[Compute num_empty_slots & new_total]
J --> K{"new_total > HISTORICAL_ROOTS_LIMIT?\n(Defensive guard — beyond spec)"}
K -- Yes --> L[Err: SlotGapTooLarge\nPrevents OOM allocation]
K -- No --> M[Push parent_root + empty slots\nto historical_block_hashes]
M --> N[Extend justified_slots]
N --> O[Update latest_block_header]
O --> P[Ok]
style K fill:#fffacd,stroke:#f0c040
style L fill:#ffe0e0,stroke:#d06060
Last reviewed commit: 16839cf
Motivation
A spec-to-code compliance audit (FINDING-005) identified that
process_block_headeradds an explicit bounds check onhistorical_block_hashesthat the spec does not have.The spec (
state.pyL281) appends tohistorical_block_hasheswithout an explicit length check, relying on the SSZ list limit (HISTORICAL_ROOTS_LIMIT = 2^18 = 262144) to enforce the bound implicitly during serialization.The Rust code checks before allocating and returns
SlotGapTooLargeif the new length would exceed the limit. This prevents OOM from a crafted block with a large slot gap. The check was correct but lacked explanation of why it exists and that it goes beyond the spec.Description
Adds a comment on the bounds check in
process_block_headerexplaining:How to Test
Documentation-only change.
cargo test --workspace --releasepasses.