Skip to content

[Provers L02] Check for non-zero target commitments#92

Merged
luiz-lvj merged 7 commits intomainfrom
fix/provers-L02
Feb 24, 2026
Merged

[Provers L02] Check for non-zero target commitments#92
luiz-lvj merged 7 commits intomainfrom
fix/provers-L02

Conversation

@luiz-lvj
Copy link
Collaborator

@luiz-lvj luiz-lvj commented Feb 23, 2026

Across multiple prover implementations, verifyTargetStateCommitment may succeed and return a zero targetStateCommitment when the expected on-chain commitment is missing or has not yet been written.

In the ZKsync ParentToChildProver contract, the function is expected to return the l2LogsRootHash stored in the L1 Gateway contract for a given batch. The verification reads the l2LogsRootHashes mapping using ProverUtils.getSlotFromBlockHeader. However, this helper does not revert when the requested storage slot does not exist or contains the default zero value (e.g., when the batch has not yet been executed on L1). Instead, it returns 0, allowing verifyTargetStateCommitment to return a zero commitment without reverting.

A similar issue exists in ChildToParentProver implementations that rely on buffer contracts to store L1 block hashes on L2 (e.g., Taiko, Linea, Scroll, and ZKsync). In these cases, verifyTargetStateCommitment is intended to return an L1 block hash read from a buffer storage slot. If the block hash has not yet been written, getSlotFromBlockHeader again returns 0, and the function may succeed despite the absence of a valid commitment.

In both scenarios, the behavior deviates from the intended specification. The verifyTargetStateCommitment function is expected to succeed only when a valid, initialized commitment exists. Silently returning zero creates ambiguity between a legitimate value and an uninitialized or missing commitment.

This PR updates the provers to revert if the targetStateCommitment is zero on the verifyTargetStateCommitment function.

Summary by CodeRabbit

Bug Fixes

  • Enhanced state commitment validation with explicit runtime checks across all blockchain prover implementations.
  • Improved error handling and messaging for invalid state commitments, preventing silent failures.

Documentation

  • Updated prover documentation to reflect refined error handling semantics.

@coderabbitai
Copy link

coderabbitai bot commented Feb 23, 2026

Warning

Rate limit exceeded

@luiz-lvj has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 23 minutes and 43 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 582b561 and 57af30d.

📒 Files selected for processing (13)
  • snapshots/verifyBroadcastMessage.json
  • src/contracts/provers/arbitrum/ChildToParentProver.sol
  • src/contracts/provers/arbitrum/ParentToChildProver.sol
  • src/contracts/provers/linea/ChildToParentProver.sol
  • src/contracts/provers/linea/ParentToChildProver.sol
  • src/contracts/provers/optimism/ChildToParentProver.sol
  • src/contracts/provers/scroll/ChildToParentProver.sol
  • src/contracts/provers/scroll/ParentToChildProver.sol
  • src/contracts/provers/taiko/ChildToParentProver.sol
  • src/contracts/provers/taiko/ParentToChildProver.sol
  • src/contracts/provers/zksync/ChildToParentProver.sol
  • src/contracts/provers/zksync/ParentToChildProver.sol
  • test/provers/scroll/ParentToChildProver.t.sol
📝 Walkthrough

Walkthrough

This PR standardizes error handling across all blockchain prover implementations by introducing a unified InvalidTargetStateCommitment error, replacing various state-related error types, and adding non-zero validation checks for computed target state commitments across ChildToParentProver and ParentToChildProver contracts.

Changes

Cohort / File(s) Summary
Documentation Updates
docs/provers/ARBITRUM.md, docs/provers/TAIKO.md
Updated error references from TargetBlockHashNotFound() and TargetStateRootNotFound() to InvalidTargetStateCommitment() in function descriptions and control flow documentation.
Arbitrum Provers
src/contracts/provers/arbitrum/ChildToParentProver.sol, src/contracts/provers/arbitrum/ParentToChildProver.sol
Added InvalidTargetStateCommitment error declaration. Replaced TargetBlockHashNotFound() with InvalidTargetStateCommitment() and added require-based non-zero validation for targetStateCommitment.
Linea Provers
src/contracts/provers/linea/ChildToParentProver.sol, src/contracts/provers/linea/ParentToChildProver.sol
Introduced InvalidTargetStateCommitment error. Replaced TargetStateRootNotFound() with InvalidTargetStateCommitment() and added require-based validation for non-zero targetStateCommitment in verifyTargetStateCommitment.
Optimism Provers
src/contracts/provers/optimism/ChildToParentProver.sol, src/contracts/provers/optimism/ParentToChildProver.sol
Added InvalidTargetStateCommitment error and runtime checks requiring computed targetStateCommitment to be non-zero in verifyTargetStateCommitment.
Scroll Provers
src/contracts/provers/scroll/ChildToParentProver.sol, src/contracts/provers/scroll/ParentToChildProver.sol
Introduced InvalidTargetStateCommitment error with require-based validation for non-zero targetStateCommitment. Added supplementary error path alongside existing state root checks.
Taiko Provers
src/contracts/provers/taiko/ChildToParentProver.sol, src/contracts/provers/taiko/ParentToChildProver.sol
Renamed public error from TargetBlockHashNotFound() to InvalidTargetStateCommitment(). Updated both getTargetStateCommitment and verifyTargetStateCommitment to use new error with require-based validation.
zkSync Provers
src/contracts/provers/zksync/ChildToParentProver.sol, src/contracts/provers/zksync/ParentToChildProver.sol
Added InvalidTargetStateCommitment error declaration. Introduced require-based non-zero checks for targetStateCommitment after computation in verifyTargetStateCommitment.

Estimated Code Review Effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly Related PRs

Suggested Reviewers

  • pepebndc
  • nahimterrazas

