fix: harden Shasta guest soundness checks - #698
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e0ba593e37
ℹ️ 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".
| anchor_block_numbers | ||
| .iter() | ||
| .all(|anchor_block_number| *anchor_block_number == last_anchor_block_number) |
There was a problem hiding this comment.
Bind stalled-anchor bypass to the full checkpoint
When l1_ancestor_headers is empty, returning true here skips verify_shasha_anchor_linkage, which is the only guest-side check that the decoded Shasta checkpoint (blockNumber, blockHash, stateRoot) is present in the L1 ancestor set. Because this helper only compares block numbers, a stalled-anchor batch whose anchor tx uses last_anchor_block_number with a forged blockHash or stateRoot still bypasses linkage verification; the new manifest check also only compares the number. Please include the full decoded checkpoint, or otherwise bind hash/root, before allowing this bypass.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
I do not think this is the right threat model for the stalled-anchor bypass.
The empty-header path is only entered for the stalled case: host preflight drops L1 ancestors only when lag > anchor_max_offset && last_anchor_block_number == min_anchor_height, and the guest-side helper added in this PR requires every decoded anchor tx checkpoint block number to equal last_anchor_block_number.
In that stalled-equal state, Anchor.anchorV4 does not write the checkpoint hash/root. Anchor._validateBlock only calls checkpointStore.saveCheckpoint({ blockNumber, blockHash, stateRoot }) when _blockParams.anchorBlockNumber > _blockState.anchorBlockNumber. For the stalled-equal case that branch is not taken. _storePreconfMetadata also stores only anchorBlockNumber, submission-window fields, and tx-list hashes; it does not store anchorBlockHash or anchorStateRoot.
So a forged hash/root in stalled anchor calldata is not anchored into L2 state and cannot become a SignalService checkpoint. The number binding is the critical check here: it prevents using the bypass for a fresh anchor that would update checkpoint state. For non-stalled anchors, l1_ancestor_headers is non-empty and verify_shasha_anchor_linkage still checks the full (blockNumber, blockHash, stateRoot) triple against the L1 ancestor headers.
d03d9b4
into
hotfix/hotfix-based-on-1.16.1
Summary
RethBlockBuilder::execute_transactions, removing legacy HEKLA/ONTAKE/PACAYA fork-choice paths from the hotfix execution path.anchor_block_numberto the decoded anchor tx checkpoint block number.last_anchor_block_numberand ancestor headers are empty.Test Plan
python3 - <<'PY' ...JSON/address validation forhost/config/chain_spec_list_devnet.jsoncargo test -p raiko-lib shasta_fork_guard --libcargo fmt --package raiko-lib --checkcargo test -p raiko-lib --lib(53 passed; 2 ignored)