fix: bind stalled anchor checkpoint to parent state - #699
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: aaf61b07de
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| let mut encoded = Vec::with_capacity(64); | ||
| encoded.extend_from_slice(&U256::from(block_number).to_be_bytes::<32>()); | ||
| encoded | ||
| .extend_from_slice(&U256::from(SHASTA_SIGNAL_SERVICE_CHECKPOINTS_SLOT).to_be_bytes::<32>()); | ||
| let block_hash_slot = U256::from_be_bytes::<32>(keccak(&encoded)); |
There was a problem hiding this comment.
Include the checkpoint VERSION mapping in slot hashing
For stalled Shasta batches with empty l1_ancestor_headers, this helper is used to load and verify the parent checkpoint, but the SignalService stores checkpoints as _checkpoints[VERSION][blockNumber], not as a flat mapping at slot 254. Hashing only blockNumber with slot 254 points at the wrong storage slots, so preflight proves empty/wrong values and read_parent_shasta_checkpoint rejects otherwise valid stalled-anchor batches before falling back to linkage verification with no headers.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
This does not apply to the checkpoint we read here.
This code is reading the L2 checkpoint storage from the L2 SignalService/anchor predeploy path, not the L1 SignalService v3.0.0 received-signal/checkpoint namespace. The L2 checkpoint storage used here is the flat mapping(uint48 => CheckpointRecord) at slot 254, so the slot is keccak256(abi.encode(blockNumber, uint256(254))), with +1 for stateRoot.
I also verified this against the current devnet L2 predeploy 0x1670010000000000000000000000000000000005: getCheckpoint(25454) returns the same blockHash/stateRoot as direct storage reads at the flat slot and slot + 1. Calling VERSION() on that L2 address reverts, which is consistent with this being the L2 checkpoint layout, not the L1 versioned SignalService layout. Adding VERSION here would read the wrong slots for this path.
abb64aa
into
hotfix/hotfix-based-on-1.16.1
Summary
SignalServicecheckpoint stored in parent state.blockHash/stateRootstorage slots so the guest witness contains them.Complexity / Review Notes
lib/src/protocol_instance.rs: derive SignalService predeploy address from Anchor predeploy address, compute_checkpoints[blockNumber]slots, read parent storage trie, compare with decoded anchor tx calldata.l1_ancestor_headers; it touches only the first block parent state because stalled anchors do not update the checkpoint..envlocal mrenclave changes are intentionally not included.Test Plan
cargo test -p raiko-libcargo check -p raiko-coregit diff --check -- core/src/preflight/mod.rs lib/src/protocol_instance.rs