Poem

🐰 Across the chains, we hop and bound,
With consistent errors, safe and sound,
Each prover now speaks the same clear tongue,
InvalidTargetStateCommitment rung!
From Arbitrum to zkSync's gleam,
Validation checks fulfill the dream! ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main change: adding validation checks to ensure target state commitments are non-zero across prover implementations.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/provers-L02

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
docs/provers/TAIKO.md (1)

14-18: ⚠️ Potential issue | 🟡 Minor

Checkpoint storage layout is inaccurate.

The storage layout notes imply blockNumber is stored in the struct, but in SignalService the mapping key is the block number and the stored struct only contains blockHash (slot+0) and stateRoot (slot+1). Please update the “Checkpoint Structure” diagram and the “Storage Slot Calculation” section to avoid incorrect slot derivation guidance.

✏️ Suggested doc fix
-    struct Checkpoint {
-        uint48 blockNumber;    // Block number this checkpoint refers to
-        bytes32 blockHash;     // Block hash of that block
-        bytes32 stateRoot;     // State root (may not always be populated)
-    }
+    struct Checkpoint {
+        bytes32 blockHash;     // Block hash of that block
+        bytes32 stateRoot;     // State root (may not always be populated)
+    }
-// slot + 0: blockNumber (packed with other small values)
-// slot + 0: blockHash (first bytes32 of struct)
-// slot + 1: stateRoot
+// slot + 0: blockHash (first bytes32 of struct)
+// slot + 1: stateRoot
+// Note: blockNumber is the mapping key, not stored in the struct

Based on learnings: “In Taiko's SignalService contract, the mapping mapping(uint48 blockNumber => CheckpointRecord checkpoint) private _checkpoints stores CheckpointRecord structs with only two fields: bytes32 blockHash (at slot+0) and bytes32 stateRoot (at slot+1). The blockNumber is the mapping key, not stored in the struct. The interface Checkpoint struct includes blockNumber only for ABI/function returns, where it's reconstructed from the function parameter.”

Also applies to: 327-333

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/provers/TAIKO.md` around lines 14 - 18, Update the "Checkpoint
Structure" diagram and "Storage Slot Calculation" text to reflect that in
SignalService the mapping mapping(uint48 blockNumber => CheckpointRecord
checkpoint) private _checkpoints stores only bytes32 blockHash (slot+0) and
bytes32 stateRoot (slot+1); blockNumber is the mapping key and is not stored
inside the CheckpointRecord struct. Replace any diagram or slot derivation that
shows uint48 blockNumber as a struct member, and add a short note that the
public/interface Checkpoint struct (used in returns) may include blockNumber for
ABI convenience but it is reconstructed from the mapping key, not stored in
storage. Ensure references to Checkpoint, CheckpointRecord, and _checkpoints are
updated accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/contracts/provers/arbitrum/ParentToChildProver.sol`:
- Around line 57-61: The check for targetStateCommitment being zero is
duplicated: remove the redundant require(targetStateCommitment != bytes32(0),
InvalidTargetStateCommitment()) so only the explicit if (...) { revert
InvalidTargetStateCommitment(); } remains; update ParentToChildProver.sol to
delete the require that references targetStateCommitment to avoid the duplicate
zero-check while preserving the single revert error path.

In `@src/contracts/provers/scroll/ParentToChildProver.sol`:
- Around line 33-35: The second require that throws InvalidTargetStateCommitment
is unreachable because StateRootNotFound() already reverts on a zero state root;
consolidate to a single standardized error by either removing the earlier
StateRootNotFound() revert and using InvalidTargetStateCommitment() in its
place, or drop the later require and keep StateRootNotFound(), ensuring only one
of the error symbols (StateRootNotFound() or InvalidTargetStateCommitment()) is
used for the zero-check in ParentToChildProver.sol (apply the same change for
the similar check around the 72-76 region).

---

Outside diff comments:
In `@docs/provers/TAIKO.md`:
- Around line 14-18: Update the "Checkpoint Structure" diagram and "Storage Slot
Calculation" text to reflect that in SignalService the mapping mapping(uint48
blockNumber => CheckpointRecord checkpoint) private _checkpoints stores only
bytes32 blockHash (slot+0) and bytes32 stateRoot (slot+1); blockNumber is the
mapping key and is not stored inside the CheckpointRecord struct. Replace any
diagram or slot derivation that shows uint48 blockNumber as a struct member, and
add a short note that the public/interface Checkpoint struct (used in returns)
may include blockNumber for ABI convenience but it is reconstructed from the
mapping key, not stored in storage. Ensure references to Checkpoint,
CheckpointRecord, and _checkpoints are updated accordingly.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7714c79 and 582b561.

📒 Files selected for processing (14)
  • docs/provers/ARBITRUM.md
  • docs/provers/TAIKO.md
  • src/contracts/provers/arbitrum/ChildToParentProver.sol
  • src/contracts/provers/arbitrum/ParentToChildProver.sol
  • src/contracts/provers/linea/ChildToParentProver.sol
  • src/contracts/provers/linea/ParentToChildProver.sol
  • src/contracts/provers/optimism/ChildToParentProver.sol
  • src/contracts/provers/optimism/ParentToChildProver.sol
  • src/contracts/provers/scroll/ChildToParentProver.sol
  • src/contracts/provers/scroll/ParentToChildProver.sol
  • src/contracts/provers/taiko/ChildToParentProver.sol
  • src/contracts/provers/taiko/ParentToChildProver.sol
  • src/contracts/provers/zksync/ChildToParentProver.sol
  • src/contracts/provers/zksync/ParentToChildProver.sol

@luiz-lvj luiz-lvj merged commit 370b0e2 into main Feb 24, 2026
3 checks passed
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.

2 participants