Skip to content

Probe nested + flat SignalService checkpoint layouts (forward-compat)#116

Draft
davidtaikocha wants to merge 5 commits into
mainfrom
fix/shasta-checkpoint-version-probe
Draft

Probe nested + flat SignalService checkpoint layouts (forward-compat)#116
davidtaikocha wants to merge 5 commits into
mainfrom
fix/shasta-checkpoint-version-probe

Conversation

@davidtaikocha

Copy link
Copy Markdown
Contributor

What & why

raiko2's Shasta stalled-anchor path reads the parent checkpoint (blockHash, stateRoot) from the L2 SignalService (checkpoint-store predeploy 0x…0005) via a witness storage proof, to pin the anchor when the L1 anchor has stalled. The record lives in a mapping at slot 254.

  • Deployed today (taiko-mono origin/main): flat mapping(uint48 blockNumber => CheckpointRecord) → record slot keccak(blockNumber ‖ 254). ← what raiko2 computes today; correct.
  • Future (taiko-alethia-protocol-v3.0.0): nested mapping(uint256 VERSION => mapping(uint48 blockNumber => CheckpointRecord)) with VERSION = 1 → record slot keccak(blockNumber ‖ keccak(VERSION ‖ 254)).

Once the nested layout ships to L2, raiko2's flat-only derivation would read an unpopulated slot, trip the non-zero guard, and make stalled-anchor proposals unprovable (a liveness halt confined to that edge case). This PR makes the read forward-compatible by probing both layouts, so no L2-side flag-day coordination is needed.

How

One derivation — shasta_checkpoint_storage_slot_candidates() — returns both candidate (blockHash, stateRoot) slot pairs in precedence order (nested-v1 first, then flat); host and guest both consume it, so their slot math cannot drift:

  • crates/primitives/src/chain_spec.rsSHASTA_CHECKPOINT_VERSION, flat + nested derivations, …_candidates(), and hardcoded self-contained slot tests.
  • crates/pipeline/src/forks/shasta/spec.rs — preflight requests eth_getProof for all candidate slots.
  • crates/guest-common/src/lib.rsverified_parent_shasta_checkpoint probes nested-then-flat.

Soundness

This is a correctness/liveness change — not soundness — and it adds no new forgery surface. Every read is MPT-proven against the parent state root (bound to the on-chain parent block hash). The probe is fail-closed: a missing/unresolvable proof → error (propagated via ?, no fallback); only a proven-zero blockHash advances to the next candidate; all-absent → error. An attacker controlling the witness can neither forge a value nor forge a slot's absence, and cannot steer which layout is read (the block-number index is itself an MPT-proven, host-cross-checked value). Verified end-to-end by an independent review.

Limitations (accepted)

  • v1 only: the probe covers flat + VERSION = 1 (the SignalService VERSION is a Solidity constant, not readable from storage). A future VERSION bump needs a one-line constant update; flagged with a loud comment.
  • Migration window: if, post-upgrade, a needed checkpoint exists only in old flat slots (not re-saved nested), behavior depends on the (still-undecided) migration plan. Still fail-closed, never a forgery.

Testing

  • cargo clippy -p raiko2-primitives -p raiko2-guest-common -p raiko2-pipeline -- -D warnings — clean.
  • cargo test -p raiko2-primitives -p raiko2-guest-common -p raiko2-pipeline — pass. The sole failure, shasta_backends_return_expected_elves, is the pre-existing missing-SP1-ELF environment issue, unrelated to this change.
  • cargo fmt --all applied.
  • Manual follow-up (infra-gated): devnet stalled-anchor preflight smoke (origin − anchor > window) to confirm a live node returns valid absence proofs for the unpopulated layout. Not runnable in this environment.

Non-blocking follow-ups

  • Remove the dead #[allow(clippy::too_many_arguments)] on read_checkpoint_record (6 args — never trips).
  • Optional belt-and-suspenders test: drop the checkpoint-store nodes from the witness and assert the guest errors (proving no flat fallback when the nested proof is missing).

Base branch

Targets main (consistent with the F-1 fix #115). Per the dual-branch convention this likely needs a port to mainnet — leaving that to maintainers.

🤖 Generated with Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant