diff --git a/.gitignore b/.gitignore index 56eeffd..0cf4bef 100644 --- a/.gitignore +++ b/.gitignore @@ -7,9 +7,6 @@ out/ /broadcast/*/31337/ /broadcast/**/dry-run/ -# Docs -docs/ - # Dotenv file .env diff --git a/.gitmodules b/.gitmodules index d641a64..8471ea6 100644 --- a/.gitmodules +++ b/.gitmodules @@ -13,3 +13,6 @@ [submodule "lib/linea-monorepo"] path = lib/linea-monorepo url = https://github.com/consensys/linea-monorepo +[submodule "lib/openzeppelin-contracts-upgradeable"] + path = lib/openzeppelin-contracts-upgradeable + url = https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable diff --git a/README.md b/README.md index 6c89933..db3575d 100644 --- a/README.md +++ b/README.md @@ -3,43 +3,43 @@ Ethereum reference implementation for [ERC-7888: Crosschain Broadcaster](https://eips.ethereum.org/EIPS/eip-7888). The standard defines a storage-proof based way to publish 32-byte messages on one chain and verify their existence on any other chain that shares a common ancestor. - **Broadcast**: `Broadcaster` stores `block.timestamp` in slot `keccak(message, publisher)`, emits `MessageBroadcast`, and prevents duplicates per publisher. -- **Prove**: `Receiver` walks a user-specified route of `BlockHashProver` contracts to recover a finalized target block hash, proves a storage slot on a remote `Broadcaster`, checks the slot is non-zero and matches the expected `(message, publisher)` slot, then returns the timestamp. -- **Upgrade safely**: `BlockHashProverPointer` holds the latest prover implementation address and code hash in a fixed slot (`BLOCK_HASH_PROVER_POINTER_SLOT`), enforcing monotonic `version()` upgrades so routes stay stable while provers evolve. -- **Reusable proving code**: `BlockHashProver` copies can be deployed on any chain; the pointer-stored code hash guarantees the copy matches the canonical implementation. +- **Prove**: `Receiver` walks a user-specified route of `StateProver` contracts to recover a finalized target block hash, proves a storage slot on a remote `Broadcaster`, checks the slot is non-zero and matches the expected `(message, publisher)` slot, then returns the timestamp. +- **Upgrade safely**: `StateProverPointer` holds the latest prover implementation address and code hash in a fixed slot (`STATE_PROVER_POINTER_SLOT`), enforcing monotonic `version()` upgrades so routes stay stable while provers evolve. +- **Reusable proving code**: `StateProver` copies can be deployed on any chain; the pointer-stored code hash guarantees the copy matches the canonical implementation. ## Contracts - `src/contracts/Broadcaster.sol`: Minimal broadcaster with deduplication and timestamp storage. - `src/contracts/Receiver.sol`: Verifies broadcast messages from remote chains using a route of block-hash provers and a final storage proof; can cache prover copies. -- `src/contracts/BlockHashProverPointer.sol`: Ownable pointer storing the current prover implementation address and code hash with version monotonicity checks. +- `src/contracts/StateProverPointer.sol`: Ownable pointer storing the current prover implementation address and code hash with version monotonicity checks. - `src/contracts/libraries/ProverUtils.sol`: Shared helpers for verifying block headers and MPT proofs (state root, account data, storage slot). -- Interfaces: `IBroadcaster`, `IReceiver`, `IBlockHashProver`, `IBlockHashProverPointer`. +- Interfaces: `IBroadcaster`, `IReceiver`, `IStateProver`, `IStateProverPointer`. ## Key concepts - **Broadcaster**: Singleton per chain that timestamps 32-byte messages in deterministic slots and emits `MessageBroadcast`. - **Receiver**: Trustlessly reads a remote `Broadcaster` slot by following a prover route, checking slot correctness, and returning `(broadcasterId, timestamp)`. -- **BlockHashProver**: Chain-specific verifier that proves a target block hash from a home chain state root and verifies arbitrary storage for that block. -- **BlockHashProverPointer**: Stable address that stores the prover implementation address and code hash in `BLOCK_HASH_PROVER_POINTER_SLOT`, enforcing increasing `version()`. -- **BlockHashProverCopy**: Locally deployed prover contract whose `codehash` matches the pointer; used by `Receiver` when proving multi-hop routes. +- **StateProver**: Chain-specific verifier that proves a target block hash from a home chain state root and verifies arbitrary storage for that block. +- **StateProverPointer**: Stable address that stores the prover implementation address and code hash in `STATE_PROVER_POINTER_SLOT`, enforcing increasing `version()`. +- **StateProverCopy**: Locally deployed prover contract whose `codehash` matches the pointer; used by `Receiver` when proving multi-hop routes. - **Route**: Ordered addresses of prover pointers from the destination back to the origin chain; hashed cumulatively in `Receiver` to produce unique IDs. ## Two-hop proof flow (L2 → L1 → L2) 1) Publisher broadcasts on L2-A → `Broadcaster` stores timestamp at `keccak(message, publisher)`. 2) On L2-B, caller gives `Receiver.verifyBroadcastMessage`: - Route: `[L2-A→L1 pointer, L1→L2-B pointer]` - - `bhpInputs[0]`: proof for L2-A block hash committed to L1 - - `bhpInputs[1]`: proof for that L1 block hash committed to L2-B + - `scpInputs[0]`: proof for L2-A block hash committed to L1 + - `scpInputs[1]`: proof for that L1 block hash committed to L2-B - `storageProof`: proof for the `(message, publisher)` slot on L2-A at the proven block hash -3) `Receiver` uses a local `BlockHashProverCopy` for hop 2 (code hash must match pointer), verifies each hop, checks the slot matches `keccak(message, publisher)`, and returns `(broadcasterId, timestamp)`. +3) `Receiver` uses a local `StateProverCopy` for hop 2 (code hash must match pointer), verifies each hop, checks the slot matches `keccak(message, publisher)`, and returns `(broadcasterId, timestamp)`. 4) Subscriber contracts compare `broadcasterId` against their allowlist and mark messages as consumed. ## How the protocol fits together 1) A publisher calls `Broadcaster.broadcastMessage(message)` on Chain A. The `(message, publisher)` slot now holds the timestamp. 2) To trustlessly read that message on Chain C, a caller provides `Receiver.verifyBroadcastMessage` with: - - `route`: addresses of the `BlockHashProverPointer` hop-by-hop path (e.g., child→L1 pointer, L1→dest pointer). - - `bhpInputs`: prover-specific inputs for each hop (built off-chain with the TS helpers). + - `route`: addresses of the `StateProverPointer` hop-by-hop path (e.g., child→L1 pointer, L1→dest pointer). + - `scpInputs`: prover-specific inputs for each hop (built off-chain with the TS helpers). - `storageProof`: a storage proof for the `Broadcaster` slot on the source chain at the proven block hash. 3) `Receiver` accumulates the route to derive unique IDs, ensures the proven slot matches `keccak(message, publisher)`, and returns `(broadcasterId, timestamp)`. -4) Before verifying, callers can seed `Receiver.updateBlockHashProverCopy` with a local prover copy whose code hash matches the pointer slot and whose `version()` increases. +4) Before verifying, callers can seed `Receiver.updateStateProverCopy` with a local prover copy whose code hash matches the pointer slot and whose `version()` increases. ## Repository layout - `src/contracts/` – Solidity contracts and interfaces. @@ -56,11 +56,11 @@ Ethereum reference implementation for [ERC-7888: Crosschain Broadcaster](https:/ ## Using the contracts (high level) - Deploy a `Broadcaster` on each chain where messages originate. -- Deploy a `BlockHashProverPointer` per chain pair direction; point it to the canonical `BlockHashProver` implementation (must expose `version()` and stable code hash). -- On destination chains, deploy `Receiver` and register local prover copies via `updateBlockHashProverCopy` once the pointer’s code hash is provably available. +- Deploy a `StateProverPointer` per chain pair direction; point it to the canonical `StateProver` implementation (must expose `version()` and stable code hash). +- On destination chains, deploy `Receiver` and register local prover copies via `updateStateProverCopy` once the pointer’s code hash is provably available. - Off-chain, use the TS helpers (or your own tooling) to: 1) Find a route (e.g., L2→L1→L2), - 2) Build `bhpInputs` per hop plus the final `storageProof`, + 2) Build `scpInputs` per hop plus the final `storageProof`, 3) Call `Receiver.verifyBroadcastMessage` with `(message, publisher)`; use the returned `broadcasterId` to authorize the source broadcaster. ## Links diff --git a/docs/ERC7888.md b/docs/ERC7888.md new file mode 100644 index 0000000..96d6860 --- /dev/null +++ b/docs/ERC7888.md @@ -0,0 +1,299 @@ +# ERC-7888: Crosschain Broadcaster + +**Reference:** [EIP-7888 Specification](https://eips.ethereum.org/EIPS/eip-7888) + +## Introduction + +ERC-7888 defines a protocol for trustless cross-chain message verification using cryptographic storage proofs. The protocol enables any chain to verify messages broadcast on any other chain that shares a common ancestor, creating a foundation for cross-chain interoperability without trusted intermediaries. + +### Core Components + +| Component | Description | +|-----------|-------------| +| **Broadcaster** | Stores messages in deterministic storage slots, one per (message, publisher) pair | +| **Receiver** | Verifies messages from remote chains using storage proofs | +| **StateProver** | Chain-specific logic for verifying state commitments and storage slots | +| **StateProverPointer** | Upgradeable pointer to the latest StateProver version | + +Each chain deploys singleton `Broadcaster` and `Receiver` contracts, enabling permissionless message broadcasting and verification across the ecosystem. + +### State Commitments + +A **state commitment** is a `bytes32` hash that commits to a chain's state at a particular point in time. The protocol supports different types of state commitments depending on what the rollup commits to its parent chain: + +- **Block hash** (recommended): A hash of the block header +- **State root**: The root of the state tree (e.g., Merkle-Patricia Trie) +- **Batch hash**: A hash of a batch of blocks (for rollups that commit batches) + +--- + +## Broadcasting Messages + +Publishers broadcast messages by calling `broadcastMessage()` on the Broadcaster contract: + +```solidity +interface IBroadcaster { + /// @notice Emitted when a message is broadcast. + event MessageBroadcast(bytes32 indexed message, address indexed publisher); + + /// @notice Broadcasts a message. Callers are called "publishers". + /// @dev MUST revert if the publisher has already broadcast the message. + /// MUST emit MessageBroadcast. + /// MUST store block.timestamp in slot keccak(message, msg.sender). + function broadcastMessage(bytes32 message) external; +} +``` + +### Storage Layout + +The Broadcaster stores `block.timestamp` in storage slot `keccak256(abi.encode(message, publisher))`, creating a unique slot per (message, publisher) pair. + +**Key constraints:** +- Each publisher can only broadcast a specific message **once** +- Applications requiring multiple broadcasts of the same logical message must implement nonce logic at the application layer + +--- + +## Message Verification + +### Routes + +A **route** is a relative path from a Receiver on a local chain to a remote chain. Routes are constructed from StateProverPointer addresses, where each pointer lives on its home chain and references a StateProver that can prove the next chain's state commitment. + +**Route validity rules:** +- Home chain of `route[0]` must equal the local chain +- Target chain of `route[i]` must equal home chain of `route[i+1]` + +### Remote Account Identifiers + +Accounts on remote chains are identified by accumulating the route addresses plus the remote address: + +```solidity +function accumulator(address[] memory elems) pure returns (bytes32 acc) { + for (uint256 i = 0; i < elems.length; i++) { + acc = keccak256(abi.encode(acc, elems[i])); + } +} +``` + +IDs depend on the route and are therefore always **relative** to a local chain. The same account on a given chain will have different IDs depending on the route taken. + +### Verification Process: `verifyBroadcastMessage` + +```solidity +interface IReceiver { + struct RemoteReadArgs { + address[] route; // StateProverPointer addresses along the route + bytes[] scpInputs; // Inputs for each StateProver + bytes proof; // Final storage proof for the message slot + } + + function verifyBroadcastMessage( + RemoteReadArgs calldata broadcasterReadArgs, + bytes32 message, + address publisher + ) external view returns (bytes32 broadcasterId, uint256 timestamp); +} +``` + +**Three-stage verification:** + +1. **Initial State Commitment Retrieval**: For the first hop, the Receiver calls the StateProverPointer at `route[0]` to get the implementation address, then calls `getTargetStateCommitment()`. Since this executes on the home chain, the prover directly accesses the target chain's state commitment. + +2. **State Commitment Chain Verification**: For subsequent hops, the Receiver uses locally stored StateProverCopies to call `verifyTargetStateCommitment()` with the previous chain's state commitment and a proof. This builds a chain: `stateCommitment[0] → stateCommitment[1] → ... → stateCommitment[n]`. + +3. **Storage Slot Verification**: With the target chain's state commitment established, the Receiver calls `verifyStorageSlot()` on the final StateProver, verifying: + - The slot matches `keccak256(abi.encode(message, publisher))` + - The slot value is non-zero (message was broadcast) + +The slot value (`block.timestamp`) is returned as the timestamp. + +--- + +## StateProvers + +A `StateProver` implements chain-specific verification logic. Each rollup stores parent/child state commitments differently, requiring custom prover implementations. + +```solidity +interface IStateProver { + /// @notice Get the state commitment of the target chain when called on the home chain. + /// @dev MUST revert if not called on the home chain. + function getTargetStateCommitment(bytes calldata input) + external view returns (bytes32 targetStateCommitment); + + /// @notice Verify the state commitment of the target chain from a remote chain. + /// @dev MUST revert if called on the home chain. + function verifyTargetStateCommitment(bytes32 homeStateCommitment, bytes calldata input) + external view returns (bytes32 targetStateCommitment); + + /// @notice Verify a storage slot given a target chain state commitment. + function verifyStorageSlot(bytes32 targetStateCommitment, bytes calldata input) + external view returns (address account, uint256 slot, bytes32 value); + + /// @notice The version of this prover implementation. + function version() external pure returns (uint256); +} +``` + +### Operating Modes + +| Mode | Function | Context | +|------|----------|---------| +| **Home chain** | `getTargetStateCommitment()` | Directly reads target chain's state commitment from storage | +| **Remote chain** | `verifyTargetStateCommitment()` | Verifies a proof establishing the target chain's state commitment | + +### Prover Requirements + +- **Code hash consistency**: StateProvers MUST ensure they have the same deployed code hash on all chains +- **Pure functions**: `verifyTargetStateCommitment()`, `verifyStorageSlot()`, and `version()` MUST be pure (with exception: MAY read `address(this).code`) +- **Chain-specific logic**: Each prover is fixed to a specific (home chain, target chain) pair + +--- + +## StateProverPointers + +Rollup storage layouts may change over time, requiring prover updates. However, routes reference provers by address, and changing addresses would break existing routes. `StateProverPointers` provide indirection to enable upgrades without breaking routes. + +```solidity +// Fixed storage slot for the code hash +uint256 constant STATE_PROVER_POINTER_SLOT = uint256(keccak256("eip7888.pointer.slot")) - 1; + +interface IStateProverPointer { + /// @notice Return the code hash of the latest version of the prover. + function implementationCodeHash() external view returns (bytes32); + + /// @notice Return the address of the latest version of the prover on the home chain. + function implementationAddress() external view returns (address); +} +``` + +### Upgrade Process + +1. Deploy a new `StateProver` with a higher `version()` number +2. Pointer owner calls `setImplementationAddress()` to point to the new prover +3. Pointer stores the new prover's code hash in `STATE_PROVER_POINTER_SLOT` +4. Receivers update local copies via `updateStateProverCopy()`, which verifies the code hash matches + +**Upgrade constraints:** +- The new StateProver MUST have the same home and target chains +- The new StateProver MUST have a higher `version()` than the previous one + +### Security Considerations for Pointer Ownership + +The StateProverPointer owner can DoS or forge messages. Therefore: + +| Target Chain | Expected Owner | +|--------------|----------------| +| Parent chain | Home chain owner | +| Child chain | Target chain owner | + +--- + +## StateProverCopies + +Receivers cannot call contracts on remote chains directly. To verify proofs from remote chains, Receivers maintain local copies of StateProvers with matching code. + +### Copy Registration + +```solidity +function updateStateProverCopy( + RemoteReadArgs calldata scpPointerReadArgs, + IStateProver scpCopy +) external returns (bytes32 scpPointerId); +``` + +The Receiver verifies: +1. The proof reads from `STATE_PROVER_POINTER_SLOT` +2. The copy's code hash matches the Pointer's stored code hash +3. The copy's version is higher than any existing copy (if updating) + +Copies are stored keyed by `scpPointerId`, calculated by accumulating hashes of addresses in the route to the Pointer. + +--- + +## Complete Interface Reference + +### IBroadcaster + +```solidity +interface IBroadcaster { + event MessageBroadcast(bytes32 indexed message, address indexed publisher); + + function broadcastMessage(bytes32 message) external; +} +``` + +### IReceiver + +```solidity +interface IReceiver { + struct RemoteReadArgs { + address[] route; + bytes[] scpInputs; + bytes proof; + } + + function verifyBroadcastMessage( + RemoteReadArgs calldata broadcasterReadArgs, + bytes32 message, + address publisher + ) external view returns (bytes32 broadcasterId, uint256 timestamp); + + function updateStateProverCopy( + RemoteReadArgs calldata scpPointerReadArgs, + IStateProver scpCopy + ) external returns (bytes32 scpPointerId); + + function stateProverCopy(bytes32 scpPointerId) + external view returns (IStateProver scpCopy); +} +``` + +### IStateProver + +```solidity +interface IStateProver { + function verifyTargetStateCommitment(bytes32 homeStateCommitment, bytes calldata input) + external view returns (bytes32 targetStateCommitment); + + function getTargetStateCommitment(bytes calldata input) + external view returns (bytes32 targetStateCommitment); + + function verifyStorageSlot(bytes32 targetStateCommitment, bytes calldata input) + external view returns (address account, uint256 slot, bytes32 value); + + function version() external pure returns (uint256); +} +``` + +### IStateProverPointer + +```solidity +interface IStateProverPointer { + function implementationCodeHash() external view returns (bytes32); + function implementationAddress() external view returns (address); +} +``` + +--- + +## Security Considerations + +### Chain Upgrades + +If a chain upgrades such that a StateProver's verification functions might return data besides a finalized target state commitment, invalid messages could be read. Either: +- The StateProver should detect such changes +- The chain owner responsible for storage layout changes should also own the StateProverPointer + +### Message Guarantees + +This protocol ensures messages **CAN** be read, not that they **WILL** be read. It is the Receiver caller's responsibility to choose which messages to read. + +Since the protocol only uses finalized blocks, messages may take time to propagate. Finalization occurs sequentially in the route, so total propagation time equals the sum of finalization times at each step. + +--- + +## Links + +- **EIP Specification**: [EIP-7888](https://eips.ethereum.org/EIPS/eip-7888) +- **Discussion**: [Ethereum Magicians Forum](https://ethereum-magicians.org/t/new-erc-cross-chain-broadcaster/22927) diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..43ecf1a --- /dev/null +++ b/docs/README.md @@ -0,0 +1,56 @@ +# Documentation + +## ERC-7888 Crosschain Broadcaster + +This repository implements [ERC-7888](https://eips.ethereum.org/EIPS/eip-7888), a protocol for trustless cross-chain message verification using cryptographic storage proofs. + +### Documentation Index + +| Document | Description | +|----------|-------------| +| [ERC7888.md](ERC7888.md) | Complete specification overview, interfaces, and protocol mechanics | +| [PROVERS.md](PROVERS.md) | Chain-specific StateProver implementations and how to add new ones | +| [TUTORIAL.md](TUTORIAL.md) | Step-by-step guide to broadcasting and verifying messages | + +### Quick Links + +- **Specification**: [EIP-7888](https://eips.ethereum.org/EIPS/eip-7888) +- **Discussion**: [Ethereum Magicians](https://ethereum-magicians.org/t/new-erc-cross-chain-broadcaster/22927) + +### Getting Started + +1. Read [ERC7888.md](ERC7888.md) for protocol fundamentals +2. Follow [TUTORIAL.md](TUTORIAL.md) to implement your first cross-chain message +3. Reference [PROVERS.md](PROVERS.md) for chain-specific details + +### Supported Chains + +| Chain | ChildToParent | ParentToChild | +|-------|---------------|---------------| +| Arbitrum | ✅ | ✅ | +| Optimism | ✅ | ✅ | +| Linea | ✅ | ✅ | +| Scroll | ✅ | ✅ | +| zkSync Era | ✅ | ✅ | +| Taiko | ✅ | ✅ | + +### Core Contracts + +``` +src/contracts/ +├── Broadcaster.sol # Message broadcasting +├── Receiver.sol # Message verification +├── StateProverPointer.sol # Upgradeable prover reference +├── interfaces/ +│ ├── IBroadcaster.sol +│ ├── IReceiver.sol +│ ├── IStateProver.sol +│ └── IStateProverPointer.sol +└── provers/ + ├── arbitrum/ + ├── optimism/ + ├── linea/ + ├── scroll/ + ├── zksync/ + └── taiko/ +``` diff --git a/foundry.lock b/foundry.lock index 5870cf2..bfc57e4 100644 --- a/foundry.lock +++ b/foundry.lock @@ -5,9 +5,18 @@ "lib/forge-std": { "rev": "8bbcf6e3f8f62f419e5429a0bd89331c85c37824" }, + "lib/linea-monorepo": { + "rev": "3e62ae1fff556279530378f11047120974e9c32a" + }, "lib/openzeppelin-contracts": { "rev": "5eb047a0c10386b6634db56327a1f74e8a0432c2" }, + "lib/openzeppelin-contracts-upgradeable": { + "tag": { + "name": "v5.5.0", + "rev": "aa677e9d28ed78fc427ec47ba2baef2030c58e7c" + } + }, "lib/scroll-contracts": { "tag": { "name": "v4.0.0", diff --git a/lib/openzeppelin-contracts b/lib/openzeppelin-contracts index 5eb047a..68e4095 160000 --- a/lib/openzeppelin-contracts +++ b/lib/openzeppelin-contracts @@ -1 +1 @@ -Subproject commit 5eb047a0c10386b6634db56327a1f74e8a0432c2 +Subproject commit 68e4095c1de9853ae264852178c4466a3046323e diff --git a/lib/openzeppelin-contracts-upgradeable b/lib/openzeppelin-contracts-upgradeable new file mode 160000 index 0000000..aa677e9 --- /dev/null +++ b/lib/openzeppelin-contracts-upgradeable @@ -0,0 +1 @@ +Subproject commit aa677e9d28ed78fc427ec47ba2baef2030c58e7c diff --git a/remappings.txt b/remappings.txt index e3b73a6..7c17139 100644 --- a/remappings.txt +++ b/remappings.txt @@ -1,5 +1,6 @@ forge-std/=lib/forge-std/src/ -@openzeppelin/=lib/openzeppelin-contracts/ +@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/ +@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/ @eth-optimism/=node_modules/@eth-optimism/ @arbitrum/nitro-contracts/=node_modules/@arbitrum/nitro-contracts block-hash-pusher/=lib/block-hash-pusher/ diff --git a/scripts/generate-optimism-test-payloads.ts b/scripts/generate-optimism-test-payloads.ts index 22f5a96..f94cc25 100644 --- a/scripts/generate-optimism-test-payloads.ts +++ b/scripts/generate-optimism-test-payloads.ts @@ -5,8 +5,8 @@ * Run with: npx hardhat run scripts/generate-optimism-test-payloads.ts * * Generates: - * - calldata_get.hex: For getTargetBlockHash() test - * - calldata_verify_target.hex: For verifyTargetBlockHash() test + * - calldata_get.hex: For getTargetStateCommitment() test + * - calldata_verify_target.hex: For verifyTargetStateCommitment() test * - calldata_verify_slot.hex: For verifyStorageSlot() test */ @@ -52,17 +52,17 @@ async function main() { // 1. Generate calldata_get.hex console.log('1️⃣ Generating calldata_get.hex...') try { - const { input, targetBlockHash } = await helper.buildInputForGetTargetBlockHash() + const { input, targetStateCommitment } = await helper.buildInputForGetTargetBlockHash() - // Format: [input (32 bytes), targetBlockHash (32 bytes)] + // Format: [input (32 bytes), targetStateCommitment (32 bytes)] const payload = encodeAbiParameters( [{ type: 'bytes32' }, { type: 'bytes32' }], - [input.padEnd(66, '0') as Hash, targetBlockHash] + [input.padEnd(66, '0') as Hash, targetStateCommitment] ) fs.writeFileSync(path.join(outputDir, 'calldata_get.hex'), payload) console.log(` ✅ Input: ${input}`) - console.log(` ✅ Target block hash: ${targetBlockHash}\n`) + console.log(` ✅ Target block hash: ${targetStateCommitment}\n`) } catch (error) { console.error(` ❌ Error: ${error}\n`) } @@ -70,17 +70,17 @@ async function main() { // 2. Generate calldata_verify_target.hex console.log('2️⃣ Generating calldata_verify_target.hex...') try { - const { input, targetBlockHash } = await helper.buildInputForVerifyTargetBlockHash(optimismBlockHash) + const { input, targetStateCommitment } = await helper.buildInputForVerifyTargetBlockHash(optimismBlockHash) - // Format: [homeBlockHash (32 bytes), targetBlockHash (32 bytes), input (variable)] + // Format: [homeBlockHash (32 bytes), targetStateCommitment (32 bytes), input (variable)] const payload = encodeAbiParameters( [{ type: 'bytes32' }, { type: 'bytes32' }, { type: 'bytes' }], - [optimismBlockHash, targetBlockHash, input] + [optimismBlockHash, targetStateCommitment, input] ) fs.writeFileSync(path.join(outputDir, 'calldata_verify_target.hex'), payload) console.log(` ✅ Home block hash: ${optimismBlockHash}`) - console.log(` ✅ Target block hash: ${targetBlockHash}`) + console.log(` ✅ Target block hash: ${targetStateCommitment}`) console.log(` ✅ Input length: ${input.length / 2 - 1} bytes\n`) } catch (error) { console.error(` ❌ Error: ${error}\n`) @@ -95,22 +95,22 @@ async function main() { // Use a RECENT L1 block instead of the one from L1Block (which might be too old for Infura) const recentL1Block = await sepoliaClient.getBlock({ blockTag: 'latest' }) - const targetBlockHash = recentL1Block.hash + const targetStateCommitment = recentL1Block.hash const { input, slotValue } = await helper.buildInputForVerifyStorageSlot( - targetBlockHash, + targetStateCommitment, knownAccount, knownSlot ) - // Format: [targetBlockHash (32 bytes), slotValue (32 bytes), input (variable)] + // Format: [targetStateCommitment (32 bytes), slotValue (32 bytes), input (variable)] const payload = encodeAbiParameters( [{ type: 'bytes32' }, { type: 'bytes32' }, { type: 'bytes' }], - [targetBlockHash, slotValue, input] + [targetStateCommitment, slotValue, input] ) fs.writeFileSync(path.join(outputDir, 'calldata_verify_slot.hex'), payload) - console.log(` ✅ Target block hash: ${targetBlockHash}`) + console.log(` ✅ Target block hash: ${targetStateCommitment}`) console.log(` ✅ Account: ${knownAccount}`) console.log(` ✅ Slot: ${knownSlot}`) console.log(` ✅ Slot value: ${slotValue}`) diff --git a/scripts/taiko/README.txt b/scripts/taiko/README.txt index b9bc28a..8a85ffa 100644 --- a/scripts/taiko/README.txt +++ b/scripts/taiko/README.txt @@ -55,7 +55,7 @@ DEPLOYMENT ./scripts/taiko/deploy.sh --force # Force re-deployment Deploys and verifies all contracts on L1 and L2: -- Broadcaster, Receiver, BlockHashProverPointer +- Broadcaster, Receiver, StateProverPointer - ParentToChildProver (L1) - reads L2 state from L1 - ChildToParentProver (L2) - reads L1 state from L2 @@ -258,7 +258,7 @@ ARCHITECTURE L1 (Ethereum/Taiko Parent - Chain ID: 32382) ├── Broadcaster (stores messages) ├── Receiver (verifies remote messages) -├── BlockHashProverPointer +├── StateProverPointer ├── ParentToChildProver (reads L2 blocks via SignalService) └── SignalService (0x53789e39E3310737E8C8cED483032AAc25B39ded) └── Stores L2 block checkpoints @@ -266,7 +266,7 @@ L1 (Ethereum/Taiko Parent - Chain ID: 32382) L2 (Taiko Child Chain - Chain ID: 167001) ├── Broadcaster (stores messages) ├── Receiver (verifies remote messages) -├── BlockHashProverPointer +├── StateProverPointer ├── ChildToParentProver (reads L1 blocks via SignalService) └── SignalService (0x1670010000000000000000000000000000000005) └── Stores L1 block checkpoints @@ -306,14 +306,14 @@ DEPLOYED CONTRACTS L1 (Taiko Parent Chain - 32382): - Broadcaster: $L1_BROADCASTER - Receiver: $L1_RECEIVER -- BlockHashProverPointer: $L1_POINTER +- StateProverPointer: $L1_POINTER - ParentToChildProver: $L1_PARENT_TO_CHILD_PROVER - ProverPointer: $L1_PROVER_POINTER L2 (Taiko Child Chain - 167001): - Broadcaster: $L2_BROADCASTER - Receiver: $L2_RECEIVER -- BlockHashProverPointer: $L2_POINTER +- StateProverPointer: $L2_POINTER - ChildToParentProver: $L2_CHILD_TO_PARENT_PROVER - ProverPointer: $L2_PROVER_POINTER @@ -359,6 +359,6 @@ IMPORTANT NOTES - storage-proof-generator uses debug_getRawHeader for Taiko - ParentToChildProver deployed on L1, reads L2 - ChildToParentProver deployed on L2, reads L1 -- Both use getTargetBlockHash in tests (direct SignalService read) -- Production would use verifyTargetBlockHash (with proofs) +- Both use getTargetStateCommitment in tests (direct SignalService read) +- Production would use verifyTargetStateCommitment (with proofs) diff --git a/scripts/taiko/deploy-all.s.sol b/scripts/taiko/deploy-all.s.sol index 6df8ee2..a6d9725 100644 --- a/scripts/taiko/deploy-all.s.sol +++ b/scripts/taiko/deploy-all.s.sol @@ -5,7 +5,7 @@ import { Script } from "forge-std/Script.sol"; import { console } from "forge-std/console.sol"; import { Broadcaster } from "../../src/contracts/Broadcaster.sol"; import { Receiver } from "../../src/contracts/Receiver.sol"; -import { BlockHashProverPointer } from "../../src/contracts/BlockHashProverPointer.sol"; +import { StateProverPointer } from "../../src/contracts/StateProverPointer.sol"; contract DeployAll is Script { function run() public { @@ -15,13 +15,13 @@ contract DeployAll is Script { Broadcaster broadcaster = new Broadcaster(); Receiver receiver = new Receiver(); - BlockHashProverPointer pointer = new BlockHashProverPointer(owner); + StateProverPointer pointer = new StateProverPointer(owner); vm.stopBroadcast(); console.log("Broadcaster:", address(broadcaster)); console.log("Receiver:", address(receiver)); - console.log("BlockHashProverPointer:", address(pointer)); + console.log("StateProverPointer:", address(pointer)); } } diff --git a/scripts/taiko/deploy.sh b/scripts/taiko/deploy.sh index 3be1565..04bf4e3 100755 --- a/scripts/taiko/deploy.sh +++ b/scripts/taiko/deploy.sh @@ -69,7 +69,7 @@ fi if [ "$SKIP_L1" = false ]; then L1_BROADCASTER=$(echo "$L1_OUTPUT" | grep -o "Broadcaster: 0x[a-fA-F0-9]*" | cut -d' ' -f2) L1_RECEIVER=$(echo "$L1_OUTPUT" | grep -o "Receiver: 0x[a-fA-F0-9]*" | cut -d' ' -f2) - L1_POINTER=$(echo "$L1_OUTPUT" | grep -o "BlockHashProverPointer: 0x[a-fA-F0-9]*" | cut -d' ' -f2) + L1_POINTER=$(echo "$L1_OUTPUT" | grep -o "StateProverPointer: 0x[a-fA-F0-9]*" | cut -d' ' -f2) fi if [ "$SKIP_L2" = false ]; then @@ -81,7 +81,7 @@ if [ "$SKIP_L2" = false ]; then L2_BROADCASTER=$(echo "$L2_OUTPUT" | grep -o "Broadcaster: 0x[a-fA-F0-9]*" | cut -d' ' -f2) L2_RECEIVER=$(echo "$L2_OUTPUT" | grep -o "Receiver: 0x[a-fA-F0-9]*" | cut -d' ' -f2) - L2_POINTER=$(echo "$L2_OUTPUT" | grep -o "BlockHashProverPointer: 0x[a-fA-F0-9]*" | cut -d' ' -f2) + L2_POINTER=$(echo "$L2_OUTPUT" | grep -o "StateProverPointer: 0x[a-fA-F0-9]*" | cut -d' ' -f2) else echo "Skipping L2 deployment (already deployed)" fi @@ -134,7 +134,7 @@ if [ "$SKIP_L1" = false ] || [ "$SKIP_L1_PROVER" = false ]; then forge verify-contract --rpc-url "$L1_RPC" --verifier blockscout --verifier-url "$L1_VERIFIER_URL" \ --constructor-args $(cast abi-encode "constructor(address)" "$TAIKO_DEPLOYER_ADDRESS") \ - "$L1_POINTER" src/contracts/BlockHashProverPointer.sol:BlockHashProverPointer || true + "$L1_POINTER" src/contracts/StateProverPointer.sol:StateProverPointer || true fi if [ "$SKIP_L1_PROVER" = false ]; then @@ -144,7 +144,7 @@ if [ "$SKIP_L1" = false ] || [ "$SKIP_L1_PROVER" = false ]; then forge verify-contract --rpc-url "$L1_RPC" --verifier blockscout --verifier-url "$L1_VERIFIER_URL" \ --constructor-args $(cast abi-encode "constructor(address)" "$TAIKO_DEPLOYER_ADDRESS") \ - "$L1_PROVER_POINTER" src/contracts/BlockHashProverPointer.sol:BlockHashProverPointer || true + "$L1_PROVER_POINTER" src/contracts/StateProverPointer.sol:StateProverPointer || true fi fi @@ -160,7 +160,7 @@ if [ "$SKIP_L2" = false ] || [ "$SKIP_L2_PROVER" = false ]; then forge verify-contract --rpc-url "$L2_RPC" --verifier blockscout --verifier-url "$L2_VERIFIER_URL" \ --constructor-args $(cast abi-encode "constructor(address)" "$TAIKO_DEPLOYER_ADDRESS") \ - "$L2_POINTER" src/contracts/BlockHashProverPointer.sol:BlockHashProverPointer || true + "$L2_POINTER" src/contracts/StateProverPointer.sol:StateProverPointer || true fi if [ "$SKIP_L2_PROVER" = false ]; then @@ -170,7 +170,7 @@ if [ "$SKIP_L2" = false ] || [ "$SKIP_L2_PROVER" = false ]; then forge verify-contract --rpc-url "$L2_RPC" --verifier blockscout --verifier-url "$L2_VERIFIER_URL" \ --constructor-args $(cast abi-encode "constructor(address)" "$TAIKO_DEPLOYER_ADDRESS") \ - "$L2_PROVER_POINTER" src/contracts/BlockHashProverPointer.sol:BlockHashProverPointer || true + "$L2_PROVER_POINTER" src/contracts/StateProverPointer.sol:StateProverPointer || true fi fi @@ -195,14 +195,14 @@ echo "" echo "L1 (Taiko Parent Chain - 32382):" echo " Broadcaster: $L1_BROADCASTER" echo " Receiver: $L1_RECEIVER" -echo " BlockHashProverPointer: $L1_POINTER" +echo " StateProverPointer: $L1_POINTER" echo " ParentToChildProver: $L1_PARENT_TO_CHILD_PROVER" echo " ProverPointer: $L1_PROVER_POINTER" echo "" echo "L2 (Taiko Child Chain - 167001):" echo " Broadcaster: $L2_BROADCASTER" echo " Receiver: $L2_RECEIVER" -echo " BlockHashProverPointer: $L2_POINTER" +echo " StateProverPointer: $L2_POINTER" echo " ChildToParentProver: $L2_CHILD_TO_PARENT_PROVER" echo " ProverPointer: $L2_PROVER_POINTER" echo "" diff --git a/scripts/taiko/provers-l2.s.sol b/scripts/taiko/provers-l2.s.sol index ea77120..d3d54a2 100644 --- a/scripts/taiko/provers-l2.s.sol +++ b/scripts/taiko/provers-l2.s.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.27; import { Script } from "forge-std/Script.sol"; import { console } from "forge-std/console.sol"; import { ChildToParentProver as TaikoChildToParentProver } from "../../src/contracts/provers/taiko/ChildToParentProver.sol"; -import { BlockHashProverPointer } from "../../src/contracts/BlockHashProverPointer.sol"; +import { StateProverPointer } from "../../src/contracts/StateProverPointer.sol"; /// @notice Deploy ChildToParentProver on L2 (Taiko Child Chain) /// @dev This script deploys the prover that allows reading L1 state from L2 @@ -24,13 +24,13 @@ contract DeployL2Prover is Script { homeChainId ); - BlockHashProverPointer blockHashProverPointer = new BlockHashProverPointer(owner); - blockHashProverPointer.setImplementationAddress(address(childToParentProver)); + StateProverPointer stateProverPointer = new StateProverPointer(owner); + stateProverPointer.setImplementationAddress(address(childToParentProver)); vm.stopBroadcast(); console.log("ChildToParentProver:", address(childToParentProver)); - console.log("L2ProverPointer:", address(blockHashProverPointer)); + console.log("L2ProverPointer:", address(stateProverPointer)); } } diff --git a/scripts/taiko/provers.s.sol b/scripts/taiko/provers.s.sol index 358493c..48b8aa3 100644 --- a/scripts/taiko/provers.s.sol +++ b/scripts/taiko/provers.s.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.27; import { Script } from "forge-std/Script.sol"; import { console } from "forge-std/console.sol"; import { ParentToChildProver as TaikoParentToChildProver } from "../../src/contracts/provers/taiko/ParentToChildProver.sol"; -import { BlockHashProverPointer } from "../../src/contracts/BlockHashProverPointer.sol"; +import { StateProverPointer } from "../../src/contracts/StateProverPointer.sol"; /// @notice Deploy ParentToChildProver on L1 (Ethereum/Taiko Parent Chain) /// @dev This script deploys the prover that allows reading L2 state from L1 @@ -24,12 +24,12 @@ contract DeployL1Prover is Script { homeChainId ); - BlockHashProverPointer blockHashProverPointer = new BlockHashProverPointer(owner); - blockHashProverPointer.setImplementationAddress(address(parentToChildProver)); + StateProverPointer stateProverPointer = new StateProverPointer(owner); + stateProverPointer.setImplementationAddress(address(parentToChildProver)); vm.stopBroadcast(); console.log("ParentToChildProver:", address(parentToChildProver)); - console.log("L1ProverPointer:", address(blockHashProverPointer)); + console.log("L1ProverPointer:", address(stateProverPointer)); } } \ No newline at end of file diff --git a/scripts/taiko/verify-message.s.sol b/scripts/taiko/verify-message.s.sol index 51c1bfa..4fe84af 100644 --- a/scripts/taiko/verify-message.s.sol +++ b/scripts/taiko/verify-message.s.sol @@ -29,13 +29,13 @@ contract VerifyMessage is Script { address[] memory route = new address[](1); route[0] = proverPointerAddress; - bytes[] memory bhpInputs = new bytes[](1); - bhpInputs[0] = abi.encode(uint48(blockNumber)); + bytes[] memory scpInputs = new bytes[](1); + scpInputs[0] = abi.encode(uint48(blockNumber)); IReceiver.RemoteReadArgs memory remoteReadArgs = IReceiver.RemoteReadArgs({ route: route, - bhpInputs: bhpInputs, - storageProof: storageProofInput + scpInputs: scpInputs, + proof: storageProofInput }); bytes32 message = 0xd9222d7d84eefb8570069f30ab4a850423ba57a374c593b67a224c430f9736df; diff --git a/scripts/taiko/verify-on-chain.s.sol b/scripts/taiko/verify-on-chain.s.sol index 268ba29..1f26da4 100644 --- a/scripts/taiko/verify-on-chain.s.sol +++ b/scripts/taiko/verify-on-chain.s.sol @@ -57,13 +57,13 @@ contract VerifyOnChain is Script { address[] memory route = new address[](1); route[0] = getL2ProverPointer(); - bytes[] memory bhpInputs = new bytes[](1); - bhpInputs[0] = abi.encode(uint48(blockNumber)); + bytes[] memory scpInputs = new bytes[](1); + scpInputs[0] = abi.encode(uint48(blockNumber)); IReceiver.RemoteReadArgs memory remoteReadArgs = IReceiver.RemoteReadArgs({ route: route, - bhpInputs: bhpInputs, - storageProof: storageProofInput + scpInputs: scpInputs, + proof: storageProofInput }); // Call the deployed Receiver contract @@ -109,13 +109,13 @@ contract VerifyOnChain is Script { address[] memory route = new address[](1); route[0] = getL1ProverPointer(); - bytes[] memory bhpInputs = new bytes[](1); - bhpInputs[0] = abi.encode(uint48(blockNumber)); + bytes[] memory scpInputs = new bytes[](1); + scpInputs[0] = abi.encode(uint48(blockNumber)); IReceiver.RemoteReadArgs memory remoteReadArgs = IReceiver.RemoteReadArgs({ route: route, - bhpInputs: bhpInputs, - storageProof: storageProofInput + scpInputs: scpInputs, + proof: storageProofInput }); // Call the deployed Receiver contract diff --git a/snapshots/verifyBroadcastMessage.json b/snapshots/verifyBroadcastMessage.json index a5222a6..3d9d274 100644 --- a/snapshots/verifyBroadcastMessage.json +++ b/snapshots/verifyBroadcastMessage.json @@ -1,9 +1,9 @@ { - "EthereumToOptimism": "1642626", - "EthereumToTaikoL2": "1055645", - "LineaL2ToEthereum": "2551772", - "ScrollL2ToEthereum": "1363073", + "EthereumToOptimism": "1642608", + "EthereumToTaikoL2": "1055627", + "LineaL2ToEthereum": "2551776", + "ScrollL2ToEthereum": "1363077", "ScrollToOptimism": "1349825", - "TaikoL2ToEthereum": "1025708", - "ZkSyncL2ToEthereum": "125497" + "TaikoL2ToEthereum": "1025690", + "ZkSyncL2ToEthereum": "125479" } \ No newline at end of file diff --git a/src/contracts/Receiver.sol b/src/contracts/Receiver.sol index 3f5af19..b24d5e6 100644 --- a/src/contracts/Receiver.sol +++ b/src/contracts/Receiver.sol @@ -2,38 +2,38 @@ pragma solidity 0.8.30; import {IReceiver} from "./interfaces/IReceiver.sol"; -import {IBlockHashProver} from "./interfaces/IBlockHashProver.sol"; -import {IBlockHashProverPointer} from "./interfaces/IBlockHashProverPointer.sol"; -import {BLOCK_HASH_PROVER_POINTER_SLOT} from "./BlockHashProverPointer.sol"; +import {IStateProver} from "./interfaces/IStateProver.sol"; +import {IStateProverPointer} from "./interfaces/IStateProverPointer.sol"; +import {STATE_PROVER_POINTER_SLOT} from "./StateProverPointer.sol"; /// @title Receiver /// @notice Verifies broadcast messages from remote chains using cryptographic storage proofs /// @dev This contract enables cross-chain message verification by: -/// 1. Maintaining local copies of BlockHashProver contracts for different chains +/// 1. Maintaining local copies of StateProver contracts for different chains /// 2. Using these provers to verify storage proofs from remote Broadcaster contracts /// 3. Following a proof route that can span multiple chain hops /// The verification process ensures that a message was actually broadcast on a remote chain /// at a specific timestamp without requiring trust in intermediaries. contract Receiver is IReceiver { - mapping(bytes32 blockHashProverPointerId => IBlockHashProver blockHashProverCopy) private _blockHashProverCopies; + mapping(bytes32 stateProverPointerId => IStateProver stateProverCopy) private _stateProverCopies; error InvalidRouteLength(); error EmptyRoute(); error ProverCopyNotFound(); error MessageNotFound(); error WrongMessageSlot(); - error WrongBlockHashProverPointerSlot(); + error WrongStateProverPointerSlot(); error DifferentCodeHash(); error NewerProverVersion(); /// @notice Verifies that a message was broadcast on a remote chain - /// @dev This function uses a chain of BlockHashProvers to verify a storage proof that demonstrates + /// @dev This function uses a chain of StateProvers to verify a storage proof that demonstrates /// a message was broadcast. The verification route can span multiple chains, with each hop /// proving the next chain's state. The function verifies: /// 1. The storage slot matches the expected slot for (message, publisher) /// 2. The slot value is non-zero (message was broadcast) /// 3. The entire proof chain is valid - /// @param broadcasterReadArgs Contains the route (chain of addresses), BlockHashProver inputs for each hop, + /// @param broadcasterReadArgs Contains the route (chain of addresses), StateProver inputs for each hop, /// and the final storage proof for the broadcaster contract /// @param message The 32-byte message that was allegedly broadcast /// @param publisher The address that allegedly broadcast the message @@ -63,51 +63,51 @@ contract Receiver is IReceiver { timestamp = uint256(slotValue); } - /// @notice Updates the local copy of a BlockHashProver for a specific remote chain - /// @dev This function verifies and stores a local copy of a BlockHashProver contract from a remote chain. + /// @notice Updates the local copy of a StateProver for a specific remote chain + /// @dev This function verifies and stores a local copy of a StateProver contract from a remote chain. /// The verification process ensures: - /// 1. The provided proof reads from the correct storage slot (BLOCK_HASH_PROVER_POINTER_SLOT) + /// 1. The provided proof reads from the correct storage slot (STATE_PROVER_POINTER_SLOT) /// 2. The code hash of the local copy matches the code hash stored in the remote pointer /// 3. The new version is newer than any existing local copy (version monotonicity) - /// This allows the Receiver to trustlessly obtain and update BlockHashProver implementations + /// This allows the Receiver to trustlessly obtain and update StateProver implementations /// needed for cross-chain message verification. - /// @param bhpPointerReadArgs Contains the route and proofs to read the remote BlockHashProverPointer's storage - /// @param bhpCopy The local deployed copy of the BlockHashProver contract - /// @return bhpPointerId A unique identifier for the remote BlockHashProverPointer (accumulated hash of route) - /// @custom:throws WrongBlockHashProverPointerSlot if the proof doesn't read from the expected slot + /// @param scpPointerReadArgs Contains the route and proofs to read the remote StateProverPointer's storage + /// @param scpCopy The local deployed copy of the StateProver contract + /// @return scpPointerId A unique identifier for the remote StateProverPointer (accumulated hash of route) + /// @custom:throws WrongStateProverPointerSlot if the proof doesn't read from the expected slot /// @custom:throws DifferentCodeHash if the local copy's code hash doesn't match the remote pointer's stored hash /// @custom:throws NewerProverVersion if an existing local copy has a version >= the new copy's version - function updateBlockHashProverCopy(RemoteReadArgs calldata bhpPointerReadArgs, IBlockHashProver bhpCopy) + function updateStateProverCopy(RemoteReadArgs calldata scpPointerReadArgs, IStateProver scpCopy) external - returns (bytes32 bhpPointerId) + returns (bytes32 scpPointerId) { uint256 slot; - bytes32 bhpCodeHash; - (bhpPointerId, slot, bhpCodeHash) = _readRemoteSlot(bhpPointerReadArgs); + bytes32 scpCodeHash; + (scpPointerId, slot, scpCodeHash) = _readRemoteSlot(scpPointerReadArgs); - if (slot != uint256(BLOCK_HASH_PROVER_POINTER_SLOT)) { - revert WrongBlockHashProverPointerSlot(); + if (slot != uint256(STATE_PROVER_POINTER_SLOT)) { + revert WrongStateProverPointerSlot(); } - if (address(bhpCopy).codehash != bhpCodeHash) { + if (address(scpCopy).codehash != scpCodeHash) { revert DifferentCodeHash(); } - IBlockHashProver oldProverCopy = _blockHashProverCopies[bhpPointerId]; + IStateProver oldProverCopy = _stateProverCopies[scpPointerId]; - if (address(oldProverCopy) != address(0) && oldProverCopy.version() >= bhpCopy.version()) { + if (address(oldProverCopy) != address(0) && oldProverCopy.version() >= scpCopy.version()) { revert NewerProverVersion(); } - _blockHashProverCopies[bhpPointerId] = bhpCopy; + _stateProverCopies[scpPointerId] = scpCopy; } - /// @notice The BlockHashProverCopy on the local chain corresponding to the bhpPointerId - /// MUST return 0 if the BlockHashProverPointer does not exist. - /// @param bhpPointerId The unique identifier of the BlockHashProverPointer. - /// @return bhpCopy The BlockHashProver copy stored on the local chain, or address(0) if not found. - function blockHashProverCopy(bytes32 bhpPointerId) external view returns (IBlockHashProver bhpCopy) { - bhpCopy = _blockHashProverCopies[bhpPointerId]; + /// @notice The StateProverCopy on the local chain corresponding to the scpPointerId + /// MUST return 0 if the StateProverPointer does not exist. + /// @param scpPointerId The unique identifier of the StateProverPointer. + /// @return scpCopy The StateProver copy stored on the local chain, or address(0) if not found. + function stateProverCopy(bytes32 scpPointerId) external view returns (IStateProver scpCopy) { + scpCopy = _stateProverCopies[scpPointerId]; } function _readRemoteSlot(RemoteReadArgs calldata readArgs) @@ -115,7 +115,7 @@ contract Receiver is IReceiver { view returns (bytes32 remoteAccountId, uint256 slot, bytes32 slotValue) { - if (readArgs.route.length != readArgs.bhpInputs.length) { + if (readArgs.route.length != readArgs.scpInputs.length) { revert InvalidRouteLength(); } @@ -123,28 +123,28 @@ contract Receiver is IReceiver { revert EmptyRoute(); } - IBlockHashProver prover; - bytes32 blockHash; + IStateProver prover; + bytes32 stateCommitment; for (uint256 i = 0; i < readArgs.route.length; i++) { remoteAccountId = accumulator(remoteAccountId, readArgs.route[i]); if (i == 0) { - prover = IBlockHashProver(IBlockHashProverPointer(readArgs.route[0]).implementationAddress()); - blockHash = prover.getTargetBlockHash(readArgs.bhpInputs[0]); + prover = IStateProver(IStateProverPointer(readArgs.route[0]).implementationAddress()); + stateCommitment = prover.getTargetStateCommitment(readArgs.scpInputs[0]); } else { - prover = _blockHashProverCopies[remoteAccountId]; + prover = _stateProverCopies[remoteAccountId]; if (address(prover) == address(0)) { revert ProverCopyNotFound(); } - blockHash = prover.verifyTargetBlockHash(blockHash, readArgs.bhpInputs[i]); + stateCommitment = prover.verifyTargetStateCommitment(stateCommitment, readArgs.scpInputs[i]); } } address remoteAccount; - (remoteAccount, slot, slotValue) = prover.verifyStorageSlot(blockHash, readArgs.storageProof); + (remoteAccount, slot, slotValue) = prover.verifyStorageSlot(stateCommitment, readArgs.proof); remoteAccountId = accumulator(remoteAccountId, remoteAccount); } diff --git a/src/contracts/BlockHashProverPointer.sol b/src/contracts/StateProverPointer.sol similarity index 72% rename from src/contracts/BlockHashProverPointer.sol rename to src/contracts/StateProverPointer.sol index 5981f69..e48368c 100644 --- a/src/contracts/BlockHashProverPointer.sol +++ b/src/contracts/StateProverPointer.sol @@ -3,17 +3,17 @@ pragma solidity 0.8.30; import {StorageSlot} from "@openzeppelin/contracts/utils/StorageSlot.sol"; import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; -import {IBlockHashProver} from "./interfaces/IBlockHashProver.sol"; -import {IBlockHashProverPointer} from "./interfaces/IBlockHashProverPointer.sol"; +import {IStateProver} from "./interfaces/IStateProver.sol"; +import {IStateProverPointer} from "./interfaces/IStateProverPointer.sol"; -bytes32 constant BLOCK_HASH_PROVER_POINTER_SLOT = bytes32(uint256(keccak256("eip7888.pointer.slot")) - 1); +bytes32 constant STATE_PROVER_POINTER_SLOT = bytes32(uint256(keccak256("eip7888.pointer.slot")) - 1); -/// @title BlockHashProverPointer -/// @notice Manages a versioned pointer to the latest BlockHashProver implementation -/// @dev This contract stores the address and code hash of the current BlockHashProver implementation. +/// @title StateProverPointer +/// @notice Manages a versioned pointer to the latest StateProver implementation +/// @dev This contract stores the address and code hash of the current StateProver implementation. /// It enforces version monotonicity to ensure that updates always move to newer versions. /// The code hash is stored in a dedicated storage slot for efficient cross-chain verification. -contract BlockHashProverPointer is IBlockHashProverPointer, Ownable { +contract StateProverPointer is IStateProverPointer, Ownable { address internal _implementationAddress; error NonIncreasingVersion(uint256 newVersion, uint256 oldVersion); @@ -21,7 +21,7 @@ contract BlockHashProverPointer is IBlockHashProverPointer, Ownable { constructor(address _initialOwner) Ownable(_initialOwner) {} - /// @notice Returns the address of the current BlockHashProver implementation + /// @notice Returns the address of the current StateProver implementation /// @return The address of the current implementation contract function implementationAddress() public view returns (address) { return _implementationAddress; @@ -30,14 +30,14 @@ contract BlockHashProverPointer is IBlockHashProverPointer, Ownable { /// @notice Return the code hash of the latest version of the prover. /// @return codeHash The code hash of the current implementation stored in the pointer slot. function implementationCodeHash() public view returns (bytes32 codeHash) { - codeHash = StorageSlot.getBytes32Slot(BLOCK_HASH_PROVER_POINTER_SLOT).value; + codeHash = StorageSlot.getBytes32Slot(STATE_PROVER_POINTER_SLOT).value; } - /// @notice Updates the BlockHashProver implementation to a new version + /// @notice Updates the StateProver implementation to a new version /// @dev This function enforces version monotonicity - the new implementation must have a higher version number /// than the current one. It also updates the stored code hash to match the new implementation. /// Can only be called by the contract owner. - /// @param _newImplementationAddress The address of the new BlockHashProver implementation + /// @param _newImplementationAddress The address of the new StateProver implementation /// @custom:throws NonIncreasingVersion if the new version is not greater than the current version function setImplementationAddress(address _newImplementationAddress) external onlyOwner { if (_newImplementationAddress == address(0)) { @@ -45,7 +45,7 @@ contract BlockHashProverPointer is IBlockHashProverPointer, Ownable { } (bool success, bytes memory returnData) = - _newImplementationAddress.staticcall(abi.encodeWithSelector(IBlockHashProver.version.selector)); + _newImplementationAddress.staticcall(abi.encodeWithSelector(IStateProver.version.selector)); if (!success || returnData.length != 32) { revert InvalidImplementationAddress(); } @@ -54,7 +54,7 @@ contract BlockHashProverPointer is IBlockHashProverPointer, Ownable { address currentImplementationAddress = implementationAddress(); if (currentImplementationAddress != address(0)) { - uint256 oldVersion = IBlockHashProver(currentImplementationAddress).version(); + uint256 oldVersion = IStateProver(currentImplementationAddress).version(); if (newVersion <= oldVersion) { revert NonIncreasingVersion(newVersion, oldVersion); } @@ -65,6 +65,6 @@ contract BlockHashProverPointer is IBlockHashProverPointer, Ownable { } function _setCodeHash(bytes32 _codeHash) internal { - StorageSlot.getBytes32Slot(BLOCK_HASH_PROVER_POINTER_SLOT).value = _codeHash; + StorageSlot.getBytes32Slot(STATE_PROVER_POINTER_SLOT).value = _codeHash; } } diff --git a/src/contracts/ZkSyncBroadcaster.sol b/src/contracts/ZkSyncBroadcaster.sol index 9fa3fc8..95081cf 100644 --- a/src/contracts/ZkSyncBroadcaster.sol +++ b/src/contracts/ZkSyncBroadcaster.sol @@ -37,8 +37,8 @@ contract ZkSyncBroadcaster is IBroadcaster { /// @dev The broadcast timestamp is stored in a deterministic storage slot calculated from hash(message, msg.sender). /// This ensures that each (message, publisher) pair can only be broadcast once. /// A MessageBroadcast event is emitted for off-chain indexing. Additionally, this ZkSync-specific - /// implementation sends an L2->L1 message via the L1Messenger containing the original message and - /// timestamp ABI encoded together (bytes32 message, bytes32 timestamp). + /// implementation sends an L2->L1 message via the L1Messenger containing the message slot and + /// timestamp ABI encoded together (bytes32 slot, uint256 timestamp). /// @param message The 32-byte message to broadcast. /// @custom:throws MessageAlreadyBroadcasted if this exact message has already been broadcast by the sender. function broadcastMessage(bytes32 message) external { @@ -53,7 +53,7 @@ contract ZkSyncBroadcaster is IBroadcaster { // store the message and its timestamp _writeStorageSlot(slot, block.timestamp); - // send the message and timestamp to L1 via ZkSync's L1Messenger + // send the slot and timestamp to L1 via ZkSync's L1Messenger _l1Messenger.sendToL1(abi.encode(slot, uint256(block.timestamp))); // emit the event diff --git a/src/contracts/interfaces/IBlockHashProver.sol b/src/contracts/interfaces/IBlockHashProver.sol deleted file mode 100644 index e7791ff..0000000 --- a/src/contracts/interfaces/IBlockHashProver.sol +++ /dev/null @@ -1,50 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.30; - -/// @notice The IBlockHashProver is responsible for retrieving the block hash of its target chain given its home chain's state. -/// The home chain's state is given either by a block hash and proof, or by the BlockHashProver executing on the home chain. -/// A single home and target chain are fixed by the logic of this contract. -interface IBlockHashProver { - /// @notice Verify the block hash of the target chain given the block hash of the home chain and a proof. - /// @dev MUST revert if called on the home chain. - /// MUST revert if the input is invalid or the input is not sufficient to determine the block hash. - /// MUST return a target chain block hash. - /// MUST be pure, with 1 exception: MAY read address(this).code. - /// @param homeBlockHash The block hash of the home chain. - /// @param input Any necessary input to determine a target chain block hash from the home chain block hash. - /// @return targetBlockHash The block hash of the target chain. - function verifyTargetBlockHash(bytes32 homeBlockHash, bytes calldata input) - external - view - returns (bytes32 targetBlockHash); - - /// @notice Get the block hash of the target chain. Does so by directly access state on the home chain. - /// @dev MUST revert if not called on the home chain. - /// MUST revert if the target chain's block hash cannot be determined. - /// MUST return a target chain block hash. - /// SHOULD use the input to determine a specific block hash to return. (e.g. input could be a block number) - /// SHOULD NOT read from its own storage. This contract is not meant to have state. - /// MAY make external calls. - /// @param input Any necessary input to fetch a target chain block hash. - /// @return targetBlockHash The block hash of the target chain. - function getTargetBlockHash(bytes calldata input) external view returns (bytes32 targetBlockHash); - - /// @notice Verify a storage slot given a target chain block hash and a proof. - /// @dev This function MUST NOT assume it is being called on the home chain. - /// MUST revert if the input is invalid or the input is not sufficient to determine a storage slot and its value. - /// MUST return a storage slot and its value on the target chain - /// MUST be pure, with 1 exception: MAY read address(this).code. - /// @param targetBlockHash The block hash of the target chain. - /// @param input Any necessary input to determine a single storage slot and its value. - /// @return account The address of the account on the target chain. - /// @return slot The storage slot of the account on the target chain. - /// @return value The value of the storage slot. - function verifyStorageSlot(bytes32 targetBlockHash, bytes calldata input) - external - view - returns (address account, uint256 slot, bytes32 value); - - /// @notice The version of the block hash prover. - /// @dev MUST be pure, with 1 exception: MAY read address(this).code. - function version() external pure returns (uint256); -} diff --git a/src/contracts/interfaces/IBroadcaster.sol b/src/contracts/interfaces/IBroadcaster.sol index 56cadc8..ea7b615 100644 --- a/src/contracts/interfaces/IBroadcaster.sol +++ b/src/contracts/interfaces/IBroadcaster.sol @@ -12,6 +12,7 @@ interface IBroadcaster { /// @dev MUST revert if the publisher has already broadcast the message. /// MUST emit MessageBroadcast. /// MUST store block.timestamp in slot keccak(message, msg.sender). + /// MAY use additional transmission mechanisms (e.g., child-to-parent native bridges) to make messages visible. /// @param message The message to broadcast. function broadcastMessage(bytes32 message) external; } diff --git a/src/contracts/interfaces/IReceiver.sol b/src/contracts/interfaces/IReceiver.sol index 21c9451..a73ebd6 100644 --- a/src/contracts/interfaces/IReceiver.sol +++ b/src/contracts/interfaces/IReceiver.sol @@ -1,27 +1,29 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.30; -import {IBlockHashProver} from "./IBlockHashProver.sol"; +import {IStateProver} from "./IStateProver.sol"; /// @notice Reads messages from a broadcaster. interface IReceiver { - /// @notice Arguments required to read storage of an account on a remote chain. - /// @dev The storage proof is always for a single slot, if the proof is for multiple slots the IReceiver MUST revert - /// @param route The home chain addresses of the BlockHashProverPointers along the route to the remote chain. - /// @param bhpInputs The inputs to the BlockHashProver / BlockHashProverCopies. - /// @param storageProof Proof passed to the last BlockHashProver / BlockHashProverCopy - /// to verify a storage slot given a target block hash. + /// @notice Arguments required to read state of an account on a remote chain. + /// @dev The proof is always for a single storage slot. If the proof is for multiple slots the IReceiver MUST revert. + /// The proof format depends on the state commitment scheme used by the StateProver (e.g., storage proofs). + /// While messages MUST be stored in storage slots, alternative reading mechanisms may be used in some cases. + /// @param route The home chain addresses of the StateProverPointers along the route to the remote chain. + /// @param scpInputs The inputs to the StateProver / StateProverCopies. + /// @param proof Proof passed to the last StateProver / StateProverCopy + /// to verify a storage slot given a target state commitment. struct RemoteReadArgs { address[] route; - bytes[] bhpInputs; - bytes storageProof; + bytes[] scpInputs; + bytes proof; } /// @notice Reads a broadcast message from a remote chain. /// @param broadcasterReadArgs A RemoteReadArgs object: /// - The route points to the broadcasting chain /// - The account proof is for the broadcaster's account - /// - The storage proof is for the message slot + /// - The proof is for the message storage slot (MAY accept proofs of other transmission mechanisms (e.g., child-to-parent native bridges) if the broadcaster contract uses other transmission mechanisms) /// @param message The message to read. /// @param publisher The address of the publisher who broadcast the message. /// @return broadcasterId The broadcaster's unique identifier. @@ -31,22 +33,20 @@ interface IReceiver { view returns (bytes32 broadcasterId, uint256 timestamp); - /// @notice Updates the block hash prover copy in storage. - /// Checks that BlockHashProverCopy has the same code hash as stored in the BlockHashProverPointer + /// @notice Updates the state commitment prover copy in storage. + /// Checks that StateProverCopy has the same code hash as stored in the StateProverPointer /// Checks that the version is increasing. - /// @param bhpPointerReadArgs A RemoteReadArgs object: - /// - The route points to the BlockHashProverPointer's home chain - /// - The account proof is for the BlockHashProverPointer's account - /// - The storage proof is for the BLOCK_HASH_PROVER_POINTER_SLOT - /// @param bhpCopy The BlockHashProver copy on the local chain. - /// @return bhpPointerId The ID of the BlockHashProverPointer - function updateBlockHashProverCopy(RemoteReadArgs calldata bhpPointerReadArgs, IBlockHashProver bhpCopy) + /// @param scpPointerReadArgs A RemoteReadArgs object: + /// - The route points to the StateProverPointer's home chain + /// - The account proof is for the StateProverPointer's account + /// - The proof is for the STATE_PROVER_POINTER_SLOT + /// @param scpCopy The StateProver copy on the local chain. + /// @return scpPointerId The ID of the StateProverPointer + function updateStateProverCopy(RemoteReadArgs calldata scpPointerReadArgs, IStateProver scpCopy) external - returns (bytes32 bhpPointerId); + returns (bytes32 scpPointerId); - /// @notice The BlockHashProverCopy on the local chain corresponding to the bhpPointerId - /// MUST return 0 if the BlockHashProverPointer does not exist. - /// @param bhpPointerId The unique identifier of the BlockHashProverPointer. - /// @return bhpCopy The BlockHashProver copy stored on the local chain, or address(0) if not found. - function blockHashProverCopy(bytes32 bhpPointerId) external view returns (IBlockHashProver bhpCopy); + /// @notice The StateProverCopy on the local chain corresponding to the scpPointerId + /// MUST return 0 if the StateProverPointer does not exist. + function stateProverCopy(bytes32 scpPointerId) external view returns (IStateProver scpCopy); } diff --git a/src/contracts/interfaces/IStateProver.sol b/src/contracts/interfaces/IStateProver.sol new file mode 100644 index 0000000..aaafd75 --- /dev/null +++ b/src/contracts/interfaces/IStateProver.sol @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.30; + +/// @notice The IStateProver is responsible for retrieving the state commitment of its target chain given its home chain's state. +/// The home chain's state is given either by a state commitment and proof, or by the StateProver executing on the home chain. +/// A single home and target chain are fixed by the logic of this contract. +interface IStateProver { + /// @notice Verify the state commitment of the target chain given the state commitment of the home chain and a proof. + /// @dev MUST revert if called on the home chain. + /// MUST revert if the input is invalid or the input is not sufficient to determine the state commitment. + /// MUST return a target chain state commitment. + /// MUST be pure, with 1 exception: MAY read address(this).code. + /// @param homeStateCommitment The state commitment of the home chain. + /// @param input Any necessary input to determine a target chain state commitment from the home chain state commitment. + /// @return targetStateCommitment The state commitment of the target chain. + function verifyTargetStateCommitment(bytes32 homeStateCommitment, bytes calldata input) + external + view + returns (bytes32 targetStateCommitment); + + /// @notice Get the state commitment of the target chain. Does so by directly accessing state on the home chain. + /// @dev MUST revert if not called on the home chain. + /// MUST revert if the target chain's state commitment cannot be determined. + /// MUST return a target chain state commitment. + /// SHOULD use the input to determine a specific state commitment to return. (e.g. input could be a block number) + /// SHOULD NOT read from its own storage. This contract is not meant to have state. + /// MAY make external calls. + /// @param input Any necessary input to fetch a target chain state commitment. + /// @return targetStateCommitment The state commitment of the target chain. + function getTargetStateCommitment(bytes calldata input) external view returns (bytes32 targetStateCommitment); + + /// @notice Verify a storage slot given a target chain state commitment and a proof. + /// @dev This function MUST NOT assume it is being called on the home chain. + /// MUST revert if the input is invalid or the input is not sufficient to determine a storage slot and its value. + /// MUST return a storage slot and its value on the target chain. + /// MUST be pure, with 1 exception: MAY read address(this).code. + /// While messages MUST be stored in storage slots, alternative reading mechanisms may be used in some cases. + /// @param targetStateCommitment The state commitment of the target chain. + /// @param input Any necessary input to determine a single storage slot and its value. + /// @return account The address of the account on the target chain. + /// @return slot The storage slot of the account on the target chain. + /// @return value The value of the storage slot. + function verifyStorageSlot(bytes32 targetStateCommitment, bytes calldata input) + external + view + returns (address account, uint256 slot, bytes32 value); + + /// @notice The version of the state commitment prover. + /// @dev MUST be pure, with 1 exception: MAY read address(this).code. + function version() external pure returns (uint256); +} diff --git a/src/contracts/interfaces/IBlockHashProverPointer.sol b/src/contracts/interfaces/IStateProverPointer.sol similarity index 66% rename from src/contracts/interfaces/IBlockHashProverPointer.sol rename to src/contracts/interfaces/IStateProverPointer.sol index c3b40d8..7e53bb1 100644 --- a/src/contracts/interfaces/IBlockHashProverPointer.sol +++ b/src/contracts/interfaces/IStateProverPointer.sol @@ -1,13 +1,13 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.30; -/// @title IBlockHashProverPointer -/// @notice Keeps the code hash of the latest version of a block hash prover. -/// MUST store the code hash in storage slot BLOCK_HASH_PROVER_POINTER_SLOT. +/// @title IStateProverPointer +/// @notice Keeps the code hash of the latest version of a state commitment prover. +/// MUST store the code hash in storage slot STATE_PROVER_POINTER_SLOT. /// Different versions of the prover MUST have the same home and target chains. -/// If the pointer's prover is updated, the new prover MUST have a higher IBlockHashProver::version() than the old one. +/// If the pointer's prover is updated, the new prover MUST have a higher IStateProver::version() than the old one. /// These pointers are always referred to by their address on their home chain. -interface IBlockHashProverPointer { +interface IStateProverPointer { /// @notice Return the code hash of the latest version of the prover. function implementationCodeHash() external view returns (bytes32); diff --git a/src/contracts/libraries/ProverUtils.sol b/src/contracts/libraries/ProverUtils.sol index 949f315..81f1e95 100644 --- a/src/contracts/libraries/ProverUtils.sol +++ b/src/contracts/libraries/ProverUtils.sol @@ -5,7 +5,7 @@ import {Lib_SecureMerkleTrie} from "@eth-optimism/contracts/libraries/trie/Lib_S import {RLP} from "@openzeppelin/contracts/utils/RLP.sol"; import {Memory} from "@openzeppelin/contracts/utils/Memory.sol"; -/// @notice Base contract for IBlockHashProver contracts. Contains helpers for verifying block headers and MPT proofs. +/// @notice Base contract for IStateProver contracts. Contains helpers for verifying block headers and MPT proofs. library ProverUtils { using Memory for bytes; using RLP for Memory.Slice; diff --git a/src/contracts/libraries/linea/SparseMerkleProof.sol b/src/contracts/libraries/linea/SparseMerkleProof.sol index 5622fa9..3c588ac 100644 --- a/src/contracts/libraries/linea/SparseMerkleProof.sol +++ b/src/contracts/libraries/linea/SparseMerkleProof.sol @@ -2,7 +2,7 @@ // Copyright 2023 Consensys Software Inc. // Adapted for use in ERC-7888 provers -pragma solidity ^0.8.28; +pragma solidity 0.8.30; import {Mimc} from "./Mimc.sol"; diff --git a/src/contracts/provers/arbitrum/ChildToParentProver.sol b/src/contracts/provers/arbitrum/ChildToParentProver.sol index ce5cf93..cf848e9 100644 --- a/src/contracts/provers/arbitrum/ChildToParentProver.sol +++ b/src/contracts/provers/arbitrum/ChildToParentProver.sol @@ -2,15 +2,15 @@ pragma solidity 0.8.30; import {ProverUtils} from "../../libraries/ProverUtils.sol"; -import {IBlockHashProver} from "../../interfaces/IBlockHashProver.sol"; +import {IStateProver} from "../../interfaces/IStateProver.sol"; import {IBuffer} from "block-hash-pusher/contracts/interfaces/IBuffer.sol"; import {SlotDerivation} from "@openzeppelin/contracts/utils/SlotDerivation.sol"; -/// @notice Arbitrum implementation of a child to parent IBlockHashProver. -/// @dev verifyTargetBlockHash and getTargetBlockHash get block hashes from the block hash buffer at 0x0000000048C4Ed10cF14A02B9E0AbDDA5227b071. +/// @notice Arbitrum implementation of a child to parent IStateProver. +/// @dev verifyTargetStateCommitment and getTargetStateCommitment get block hashes from the block hash buffer at 0x0000000048C4Ed10cF14A02B9E0AbDDA5227b071. /// See https://github.com/OffchainLabs/block-hash-pusher/blob/a1e26f2e42e6306d1e7f03c5d20fa6aa64ff7a12 for more details. /// verifyStorageSlot is implemented to work against any parent chain with a standard Ethereum block header and state trie. -contract ChildToParentProver is IBlockHashProver { +contract ChildToParentProver is IStateProver { /// @dev Address of the block hash buffer contract /// See https://github.com/OffchainLabs/block-hash-pusher/blob/a1e26f2e42e6306d1e7f03c5d20fa6aa64ff7a12/.env.example#L12 address public constant blockHashBuffer = 0x0000000048C4Ed10cF14A02B9E0AbDDA5227b071; @@ -18,6 +18,7 @@ contract ChildToParentProver is IBlockHashProver { /// See https://github.com/OffchainLabs/block-hash-pusher/blob/a1e26f2e42e6306d1e7f03c5d20fa6aa64ff7a12/contracts/Buffer.sol#L32 uint256 public constant blockHashMappingSlot = 51; + /// @dev The chain Id of the home chain, i.e., the child chain. uint256 public immutable homeChainId; error CallNotOnHomeChain(); @@ -30,10 +31,10 @@ contract ChildToParentProver is IBlockHashProver { /// @notice Get a parent chain block hash from the buffer at `blockHashBuffer` using a storage proof /// @param homeBlockHash The block hash of the home chain. /// @param input ABI encoded (bytes blockHeader, uint256 targetBlockNumber, bytes accountProof, bytes storageProof) - function verifyTargetBlockHash(bytes32 homeBlockHash, bytes calldata input) + function verifyTargetStateCommitment(bytes32 homeBlockHash, bytes calldata input) external view - returns (bytes32 targetBlockHash) + returns (bytes32 targetStateCommitment) { if (block.chainid == homeChainId) { revert CallOnHomeChain(); @@ -47,14 +48,14 @@ contract ChildToParentProver is IBlockHashProver { uint256 slot = uint256(SlotDerivation.deriveMapping(bytes32(blockHashMappingSlot), targetBlockNumber)); // verify proofs and get the block hash - targetBlockHash = ProverUtils.getSlotFromBlockHeader( + targetStateCommitment = ProverUtils.getSlotFromBlockHeader( homeBlockHash, rlpBlockHeader, blockHashBuffer, slot, accountProof, storageProof ); } /// @notice Get a parent chain block hash from the buffer at `blockHashBuffer`. /// @param input ABI encoded (uint256 targetBlockNumber) - function getTargetBlockHash(bytes calldata input) external view returns (bytes32 targetBlockHash) { + function getTargetStateCommitment(bytes calldata input) external view returns (bytes32 targetStateCommitment) { if (block.chainid != homeChainId) { revert CallNotOnHomeChain(); } @@ -62,13 +63,13 @@ contract ChildToParentProver is IBlockHashProver { uint256 targetBlockNumber = abi.decode(input, (uint256)); // get the block hash from the buffer - targetBlockHash = IBuffer(blockHashBuffer).parentChainBlockHash(targetBlockNumber); + targetStateCommitment = IBuffer(blockHashBuffer).parentChainBlockHash(targetBlockNumber); } /// @notice Verify a storage slot given a target chain block hash and a proof. - /// @param targetBlockHash The block hash of the target chain. + /// @param targetStateCommitment The block hash of the target chain. /// @param input ABI encoded (bytes blockHeader, address account, uint256 slot, bytes accountProof, bytes storageProof) - function verifyStorageSlot(bytes32 targetBlockHash, bytes calldata input) + function verifyStorageSlot(bytes32 targetStateCommitment, bytes calldata input) external pure returns (address account, uint256 slot, bytes32 value) @@ -82,11 +83,11 @@ contract ChildToParentProver is IBlockHashProver { // verify proofs and get the value value = ProverUtils.getSlotFromBlockHeader( - targetBlockHash, rlpBlockHeader, account, slot, accountProof, storageProof + targetStateCommitment, rlpBlockHeader, account, slot, accountProof, storageProof ); } - /// @inheritdoc IBlockHashProver + /// @inheritdoc IStateProver function version() external pure returns (uint256) { return 1; } diff --git a/src/contracts/provers/arbitrum/ParentToChildProver.sol b/src/contracts/provers/arbitrum/ParentToChildProver.sol index 3a51d07..66caaf2 100644 --- a/src/contracts/provers/arbitrum/ParentToChildProver.sol +++ b/src/contracts/provers/arbitrum/ParentToChildProver.sol @@ -2,14 +2,14 @@ pragma solidity 0.8.30; import {ProverUtils} from "../../libraries/ProverUtils.sol"; -import {IBlockHashProver} from "../../interfaces/IBlockHashProver.sol"; +import {IStateProver} from "../../interfaces/IStateProver.sol"; import {IOutbox} from "@arbitrum/nitro-contracts/src/bridge/IOutbox.sol"; import {SlotDerivation} from "@openzeppelin/contracts/utils/SlotDerivation.sol"; -/// @notice Arbitrum implementation of a parent to child IBlockHashProver. -/// @dev verifyTargetBlockHash and getTargetBlockHash get block hashes from the child chain's Outbox contract. +/// @notice Arbitrum implementation of a parent to child IStateProver. +/// @dev verifyTargetStateCommitment and getTargetStateCommitment get block hashes from the child chain's Outbox contract. /// verifyStorageSlot is implemented to work against any Arbitrum child chain with a standard Ethereum block header and state trie. -contract ParentToChildProver is IBlockHashProver { +contract ParentToChildProver is IStateProver { /// @dev Address of the child chain's Outbox contract address public immutable outbox; /// @dev Storage slot the Outbox contract uses to store roots. @@ -33,10 +33,10 @@ contract ParentToChildProver is IBlockHashProver { /// @notice Verify a target chain block hash given a home chain block hash and a proof. /// @param homeBlockHash The block hash of the home chain. /// @param input ABI encoded (bytes blockHeader, bytes32 sendRoot, bytes accountProof, bytes storageProof) - function verifyTargetBlockHash(bytes32 homeBlockHash, bytes calldata input) + function verifyTargetStateCommitment(bytes32 homeBlockHash, bytes calldata input) external view - returns (bytes32 targetBlockHash) + returns (bytes32 targetStateCommitment) { if (block.chainid == homeChainId) { revert CallOnHomeChain(); @@ -51,17 +51,17 @@ contract ParentToChildProver is IBlockHashProver { uint256 slot = uint256(SlotDerivation.deriveMapping(bytes32(rootsSlot), sendRoot)); // verify proofs and get the block hash - targetBlockHash = + targetStateCommitment = ProverUtils.getSlotFromBlockHeader(homeBlockHash, rlpBlockHeader, outbox, slot, accountProof, storageProof); - if (targetBlockHash == bytes32(0)) { + if (targetStateCommitment == bytes32(0)) { revert TargetBlockHashNotFound(); } } /// @notice Get a target chain block hash given a target chain sendRoot /// @param input ABI encoded (bytes32 sendRoot) - function getTargetBlockHash(bytes calldata input) external view returns (bytes32 targetBlockHash) { + function getTargetStateCommitment(bytes calldata input) external view returns (bytes32 targetStateCommitment) { if (block.chainid != homeChainId) { revert CallNotOnHomeChain(); } @@ -69,17 +69,17 @@ contract ParentToChildProver is IBlockHashProver { // decode the input bytes32 sendRoot = abi.decode(input, (bytes32)); // get the target block hash from the outbox - targetBlockHash = IOutbox(outbox).roots(sendRoot); + targetStateCommitment = IOutbox(outbox).roots(sendRoot); - if (targetBlockHash == bytes32(0)) { + if (targetStateCommitment == bytes32(0)) { revert TargetBlockHashNotFound(); } } /// @notice Verify a storage slot given a target chain block hash and a proof. - /// @param targetBlockHash The block hash of the target chain. + /// @param targetStateCommitment The block hash of the target chain. /// @param input ABI encoded (bytes blockHeader, address account, uint256 slot, bytes accountProof, bytes storageProof) - function verifyStorageSlot(bytes32 targetBlockHash, bytes calldata input) + function verifyStorageSlot(bytes32 targetStateCommitment, bytes calldata input) external pure returns (address account, uint256 slot, bytes32 value) @@ -93,11 +93,11 @@ contract ParentToChildProver is IBlockHashProver { // verify proofs and get the value value = ProverUtils.getSlotFromBlockHeader( - targetBlockHash, rlpBlockHeader, account, slot, accountProof, storageProof + targetStateCommitment, rlpBlockHeader, account, slot, accountProof, storageProof ); } - /// @inheritdoc IBlockHashProver + /// @inheritdoc IStateProver function version() external pure returns (uint256) { return 1; } diff --git a/src/contracts/provers/linea/ChildToParentProver.sol b/src/contracts/provers/linea/ChildToParentProver.sol index e7c122c..42d4091 100644 --- a/src/contracts/provers/linea/ChildToParentProver.sol +++ b/src/contracts/provers/linea/ChildToParentProver.sol @@ -1,17 +1,22 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.28; +pragma solidity 0.8.30; import {ProverUtils} from "../../libraries/ProverUtils.sol"; -import {IBlockHashProver} from "../../interfaces/IBlockHashProver.sol"; +import {IStateProver} from "../../interfaces/IStateProver.sol"; import {IBuffer} from "block-hash-pusher/contracts/interfaces/IBuffer.sol"; import {SlotDerivation} from "@openzeppelin/contracts/utils/SlotDerivation.sol"; -contract ChildToParentProver is IBlockHashProver { +/// @notice Linea implementation of a child to parent IStateProver. +/// @dev verifyTargetStateCommitment and getTargetStateCommitment get block hashes from the block hash buffer. +/// verifyStorageSlot is implemented to work against any parent chain with a standard Ethereum block header and state trie. +contract ChildToParentProver is IStateProver { + /// @dev Address of the block hash buffer contract. address public immutable blockHashBuffer; /// @dev Storage slot the buffer contract uses to store block hashes. - /// See https://github.com/OffchainLabs/block-hash-pusher/blob/a1e26f2e42e6306d1e7f03c5d20fa6aa64ff7a12/contracts/Buffer.sol#L32 + /// See https://github.com/openintentsframework/broadcaster/blob/main/src/contracts/block-hash-pusher/BaseBuffer.sol uint256 public constant blockHashMappingSlot = 51; + /// @dev The chain ID of the home chain (child chain). uint256 public immutable homeChainId; error CallNotOnHomeChain(); @@ -25,10 +30,10 @@ contract ChildToParentProver is IBlockHashProver { /// @notice Get a parent chain block hash from the buffer at `blockHashBuffer` using a storage proof /// @param homeBlockHash The block hash of the home chain. /// @param input ABI encoded (bytes blockHeader, uint256 targetBlockNumber, bytes accountProof, bytes storageProof) - function verifyTargetBlockHash(bytes32 homeBlockHash, bytes calldata input) + function verifyTargetStateCommitment(bytes32 homeBlockHash, bytes calldata input) external view - returns (bytes32 targetBlockHash) + returns (bytes32 targetStateCommitment) { if (block.chainid == homeChainId) { revert CallOnHomeChain(); @@ -42,28 +47,28 @@ contract ChildToParentProver is IBlockHashProver { uint256 slot = uint256(SlotDerivation.deriveMapping(bytes32(blockHashMappingSlot), targetBlockNumber)); // verify proofs and get the block hash - targetBlockHash = ProverUtils.getSlotFromBlockHeader( + targetStateCommitment = ProverUtils.getSlotFromBlockHeader( homeBlockHash, rlpBlockHeader, blockHashBuffer, slot, accountProof, storageProof ); } /// @notice Get a parent chain block hash from the buffer at `blockHashBuffer`. /// @param input ABI encoded (uint256 targetBlockNumber) - function getTargetBlockHash(bytes calldata input) external view returns (bytes32 targetBlockHash) { + function getTargetStateCommitment(bytes calldata input) external view returns (bytes32 targetStateCommitment) { if (block.chainid != homeChainId) { revert CallNotOnHomeChain(); } - //decode the input + // decode the input uint256 targetBlockNumber = abi.decode(input, (uint256)); // get the block hash from the buffer - targetBlockHash = IBuffer(blockHashBuffer).parentChainBlockHash(targetBlockNumber); + targetStateCommitment = IBuffer(blockHashBuffer).parentChainBlockHash(targetBlockNumber); } /// @notice Verify a storage slot given a target chain block hash and a proof. - /// @param targetBlockHash The block hash of the target chain. + /// @param targetStateCommitment The block hash of the target chain. /// @param input ABI encoded (bytes blockHeader, address account, uint256 slot, bytes accountProof, bytes storageProof) - function verifyStorageSlot(bytes32 targetBlockHash, bytes calldata input) + function verifyStorageSlot(bytes32 targetStateCommitment, bytes calldata input) external pure returns (address account, uint256 slot, bytes32 value) @@ -77,11 +82,11 @@ contract ChildToParentProver is IBlockHashProver { // verify proofs and get the value value = ProverUtils.getSlotFromBlockHeader( - targetBlockHash, rlpBlockHeader, account, slot, accountProof, storageProof + targetStateCommitment, rlpBlockHeader, account, slot, accountProof, storageProof ); } - /// @inheritdoc IBlockHashProver + /// @inheritdoc IStateProver function version() external pure returns (uint256) { return 1; } diff --git a/src/contracts/provers/linea/ParentToChildProver.sol b/src/contracts/provers/linea/ParentToChildProver.sol index 4fbf8ec..45f599e 100644 --- a/src/contracts/provers/linea/ParentToChildProver.sol +++ b/src/contracts/provers/linea/ParentToChildProver.sol @@ -1,28 +1,22 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.28; +pragma solidity 0.8.30; import {SparseMerkleProof} from "../../libraries/linea/SparseMerkleProof.sol"; import {ProverUtils} from "../../libraries/ProverUtils.sol"; -import {IBlockHashProver} from "../../interfaces/IBlockHashProver.sol"; +import {IStateProver} from "../../interfaces/IStateProver.sol"; import {SlotDerivation} from "@openzeppelin/contracts/utils/SlotDerivation.sol"; - -interface ILineaRollup { - /// @notice Returns the state root hash for a given L2 block number - /// @param blockNumber The L2 block number - /// @return The state root hash (bytes32(0) if not finalized) - function stateRootHashes(uint256 blockNumber) external view returns (bytes32); -} +import {ZkEvmV2} from "@linea-contracts/rollup/ZkEvmV2.sol"; /// @title Linea ParentToChildProver /// @notice Enables verification of Linea L2 state from Ethereum L1 /// @dev Home chain: L1 (Ethereum). Target chain: L2 (Linea). -/// On L1: getTargetBlockHash reads L2 state root directly from LineaRollup -/// On L2: verifyTargetBlockHash proves L2 state root from L1 LineaRollup storage +/// On L1: getTargetStateCommitment reads L2 state root directly from LineaRollup +/// On L2: verifyTargetStateCommitment proves L2 state root from L1 LineaRollup storage /// verifyStorageSlot: Verifies storage against the L2 state root using Sparse Merkle Tree proofs /// /// Note: Linea uses Sparse Merkle Tree (SMT) with MiMC hashing, NOT Merkle-Patricia Trie (MPT). /// The state root stored on L1 is the SMT root, which requires linea_getProof for verification. -contract ParentToChildProver is IBlockHashProver { +contract ParentToChildProver is IStateProver { /// @dev Address of the LineaRollup contract on L1 address public immutable lineaRollup; @@ -52,13 +46,13 @@ contract ParentToChildProver is IBlockHashProver { /// @notice Verify L2 state root using L1 LineaRollup storage proof /// @dev Called on non-home chains (e.g., for two-hop L2→L2 verification) /// Uses standard MPT proof for L1 state (Ethereum uses MPT) - /// @param homeBlockHash The L1 block hash + /// @param homeStateCommitment The L1 block hash /// @param input ABI encoded (bytes rlpBlockHeader, uint256 l2BlockNumber, bytes accountProof, bytes storageProof) - /// @return targetBlockHash The L2 state root (named "blockHash" for interface compatibility) - function verifyTargetBlockHash(bytes32 homeBlockHash, bytes calldata input) + /// @return targetStateCommitment The L2 state root + function verifyTargetStateCommitment(bytes32 homeStateCommitment, bytes calldata input) external view - returns (bytes32 targetBlockHash) + returns (bytes32 targetStateCommitment) { if (block.chainid == homeChainId) { revert CallOnHomeChain(); @@ -73,11 +67,11 @@ contract ParentToChildProver is IBlockHashProver { // Verify proofs and get the L2 state root from L1's LineaRollup // Note: L1 (Ethereum) uses MPT, so we use ProverUtils here - targetBlockHash = ProverUtils.getSlotFromBlockHeader( - homeBlockHash, rlpBlockHeader, lineaRollup, slot, accountProof, storageProof + targetStateCommitment = ProverUtils.getSlotFromBlockHeader( + homeStateCommitment, rlpBlockHeader, lineaRollup, slot, accountProof, storageProof ); - if (targetBlockHash == bytes32(0)) { + if (targetStateCommitment == bytes32(0)) { revert TargetStateRootNotFound(); } } @@ -85,8 +79,8 @@ contract ParentToChildProver is IBlockHashProver { /// @notice Get L2 state root directly from L1 LineaRollup /// @dev Called on home chain (L1) /// @param input ABI encoded (uint256 l2BlockNumber) - /// @return targetBlockHash The L2 state root - function getTargetBlockHash(bytes calldata input) external view returns (bytes32 targetBlockHash) { + /// @return targetStateCommitment The L2 state root + function getTargetStateCommitment(bytes calldata input) external view returns (bytes32 targetStateCommitment) { if (block.chainid != homeChainId) { revert CallNotOnHomeChain(); } @@ -95,16 +89,16 @@ contract ParentToChildProver is IBlockHashProver { uint256 l2BlockNumber = abi.decode(input, (uint256)); // Get the state root from LineaRollup - targetBlockHash = ILineaRollup(lineaRollup).stateRootHashes(l2BlockNumber); + targetStateCommitment = ZkEvmV2(lineaRollup).stateRootHashes(l2BlockNumber); - if (targetBlockHash == bytes32(0)) { + if (targetStateCommitment == bytes32(0)) { revert TargetStateRootNotFound(); } } /// @notice Verify a storage slot given a target chain state root and a Sparse Merkle Tree proof /// @dev Works on any chain. Uses Linea's SMT verification with MiMC hashing. - /// IMPORTANT: For Linea, targetBlockHash is the L2 SMT STATE ROOT (not block hash) + /// IMPORTANT: For Linea, targetStateCommitment is the L2 SMT STATE ROOT (not block hash) /// Proofs must be generated using linea_getProof RPC method. /// /// Input format from linea_getProof: @@ -112,7 +106,7 @@ contract ParentToChildProver is IBlockHashProver { /// - accountProof: from accountProof.proof.proofRelatedNodes (42 elements) /// - accountValue: from accountProof.proof.value (192 bytes) /// - storageLeafIndex: from storageProofs[0].leafIndex - /// - storageProof: from storageProofs[0].proof.proofRelatedNodes (42 elements) + /// - proof: from storageProofs[0].proof.proofRelatedNodes (42 elements) /// - storageValue: the claimed storage value (32 bytes, to verify) /// /// Security: This function verifies that: @@ -123,12 +117,12 @@ contract ParentToChildProver is IBlockHashProver { /// 5. The storage proof corresponds to the claimed slot (hKey check) /// 6. The storage value matches the proof's hValue /// - /// @param targetBlockHash The L2 SMT state root (from getTargetBlockHash or verifyTargetBlockHash) + /// @param targetStateCommitment The L2 SMT state root (from getTargetStateCommitment or verifyTargetStateCommitment) /// @param input ABI encoded proof data from linea_getProof /// @return account The address of the account on L2 /// @return slot The storage slot /// @return value The value at the storage slot - function verifyStorageSlot(bytes32 targetBlockHash, bytes calldata input) + function verifyStorageSlot(bytes32 targetStateCommitment, bytes calldata input) external pure returns (address account, uint256 slot, bytes32 value) @@ -153,7 +147,7 @@ contract ParentToChildProver is IBlockHashProver { ) = abi.decode(input, (address, uint256, uint256, bytes[], bytes, uint256, bytes[], bytes32)); // Step 1: Verify account proof against L2 state root (SMT) - bool accountValid = SparseMerkleProof.verifyProof(accountProof, accountLeafIndex, targetBlockHash); + bool accountValid = SparseMerkleProof.verifyProof(accountProof, accountLeafIndex, targetStateCommitment); if (!accountValid) { revert InvalidAccountProof(); } @@ -201,7 +195,7 @@ contract ParentToChildProver is IBlockHashProver { value = claimedStorageValue; } - /// @inheritdoc IBlockHashProver + /// @inheritdoc IStateProver function version() external pure returns (uint256) { return 1; } diff --git a/src/contracts/provers/optimism/ChildToParentProver.sol b/src/contracts/provers/optimism/ChildToParentProver.sol index 51f9800..acef4fc 100644 --- a/src/contracts/provers/optimism/ChildToParentProver.sol +++ b/src/contracts/provers/optimism/ChildToParentProver.sol @@ -2,21 +2,21 @@ pragma solidity 0.8.30; import {ProverUtils} from "../../libraries/ProverUtils.sol"; -import {IBlockHashProver} from "../../interfaces/IBlockHashProver.sol"; +import {IStateProver} from "../../interfaces/IStateProver.sol"; interface IL1Block { function hash() external view returns (bytes32); } -/// @notice OP-stack implementation of a child to parent IBlockHashProver. -/// @dev verifyTargetBlockHash and getTargetBlockHash get block hashes from the L1Block predeploy. +/// @notice OP-stack implementation of a child to parent IStateProver. +/// @dev verifyTargetStateCommitment and getTargetStateCommitment get block hashes from the L1Block predeploy. /// verifyStorageSlot is implemented to work against any target chain with a standard Ethereum block header and state trie. /// /// @dev Note: L1Block only stores the LATEST L1 block hash. /// Historical messages CAN be verified by generating fresh proofs on-demand. /// Pre-generated proofs become stale when L1Block updates (~5 minutes). /// Operational difference from Arbitrum: proofs must be generated just-in-time rather than pre-cached. -contract ChildToParentProver is IBlockHashProver { +contract ChildToParentProver is IStateProver { address public constant l1BlockPredeploy = 0x4200000000000000000000000000000000000015; uint256 public constant l1BlockHashSlot = 2; // hash is at slot 2 @@ -33,10 +33,10 @@ contract ChildToParentProver is IBlockHashProver { /// @notice Verify the latest available target block hash given a home chain block hash and a storage proof of the L1Block predeploy. /// @param homeBlockHash The block hash of the home chain. /// @param input ABI encoded (bytes blockHeader, bytes accountProof, bytes storageProof) - function verifyTargetBlockHash(bytes32 homeBlockHash, bytes calldata input) + function verifyTargetStateCommitment(bytes32 homeBlockHash, bytes calldata input) external view - returns (bytes32 targetBlockHash) + returns (bytes32 targetStateCommitment) { if (block.chainid == homeChainId) { revert CallOnHomeChain(); @@ -49,7 +49,7 @@ contract ChildToParentProver is IBlockHashProver { (rlpBlockHeader, accountProof, storageProof) = abi.decode(input, (bytes, bytes, bytes)); // verify proofs and get the value - targetBlockHash = ProverUtils.getSlotFromBlockHeader( + targetStateCommitment = ProverUtils.getSlotFromBlockHeader( homeBlockHash, rlpBlockHeader, l1BlockPredeploy, l1BlockHashSlot, accountProof, storageProof ); } @@ -63,7 +63,7 @@ contract ChildToParentProver is IBlockHashProver { /// If the L1Block is consistently updated too frequently, calls to the Receiver may be DoS'd. /// In this case, this prover contract may need to be modified to use a different source of block hashes, /// such as a backup contract that calls the L1Block predeploy and caches the latest block hash. - function getTargetBlockHash(bytes calldata) external view returns (bytes32 targetBlockHash) { + function getTargetStateCommitment(bytes calldata) external view returns (bytes32 targetStateCommitment) { if (block.chainid != homeChainId) { revert CallNotOnHomeChain(); } @@ -71,9 +71,9 @@ contract ChildToParentProver is IBlockHashProver { } /// @notice Verify a storage slot given a target chain block hash and a proof. - /// @param targetBlockHash The block hash of the target chain. + /// @param targetStateCommitment The block hash of the target chain. /// @param input ABI encoded (bytes blockHeader, address account, uint256 slot, bytes accountProof, bytes storageProof) - function verifyStorageSlot(bytes32 targetBlockHash, bytes calldata input) + function verifyStorageSlot(bytes32 targetStateCommitment, bytes calldata input) external pure returns (address account, uint256 slot, bytes32 value) @@ -87,11 +87,11 @@ contract ChildToParentProver is IBlockHashProver { // verify proofs and get the value value = ProverUtils.getSlotFromBlockHeader( - targetBlockHash, rlpBlockHeader, account, slot, accountProof, storageProof + targetStateCommitment, rlpBlockHeader, account, slot, accountProof, storageProof ); } - /// @inheritdoc IBlockHashProver + /// @inheritdoc IStateProver function version() external pure returns (uint256) { return 1; } diff --git a/src/contracts/provers/optimism/ParentToChildProver.sol b/src/contracts/provers/optimism/ParentToChildProver.sol index 3f7cc19..97c276d 100644 --- a/src/contracts/provers/optimism/ParentToChildProver.sol +++ b/src/contracts/provers/optimism/ParentToChildProver.sol @@ -4,7 +4,7 @@ pragma solidity 0.8.30; import {Lib_SecureMerkleTrie} from "@eth-optimism/contracts/libraries/trie/Lib_SecureMerkleTrie.sol"; import {Lib_RLPReader} from "@eth-optimism/contracts/libraries/rlp/Lib_RLPReader.sol"; import {ProverUtils} from "../../libraries/ProverUtils.sol"; -import {IBlockHashProver} from "../../interfaces/IBlockHashProver.sol"; +import {IStateProver} from "../../interfaces/IStateProver.sol"; import {Bytes} from "@openzeppelin/contracts/utils/Bytes.sol"; interface IAnchorStateRegistry { @@ -15,10 +15,10 @@ interface IFaultDisputeGame { function rootClaim() external view returns (bytes32); } -/// @notice OP-stack implementation of a parent to child IBlockHashProver. -/// @dev verifyTargetBlockHash and getTargetBlockHash get block hashes from a valid fault dispute game proxy contract. +/// @notice OP-stack implementation of a parent to child IStateProver. +/// @dev verifyTargetStateCommitment and getTargetStateCommitment get block hashes from a valid fault dispute game proxy contract. /// verifyStorageSlot is implemented to work against any OP-stack child chain with a standard Ethereum block header and state trie. -contract ParentToChildProver is IBlockHashProver { +contract ParentToChildProver is IStateProver { using Lib_RLPReader for Lib_RLPReader.RLPItem; struct OutputRootProof { @@ -59,10 +59,10 @@ contract ParentToChildProver is IBlockHashProver { /// bytes gameProxyAccountProof, /// bytes gameProxyCode, /// bytes rootClaimPreimage) - function verifyTargetBlockHash(bytes32 homeBlockHash, bytes calldata input) + function verifyTargetStateCommitment(bytes32 homeBlockHash, bytes calldata input) external view - returns (bytes32 targetBlockHash) + returns (bytes32 targetStateCommitment) { if (block.chainid == homeChainId) { revert CallOnHomeChain(); @@ -117,7 +117,7 @@ contract ParentToChildProver is IBlockHashProver { /// 2. Verify the root claim preimage against the game's root claim. /// 3. Return the latest block hash from the root claim preimage. /// @param input ABI encoded (address gameProxy, OutputRootProof rootClaimPreimage) - function getTargetBlockHash(bytes calldata input) external view returns (bytes32 targetBlockHash) { + function getTargetStateCommitment(bytes calldata input) external view returns (bytes32 targetStateCommitment) { if (block.chainid != homeChainId) { revert CallNotOnHomeChain(); } @@ -135,9 +135,9 @@ contract ParentToChildProver is IBlockHashProver { } /// @notice Verify a storage slot given a target chain block hash and a proof. - /// @param targetBlockHash The block hash of the target chain. + /// @param targetStateCommitment The block hash of the target chain. /// @param input ABI encoded (bytes blockHeader, address account, uint256 slot, bytes accountProof, bytes storageProof) - function verifyStorageSlot(bytes32 targetBlockHash, bytes calldata input) + function verifyStorageSlot(bytes32 targetStateCommitment, bytes calldata input) external pure returns (address account, uint256 slot, bytes32 value) @@ -151,11 +151,11 @@ contract ParentToChildProver is IBlockHashProver { // verify proofs and get the value value = ProverUtils.getSlotFromBlockHeader( - targetBlockHash, rlpBlockHeader, account, slot, accountProof, storageProof + targetStateCommitment, rlpBlockHeader, account, slot, accountProof, storageProof ); } - /// @inheritdoc IBlockHashProver + /// @inheritdoc IStateProver function version() external pure returns (uint256) { return 1; } diff --git a/src/contracts/provers/scroll/ChildToParentProver.sol b/src/contracts/provers/scroll/ChildToParentProver.sol index ad442cf..fdcba4a 100644 --- a/src/contracts/provers/scroll/ChildToParentProver.sol +++ b/src/contracts/provers/scroll/ChildToParentProver.sol @@ -1,21 +1,23 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.28; +pragma solidity 0.8.30; import {ProverUtils} from "../../libraries/ProverUtils.sol"; -import {IBlockHashProver} from "../../interfaces/IBlockHashProver.sol"; +import {IStateProver} from "../../interfaces/IStateProver.sol"; import {IBuffer} from "block-hash-pusher/contracts/interfaces/IBuffer.sol"; import {SlotDerivation} from "@openzeppelin/contracts/utils/SlotDerivation.sol"; -/// @notice implementation of a child to parent IBlockHashProver. -/// @dev verifyTargetBlockHash and getTargetBlockHash get block hashes from the block hash buffer on Scroll. -/// See https://github.com/OffchainLabs/block-hash-pusher/blob/a1e26f2e42e6306d1e7f03c5d20fa6aa64ff7a12 for more details. +/// @notice Scroll implementation of a child to parent IStateProver. +/// @dev verifyTargetStateCommitment and getTargetStateCommitment get block hashes from the block hash buffer. +/// See https://github.com/openintentsframework/broadcaster/blob/main/src/contracts/block-hash-pusher for more details. /// verifyStorageSlot is implemented to work against any parent chain with a standard Ethereum block header and state trie. -contract ChildToParentProver is IBlockHashProver { +contract ChildToParentProver is IStateProver { + /// @dev Address of the block hash buffer contract. address public immutable blockHashBuffer; /// @dev Storage slot the buffer contract uses to store block hashes. - /// See https://github.com/OffchainLabs/block-hash-pusher/blob/a1e26f2e42e6306d1e7f03c5d20fa6aa64ff7a12/contracts/Buffer.sol#L32 + /// https://github.com/openintentsframework/broadcaster/blob/main/src/contracts/block-hash-pusher/BaseBuffer.sol uint256 public constant blockHashMappingSlot = 51; + /// @dev The chain ID of the home chain (child chain). uint256 public immutable homeChainId; error CallNotOnHomeChain(); @@ -29,10 +31,10 @@ contract ChildToParentProver is IBlockHashProver { /// @notice Get a parent chain block hash from the buffer at `blockHashBuffer` using a storage proof /// @param homeBlockHash The block hash of the home chain. /// @param input ABI encoded (bytes blockHeader, uint256 targetBlockNumber, bytes accountProof, bytes storageProof) - function verifyTargetBlockHash(bytes32 homeBlockHash, bytes calldata input) + function verifyTargetStateCommitment(bytes32 homeBlockHash, bytes calldata input) external view - returns (bytes32 targetBlockHash) + returns (bytes32 targetStateCommitment) { if (block.chainid == homeChainId) { revert CallOnHomeChain(); @@ -46,28 +48,28 @@ contract ChildToParentProver is IBlockHashProver { uint256 slot = uint256(SlotDerivation.deriveMapping(bytes32(blockHashMappingSlot), targetBlockNumber)); // verify proofs and get the block hash - targetBlockHash = ProverUtils.getSlotFromBlockHeader( + targetStateCommitment = ProverUtils.getSlotFromBlockHeader( homeBlockHash, rlpBlockHeader, blockHashBuffer, slot, accountProof, storageProof ); } /// @notice Get a parent chain block hash from the buffer at `blockHashBuffer`. /// @param input ABI encoded (uint256 targetBlockNumber) - function getTargetBlockHash(bytes calldata input) external view returns (bytes32 targetBlockHash) { + function getTargetStateCommitment(bytes calldata input) external view returns (bytes32 targetStateCommitment) { if (block.chainid != homeChainId) { revert CallNotOnHomeChain(); } - //decode the input + // decode the input uint256 targetBlockNumber = abi.decode(input, (uint256)); // get the block hash from the buffer - targetBlockHash = IBuffer(blockHashBuffer).parentChainBlockHash(targetBlockNumber); + targetStateCommitment = IBuffer(blockHashBuffer).parentChainBlockHash(targetBlockNumber); } /// @notice Verify a storage slot given a target chain block hash and a proof. - /// @param targetBlockHash The block hash of the target chain. + /// @param targetStateCommitment The block hash of the target chain. /// @param input ABI encoded (bytes blockHeader, address account, uint256 slot, bytes accountProof, bytes storageProof) - function verifyStorageSlot(bytes32 targetBlockHash, bytes calldata input) + function verifyStorageSlot(bytes32 targetStateCommitment, bytes calldata input) external pure returns (address account, uint256 slot, bytes32 value) @@ -81,11 +83,11 @@ contract ChildToParentProver is IBlockHashProver { // verify proofs and get the value value = ProverUtils.getSlotFromBlockHeader( - targetBlockHash, rlpBlockHeader, account, slot, accountProof, storageProof + targetStateCommitment, rlpBlockHeader, account, slot, accountProof, storageProof ); } - /// @inheritdoc IBlockHashProver + /// @inheritdoc IStateProver function version() external pure returns (uint256) { return 1; } diff --git a/src/contracts/provers/scroll/ParentToChildProver.sol b/src/contracts/provers/scroll/ParentToChildProver.sol index 4471f17..c974ead 100644 --- a/src/contracts/provers/scroll/ParentToChildProver.sol +++ b/src/contracts/provers/scroll/ParentToChildProver.sol @@ -2,33 +2,21 @@ pragma solidity 0.8.30; import {ProverUtils} from "../../libraries/ProverUtils.sol"; -import {IBlockHashProver} from "../../interfaces/IBlockHashProver.sol"; +import {IStateProver} from "../../interfaces/IStateProver.sol"; import {SlotDerivation} from "@openzeppelin/contracts/utils/SlotDerivation.sol"; +import {IScrollChain} from "@scroll-tech/scroll-contracts/L1/rollup/IScrollChain.sol"; -/// @notice Interface for Scroll's ScrollChain contract on L1 -interface IScrollChain { - /// @notice Returns the finalized state root for a given batch index - /// @param batchIndex The index of the batch - /// @return The state root of the finalized batch - function finalizedStateRoots(uint256 batchIndex) external view returns (bytes32); - - /// @notice Returns whether a batch is finalized - /// @param batchIndex The index of the batch - /// @return Whether the batch is finalized - function isBatchFinalized(uint256 batchIndex) external view returns (bool); -} - -/// @notice Scroll implementation of a parent to child IBlockHashProver. +/// @notice Scroll implementation of a parent to child IStateProver. /// @dev Home chain: L1 (Ethereum). Target chain: L2 (Scroll). -/// getTargetBlockHash reads finalized L2 state roots directly from L1's ScrollChain. -/// verifyTargetBlockHash verifies L2 state roots via storage proof against L1's ScrollChain. +/// getTargetStateCommitment reads finalized L2 state roots directly from L1's ScrollChain. +/// verifyTargetStateCommitment verifies L2 state roots via storage proof against L1's ScrollChain. /// verifyStorageSlot verifies storage against the L2 state root using standard MPT proofs. /// /// NOTE: Unlike other provers that return block hashes, Scroll stores STATE ROOTS directly -/// in the ScrollChain contract. The "targetBlockHash" returned by this prover is actually +/// in the ScrollChain contract. The "targetStateCommitment" returned by this prover is actually /// the L2 state root, which can be used directly for MPT verification without needing /// the L2 block header. -contract ParentToChildProver is IBlockHashProver { +contract ParentToChildProver is IStateProver { /// @dev Address of the ScrollChain contract on L1 address public immutable scrollChain; @@ -58,11 +46,11 @@ contract ParentToChildProver is IBlockHashProver { /// @dev Called on non-home chains (e.g., another L2 that has L1 block hashes) /// @param homeBlockHash The L1 block hash /// @param input ABI encoded (bytes rlpBlockHeader, uint256 batchIndex, bytes accountProof, bytes storageProof) - /// @return targetBlockHash The L2 state root stored in L1's ScrollChain (NOTE: this is a state root, not a block hash) - function verifyTargetBlockHash(bytes32 homeBlockHash, bytes calldata input) + /// @return targetStateCommitment The L2 state root stored in L1's ScrollChain (NOTE: this is a state root, not a block hash) + function verifyTargetStateCommitment(bytes32 homeBlockHash, bytes calldata input) external view - returns (bytes32 targetBlockHash) + returns (bytes32 targetStateCommitment) { if (block.chainid == homeChainId) { revert CallOnHomeChain(); @@ -77,11 +65,11 @@ contract ParentToChildProver is IBlockHashProver { uint256 slot = uint256(SlotDerivation.deriveMapping(bytes32(finalizedStateRootsSlot), batchIndex)); // Verify proofs and get the L2 state root from L1's ScrollChain - targetBlockHash = ProverUtils.getSlotFromBlockHeader( + targetStateCommitment = ProverUtils.getSlotFromBlockHeader( homeBlockHash, rlpBlockHeader, scrollChain, slot, accountProof, storageProof ); - if (targetBlockHash == bytes32(0)) { + if (targetStateCommitment == bytes32(0)) { revert StateRootNotFound(); } } @@ -89,8 +77,8 @@ contract ParentToChildProver is IBlockHashProver { /// @notice Get L2 state root directly from L1 ScrollChain /// @dev Called on home chain (L1) /// @param input ABI encoded (uint256 batchIndex) - /// @return targetBlockHash The L2 state root (NOTE: this is a state root, not a block hash) - function getTargetBlockHash(bytes calldata input) external view returns (bytes32 targetBlockHash) { + /// @return targetStateCommitment The L2 state root (NOTE: this is a state root, not a block hash) + function getTargetStateCommitment(bytes calldata input) external view returns (bytes32 targetStateCommitment) { if (block.chainid != homeChainId) { revert CallNotOnHomeChain(); } @@ -99,9 +87,9 @@ contract ParentToChildProver is IBlockHashProver { uint256 batchIndex = abi.decode(input, (uint256)); // Get the state root from ScrollChain - targetBlockHash = IScrollChain(scrollChain).finalizedStateRoots(batchIndex); + targetStateCommitment = IScrollChain(scrollChain).finalizedStateRoots(batchIndex); - if (targetBlockHash == bytes32(0)) { + if (targetStateCommitment == bytes32(0)) { revert StateRootNotFound(); } } @@ -109,17 +97,17 @@ contract ParentToChildProver is IBlockHashProver { /// @notice Verify a storage slot given an L2 state root and a proof /// @dev Since Scroll stores state roots directly (not block hashes), we can verify /// the storage proof directly against the state root without needing the block header. - /// @param targetBlockHash The L2 state root (NOTE: despite the name, this is a state root) + /// @param targetStateCommitment The L2 state root (NOTE: despite the name, this is a state root) /// @param input ABI encoded (address account, uint256 slot, bytes accountProof, bytes storageProof) /// @return account The address of the account on L2 /// @return slot The storage slot /// @return value The value of the storage slot - function verifyStorageSlot(bytes32 targetBlockHash, bytes calldata input) + function verifyStorageSlot(bytes32 targetStateCommitment, bytes calldata input) external pure returns (address account, uint256 slot, bytes32 value) { - // Decode the input - note: no block header needed since targetBlockHash IS the state root + // Decode the input - note: no block header needed since targetStateCommitment IS the state root bytes memory accountProof; bytes memory storageProof; (account, slot, accountProof, storageProof) = abi.decode(input, (address, uint256, bytes, bytes)); @@ -127,7 +115,7 @@ contract ParentToChildProver is IBlockHashProver { // Verify proofs directly against the state root // This works because ScrollChain stores state roots, not block hashes value = ProverUtils.getStorageSlotFromStateRoot( - targetBlockHash, // This is actually the state root + targetStateCommitment, // This is actually the state root accountProof, storageProof, account, @@ -135,7 +123,7 @@ contract ParentToChildProver is IBlockHashProver { ); } - /// @inheritdoc IBlockHashProver + /// @inheritdoc IStateProver function version() external pure returns (uint256) { return 1; } diff --git a/src/contracts/provers/taiko/ChildToParentProver.sol b/src/contracts/provers/taiko/ChildToParentProver.sol index ccde7c5..b3ab4d8 100644 --- a/src/contracts/provers/taiko/ChildToParentProver.sol +++ b/src/contracts/provers/taiko/ChildToParentProver.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.28; +pragma solidity 0.8.30; import {ProverUtils} from "../../libraries/ProverUtils.sol"; -import {IBlockHashProver} from "../../interfaces/IBlockHashProver.sol"; +import {IStateProver} from "../../interfaces/IStateProver.sol"; import {SlotDerivation} from "@openzeppelin/contracts/utils/SlotDerivation.sol"; interface ICheckpointStore { @@ -15,12 +15,12 @@ interface ICheckpointStore { function getCheckpoint(uint48 _blockNumber) external view returns (Checkpoint memory); } -/// @notice Taiko implementation of a child to parent IBlockHashProver. +/// @notice Taiko implementation of a child to parent IStateProver. /// @dev Home chain: L2 (Taiko). Target chain: L1 (Ethereum). -/// verifyTargetBlockHash gets L1 block hashes from L2's SignalService checkpoint storage. -/// getTargetBlockHash reads L1 block hashes directly from L2's SignalService. +/// verifyTargetStateCommitment gets L1 block hashes from L2's SignalService checkpoint storage. +/// getTargetStateCommitment reads L1 block hashes directly from L2's SignalService. /// verifyStorageSlot works against any Ethereum-compatible chain with standard block headers. -contract ChildToParentProver is IBlockHashProver { +contract ChildToParentProver is IStateProver { /// @dev Address of the L2 SignalService contract address public immutable signalService; @@ -46,11 +46,11 @@ contract ChildToParentProver is IBlockHashProver { /// @dev Called on non-home chains (e.g., Ethereum L1) /// @param homeBlockHash The L2 block hash /// @param input ABI encoded (bytes rlpBlockHeader, uint48 l1BlockNumber, bytes accountProof, bytes storageProof) - /// @return targetBlockHash The L1 block hash stored in L2's SignalService - function verifyTargetBlockHash(bytes32 homeBlockHash, bytes calldata input) + /// @return targetStateCommitment The L1 block hash stored in L2's SignalService + function verifyTargetStateCommitment(bytes32 homeBlockHash, bytes calldata input) external view - returns (bytes32 targetBlockHash) + returns (bytes32 targetStateCommitment) { if (block.chainid == homeChainId) { revert CallOnHomeChain(); @@ -66,11 +66,11 @@ contract ChildToParentProver is IBlockHashProver { // Verify proofs and get the L1 block hash from L2's SignalService // CheckpointRecord.blockHash is stored at the base slot - targetBlockHash = ProverUtils.getSlotFromBlockHeader( + targetStateCommitment = ProverUtils.getSlotFromBlockHeader( homeBlockHash, rlpBlockHeader, signalService, slot, accountProof, storageProof ); - if (targetBlockHash == bytes32(0)) { + if (targetStateCommitment == bytes32(0)) { revert TargetBlockHashNotFound(); } } @@ -78,8 +78,8 @@ contract ChildToParentProver is IBlockHashProver { /// @notice Get L1 block hash directly from L2 SignalService /// @dev Called on home chain (L2) /// @param input ABI encoded (uint48 l1BlockNumber) - /// @return targetBlockHash The L1 block hash - function getTargetBlockHash(bytes calldata input) external view returns (bytes32 targetBlockHash) { + /// @return targetStateCommitment The L1 block hash + function getTargetStateCommitment(bytes calldata input) external view returns (bytes32 targetStateCommitment) { if (block.chainid != homeChainId) { revert CallNotOnHomeChain(); } @@ -90,20 +90,20 @@ contract ChildToParentProver is IBlockHashProver { // Get the checkpoint from SignalService ICheckpointStore.Checkpoint memory checkpoint = ICheckpointStore(signalService).getCheckpoint(l1BlockNumber); - targetBlockHash = checkpoint.blockHash; + targetStateCommitment = checkpoint.blockHash; - if (targetBlockHash == bytes32(0)) { + if (targetStateCommitment == bytes32(0)) { revert TargetBlockHashNotFound(); } } /// @notice Verify a storage slot given a target chain block hash and a proof - /// @param targetBlockHash The block hash of the target chain (L1) + /// @param targetStateCommitment The block hash of the target chain (L1) /// @param input ABI encoded (bytes blockHeader, address account, uint256 slot, bytes accountProof, bytes storageProof) /// @return account The address of the account on the target chain /// @return slot The storage slot of the account on the target chain /// @return value The value of the storage slot - function verifyStorageSlot(bytes32 targetBlockHash, bytes calldata input) + function verifyStorageSlot(bytes32 targetStateCommitment, bytes calldata input) external pure returns (address account, uint256 slot, bytes32 value) @@ -117,11 +117,11 @@ contract ChildToParentProver is IBlockHashProver { // Verify proofs and get the value value = ProverUtils.getSlotFromBlockHeader( - targetBlockHash, rlpBlockHeader, account, slot, accountProof, storageProof + targetStateCommitment, rlpBlockHeader, account, slot, accountProof, storageProof ); } - /// @inheritdoc IBlockHashProver + /// @inheritdoc IStateProver function version() external pure returns (uint256) { return 1; } diff --git a/src/contracts/provers/taiko/ParentToChildProver.sol b/src/contracts/provers/taiko/ParentToChildProver.sol index 7135dde..228b3e5 100644 --- a/src/contracts/provers/taiko/ParentToChildProver.sol +++ b/src/contracts/provers/taiko/ParentToChildProver.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.28; +pragma solidity 0.8.30; import {ProverUtils} from "../../libraries/ProverUtils.sol"; -import {IBlockHashProver} from "../../interfaces/IBlockHashProver.sol"; +import {IStateProver} from "../../interfaces/IStateProver.sol"; import {SlotDerivation} from "@openzeppelin/contracts/utils/SlotDerivation.sol"; interface ICheckpointStore { @@ -15,12 +15,12 @@ interface ICheckpointStore { function getCheckpoint(uint48 _blockNumber) external view returns (Checkpoint memory); } -/// @notice Taiko implementation of a parent to child IBlockHashProver. +/// @notice Taiko implementation of a parent to child IStateProver. /// @dev Home chain: L1 (Ethereum). Target chain: L2 (Taiko). -/// verifyTargetBlockHash gets L2 block hashes from L1's SignalService checkpoint storage. -/// getTargetBlockHash reads L2 block hashes directly from L1's SignalService. +/// verifyTargetStateCommitment gets L2 block hashes from L1's SignalService checkpoint storage. +/// getTargetStateCommitment reads L2 block hashes directly from L1's SignalService. /// verifyStorageSlot works against any Ethereum-compatible chain with standard block headers. -contract ParentToChildProver is IBlockHashProver { +contract ParentToChildProver is IStateProver { /// @dev Address of the L1 SignalService contract address public immutable signalService; @@ -46,11 +46,11 @@ contract ParentToChildProver is IBlockHashProver { /// @dev Called on non-home chains (e.g., Taiko L2) /// @param homeBlockHash The L1 block hash /// @param input ABI encoded (bytes rlpBlockHeader, uint48 l2BlockNumber, bytes accountProof, bytes storageProof) - /// @return targetBlockHash The L2 block hash stored in L1's SignalService - function verifyTargetBlockHash(bytes32 homeBlockHash, bytes calldata input) + /// @return targetStateCommitment The L2 block hash stored in L1's SignalService + function verifyTargetStateCommitment(bytes32 homeBlockHash, bytes calldata input) external view - returns (bytes32 targetBlockHash) + returns (bytes32 targetStateCommitment) { if (block.chainid == homeChainId) { revert CallOnHomeChain(); @@ -66,11 +66,11 @@ contract ParentToChildProver is IBlockHashProver { // Verify proofs and get the L2 block hash from L1's SignalService // CheckpointRecord.blockHash is stored at the base slot - targetBlockHash = ProverUtils.getSlotFromBlockHeader( + targetStateCommitment = ProverUtils.getSlotFromBlockHeader( homeBlockHash, rlpBlockHeader, signalService, slot, accountProof, storageProof ); - if (targetBlockHash == bytes32(0)) { + if (targetStateCommitment == bytes32(0)) { revert TargetBlockHashNotFound(); } } @@ -78,8 +78,8 @@ contract ParentToChildProver is IBlockHashProver { /// @notice Get L2 block hash directly from L1 SignalService /// @dev Called on home chain (L1) /// @param input ABI encoded (uint48 l2BlockNumber) - /// @return targetBlockHash The L2 block hash - function getTargetBlockHash(bytes calldata input) external view returns (bytes32 targetBlockHash) { + /// @return targetStateCommitment The L2 block hash + function getTargetStateCommitment(bytes calldata input) external view returns (bytes32 targetStateCommitment) { if (block.chainid != homeChainId) { revert CallNotOnHomeChain(); } @@ -90,20 +90,20 @@ contract ParentToChildProver is IBlockHashProver { // Get the checkpoint from SignalService ICheckpointStore.Checkpoint memory checkpoint = ICheckpointStore(signalService).getCheckpoint(l2BlockNumber); - targetBlockHash = checkpoint.blockHash; + targetStateCommitment = checkpoint.blockHash; - if (targetBlockHash == bytes32(0)) { + if (targetStateCommitment == bytes32(0)) { revert TargetBlockHashNotFound(); } } /// @notice Verify a storage slot given a target chain block hash and a proof - /// @param targetBlockHash The block hash of the target chain (L2) + /// @param targetStateCommitment The block hash of the target chain (L2) /// @param input ABI encoded (bytes blockHeader, address account, uint256 slot, bytes accountProof, bytes storageProof) /// @return account The address of the account on the target chain /// @return slot The storage slot of the account on the target chain /// @return value The value of the storage slot - function verifyStorageSlot(bytes32 targetBlockHash, bytes calldata input) + function verifyStorageSlot(bytes32 targetStateCommitment, bytes calldata input) external pure returns (address account, uint256 slot, bytes32 value) @@ -117,11 +117,11 @@ contract ParentToChildProver is IBlockHashProver { // Verify proofs and get the value value = ProverUtils.getSlotFromBlockHeader( - targetBlockHash, rlpBlockHeader, account, slot, accountProof, storageProof + targetStateCommitment, rlpBlockHeader, account, slot, accountProof, storageProof ); } - /// @inheritdoc IBlockHashProver + /// @inheritdoc IStateProver function version() external pure returns (uint256) { return 1; } diff --git a/src/contracts/provers/zksync/ChildToParentProver.sol b/src/contracts/provers/zksync/ChildToParentProver.sol index e65ea18..a025a69 100644 --- a/src/contracts/provers/zksync/ChildToParentProver.sol +++ b/src/contracts/provers/zksync/ChildToParentProver.sol @@ -1,21 +1,23 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.28; +pragma solidity 0.8.30; import {ProverUtils} from "../../libraries/ProverUtils.sol"; -import {IBlockHashProver} from "../../interfaces/IBlockHashProver.sol"; +import {IStateProver} from "../../interfaces/IStateProver.sol"; import {IBuffer} from "block-hash-pusher/contracts/interfaces/IBuffer.sol"; import {SlotDerivation} from "@openzeppelin/contracts/utils/SlotDerivation.sol"; -/// @notice implementation of a child to parent IBlockHashProver. -/// @dev verifyTargetBlockHash and getTargetBlockHash get block hashes from the block hash buffer on ZkSync. -/// See https://github.com/OffchainLabs/block-hash-pusher/blob/a1e26f2e42e6306d1e7f03c5d20fa6aa64ff7a12 for more details. +/// @notice ZkSync implementation of a child to parent IStateProver. +/// @dev verifyTargetStateCommitment and getTargetStateCommitment get block hashes from the block hash buffer. +/// See https://github.com/openintentsframework/broadcaster/blob/main/src/contracts/block-hash-pusher for more details. /// verifyStorageSlot is implemented to work against any parent chain with a standard Ethereum block header and state trie. -contract ChildToParentProver is IBlockHashProver { +contract ChildToParentProver is IStateProver { + /// @dev Address of the block hash buffer contract. address public immutable blockHashBuffer; /// @dev Storage slot the buffer contract uses to store block hashes. - /// See https://github.com/OffchainLabs/block-hash-pusher/blob/a1e26f2e42e6306d1e7f03c5d20fa6aa64ff7a12/contracts/Buffer.sol#L32 + /// See https://github.com/openintentsframework/broadcaster/blob/main/src/contracts/block-hash-pusher/BaseBuffer.sol uint256 public constant blockHashMappingSlot = 51; + /// @dev The chain ID of the home chain (child chain). uint256 public immutable homeChainId; error CallNotOnHomeChain(); @@ -29,10 +31,10 @@ contract ChildToParentProver is IBlockHashProver { /// @notice Get a parent chain block hash from the buffer at `blockHashBuffer` using a storage proof /// @param homeBlockHash The block hash of the home chain. /// @param input ABI encoded (bytes blockHeader, uint256 targetBlockNumber, bytes accountProof, bytes storageProof) - function verifyTargetBlockHash(bytes32 homeBlockHash, bytes calldata input) + function verifyTargetStateCommitment(bytes32 homeBlockHash, bytes calldata input) external view - returns (bytes32 targetBlockHash) + returns (bytes32 targetStateCommitment) { if (block.chainid == homeChainId) { revert CallOnHomeChain(); @@ -46,28 +48,28 @@ contract ChildToParentProver is IBlockHashProver { uint256 slot = uint256(SlotDerivation.deriveMapping(bytes32(blockHashMappingSlot), targetBlockNumber)); // verify proofs and get the block hash - targetBlockHash = ProverUtils.getSlotFromBlockHeader( + targetStateCommitment = ProverUtils.getSlotFromBlockHeader( homeBlockHash, rlpBlockHeader, blockHashBuffer, slot, accountProof, storageProof ); } /// @notice Get a parent chain block hash from the buffer at `blockHashBuffer`. /// @param input ABI encoded (uint256 targetBlockNumber) - function getTargetBlockHash(bytes calldata input) external view returns (bytes32 targetBlockHash) { + function getTargetStateCommitment(bytes calldata input) external view returns (bytes32 targetStateCommitment) { if (block.chainid != homeChainId) { revert CallNotOnHomeChain(); } - //decode the input + // decode the input uint256 targetBlockNumber = abi.decode(input, (uint256)); // get the block hash from the buffer - targetBlockHash = IBuffer(blockHashBuffer).parentChainBlockHash(targetBlockNumber); + targetStateCommitment = IBuffer(blockHashBuffer).parentChainBlockHash(targetBlockNumber); } /// @notice Verify a storage slot given a target chain block hash and a proof. - /// @param targetBlockHash The block hash of the target chain. + /// @param targetStateCommitment The block hash of the target chain. /// @param input ABI encoded (bytes blockHeader, address account, uint256 slot, bytes accountProof, bytes storageProof) - function verifyStorageSlot(bytes32 targetBlockHash, bytes calldata input) + function verifyStorageSlot(bytes32 targetStateCommitment, bytes calldata input) external pure returns (address account, uint256 slot, bytes32 value) @@ -81,11 +83,11 @@ contract ChildToParentProver is IBlockHashProver { // verify proofs and get the value value = ProverUtils.getSlotFromBlockHeader( - targetBlockHash, rlpBlockHeader, account, slot, accountProof, storageProof + targetStateCommitment, rlpBlockHeader, account, slot, accountProof, storageProof ); } - /// @inheritdoc IBlockHashProver + /// @inheritdoc IStateProver function version() external pure returns (uint256) { return 1; } diff --git a/src/contracts/provers/zksync/ParentToChildProver.sol b/src/contracts/provers/zksync/ParentToChildProver.sol index 1d7588b..da08949 100644 --- a/src/contracts/provers/zksync/ParentToChildProver.sol +++ b/src/contracts/provers/zksync/ParentToChildProver.sol @@ -2,9 +2,8 @@ pragma solidity 0.8.30; import {ProverUtils} from "../../libraries/ProverUtils.sol"; -import {IBlockHashProver} from "../../interfaces/IBlockHashProver.sol"; +import {IStateProver} from "../../interfaces/IStateProver.sol"; import {SlotDerivation} from "@openzeppelin/contracts/utils/SlotDerivation.sol"; - import {MessageHashing, ProofData} from "./libraries/MessageHashing.sol"; /// @notice Interface for interacting with ZkChain contracts to retrieve L2 logs root hashes. @@ -54,14 +53,14 @@ struct ZkSyncProof { bytes32[] proof; } -/// @notice ZkSync implementation of a parent to child IBlockHashProver. +/// @notice ZkSync implementation of a parent to child IStateProver. /// @dev This contract verifies L2 logs root hashes from ZkSync child chains on the parent chain (L1). -/// The `verifyTargetBlockHash` and `getTargetBlockHash` functions retrieve L2 logs root hashes +/// The `verifyTargetStateCommitment` and `getTargetStateCommitment` functions retrieve L2 logs root hashes /// from the child chain's ZkChain contract. The `verifyStorageSlot` function is implemented /// to work against any ZkSync child chain with a standard Ethereum block header and state trie. /// This implementation is used to verify zkChain L2 log hash inclusion on L1 for messages that /// use the gateway as a middleware between the L2 and the L1. -contract ParentToChildProver is IBlockHashProver { +contract ParentToChildProver is IStateProver { /// @notice The ZkChain contract address on the gateway chain that stores L2 logs root hashes. IZkChain public immutable gatewayZkChain; @@ -81,7 +80,7 @@ contract ParentToChildProver is IBlockHashProver { error L2LogsRootHashNotFound(); /// @notice Error thrown when an operation is attempted on a chain that is not the home chain. - error NotInHomeChain(); + error CallNotOnHomeChain(); /// @notice Error thrown when the batch settlement root does not match the expected target batch root. error BatchSettlementRootMismatch(); @@ -112,16 +111,16 @@ contract ParentToChildProver is IBlockHashProver { /// @notice Verify a target chain L2 logs root hash given a home chain block hash and a proof. /// @dev Verifies that the L2 logs root hash for a specific batch is stored in the gateway ZkChain contract /// by checking the storage slot using storage proofs against the home chain block header. - /// @param homeBlockHash The block hash of the home chain (L1) containing the gateway ZkChain state. + /// @param homeStateCommitment The block hash of the home chain (L1) containing the gateway ZkChain state. /// @param input ABI encoded tuple: (bytes rlpBlockHeader, uint256 batchNumber, bytes storageProof). /// - rlpBlockHeader: RLP-encoded block header of the home chain. /// - batchNumber: The batch number for which to retrieve the L2 logs root hash. - /// - storageProof: Storage proof for the storage slot containing the L2 logs root hash. - /// @return targetL2LogsRootHash The L2 logs root hash for the specified batch number. - function verifyTargetBlockHash(bytes32 homeBlockHash, bytes calldata input) + /// - proof: Storage proof for the storage slot containing the L2 logs root hash. + /// @return targetStateCommitment The L2 logs root hash for the specified batch number. + function verifyTargetStateCommitment(bytes32 homeStateCommitment, bytes calldata input) external view - returns (bytes32 targetL2LogsRootHash) + returns (bytes32 targetStateCommitment) { if (block.chainid == homeChainId) { revert CallOnHomeChain(); @@ -132,9 +131,9 @@ contract ParentToChildProver is IBlockHashProver { uint256 slot = uint256(SlotDerivation.deriveMapping(bytes32(l2LogsRootHashSlot), batchNumber)); - // verify proofs and get the block hash - targetL2LogsRootHash = ProverUtils.getSlotFromBlockHeader( - homeBlockHash, rlpBlockHeader, address(gatewayZkChain), slot, accountProof, storageProof + // verify proofs and get the L2 logs root hash + targetStateCommitment = ProverUtils.getSlotFromBlockHeader( + homeStateCommitment, rlpBlockHeader, address(gatewayZkChain), slot, accountProof, storageProof ); } @@ -142,17 +141,17 @@ contract ParentToChildProver is IBlockHashProver { /// @dev Directly queries the gateway ZkChain contract on the home chain to retrieve the L2 logs root hash. /// This function must be called on the home chain where the gateway ZkChain contract is deployed. /// @param input ABI encoded uint256 batchNumber - the batch number for which to retrieve the L2 logs root hash. - /// @return l2LogsRootHash The L2 logs root hash for the specified batch number. + /// @return targetStateCommitment The L2 logs root hash for the specified batch number. /// @custom:reverts L2LogsRootHashNotFound if the L2 logs root hash is not found (returns zero). - function getTargetBlockHash(bytes calldata input) external view returns (bytes32 l2LogsRootHash) { + function getTargetStateCommitment(bytes calldata input) external view returns (bytes32 targetStateCommitment) { if (block.chainid != homeChainId) { - revert NotInHomeChain(); + revert CallNotOnHomeChain(); } uint256 batchNumber = abi.decode(input, (uint256)); - l2LogsRootHash = gatewayZkChain.l2LogsRootHash(batchNumber); + targetStateCommitment = gatewayZkChain.l2LogsRootHash(batchNumber); - if (l2LogsRootHash == bytes32(0)) { + if (targetStateCommitment == bytes32(0)) { revert L2LogsRootHashNotFound(); } } @@ -161,7 +160,7 @@ contract ParentToChildProver is IBlockHashProver { /// @dev Verifies that an L2 message is included in a batch by checking its inclusion in the L2 logs Merkle tree. /// The message data is expected to contain a message hash and timestamp, which are used to derive /// the storage slot and value on the target chain. - /// @param targetL2LogRootHash The L2 logs root hash of the target chain batch to verify against. + /// @param targetStateCommitment The L2 logs root hash of the target chain batch to verify against. /// @param input ABI encoded ZkSyncProof containing: /// - batchNumber: The batch number containing the message. /// - index: The leaf proof mask for the message in the Merkle tree. @@ -171,7 +170,7 @@ contract ParentToChildProver is IBlockHashProver { /// @return slot The storage slot derived from the account address and message hash. /// @return value The timestamp value stored in the message data. /// @custom:reverts BatchSettlementRootMismatch if the message is not included in the batch. - function verifyStorageSlot(bytes32 targetL2LogRootHash, bytes calldata input) + function verifyStorageSlot(bytes32 targetStateCommitment, bytes calldata input) external view returns (address account, uint256 slot, bytes32 value) @@ -194,7 +193,7 @@ contract ParentToChildProver is IBlockHashProver { _leafProofMask: proof.index, _leaf: hashedLog, _proof: proof.proof, - _targetBatchRoot: targetL2LogRootHash + _targetBatchRoot: targetStateCommitment })) { revert BatchSettlementRootMismatch(); } @@ -275,9 +274,7 @@ contract ParentToChildProver is IBlockHashProver { }); } - /// @notice Returns the version of this block hash prover implementation. - /// @inheritdoc IBlockHashProver - /// @return The version number (currently 1). + /// @inheritdoc IStateProver function version() external pure returns (uint256) { return 1; } diff --git a/src/ts/ChildToParentProverHelper.ts b/src/ts/ChildToParentProverHelper.ts index f5027a9..dbc7051 100644 --- a/src/ts/ChildToParentProverHelper.ts +++ b/src/ts/ChildToParentProverHelper.ts @@ -16,7 +16,7 @@ import { childToParentProverAbi, iBufferAbi } from '../../wagmi/abi' /** * ChildToParentProverHelper is a class that provides helper methods for interacting - * with the child to parent IBlockHashProver contract. + * with the child to parent IStateProver contract. * * It extends the BaseProverHelper class and implements the IProverHelper interface. * @@ -38,27 +38,27 @@ export class ChildToParentProverHelper */ async buildInputForGetTargetBlockHash(): Promise<{ input: Hex - targetBlockHash: Hash + targetStateCommitment: Hash }> { - const { targetBlockHash, targetBlockNumber } = + const { targetStateCommitment, targetBlockNumber } = await this._findLatestAvailableTargetChainBlock( await this.homeChainClient.getBlockNumber() ) return { input: encodeAbiParameters([{ type: 'uint256' }], [targetBlockNumber]), - targetBlockHash, + targetStateCommitment, } } async buildInputForGetTargetBlockHashByBlockNumber(blockNumber: bigint): Promise<{ input: Hex - targetBlockHash: Hash + targetStateCommitment: Hash }> { //// TODO return { input: encodeAbiParameters([{ type: 'uint256' }], [blockNumber]), - targetBlockHash: '0x' as `0x${string}`, + targetStateCommitment: '0x' as `0x${string}`, } } @@ -67,11 +67,11 @@ export class ChildToParentProverHelper */ async buildInputForVerifyTargetBlockHash( homeBlockHash: Hash - ): Promise<{ input: Hex; targetBlockHash: Hash }> { + ): Promise<{ input: Hex; targetStateCommitment: Hash }> { const homeBlockNumber = ( await this.homeChainClient.getBlock({ blockHash: homeBlockHash }) ).number - const { targetBlockHash, targetBlockNumber } = + const { targetStateCommitment, targetBlockNumber } = await this._findLatestAvailableTargetChainBlock(homeBlockNumber) const slot = hexToBigInt( @@ -104,7 +104,7 @@ export class ChildToParentProverHelper return { input, - targetBlockHash, + targetStateCommitment, } } @@ -112,18 +112,18 @@ export class ChildToParentProverHelper * @see IProverHelper.buildInputForVerifyStorageSlot */ async buildInputForVerifyStorageSlot( - targetBlockHash: Hash, + targetStateCommitment: Hash, account: Address, slot: bigint ): Promise<{ input: Hex; slotValue: Hash }> { const rlpBlockHeader = await this._getRlpBlockHeader( 'target', - targetBlockHash + targetStateCommitment ) const { rlpAccountProof, rlpStorageProof, slotValue } = await this._getRlpStorageAndAccountProof( 'target', - targetBlockHash, + targetStateCommitment, account, slot ) @@ -146,20 +146,20 @@ export class ChildToParentProverHelper async _findLatestAvailableTargetChainBlock(homeBlockNumber: bigint): Promise<{ targetBlockNumber: bigint - targetBlockHash: Hash + targetStateCommitment: Hash }> { const bufferContract = this._bufferContract() const targetBlockNumber = await bufferContract.read.newestBlockNumber({ blockNumber: homeBlockNumber, }) - const targetBlockHash = await bufferContract.read.parentChainBlockHash( + const targetStateCommitment = await bufferContract.read.parentChainBlockHash( [targetBlockNumber], { blockNumber: homeBlockNumber } ) return { targetBlockNumber, - targetBlockHash, + targetStateCommitment, } } diff --git a/src/ts/IProverHelper.ts b/src/ts/IProverHelper.ts index b8a1c3b..5806668 100644 --- a/src/ts/IProverHelper.ts +++ b/src/ts/IProverHelper.ts @@ -1,45 +1,45 @@ import { Address, Hash, Hex } from 'viem' /** - * IProverHelper defines the interface that IBlockHashProver helper classes must implement. + * IProverHelper defines the interface that IStateProver helper classes must implement. * * Implementations should avoid relying on specialized RPC capabilities such as large log queries. */ export interface IProverHelper { /** - * Builds the bytes input argument for the IBlockHashProver::getTargetBlockHash function. - * Finds the newest block hash that can be returned by getTargetBlockHash on the prover. + * Builds the bytes input argument for the IStateProver::getTargetStateCommitment function. + * Finds the newest block hash that can be returned by getTargetStateCommitment on the prover. * @returns The input bytes and the resulting target block hash. */ buildInputForGetTargetBlockHash(): Promise<{ input: Hex - targetBlockHash: Hash + targetStateCommitment: Hash }> /** - * Build the bytes input argument for the IBlockHashProver::verifyTargetBlockHash function. - * Finds the newest block hash that can be returned by verifyTargetBlockHash on the prover given the home block hash. + * Build the bytes input argument for the IStateProver::verifyTargetStateCommitment function. + * Finds the newest block hash that can be returned by verifyTargetStateCommitment on the prover given the home block hash. * @param homeBlockHash Home chain block hash that will be passed to the prover and proven against */ buildInputForVerifyTargetBlockHash( homeBlockHash: Hash - ): Promise<{ input: Hex; targetBlockHash: Hash }> + ): Promise<{ input: Hex; targetStateCommitment: Hash }> /** - * Build the bytes input argument for the IBlockHashProver::verifyStorageSlot function. - * @param targetBlockHash Target chain block hash that will be passed to the prover and proven against + * Build the bytes input argument for the IStateProver::verifyStorageSlot function. + * @param targetStateCommitment Target chain block hash that will be passed to the prover and proven against * @param account The account to prove the storage slot for * @param slot The storage slot to prove * @returns The input bytes and the slot value */ buildInputForVerifyStorageSlot( - targetBlockHash: Hash, + targetStateCommitment: Hash, account: Address, slot: bigint ): Promise<{ input: Hex; slotValue: Hash }> buildInputForGetTargetBlockHashByBlockNumber(blockNumber: bigint): Promise<{ input: Hex - targetBlockHash: Hash + targetStateCommitment: Hash }> } diff --git a/src/ts/ParentToChildProverHelper.ts b/src/ts/ParentToChildProverHelper.ts index 48aca5e..6a14d92 100644 --- a/src/ts/ParentToChildProverHelper.ts +++ b/src/ts/ParentToChildProverHelper.ts @@ -35,21 +35,21 @@ export class ParentToChildProverHelper */ async buildInputForGetTargetBlockHash(): Promise<{ input: Hex - targetBlockHash: Hash + targetStateCommitment: Hash }> { - const { targetBlockHash, sendRoot } = + const { targetStateCommitment, sendRoot } = await this._findLatestAvailableTargetChainBlock( await this.homeChainClient.getBlockNumber() ) return { input: encodeAbiParameters([{ type: 'bytes32' }], [sendRoot]), - targetBlockHash, + targetStateCommitment, } } async buildInputForGetTargetBlockHashByBlockNumber(blockNumber: bigint): Promise<{ input: Hex - targetBlockHash: Hash + targetStateCommitment: Hash }> { console.log("blockNumber", blockNumber); @@ -58,12 +58,12 @@ export class ParentToChildProverHelper // // @ts-ignore // console.log("sendRoot", targetBlock.sendRoot); - const { targetBlockHash, sendRoot } = await this._findLatestAvailableTargetChainBlock(blockNumber); + const { targetStateCommitment, sendRoot } = await this._findLatestAvailableTargetChainBlock(blockNumber); return { // @ts-ignore input: encodeAbiParameters([{ type: 'bytes32' }], [sendRoot]), - targetBlockHash: targetBlockHash, + targetStateCommitment: targetStateCommitment, } } @@ -72,8 +72,8 @@ export class ParentToChildProverHelper */ async buildInputForVerifyTargetBlockHash( homeBlockHash: Hash - ): Promise<{ input: Hex; targetBlockHash: Hash }> { - const { targetBlockHash, sendRoot } = + ): Promise<{ input: Hex; targetStateCommitment: Hash }> { + const { targetStateCommitment, sendRoot } = await this._findLatestAvailableTargetChainBlock( (await this.homeChainClient.getBlock({ blockHash: homeBlockHash })) .number @@ -111,7 +111,7 @@ export class ParentToChildProverHelper return { input, - targetBlockHash, + targetStateCommitment, } } @@ -119,18 +119,18 @@ export class ParentToChildProverHelper * @see IProverHelper.buildInputForVerifyStorageSlot */ async buildInputForVerifyStorageSlot( - targetBlockHash: Hash, + targetStateCommitment: Hash, account: Address, slot: bigint ): Promise<{ input: Hex; slotValue: Hash }> { const rlpBlockHeader = await this._getRlpBlockHeader( 'target', - targetBlockHash + targetStateCommitment ) const { rlpAccountProof, rlpStorageProof, slotValue } = await this._getRlpStorageAndAccountProof( 'target', - targetBlockHash, + targetStateCommitment, account, slot ) @@ -162,7 +162,7 @@ export class ParentToChildProverHelper overrides?: { logBlockRangeSize?: bigint; maxLogLookback?: bigint } ): Promise<{ sendRoot: Hash - targetBlockHash: Hash + targetStateCommitment: Hash }> { const logBlockRangeSize = overrides?.logBlockRangeSize ?? this.defaultLogBlockRangeSize @@ -207,7 +207,7 @@ export class ParentToChildProverHelper return { sendRoot: latestEvent.args.outputRoot!, - targetBlockHash: latestEvent.args.l2BlockHash!, + targetStateCommitment: latestEvent.args.l2BlockHash!, } } diff --git a/src/ts/optimism/ChildToParentProverHelper.ts b/src/ts/optimism/ChildToParentProverHelper.ts index e366110..8dde1ec 100644 --- a/src/ts/optimism/ChildToParentProverHelper.ts +++ b/src/ts/optimism/ChildToParentProverHelper.ts @@ -24,40 +24,40 @@ export class OptimismChildToParentProverHelper readonly l1BlockHashSlot: bigint = 2n // hash is at slot 2 /** - * Build input for getTargetBlockHash() + * Build input for getTargetStateCommitment() * For Optimism, this reads the L1Block predeploy directly, so input can be empty */ async buildInputForGetTargetBlockHash(): Promise<{ input: Hex - targetBlockHash: Hash + targetStateCommitment: Hash }> { // Read the L1 block hash directly from the L1Block predeploy - const targetBlockHash = await this.homeChainClient.getStorageAt({ + const targetStateCommitment = await this.homeChainClient.getStorageAt({ address: this.l1BlockPredeploy, slot: `0x${this.l1BlockHashSlot.toString(16)}` as Hex, }) as Hash - // getTargetBlockHash() on Optimism doesn't need any input + // getTargetStateCommitment() on Optimism doesn't need any input // It reads the predeploy directly return { input: '0x' as Hex, - targetBlockHash, + targetStateCommitment, } } /** - * Build input for verifyTargetBlockHash() + * Build input for verifyTargetStateCommitment() * This requires Merkle proofs of the L1Block predeploy's storage */ async buildInputForVerifyTargetBlockHash( homeBlockHash: Hash - ): Promise<{ input: Hex; targetBlockHash: Hash }> { + ): Promise<{ input: Hex; targetStateCommitment: Hash }> { const homeBlockNumber = ( await this.homeChainClient.getBlock({ blockHash: homeBlockHash }) ).number // Read the L1 block hash from the predeploy at this specific block - const targetBlockHash = await this.homeChainClient.getStorageAt({ + const targetStateCommitment = await this.homeChainClient.getStorageAt({ address: this.l1BlockPredeploy, slot: `0x${this.l1BlockHashSlot.toString(16)}` as Hex, blockNumber: homeBlockNumber, @@ -87,7 +87,7 @@ export class OptimismChildToParentProverHelper return { input, - targetBlockHash, + targetStateCommitment, } } @@ -96,18 +96,18 @@ export class OptimismChildToParentProverHelper * This verifies a storage slot on the target chain (Ethereum L1) */ async buildInputForVerifyStorageSlot( - targetBlockHash: Hash, + targetStateCommitment: Hash, account: Address, slot: bigint ): Promise<{ input: Hex; slotValue: Hash }> { const rlpBlockHeader = await this._getRlpBlockHeader( 'target', - targetBlockHash + targetStateCommitment ) const { rlpAccountProof, rlpStorageProof, slotValue } = await this._getRlpStorageAndAccountProof( 'target', - targetBlockHash, + targetStateCommitment, account, slot ) diff --git a/test/BlockHashProverPointer.t.sol b/test/BlockHashProverPointer.t.sol index 3fb3d93..f04a90a 100644 --- a/test/BlockHashProverPointer.t.sol +++ b/test/BlockHashProverPointer.t.sol @@ -2,55 +2,55 @@ pragma solidity 0.8.30; import {Test} from "forge-std/Test.sol"; -import {BlockHashProverPointer} from "../src/contracts/BlockHashProverPointer.sol"; +import {StateProverPointer} from "../src/contracts/StateProverPointer.sol"; import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; import {MockProver} from "./mocks/MockProver.sol"; -contract BlockHashProverPointerTest is Test { - BlockHashProverPointer public blockHashProverPointer; +contract StateProverPointerTest is Test { + StateProverPointer public stateProverPointer; MockProver public mockProver; address public owner = makeAddr("owner"); function setUp() public { - blockHashProverPointer = new BlockHashProverPointer(owner); + stateProverPointer = new StateProverPointer(owner); mockProver = new MockProver(); } function test_checkOwner() public view { - assertEq(blockHashProverPointer.owner(), owner); + assertEq(stateProverPointer.owner(), owner); } function test_setImplementationAddress() public { vm.prank(owner); - blockHashProverPointer.setImplementationAddress(address(mockProver)); - assertEq(blockHashProverPointer.implementationAddress(), address(mockProver)); - assertEq(blockHashProverPointer.implementationCodeHash(), address(mockProver).codehash); + stateProverPointer.setImplementationAddress(address(mockProver)); + assertEq(stateProverPointer.implementationAddress(), address(mockProver)); + assertEq(stateProverPointer.implementationCodeHash(), address(mockProver).codehash); } function test_setImplementationAddress_reverts_if_not_owner() public { vm.prank(makeAddr("notOwner")); vm.expectRevert(abi.encodeWithSelector(Ownable.OwnableUnauthorizedAccount.selector, makeAddr("notOwner"))); - blockHashProverPointer.setImplementationAddress(address(mockProver)); + stateProverPointer.setImplementationAddress(address(mockProver)); } function test_setImplementationAddress_reverts_if_version_is_not_increasing() public { vm.prank(owner); - blockHashProverPointer.setImplementationAddress(address(mockProver)); + stateProverPointer.setImplementationAddress(address(mockProver)); vm.prank(owner); - vm.expectRevert(abi.encodeWithSelector(BlockHashProverPointer.NonIncreasingVersion.selector, 1, 1)); - blockHashProverPointer.setImplementationAddress(address(mockProver)); + vm.expectRevert(abi.encodeWithSelector(StateProverPointer.NonIncreasingVersion.selector, 1, 1)); + stateProverPointer.setImplementationAddress(address(mockProver)); } function test_setImplementationAddress_reverts_if_implementation_address_is_invalid() public { vm.prank(owner); - vm.expectRevert(abi.encodeWithSelector(BlockHashProverPointer.InvalidImplementationAddress.selector)); - blockHashProverPointer.setImplementationAddress(address(0)); + vm.expectRevert(abi.encodeWithSelector(StateProverPointer.InvalidImplementationAddress.selector)); + stateProverPointer.setImplementationAddress(address(0)); } function test_setImplementationAddress_reverts_if_implementation_address_is_invalid_eoa() public { vm.prank(owner); - vm.expectRevert(abi.encodeWithSelector(BlockHashProverPointer.InvalidImplementationAddress.selector)); - blockHashProverPointer.setImplementationAddress(makeAddr("invalid")); + vm.expectRevert(abi.encodeWithSelector(StateProverPointer.InvalidImplementationAddress.selector)); + stateProverPointer.setImplementationAddress(makeAddr("invalid")); } } diff --git a/test/Receiver.Taiko.t.sol b/test/Receiver.Taiko.t.sol index 9de3e9d..aa8dedf 100644 --- a/test/Receiver.Taiko.t.sol +++ b/test/Receiver.Taiko.t.sol @@ -5,7 +5,7 @@ import {console, Test} from "forge-std/Test.sol"; import {stdJson} from "forge-std/StdJson.sol"; import {Receiver} from "../src/contracts/Receiver.sol"; import {IReceiver} from "../src/contracts/interfaces/IReceiver.sol"; -import {BlockHashProverPointer} from "../src/contracts/BlockHashProverPointer.sol"; +import {StateProverPointer} from "../src/contracts/StateProverPointer.sol"; import {ParentToChildProver as TaikoParentToChildProver} from "../src/contracts/provers/taiko/ParentToChildProver.sol"; import {ChildToParentProver as TaikoChildToParentProver} from "../src/contracts/provers/taiko/ChildToParentProver.sol"; @@ -25,7 +25,7 @@ contract ReceiverTaikoTest is Test { Receiver public receiver; TaikoParentToChildProver public parentToChildProver; TaikoChildToParentProver public childToParentProver; - BlockHashProverPointer public blockHashProverPointer; + StateProverPointer public stateProverPointer; // Note: We don't use forks anymore to avoid dependency on Taiko internal testnet // which can be reset at any time. Instead, we use local mocks with vm.chainId(). @@ -48,10 +48,10 @@ contract ReceiverTaikoTest is Test { receiver = new Receiver(); parentToChildProver = new TaikoParentToChildProver(L1_SIGNAL_SERVICE, CHECKPOINTS_SLOT, L1_CHAIN_ID); - blockHashProverPointer = new BlockHashProverPointer(owner); + stateProverPointer = new StateProverPointer(owner); vm.prank(owner); - blockHashProverPointer.setImplementationAddress(address(parentToChildProver)); + stateProverPointer.setImplementationAddress(address(parentToChildProver)); // Read proof data string memory proofPath = "test/payloads/taiko/taikoProofL2.json"; @@ -82,13 +82,13 @@ contract ReceiverTaikoTest is Test { bytes memory storageProofInput = abi.encode(rlpBlockHeader, account, slot, rlpAccountProof, rlpStorageProof); address[] memory route = new address[](1); - route[0] = address(blockHashProverPointer); + route[0] = address(stateProverPointer); - bytes[] memory bhpInputs = new bytes[](1); - bhpInputs[0] = abi.encode(uint48(blockNumber)); + bytes[] memory scpInputs = new bytes[](1); + scpInputs[0] = abi.encode(uint48(blockNumber)); IReceiver.RemoteReadArgs memory remoteReadArgs = - IReceiver.RemoteReadArgs({route: route, bhpInputs: bhpInputs, storageProof: storageProofInput}); + IReceiver.RemoteReadArgs({route: route, scpInputs: scpInputs, proof: storageProofInput}); // Read message info (auto-generated by broadcast-and-prove.sh) string memory infoPath = "test/payloads/taiko/taikoProofL2-info.json"; @@ -111,10 +111,10 @@ contract ReceiverTaikoTest is Test { receiver = new Receiver(); parentToChildProver = new TaikoParentToChildProver(L1_SIGNAL_SERVICE, CHECKPOINTS_SLOT, L1_CHAIN_ID); - blockHashProverPointer = new BlockHashProverPointer(owner); + stateProverPointer = new StateProverPointer(owner); vm.prank(owner); - blockHashProverPointer.setImplementationAddress(address(parentToChildProver)); + stateProverPointer.setImplementationAddress(address(parentToChildProver)); // Read proof data string memory proofPath = "test/payloads/taiko/taikoProofL2.json"; @@ -141,13 +141,13 @@ contract ReceiverTaikoTest is Test { bytes memory storageProofInput = abi.encode(rlpBlockHeader, account, slot, rlpAccountProof, rlpStorageProof); address[] memory route = new address[](1); - route[0] = address(blockHashProverPointer); + route[0] = address(stateProverPointer); - bytes[] memory bhpInputs = new bytes[](1); - bhpInputs[0] = abi.encode(uint48(blockNumber)); + bytes[] memory scpInputs = new bytes[](1); + scpInputs[0] = abi.encode(uint48(blockNumber)); IReceiver.RemoteReadArgs memory remoteReadArgs = - IReceiver.RemoteReadArgs({route: route, bhpInputs: bhpInputs, storageProof: storageProofInput}); + IReceiver.RemoteReadArgs({route: route, scpInputs: scpInputs, proof: storageProofInput}); // Read message info (auto-generated by broadcast-and-prove.sh) string memory infoPath = "test/payloads/taiko/taikoProofL2-info.json"; @@ -171,10 +171,10 @@ contract ReceiverTaikoTest is Test { receiver = new Receiver(); childToParentProver = new TaikoChildToParentProver(L2_SIGNAL_SERVICE, CHECKPOINTS_SLOT, L2_CHAIN_ID); - blockHashProverPointer = new BlockHashProverPointer(owner); + stateProverPointer = new StateProverPointer(owner); vm.prank(owner); - blockHashProverPointer.setImplementationAddress(address(childToParentProver)); + stateProverPointer.setImplementationAddress(address(childToParentProver)); // Read proof data string memory proofPath = "test/payloads/taiko/taikoProofL1.json"; @@ -204,13 +204,13 @@ contract ReceiverTaikoTest is Test { bytes memory storageProofInput = abi.encode(rlpBlockHeader, account, slot, rlpAccountProof, rlpStorageProof); address[] memory route = new address[](1); - route[0] = address(blockHashProverPointer); + route[0] = address(stateProverPointer); - bytes[] memory bhpInputs = new bytes[](1); - bhpInputs[0] = abi.encode(uint48(blockNumber)); + bytes[] memory scpInputs = new bytes[](1); + scpInputs[0] = abi.encode(uint48(blockNumber)); IReceiver.RemoteReadArgs memory remoteReadArgs = - IReceiver.RemoteReadArgs({route: route, bhpInputs: bhpInputs, storageProof: storageProofInput}); + IReceiver.RemoteReadArgs({route: route, scpInputs: scpInputs, proof: storageProofInput}); // Read message info (auto-generated by broadcast-and-prove.sh) string memory infoPath = "test/payloads/taiko/taikoProofL1-info.json"; diff --git a/test/Receiver.ethereum.t.sol b/test/Receiver.ethereum.t.sol index 8aa3f71..9b098c0 100644 --- a/test/Receiver.ethereum.t.sol +++ b/test/Receiver.ethereum.t.sol @@ -5,10 +5,10 @@ import {console, Test} from "forge-std/Test.sol"; import {stdJson} from "forge-std/StdJson.sol"; import {Receiver} from "../src/contracts/Receiver.sol"; import {IReceiver} from "../src/contracts/interfaces/IReceiver.sol"; -import {IBlockHashProver} from "../src/contracts/interfaces/IBlockHashProver.sol"; +import {IStateProver} from "../src/contracts/interfaces/IStateProver.sol"; import {IOutbox} from "@arbitrum/nitro-contracts/src/bridge/IOutbox.sol"; -import {IBlockHashProverPointer} from "../src/contracts/interfaces/IBlockHashProverPointer.sol"; -import {BLOCK_HASH_PROVER_POINTER_SLOT} from "../src/contracts/BlockHashProverPointer.sol"; +import {IStateProverPointer} from "../src/contracts/interfaces/IStateProverPointer.sol"; +import {STATE_PROVER_POINTER_SLOT} from "../src/contracts/StateProverPointer.sol"; import {BlockHeaders} from "./utils/BlockHeaders.sol"; import {IBuffer} from "block-hash-pusher/contracts/interfaces/IBuffer.sol"; import {BufferMock} from "./mocks/BufferMock.sol"; @@ -32,7 +32,7 @@ import { import { ParentToChildProver as ScrollParentToChildProver } from "../src/contracts/provers/scroll/ParentToChildProver.sol"; -import {BlockHashProverPointer} from "../src/contracts/BlockHashProverPointer.sol"; +import {StateProverPointer} from "../src/contracts/StateProverPointer.sol"; import {RLP} from "@openzeppelin/contracts/utils/RLP.sol"; import {MockZkChain} from "./provers/zksync/ParentChildToProver.t.sol"; @@ -129,10 +129,10 @@ contract ReceiverTest is Test { ZksyncParentToChildProver parentToChildProver = new ZksyncParentToChildProver(address(mockZkChain), 0, 300, 32657, block.chainid); - BlockHashProverPointer blockHashProverPointer = new BlockHashProverPointer(owner); + StateProverPointer stateProverPointer = new StateProverPointer(owner); vm.prank(owner); - blockHashProverPointer.setImplementationAddress(address(parentToChildProver)); + stateProverPointer.setImplementationAddress(address(parentToChildProver)); bytes32 message = 0x0000000000000000000000000000000000000000000000000000000074657374; // "test" address publisher = 0x9a56fFd72F4B526c523C733F1F74197A51c495E1; @@ -144,15 +144,15 @@ contract ReceiverTest is Test { bytes memory input = abi.encode(proof, publisher, message); address[] memory route = new address[](1); - route[0] = address(blockHashProverPointer); + route[0] = address(stateProverPointer); - bytes[] memory bhpInputs = new bytes[](1); - bhpInputs[0] = abi.encode(47506); + bytes[] memory scpInputs = new bytes[](1); + scpInputs[0] = abi.encode(47506); bytes memory storageProofToLastProver = input; IReceiver.RemoteReadArgs memory remoteReadArgs = - IReceiver.RemoteReadArgs({route: route, bhpInputs: bhpInputs, storageProof: storageProofToLastProver}); + IReceiver.RemoteReadArgs({route: route, scpInputs: scpInputs, proof: storageProofToLastProver}); (bytes32 broadcasterId, uint256 timestamp) = receiver.verifyBroadcastMessage(remoteReadArgs, message, publisher); @@ -167,7 +167,7 @@ contract ReceiverTest is Test { keccak256( abi.encode( bytes32(0x0000000000000000000000000000000000000000000000000000000000000000), - address(blockHashProverPointer) + address(stateProverPointer) ) ), expectedAccount @@ -188,10 +188,10 @@ contract ReceiverTest is Test { ZksyncParentToChildProver parentToChildProver = new ZksyncParentToChildProver(address(mockZkChain), 0, 300, 32657, block.chainid); - BlockHashProverPointer blockHashProverPointer = new BlockHashProverPointer(owner); + StateProverPointer stateProverPointer = new StateProverPointer(owner); vm.prank(owner); - blockHashProverPointer.setImplementationAddress(address(parentToChildProver)); + stateProverPointer.setImplementationAddress(address(parentToChildProver)); bytes32 message = 0x0000000000000000000000000000000000000000000000000000000000000000; address publisher = 0x9a56fFd72F4B526c523C733F1F74197A51c495E1; @@ -203,15 +203,15 @@ contract ReceiverTest is Test { bytes memory input = abi.encode(proof, publisher, message); address[] memory route = new address[](1); - route[0] = address(blockHashProverPointer); + route[0] = address(stateProverPointer); - bytes[] memory bhpInputs = new bytes[](1); - bhpInputs[0] = abi.encode(47506); + bytes[] memory scpInputs = new bytes[](1); + scpInputs[0] = abi.encode(47506); bytes memory storageProofToLastProver = input; IReceiver.RemoteReadArgs memory remoteReadArgs = - IReceiver.RemoteReadArgs({route: route, bhpInputs: bhpInputs, storageProof: storageProofToLastProver}); + IReceiver.RemoteReadArgs({route: route, scpInputs: scpInputs, proof: storageProofToLastProver}); vm.expectRevert(ZksyncParentToChildProver.SlotMismatch.selector); receiver.verifyBroadcastMessage(remoteReadArgs, message, publisher); diff --git a/test/Receiver.t.sol b/test/Receiver.t.sol index 0774cbf..a7f1aaf 100644 --- a/test/Receiver.t.sol +++ b/test/Receiver.t.sol @@ -5,10 +5,10 @@ import {console, Test} from "forge-std/Test.sol"; import {stdJson} from "forge-std/StdJson.sol"; import {Receiver} from "../src/contracts/Receiver.sol"; import {IReceiver} from "../src/contracts/interfaces/IReceiver.sol"; -import {IBlockHashProver} from "../src/contracts/interfaces/IBlockHashProver.sol"; +import {IStateProver} from "../src/contracts/interfaces/IStateProver.sol"; import {IOutbox} from "@arbitrum/nitro-contracts/src/bridge/IOutbox.sol"; -import {IBlockHashProverPointer} from "../src/contracts/interfaces/IBlockHashProverPointer.sol"; -import {BLOCK_HASH_PROVER_POINTER_SLOT} from "../src/contracts/BlockHashProverPointer.sol"; +import {IStateProverPointer} from "../src/contracts/interfaces/IStateProverPointer.sol"; +import {STATE_PROVER_POINTER_SLOT} from "../src/contracts/StateProverPointer.sol"; import {BlockHeaders} from "./utils/BlockHeaders.sol"; import {IBuffer} from "block-hash-pusher/contracts/interfaces/IBuffer.sol"; import {BufferMock} from "./mocks/BufferMock.sol"; @@ -27,7 +27,7 @@ import { import { ParentToChildProver as ScrollParentToChildProver } from "../src/contracts/provers/scroll/ParentToChildProver.sol"; -import {BlockHashProverPointer} from "../src/contracts/BlockHashProverPointer.sol"; +import {StateProverPointer} from "../src/contracts/StateProverPointer.sol"; import {RLP} from "@openzeppelin/contracts/utils/RLP.sol"; interface IL1Block { @@ -61,7 +61,7 @@ contract ReceiverTest is Test { IOutbox public outbox; // On-chain deployed ArbParentToChildProver on Sepolia - address constant ON_CHAIN_ARB_PROVER = 0x9e8BA3Ce052f2139f824885a78240839749F3370; + address constant ON_CHAIN_ARB_PROVER = 0x2aA03593adf471d6dd7c041984e6D7D6045E373f; address owner = makeAddr("owner"); @@ -105,10 +105,10 @@ contract ReceiverTest is Test { receiver = new Receiver(); ArbChildToParentProver childToParentProver = new ArbChildToParentProver(block.chainid); - BlockHashProverPointer blockHashProverPointer = new BlockHashProverPointer(owner); + StateProverPointer stateProverPointer = new StateProverPointer(owner); vm.prank(owner); - blockHashProverPointer.setImplementationAddress(address(childToParentProver)); + stateProverPointer.setImplementationAddress(address(childToParentProver)); bytes32 message = 0x0000000000000000000000000000000000000000000000000000000074657374; // "test" address publisher = 0x9a56fFd72F4B526c523C733F1F74197A51c495E1; @@ -145,15 +145,15 @@ contract ReceiverTest is Test { bytes memory input = abi.encode(rlpBlockHeader, account, expectedSlot, rlpAccountProof, rlpStorageProof); address[] memory route = new address[](1); - route[0] = address(blockHashProverPointer); + route[0] = address(stateProverPointer); - bytes[] memory bhpInputs = new bytes[](1); - bhpInputs[0] = abi.encode(blockNumber); + bytes[] memory scpInputs = new bytes[](1); + scpInputs[0] = abi.encode(blockNumber); bytes memory storageProofToLastProver = input; IReceiver.RemoteReadArgs memory remoteReadArgs = - IReceiver.RemoteReadArgs({route: route, bhpInputs: bhpInputs, storageProof: storageProofToLastProver}); + IReceiver.RemoteReadArgs({route: route, scpInputs: scpInputs, proof: storageProofToLastProver}); (bytes32 broadcasterId, uint256 timestamp) = receiver.verifyBroadcastMessage(remoteReadArgs, message, publisher); @@ -164,7 +164,7 @@ contract ReceiverTest is Test { keccak256( abi.encode( bytes32(0x0000000000000000000000000000000000000000000000000000000000000000), - address(blockHashProverPointer) + address(stateProverPointer) ) ), account @@ -181,10 +181,10 @@ contract ReceiverTest is Test { receiver = new Receiver(); ArbParentToChildProver parentToChildProver = _getOnChainArbProverCopy(); - BlockHashProverPointer blockHashProverPointer = new BlockHashProverPointer(owner); + StateProverPointer stateProverPointer = new StateProverPointer(owner); vm.prank(owner); - blockHashProverPointer.setImplementationAddress(address(parentToChildProver)); + stateProverPointer.setImplementationAddress(address(parentToChildProver)); bytes32 message = 0x0000000000000000000000000000000000000000000000000000000074657374; // "test" address publisher = 0x9a56fFd72F4B526c523C733F1F74197A51c495E1; @@ -217,15 +217,15 @@ contract ReceiverTest is Test { outbox.updateSendRoot(sendRoot, blockHash); address[] memory route = new address[](1); - route[0] = address(blockHashProverPointer); + route[0] = address(stateProverPointer); - bytes[] memory bhpInputs = new bytes[](1); - bhpInputs[0] = abi.encode(sendRoot); + bytes[] memory scpInputs = new bytes[](1); + scpInputs[0] = abi.encode(sendRoot); bytes memory storageProofToLastProver = input; IReceiver.RemoteReadArgs memory remoteReadArgs = - IReceiver.RemoteReadArgs({route: route, bhpInputs: bhpInputs, storageProof: storageProofToLastProver}); + IReceiver.RemoteReadArgs({route: route, scpInputs: scpInputs, proof: storageProofToLastProver}); (bytes32 broadcasterId, uint256 timestamp) = receiver.verifyBroadcastMessage(remoteReadArgs, message, publisher); @@ -236,7 +236,7 @@ contract ReceiverTest is Test { keccak256( abi.encode( bytes32(0x0000000000000000000000000000000000000000000000000000000000000000), - address(blockHashProverPointer) + address(stateProverPointer) ) ), account @@ -253,10 +253,10 @@ contract ReceiverTest is Test { OPChildToParentProver childToParentProver = new OPChildToParentProver(block.chainid); - BlockHashProverPointer blockHashProverPointer = new BlockHashProverPointer(owner); + StateProverPointer stateProverPointer = new StateProverPointer(owner); vm.prank(owner); - blockHashProverPointer.setImplementationAddress(address(childToParentProver)); + stateProverPointer.setImplementationAddress(address(childToParentProver)); bytes32 message = 0x0000000000000000000000000000000000000000000000000000000074657374; // "test" address publisher = 0x9a56fFd72F4B526c523C733F1F74197A51c495E1; @@ -290,15 +290,15 @@ contract ReceiverTest is Test { ); address[] memory route = new address[](1); - route[0] = address(blockHashProverPointer); + route[0] = address(stateProverPointer); - bytes[] memory bhpInputs = new bytes[](1); - bhpInputs[0] = bytes(""); + bytes[] memory scpInputs = new bytes[](1); + scpInputs[0] = bytes(""); bytes memory storageProofToLastProver = input; IReceiver.RemoteReadArgs memory remoteReadArgs = - IReceiver.RemoteReadArgs({route: route, bhpInputs: bhpInputs, storageProof: storageProofToLastProver}); + IReceiver.RemoteReadArgs({route: route, scpInputs: scpInputs, proof: storageProofToLastProver}); (bytes32 broadcasterId, uint256 timestamp) = receiver.verifyBroadcastMessage(remoteReadArgs, message, publisher); @@ -309,7 +309,7 @@ contract ReceiverTest is Test { keccak256( abi.encode( bytes32(0x0000000000000000000000000000000000000000000000000000000000000000), - address(blockHashProverPointer) + address(stateProverPointer) ) ), account @@ -320,21 +320,21 @@ contract ReceiverTest is Test { assertEq(timestamp, uint256(value), "wrong timestamp"); } - function test_updateBlockHashProverCopy_from_Arbitrum_into_OP() public { + function test_updateStateProverCopy_from_Arbitrum_into_OP() public { vm.selectFork(optimismForkId); receiver = new Receiver(); OPChildToParentProver childToParentProver = new OPChildToParentProver(block.chainid); - BlockHashProverPointer blockHashProverPointer = new BlockHashProverPointer(owner); + StateProverPointer stateProverPointer = new StateProverPointer(owner); vm.prank(owner); - blockHashProverPointer.setImplementationAddress(address(childToParentProver)); + stateProverPointer.setImplementationAddress(address(childToParentProver)); uint256 expectedSlot = uint256(keccak256("eip7888.pointer.slot")) - 1; - string memory path = "test/payloads/ethereum/arb_pointer_proof_block_9868604.json"; + string memory path = "test/payloads/ethereum/arb_pointer_proof_block_10128554.json"; string memory json = vm.readFile(path); uint256 blockNumber = json.readUint(".blockNumber"); @@ -361,28 +361,28 @@ contract ReceiverTest is Test { ); address[] memory route = new address[](1); - route[0] = address(blockHashProverPointer); + route[0] = address(stateProverPointer); - bytes[] memory bhpInputs = new bytes[](1); - bhpInputs[0] = bytes(""); + bytes[] memory scpInputs = new bytes[](1); + scpInputs[0] = bytes(""); bytes memory storageProofToLastProver = input; IReceiver.RemoteReadArgs memory remoteReadArgs = - IReceiver.RemoteReadArgs({route: route, bhpInputs: bhpInputs, storageProof: storageProofToLastProver}); + IReceiver.RemoteReadArgs({route: route, scpInputs: scpInputs, proof: storageProofToLastProver}); ArbParentToChildProver arbParentToChildProverCopy = _getOnChainArbProverCopy(); - bytes32 bhpPointerId = receiver.updateBlockHashProverCopy(remoteReadArgs, arbParentToChildProverCopy); + bytes32 scpPointerId = receiver.updateStateProverCopy(remoteReadArgs, arbParentToChildProverCopy); assertEq( - bhpPointerId, + scpPointerId, keccak256( abi.encode( keccak256( abi.encode( bytes32(0x0000000000000000000000000000000000000000000000000000000000000000), - address(blockHashProverPointer) + address(stateProverPointer) ) ), account @@ -393,17 +393,17 @@ contract ReceiverTest is Test { assertEq(address(arbParentToChildProverCopy).codehash, value, "wrong storage slot value"); } - function test_updateBlockHashProverCopy_from_Arbitrum_into_OP_reverts_when_different_code_hash() public { + function test_updateStateProverCopy_from_Arbitrum_into_OP_reverts_when_different_code_hash() public { vm.selectFork(optimismForkId); receiver = new Receiver(); OPChildToParentProver childToParentProver = new OPChildToParentProver(block.chainid); - BlockHashProverPointer blockHashProverPointer = new BlockHashProverPointer(owner); + StateProverPointer stateProverPointer = new StateProverPointer(owner); vm.prank(owner); - blockHashProverPointer.setImplementationAddress(address(childToParentProver)); + stateProverPointer.setImplementationAddress(address(childToParentProver)); uint256 expectedSlot = uint256(keccak256("eip7888.pointer.slot")) - 1; @@ -434,20 +434,20 @@ contract ReceiverTest is Test { ); address[] memory route = new address[](1); - route[0] = address(blockHashProverPointer); + route[0] = address(stateProverPointer); - bytes[] memory bhpInputs = new bytes[](1); - bhpInputs[0] = bytes(""); + bytes[] memory scpInputs = new bytes[](1); + scpInputs[0] = bytes(""); bytes memory storageProofToLastProver = input; IReceiver.RemoteReadArgs memory remoteReadArgs = - IReceiver.RemoteReadArgs({route: route, bhpInputs: bhpInputs, storageProof: storageProofToLastProver}); + IReceiver.RemoteReadArgs({route: route, scpInputs: scpInputs, proof: storageProofToLastProver}); ArbParentToChildProver arbParentToChildProverCopy = _getOnChainArbProverCopy(); vm.expectRevert(Receiver.DifferentCodeHash.selector); - receiver.updateBlockHashProverCopy(remoteReadArgs, arbParentToChildProverCopy); + receiver.updateStateProverCopy(remoteReadArgs, arbParentToChildProverCopy); } function test_verifyBroadcastMessage_from_Arbitrum_into_OP() public { @@ -457,19 +457,19 @@ contract ReceiverTest is Test { OPChildToParentProver childToParentProver = new OPChildToParentProver(block.chainid); - BlockHashProverPointer blockHashProverPointer = new BlockHashProverPointer(owner); + StateProverPointer stateProverPointer = new StateProverPointer(owner); ArbParentToChildProver arbParentToChildProverCopy = _getOnChainArbProverCopy(); address arbParentToChildProverPointerAddress; vm.prank(owner); - blockHashProverPointer.setImplementationAddress(address(childToParentProver)); + stateProverPointer.setImplementationAddress(address(childToParentProver)); // Update the Arbitrum Prover (ParentToChildProver) copy on OP chain { uint256 expectedSlot = uint256(keccak256("eip7888.pointer.slot")) - 1; - string memory path = "test/payloads/ethereum/arb_pointer_proof_block_9868604.json"; + string memory path = "test/payloads/ethereum/arb_pointer_proof_block_10128554.json"; string memory json = vm.readFile(path); uint256 blockNumber = json.readUint(".blockNumber"); @@ -499,26 +499,26 @@ contract ReceiverTest is Test { ); address[] memory route = new address[](1); - route[0] = address(blockHashProverPointer); + route[0] = address(stateProverPointer); - bytes[] memory bhpInputs = new bytes[](1); - bhpInputs[0] = bytes(""); + bytes[] memory scpInputs = new bytes[](1); + scpInputs[0] = bytes(""); bytes memory storageProofToLastProver = inputForOPChildToParentProver; IReceiver.RemoteReadArgs memory remoteReadArgs = - IReceiver.RemoteReadArgs({route: route, bhpInputs: bhpInputs, storageProof: storageProofToLastProver}); + IReceiver.RemoteReadArgs({route: route, scpInputs: scpInputs, proof: storageProofToLastProver}); - bytes32 bhpPointerId = receiver.updateBlockHashProverCopy(remoteReadArgs, arbParentToChildProverCopy); + bytes32 scpPointerId = receiver.updateStateProverCopy(remoteReadArgs, arbParentToChildProverCopy); assertEq( - bhpPointerId, + scpPointerId, keccak256( abi.encode( keccak256( abi.encode( bytes32(0x0000000000000000000000000000000000000000000000000000000000000000), - address(blockHashProverPointer) + address(stateProverPointer) ) ), account @@ -530,10 +530,10 @@ contract ReceiverTest is Test { } // Construct the route to verify a message broadcasted on Arbitrum chain in OP - // We need to construct three inputs: one for OPChildToParentProver getTargetBlockHash, - // one for ArbParentToChildProver verifyTargetBlockHash, and one for ArbParentToChildProver verifyStorageSlot + // We need to construct three inputs: one for OPChildToParentProver getTargetStateCommitment, + // one for ArbParentToChildProver verifyTargetStateCommitment, and one for ArbParentToChildProver verifyStorageSlot // the input to verifyStorageSlot is the proof of the broadcasted message itself. - // the input for verifyTargetBlockHash is the storage proof of the slot on the outbox contract. + // the input for verifyTargetStateCommitment is the storage proof of the slot on the outbox contract. string memory pathEthereum = "test/payloads/ethereum/output_storage_proof_block_9567705.json"; @@ -563,23 +563,23 @@ contract ReceiverTest is Test { ); address[] memory route = new address[](2); - route[0] = address(blockHashProverPointer); + route[0] = address(stateProverPointer); route[1] = arbParentToChildProverPointerAddress; bytes memory input0 = bytes(""); bytes memory input1 = abi.encode(rlpBlockHeaderEthereum, sendRootArbitrum, rlpAccountProofEthereum, rlpStorageProofEthereum); - bytes[] memory bhpInputs = new bytes[](2); - bhpInputs[0] = input0; - bhpInputs[1] = input1; + bytes[] memory scpInputs = new bytes[](2); + scpInputs[0] = input0; + scpInputs[1] = input1; bytes memory storageProofToLastProver = abi.encode( rlpBlockHeaderArbitrum, accountArbitrum, slotArbitrum, rlpAccountProofArbitrum, rlpStorageProofArbitrum ); IReceiver.RemoteReadArgs memory remoteReadArgs = - IReceiver.RemoteReadArgs({route: route, bhpInputs: bhpInputs, storageProof: storageProofToLastProver}); + IReceiver.RemoteReadArgs({route: route, scpInputs: scpInputs, proof: storageProofToLastProver}); bytes32 message = 0x0000000000000000000000000000000000000000000000000000000074657374; // "test" address publisher = 0x9a56fFd72F4B526c523C733F1F74197A51c495E1; @@ -593,7 +593,7 @@ contract ReceiverTest is Test { keccak256( abi.encode( bytes32(0x0000000000000000000000000000000000000000000000000000000000000000), - address(blockHashProverPointer) + address(stateProverPointer) ) ), arbParentToChildProverPointerAddress @@ -607,7 +607,7 @@ contract ReceiverTest is Test { assertEq(timestamp, uint256(valueArbitrum), "wrong timestamp"); } - function test_updateBlockHashProverCopy_from_Arbitrum_into_Zksync() public { + function test_updateStateProverCopy_from_Arbitrum_into_Zksync() public { vm.selectFork(zksyncForkId); receiver = new Receiver(); @@ -616,14 +616,14 @@ contract ReceiverTest is Test { ZksyncChildToParentProver childToParentProver = new ZksyncChildToParentProver(address(buffer), block.chainid); - BlockHashProverPointer blockHashProverPointer = new BlockHashProverPointer(owner); + StateProverPointer stateProverPointer = new StateProverPointer(owner); vm.prank(owner); - blockHashProverPointer.setImplementationAddress(address(childToParentProver)); + stateProverPointer.setImplementationAddress(address(childToParentProver)); uint256 expectedSlot = uint256(keccak256("eip7888.pointer.slot")) - 1; - string memory path = "test/payloads/ethereum/arb_pointer_proof_block_9868604.json"; + string memory path = "test/payloads/ethereum/arb_pointer_proof_block_10128554.json"; string memory json = vm.readFile(path); uint256 blockNumber = json.readUint(".blockNumber"); @@ -648,28 +648,28 @@ contract ReceiverTest is Test { buffer.receiveHashes(blockNumber, blockHashes); address[] memory route = new address[](1); - route[0] = address(blockHashProverPointer); + route[0] = address(stateProverPointer); - bytes[] memory bhpInputs = new bytes[](1); - bhpInputs[0] = abi.encode(blockNumber); + bytes[] memory scpInputs = new bytes[](1); + scpInputs[0] = abi.encode(blockNumber); bytes memory storageProofToLastProver = input; IReceiver.RemoteReadArgs memory remoteReadArgs = - IReceiver.RemoteReadArgs({route: route, bhpInputs: bhpInputs, storageProof: storageProofToLastProver}); + IReceiver.RemoteReadArgs({route: route, scpInputs: scpInputs, proof: storageProofToLastProver}); ArbParentToChildProver arbParentToChildProverCopy = _getOnChainArbProverCopy(); - bytes32 bhpPointerId = receiver.updateBlockHashProverCopy(remoteReadArgs, arbParentToChildProverCopy); + bytes32 scpPointerId = receiver.updateStateProverCopy(remoteReadArgs, arbParentToChildProverCopy); assertEq( - bhpPointerId, + scpPointerId, keccak256( abi.encode( keccak256( abi.encode( bytes32(0x0000000000000000000000000000000000000000000000000000000000000000), - address(blockHashProverPointer) + address(stateProverPointer) ) ), account @@ -689,19 +689,19 @@ contract ReceiverTest is Test { ZksyncChildToParentProver childToParentProver = new ZksyncChildToParentProver(address(buffer), block.chainid); - BlockHashProverPointer blockHashProverPointer = new BlockHashProverPointer(owner); + StateProverPointer stateProverPointer = new StateProverPointer(owner); ArbParentToChildProver arbParentToChildProverCopy = _getOnChainArbProverCopy(); address arbParentToChildProverPointerAddress; vm.prank(owner); - blockHashProverPointer.setImplementationAddress(address(childToParentProver)); + stateProverPointer.setImplementationAddress(address(childToParentProver)); // Update the Arbitrum Prover (ParentToChildProver) copy on ZKSync chain { uint256 expectedSlot = uint256(keccak256("eip7888.pointer.slot")) - 1; - string memory path = "test/payloads/ethereum/arb_pointer_proof_block_9868604.json"; + string memory path = "test/payloads/ethereum/arb_pointer_proof_block_10128554.json"; string memory json = vm.readFile(path); uint256 blockNumber = json.readUint(".blockNumber"); @@ -729,26 +729,26 @@ contract ReceiverTest is Test { buffer.receiveHashes(blockNumber, blockHashes); address[] memory route = new address[](1); - route[0] = address(blockHashProverPointer); + route[0] = address(stateProverPointer); - bytes[] memory bhpInputs = new bytes[](1); - bhpInputs[0] = abi.encode(blockNumber); + bytes[] memory scpInputs = new bytes[](1); + scpInputs[0] = abi.encode(blockNumber); bytes memory storageProofToLastProver = inputForOPChildToParentProver; IReceiver.RemoteReadArgs memory remoteReadArgs = - IReceiver.RemoteReadArgs({route: route, bhpInputs: bhpInputs, storageProof: storageProofToLastProver}); + IReceiver.RemoteReadArgs({route: route, scpInputs: scpInputs, proof: storageProofToLastProver}); - bytes32 bhpPointerId = receiver.updateBlockHashProverCopy(remoteReadArgs, arbParentToChildProverCopy); + bytes32 scpPointerId = receiver.updateStateProverCopy(remoteReadArgs, arbParentToChildProverCopy); assertEq( - bhpPointerId, + scpPointerId, keccak256( abi.encode( keccak256( abi.encode( bytes32(0x0000000000000000000000000000000000000000000000000000000000000000), - address(blockHashProverPointer) + address(stateProverPointer) ) ), account @@ -760,10 +760,10 @@ contract ReceiverTest is Test { } // Construct the route to verify a message broadcasted on Arbitrum chain in ZkSync - // We need to construct three inputs: one for ZksyncChildToParentProver getTargetBlockHash, - // one for ArbParentToChildProver verifyTargetBlockHash, and one for ArbParentToChildProver verifyStorageSlot + // We need to construct three inputs: one for ZksyncChildToParentProver getTargetStateCommitment, + // one for ArbParentToChildProver verifyTargetStateCommitment, and one for ArbParentToChildProver verifyStorageSlot // the input to verifyStorageSlot is the proof of the broadcasted message itself. - // the input for verifyTargetBlockHash is the storage proof of the slot on the outbox contract. + // the input for verifyTargetStateCommitment is the storage proof of the slot on the outbox contract. string memory pathEthereum = "test/payloads/ethereum/output_storage_proof_block_9567705.json"; @@ -791,23 +791,23 @@ contract ReceiverTest is Test { buffer.receiveHashes(blockNumberEthereum, blockHashes); address[] memory route = new address[](2); - route[0] = address(blockHashProverPointer); + route[0] = address(stateProverPointer); route[1] = arbParentToChildProverPointerAddress; bytes memory input0 = abi.encode(blockNumberEthereum); bytes memory input1 = abi.encode(rlpBlockHeaderEthereum, sendRootArbitrum, rlpAccountProofEthereum, rlpStorageProofEthereum); - bytes[] memory bhpInputs = new bytes[](2); - bhpInputs[0] = input0; - bhpInputs[1] = input1; + bytes[] memory scpInputs = new bytes[](2); + scpInputs[0] = input0; + scpInputs[1] = input1; bytes memory storageProofToLastProver = abi.encode( rlpBlockHeaderArbitrum, accountArbitrum, slotArbitrum, rlpAccountProofArbitrum, rlpStorageProofArbitrum ); IReceiver.RemoteReadArgs memory remoteReadArgs = - IReceiver.RemoteReadArgs({route: route, bhpInputs: bhpInputs, storageProof: storageProofToLastProver}); + IReceiver.RemoteReadArgs({route: route, scpInputs: scpInputs, proof: storageProofToLastProver}); bytes32 message = 0x0000000000000000000000000000000000000000000000000000000074657374; // "test" address publisher = 0x9a56fFd72F4B526c523C733F1F74197A51c495E1; @@ -821,7 +821,7 @@ contract ReceiverTest is Test { keccak256( abi.encode( bytes32(0x0000000000000000000000000000000000000000000000000000000000000000), - address(blockHashProverPointer) + address(stateProverPointer) ) ), arbParentToChildProverPointerAddress @@ -835,7 +835,7 @@ contract ReceiverTest is Test { assertEq(timestamp, uint256(valueArbitrum), "wrong timestamp"); } - function test_updateBlockHashProverCopy_from_Arbitrum_into_Linea() public { + function test_updateStateProverCopy_from_Arbitrum_into_Linea() public { vm.selectFork(lineaForkId); receiver = new Receiver(); @@ -844,14 +844,14 @@ contract ReceiverTest is Test { LineaChildToParentProver childToParentProver = new LineaChildToParentProver(address(buffer), block.chainid); - BlockHashProverPointer blockHashProverPointer = new BlockHashProverPointer(owner); + StateProverPointer stateProverPointer = new StateProverPointer(owner); vm.prank(owner); - blockHashProverPointer.setImplementationAddress(address(childToParentProver)); + stateProverPointer.setImplementationAddress(address(childToParentProver)); uint256 expectedSlot = uint256(keccak256("eip7888.pointer.slot")) - 1; - string memory path = "test/payloads/ethereum/arb_pointer_proof_block_9868604.json"; + string memory path = "test/payloads/ethereum/arb_pointer_proof_block_10128554.json"; string memory json = vm.readFile(path); uint256 blockNumber = json.readUint(".blockNumber"); @@ -876,28 +876,28 @@ contract ReceiverTest is Test { buffer.receiveHashes(blockNumber, blockHashes); address[] memory route = new address[](1); - route[0] = address(blockHashProverPointer); + route[0] = address(stateProverPointer); - bytes[] memory bhpInputs = new bytes[](1); - bhpInputs[0] = abi.encode(blockNumber); + bytes[] memory scpInputs = new bytes[](1); + scpInputs[0] = abi.encode(blockNumber); bytes memory storageProofToLastProver = input; IReceiver.RemoteReadArgs memory remoteReadArgs = - IReceiver.RemoteReadArgs({route: route, bhpInputs: bhpInputs, storageProof: storageProofToLastProver}); + IReceiver.RemoteReadArgs({route: route, scpInputs: scpInputs, proof: storageProofToLastProver}); ArbParentToChildProver arbParentToChildProverCopy = _getOnChainArbProverCopy(); - bytes32 bhpPointerId = receiver.updateBlockHashProverCopy(remoteReadArgs, arbParentToChildProverCopy); + bytes32 scpPointerId = receiver.updateStateProverCopy(remoteReadArgs, arbParentToChildProverCopy); assertEq( - bhpPointerId, + scpPointerId, keccak256( abi.encode( keccak256( abi.encode( bytes32(0x0000000000000000000000000000000000000000000000000000000000000000), - address(blockHashProverPointer) + address(stateProverPointer) ) ), account @@ -917,19 +917,19 @@ contract ReceiverTest is Test { LineaChildToParentProver childToParentProver = new LineaChildToParentProver(address(buffer), block.chainid); - BlockHashProverPointer blockHashProverPointer = new BlockHashProverPointer(owner); + StateProverPointer stateProverPointer = new StateProverPointer(owner); ArbParentToChildProver arbParentToChildProverCopy = _getOnChainArbProverCopy(); address arbParentToChildProverPointerAddress; vm.prank(owner); - blockHashProverPointer.setImplementationAddress(address(childToParentProver)); + stateProverPointer.setImplementationAddress(address(childToParentProver)); // Update the Arbitrum Prover (ParentToChildProver) copy on Linea chain { uint256 expectedSlot = uint256(keccak256("eip7888.pointer.slot")) - 1; - string memory path = "test/payloads/ethereum/arb_pointer_proof_block_9868604.json"; + string memory path = "test/payloads/ethereum/arb_pointer_proof_block_10128554.json"; string memory json = vm.readFile(path); uint256 blockNumber = json.readUint(".blockNumber"); @@ -957,26 +957,26 @@ contract ReceiverTest is Test { buffer.receiveHashes(blockNumber, blockHashes); address[] memory route = new address[](1); - route[0] = address(blockHashProverPointer); + route[0] = address(stateProverPointer); - bytes[] memory bhpInputs = new bytes[](1); - bhpInputs[0] = abi.encode(blockNumber); + bytes[] memory scpInputs = new bytes[](1); + scpInputs[0] = abi.encode(blockNumber); bytes memory storageProofToLastProver = inputForOPChildToParentProver; IReceiver.RemoteReadArgs memory remoteReadArgs = - IReceiver.RemoteReadArgs({route: route, bhpInputs: bhpInputs, storageProof: storageProofToLastProver}); + IReceiver.RemoteReadArgs({route: route, scpInputs: scpInputs, proof: storageProofToLastProver}); - bytes32 bhpPointerId = receiver.updateBlockHashProverCopy(remoteReadArgs, arbParentToChildProverCopy); + bytes32 scpPointerId = receiver.updateStateProverCopy(remoteReadArgs, arbParentToChildProverCopy); assertEq( - bhpPointerId, + scpPointerId, keccak256( abi.encode( keccak256( abi.encode( bytes32(0x0000000000000000000000000000000000000000000000000000000000000000), - address(blockHashProverPointer) + address(stateProverPointer) ) ), account @@ -988,10 +988,10 @@ contract ReceiverTest is Test { } // Construct the route to verify a message broadcasted on Arbitrum chain in Linea - // We need to construct three inputs: one for LineaChildToParentProver getTargetBlockHash, - // one for ArbParentToChildProver verifyTargetBlockHash, and one for ArbParentToChildProver verifyStorageSlot + // We need to construct three inputs: one for LineaChildToParentProver getTargetStateCommitment, + // one for ArbParentToChildProver verifyTargetStateCommitment, and one for ArbParentToChildProver verifyStorageSlot // the input to verifyStorageSlot is the proof of the broadcasted message itself. - // the input for verifyTargetBlockHash is the storage proof of the slot on the outbox contract. + // the input for verifyTargetStateCommitment is the storage proof of the slot on the outbox contract. string memory pathEthereum = "test/payloads/ethereum/output_storage_proof_block_9567705.json"; @@ -1019,23 +1019,23 @@ contract ReceiverTest is Test { buffer.receiveHashes(blockNumberEthereum, blockHashes); address[] memory route = new address[](2); - route[0] = address(blockHashProverPointer); + route[0] = address(stateProverPointer); route[1] = arbParentToChildProverPointerAddress; bytes memory input0 = abi.encode(blockNumberEthereum); bytes memory input1 = abi.encode(rlpBlockHeaderEthereum, sendRootArbitrum, rlpAccountProofEthereum, rlpStorageProofEthereum); - bytes[] memory bhpInputs = new bytes[](2); - bhpInputs[0] = input0; - bhpInputs[1] = input1; + bytes[] memory scpInputs = new bytes[](2); + scpInputs[0] = input0; + scpInputs[1] = input1; bytes memory storageProofToLastProver = abi.encode( rlpBlockHeaderArbitrum, accountArbitrum, slotArbitrum, rlpAccountProofArbitrum, rlpStorageProofArbitrum ); IReceiver.RemoteReadArgs memory remoteReadArgs = - IReceiver.RemoteReadArgs({route: route, bhpInputs: bhpInputs, storageProof: storageProofToLastProver}); + IReceiver.RemoteReadArgs({route: route, scpInputs: scpInputs, proof: storageProofToLastProver}); bytes32 message = 0x0000000000000000000000000000000000000000000000000000000074657374; // "test" address publisher = 0x9a56fFd72F4B526c523C733F1F74197A51c495E1; @@ -1049,7 +1049,7 @@ contract ReceiverTest is Test { keccak256( abi.encode( bytes32(0x0000000000000000000000000000000000000000000000000000000000000000), - address(blockHashProverPointer) + address(stateProverPointer) ) ), arbParentToChildProverPointerAddress @@ -1063,7 +1063,7 @@ contract ReceiverTest is Test { assertEq(timestamp, uint256(valueArbitrum), "wrong timestamp"); } - function test_updateBlockHashProverCopy_from_Arbitrum_into_Scroll() public { + function test_updateStateProverCopy_from_Arbitrum_into_Scroll() public { vm.selectFork(scrollForkId); receiver = new Receiver(); @@ -1072,14 +1072,14 @@ contract ReceiverTest is Test { ScrollChildToParentProver childToParentProver = new ScrollChildToParentProver(address(buffer), block.chainid); - BlockHashProverPointer blockHashProverPointer = new BlockHashProverPointer(owner); + StateProverPointer stateProverPointer = new StateProverPointer(owner); vm.prank(owner); - blockHashProverPointer.setImplementationAddress(address(childToParentProver)); + stateProverPointer.setImplementationAddress(address(childToParentProver)); uint256 expectedSlot = uint256(keccak256("eip7888.pointer.slot")) - 1; - string memory path = "test/payloads/ethereum/arb_pointer_proof_block_9868604.json"; + string memory path = "test/payloads/ethereum/arb_pointer_proof_block_10128554.json"; string memory json = vm.readFile(path); uint256 blockNumber = json.readUint(".blockNumber"); @@ -1104,28 +1104,28 @@ contract ReceiverTest is Test { buffer.receiveHashes(blockNumber, blockHashes); address[] memory route = new address[](1); - route[0] = address(blockHashProverPointer); + route[0] = address(stateProverPointer); - bytes[] memory bhpInputs = new bytes[](1); - bhpInputs[0] = abi.encode(blockNumber); + bytes[] memory scpInputs = new bytes[](1); + scpInputs[0] = abi.encode(blockNumber); bytes memory storageProofToLastProver = input; IReceiver.RemoteReadArgs memory remoteReadArgs = - IReceiver.RemoteReadArgs({route: route, bhpInputs: bhpInputs, storageProof: storageProofToLastProver}); + IReceiver.RemoteReadArgs({route: route, scpInputs: scpInputs, proof: storageProofToLastProver}); ArbParentToChildProver arbParentToChildProverCopy = _getOnChainArbProverCopy(); - bytes32 bhpPointerId = receiver.updateBlockHashProverCopy(remoteReadArgs, arbParentToChildProverCopy); + bytes32 scpPointerId = receiver.updateStateProverCopy(remoteReadArgs, arbParentToChildProverCopy); assertEq( - bhpPointerId, + scpPointerId, keccak256( abi.encode( keccak256( abi.encode( bytes32(0x0000000000000000000000000000000000000000000000000000000000000000), - address(blockHashProverPointer) + address(stateProverPointer) ) ), account @@ -1145,19 +1145,19 @@ contract ReceiverTest is Test { ScrollChildToParentProver childToParentProver = new ScrollChildToParentProver(address(buffer), block.chainid); - BlockHashProverPointer blockHashProverPointer = new BlockHashProverPointer(owner); + StateProverPointer stateProverPointer = new StateProverPointer(owner); ArbParentToChildProver arbParentToChildProverCopy = _getOnChainArbProverCopy(); address arbParentToChildProverPointerAddress; vm.prank(owner); - blockHashProverPointer.setImplementationAddress(address(childToParentProver)); + stateProverPointer.setImplementationAddress(address(childToParentProver)); // Update the Arbitrum Prover (ParentToChildProver) copy on Scroll chain { uint256 expectedSlot = uint256(keccak256("eip7888.pointer.slot")) - 1; - string memory path = "test/payloads/ethereum/arb_pointer_proof_block_9868604.json"; + string memory path = "test/payloads/ethereum/arb_pointer_proof_block_10128554.json"; string memory json = vm.readFile(path); uint256 blockNumber = json.readUint(".blockNumber"); @@ -1185,26 +1185,26 @@ contract ReceiverTest is Test { buffer.receiveHashes(blockNumber, blockHashes); address[] memory route = new address[](1); - route[0] = address(blockHashProverPointer); + route[0] = address(stateProverPointer); - bytes[] memory bhpInputs = new bytes[](1); - bhpInputs[0] = abi.encode(blockNumber); + bytes[] memory scpInputs = new bytes[](1); + scpInputs[0] = abi.encode(blockNumber); bytes memory storageProofToLastProver = inputForOPChildToParentProver; IReceiver.RemoteReadArgs memory remoteReadArgs = - IReceiver.RemoteReadArgs({route: route, bhpInputs: bhpInputs, storageProof: storageProofToLastProver}); + IReceiver.RemoteReadArgs({route: route, scpInputs: scpInputs, proof: storageProofToLastProver}); - bytes32 bhpPointerId = receiver.updateBlockHashProverCopy(remoteReadArgs, arbParentToChildProverCopy); + bytes32 scpPointerId = receiver.updateStateProverCopy(remoteReadArgs, arbParentToChildProverCopy); assertEq( - bhpPointerId, + scpPointerId, keccak256( abi.encode( keccak256( abi.encode( bytes32(0x0000000000000000000000000000000000000000000000000000000000000000), - address(blockHashProverPointer) + address(stateProverPointer) ) ), account @@ -1216,10 +1216,10 @@ contract ReceiverTest is Test { } // Construct the route to verify a message broadcasted on Arbitrum chain in Scroll - // We need to construct three inputs: one for ScrollChildToParentProver getTargetBlockHash, - // one for ArbParentToChildProver verifyTargetBlockHash, and one for ArbParentToChildProver verifyStorageSlot + // We need to construct three inputs: one for ScrollChildToParentProver getTargetStateCommitment, + // one for ArbParentToChildProver verifyTargetStateCommitment, and one for ArbParentToChildProver verifyStorageSlot // the input to verifyStorageSlot is the proof of the broadcasted message itself. - // the input for verifyTargetBlockHash is the storage proof of the slot on the outbox contract. + // the input for verifyTargetStateCommitment is the storage proof of the slot on the outbox contract. string memory pathEthereum = "test/payloads/ethereum/output_storage_proof_block_9567705.json"; @@ -1247,23 +1247,23 @@ contract ReceiverTest is Test { buffer.receiveHashes(blockNumberEthereum, blockHashes); address[] memory route = new address[](2); - route[0] = address(blockHashProverPointer); + route[0] = address(stateProverPointer); route[1] = arbParentToChildProverPointerAddress; bytes memory input0 = abi.encode(blockNumberEthereum); bytes memory input1 = abi.encode(rlpBlockHeaderEthereum, sendRootArbitrum, rlpAccountProofEthereum, rlpStorageProofEthereum); - bytes[] memory bhpInputs = new bytes[](2); - bhpInputs[0] = input0; - bhpInputs[1] = input1; + bytes[] memory scpInputs = new bytes[](2); + scpInputs[0] = input0; + scpInputs[1] = input1; bytes memory storageProofToLastProver = abi.encode( rlpBlockHeaderArbitrum, accountArbitrum, slotArbitrum, rlpAccountProofArbitrum, rlpStorageProofArbitrum ); IReceiver.RemoteReadArgs memory remoteReadArgs = - IReceiver.RemoteReadArgs({route: route, bhpInputs: bhpInputs, storageProof: storageProofToLastProver}); + IReceiver.RemoteReadArgs({route: route, scpInputs: scpInputs, proof: storageProofToLastProver}); bytes32 message = 0x0000000000000000000000000000000000000000000000000000000074657374; // "test" address publisher = 0x9a56fFd72F4B526c523C733F1F74197A51c495E1; @@ -1277,7 +1277,7 @@ contract ReceiverTest is Test { keccak256( abi.encode( bytes32(0x0000000000000000000000000000000000000000000000000000000000000000), - address(blockHashProverPointer) + address(stateProverPointer) ) ), arbParentToChildProverPointerAddress @@ -1306,10 +1306,10 @@ contract ReceiverTest is Test { ScrollParentToChildProver parentToChildProver = new ScrollParentToChildProver(scrollChain, finalizedStateRootsSlot, homeChainId); - BlockHashProverPointer blockHashProverPointer = new BlockHashProverPointer(owner); + StateProverPointer stateProverPointer = new StateProverPointer(owner); vm.prank(owner); - blockHashProverPointer.setImplementationAddress(address(parentToChildProver)); + stateProverPointer.setImplementationAddress(address(parentToChildProver)); // Load the E2E proof data string memory path = "test/payloads/scroll/e2e-proof.json"; @@ -1362,10 +1362,10 @@ contract ReceiverTest is Test { ScrollParentToChildProver parentToChildProver = new ScrollParentToChildProver(scrollChain, finalizedStateRootsSlot, homeChainId); - BlockHashProverPointer blockHashProverPointer = new BlockHashProverPointer(owner); + StateProverPointer stateProverPointer = new StateProverPointer(owner); vm.prank(owner); - blockHashProverPointer.setImplementationAddress(address(parentToChildProver)); + stateProverPointer.setImplementationAddress(address(parentToChildProver)); // Load the E2E proof data string memory path = "test/payloads/scroll/e2e-proof.json"; @@ -1394,13 +1394,13 @@ contract ReceiverTest is Test { bytes memory storageProofInput = abi.encode(account, storageSlot, rlpAccountProof, rlpStorageProof); address[] memory route = new address[](1); - route[0] = address(blockHashProverPointer); + route[0] = address(stateProverPointer); - bytes[] memory bhpInputs = new bytes[](1); - bhpInputs[0] = abi.encode(batchIndex); + bytes[] memory scpInputs = new bytes[](1); + scpInputs[0] = abi.encode(batchIndex); IReceiver.RemoteReadArgs memory remoteReadArgs = - IReceiver.RemoteReadArgs({route: route, bhpInputs: bhpInputs, storageProof: storageProofInput}); + IReceiver.RemoteReadArgs({route: route, scpInputs: scpInputs, proof: storageProofInput}); (bytes32 broadcasterId, uint256 timestamp) = receiver.verifyBroadcastMessage(remoteReadArgs, message, publisher); @@ -1411,7 +1411,7 @@ contract ReceiverTest is Test { keccak256( abi.encode( bytes32(0x0000000000000000000000000000000000000000000000000000000000000000), - address(blockHashProverPointer) + address(stateProverPointer) ) ), account @@ -1440,10 +1440,10 @@ contract ReceiverTest is Test { LineaParentToChildProver parentToChildProver = new LineaParentToChildProver(lineaRollup, stateRootHashesSlot, homeChainId); - BlockHashProverPointer blockHashProverPointer = new BlockHashProverPointer(owner); + StateProverPointer stateProverPointer = new StateProverPointer(owner); vm.prank(owner); - blockHashProverPointer.setImplementationAddress(address(parentToChildProver)); + stateProverPointer.setImplementationAddress(address(parentToChildProver)); // Read the SMT proof data string memory path = "test/payloads/linea/lineaProofL2-smt.json"; @@ -1466,15 +1466,15 @@ contract ReceiverTest is Test { // Construct the route address[] memory route = new address[](1); - route[0] = address(blockHashProverPointer); + route[0] = address(stateProverPointer); - bytes[] memory bhpInputs = new bytes[](1); - bhpInputs[0] = abi.encode(l2BlockNumber); + bytes[] memory scpInputs = new bytes[](1); + scpInputs[0] = abi.encode(l2BlockNumber); bytes memory storageProofToLastProver = smtProof; IReceiver.RemoteReadArgs memory remoteReadArgs = - IReceiver.RemoteReadArgs({route: route, bhpInputs: bhpInputs, storageProof: storageProofToLastProver}); + IReceiver.RemoteReadArgs({route: route, scpInputs: scpInputs, proof: storageProofToLastProver}); // Calculate expected message hash from the slot // The slot is keccak256(abi.encode(message, publisher)) @@ -1496,7 +1496,7 @@ contract ReceiverTest is Test { keccak256( abi.encode( bytes32(0x0000000000000000000000000000000000000000000000000000000000000000), - address(blockHashProverPointer) + address(stateProverPointer) ) ), account diff --git a/test/VerifyBroadcastMessageBenchmark.t.sol b/test/VerifyBroadcastMessageBenchmark.t.sol index 589c92c..01ad5a1 100644 --- a/test/VerifyBroadcastMessageBenchmark.t.sol +++ b/test/VerifyBroadcastMessageBenchmark.t.sol @@ -5,7 +5,7 @@ import {Test, console} from "forge-std/Test.sol"; import {stdJson} from "forge-std/StdJson.sol"; import {Receiver} from "../src/contracts/Receiver.sol"; import {IReceiver} from "../src/contracts/interfaces/IReceiver.sol"; -import {BlockHashProverPointer} from "../src/contracts/BlockHashProverPointer.sol"; +import {StateProverPointer} from "../src/contracts/StateProverPointer.sol"; // Provers import {ParentToChildProver as TaikoP2C} from "../src/contracts/provers/taiko/ParentToChildProver.sol"; @@ -50,7 +50,7 @@ contract VerifyBroadcastMessageBenchmark is Test { receiver = new Receiver(); TaikoP2C prover = new TaikoP2C(address(uint160(SIGNAL_SERVICE)), CHECKPOINTS_SLOT, L1_CHAIN_ID); - BlockHashProverPointer pointer = new BlockHashProverPointer(owner); + StateProverPointer pointer = new StateProverPointer(owner); vm.prank(owner); pointer.setImplementationAddress(address(prover)); @@ -83,11 +83,11 @@ contract VerifyBroadcastMessageBenchmark is Test { address[] memory route = new address[](1); route[0] = address(pointer); - bytes[] memory bhpInputs = new bytes[](1); - bhpInputs[0] = abi.encode(uint48(blockNumber)); + bytes[] memory scpInputs = new bytes[](1); + scpInputs[0] = abi.encode(uint48(blockNumber)); IReceiver.RemoteReadArgs memory args = - IReceiver.RemoteReadArgs({route: route, bhpInputs: bhpInputs, storageProof: storageProof}); + IReceiver.RemoteReadArgs({route: route, scpInputs: scpInputs, proof: storageProof}); // Call and snapshot receiver.verifyBroadcastMessage(args, message, publisher); @@ -105,7 +105,7 @@ contract VerifyBroadcastMessageBenchmark is Test { receiver = new Receiver(); ScrollP2C prover = new ScrollP2C(SCROLL_CHAIN, STATE_ROOTS_SLOT, HOME_CHAIN_ID); - BlockHashProverPointer pointer = new BlockHashProverPointer(owner); + StateProverPointer pointer = new StateProverPointer(owner); vm.prank(owner); pointer.setImplementationAddress(address(prover)); @@ -132,11 +132,11 @@ contract VerifyBroadcastMessageBenchmark is Test { address[] memory route = new address[](1); route[0] = address(pointer); - bytes[] memory bhpInputs = new bytes[](1); - bhpInputs[0] = abi.encode(batchIndex); + bytes[] memory scpInputs = new bytes[](1); + scpInputs[0] = abi.encode(batchIndex); IReceiver.RemoteReadArgs memory args = - IReceiver.RemoteReadArgs({route: route, bhpInputs: bhpInputs, storageProof: storageProof}); + IReceiver.RemoteReadArgs({route: route, scpInputs: scpInputs, proof: storageProof}); // Call and snapshot receiver.verifyBroadcastMessage(args, message, publisher); @@ -154,7 +154,7 @@ contract VerifyBroadcastMessageBenchmark is Test { receiver = new Receiver(); LineaP2C prover = new LineaP2C(LINEA_ROLLUP, STATE_ROOT_SLOT, HOME_CHAIN_ID); - BlockHashProverPointer pointer = new BlockHashProverPointer(owner); + StateProverPointer pointer = new StateProverPointer(owner); vm.prank(owner); pointer.setImplementationAddress(address(prover)); @@ -180,11 +180,11 @@ contract VerifyBroadcastMessageBenchmark is Test { address[] memory route = new address[](1); route[0] = address(pointer); - bytes[] memory bhpInputs = new bytes[](1); - bhpInputs[0] = abi.encode(l2BlockNumber); + bytes[] memory scpInputs = new bytes[](1); + scpInputs[0] = abi.encode(l2BlockNumber); IReceiver.RemoteReadArgs memory args = - IReceiver.RemoteReadArgs({route: route, bhpInputs: bhpInputs, storageProof: smtProof}); + IReceiver.RemoteReadArgs({route: route, scpInputs: scpInputs, proof: smtProof}); // Call and snapshot receiver.verifyBroadcastMessage(args, message, publisher); @@ -254,10 +254,10 @@ contract VerifyBroadcastMessageBenchmark is Test { receiver = new Receiver(); ZksyncP2C parentToChildProver = new ZksyncP2C(address(mockZkChain), 0, 300, 32657, block.chainid); - BlockHashProverPointer blockHashProverPointer = new BlockHashProverPointer(owner); + StateProverPointer stateProverPointer = new StateProverPointer(owner); vm.prank(owner); - blockHashProverPointer.setImplementationAddress(address(parentToChildProver)); + stateProverPointer.setImplementationAddress(address(parentToChildProver)); bytes32 message = 0x0000000000000000000000000000000000000000000000000000000074657374; // "test" address publisher = 0x9a56fFd72F4B526c523C733F1F74197A51c495E1; @@ -267,15 +267,15 @@ contract VerifyBroadcastMessageBenchmark is Test { bytes memory input = abi.encode(proof, publisher, message); address[] memory route = new address[](1); - route[0] = address(blockHashProverPointer); + route[0] = address(stateProverPointer); - bytes[] memory bhpInputs = new bytes[](1); - bhpInputs[0] = abi.encode(47506); + bytes[] memory scpInputs = new bytes[](1); + scpInputs[0] = abi.encode(47506); bytes memory storageProofToLastProver = input; IReceiver.RemoteReadArgs memory remoteReadArgs = - IReceiver.RemoteReadArgs({route: route, bhpInputs: bhpInputs, storageProof: storageProofToLastProver}); + IReceiver.RemoteReadArgs({route: route, scpInputs: scpInputs, proof: storageProofToLastProver}); receiver.verifyBroadcastMessage(remoteReadArgs, message, publisher); vm.snapshotGasLastCall("verifyBroadcastMessage", "ZkSyncL2ToEthereum"); @@ -296,7 +296,7 @@ contract VerifyBroadcastMessageBenchmark is Test { receiver = new Receiver(); TaikoC2P prover = new TaikoC2P(address(uint160(SIGNAL_SERVICE)), CHECKPOINTS_SLOT, L2_CHAIN_ID); - BlockHashProverPointer pointer = new BlockHashProverPointer(owner); + StateProverPointer pointer = new StateProverPointer(owner); vm.prank(owner); pointer.setImplementationAddress(address(prover)); @@ -329,11 +329,11 @@ contract VerifyBroadcastMessageBenchmark is Test { address[] memory route = new address[](1); route[0] = address(pointer); - bytes[] memory bhpInputs = new bytes[](1); - bhpInputs[0] = abi.encode(uint48(blockNumber)); + bytes[] memory scpInputs = new bytes[](1); + scpInputs[0] = abi.encode(uint48(blockNumber)); IReceiver.RemoteReadArgs memory args = - IReceiver.RemoteReadArgs({route: route, bhpInputs: bhpInputs, storageProof: storageProof}); + IReceiver.RemoteReadArgs({route: route, scpInputs: scpInputs, proof: storageProof}); // Call and snapshot receiver.verifyBroadcastMessage(args, message, publisher); @@ -349,7 +349,7 @@ contract VerifyBroadcastMessageBenchmark is Test { receiver = new Receiver(); OptimismC2P prover = new OptimismC2P(L2_CHAIN_ID); - BlockHashProverPointer pointer = new BlockHashProverPointer(owner); + StateProverPointer pointer = new StateProverPointer(owner); vm.prank(owner); pointer.setImplementationAddress(address(prover)); @@ -378,11 +378,11 @@ contract VerifyBroadcastMessageBenchmark is Test { address[] memory route = new address[](1); route[0] = address(pointer); - bytes[] memory bhpInputs = new bytes[](1); - bhpInputs[0] = bytes(""); + bytes[] memory scpInputs = new bytes[](1); + scpInputs[0] = bytes(""); IReceiver.RemoteReadArgs memory args = - IReceiver.RemoteReadArgs({route: route, bhpInputs: bhpInputs, storageProof: storageProof}); + IReceiver.RemoteReadArgs({route: route, scpInputs: scpInputs, proof: storageProof}); // Call and snapshot receiver.verifyBroadcastMessage(args, message, publisher); @@ -409,7 +409,7 @@ contract VerifyBroadcastMessageBenchmark is Test { // First hop prover: Optimism C2P (gets Ethereum block hash on OP L2) OptimismC2P opC2PProver = new OptimismC2P(OP_L2_CHAIN_ID); - BlockHashProverPointer opPointer = new BlockHashProverPointer(owner); + StateProverPointer opPointer = new StateProverPointer(owner); vm.prank(owner); opPointer.setImplementationAddress(address(opC2PProver)); @@ -424,9 +424,9 @@ contract VerifyBroadcastMessageBenchmark is Test { // Register the Scroll prover copy in receiver's mapping bytes32 acc1 = keccak256(abi.encode(bytes32(0), address(opPointer))); - bytes32 bhpPointerId = keccak256(abi.encode(acc1, scrollPointerAddress)); + bytes32 scpPointerId = keccak256(abi.encode(acc1, scrollPointerAddress)); - bytes32 mappingSlot = keccak256(abi.encode(bhpPointerId, uint256(0))); + bytes32 mappingSlot = keccak256(abi.encode(scpPointerId, uint256(0))); vm.store(address(receiver), mappingSlot, bytes32(uint256(uint160(address(scrollP2CProverCopy))))); // Load Ethereum proof for first hop @@ -463,7 +463,7 @@ contract VerifyBroadcastMessageBenchmark is Test { vm.startSnapshotGas("verifyBroadcastMessage", "ScrollToOptimism"); // First: get Ethereum block hash via OP C2P (simulates first hop verification) - opC2PProver.getTargetBlockHash(bytes("")); + opC2PProver.getTargetStateCommitment(bytes("")); // Second: verify Scroll storage using Scroll P2C (simulates second hop verification) scrollP2CProverCopy.verifyStorageSlot(scrollStateRoot, scrollStorageProof); diff --git a/test/mocks/MockProver.sol b/test/mocks/MockProver.sol index 3d3d32c..7cc7801 100644 --- a/test/mocks/MockProver.sol +++ b/test/mocks/MockProver.sol @@ -1,27 +1,27 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.30; -import {IBlockHashProver} from "src/contracts/interfaces/IBlockHashProver.sol"; +import {IStateProver} from "src/contracts/interfaces/IStateProver.sol"; -contract MockProver is IBlockHashProver { - function verifyTargetBlockHash( +contract MockProver is IStateProver { + function verifyTargetStateCommitment( bytes32 homeBlockHash, bytes calldata /*input*/ ) external pure - returns (bytes32 targetBlockHash) + returns (bytes32 targetStateCommitment) { return homeBlockHash; } - function getTargetBlockHash(bytes calldata input) external pure returns (bytes32 targetBlockHash) { - targetBlockHash = abi.decode(input, (bytes32)); + function getTargetStateCommitment(bytes calldata input) external pure returns (bytes32 targetStateCommitment) { + targetStateCommitment = abi.decode(input, (bytes32)); } function verifyStorageSlot( bytes32, - /*targetBlockHash*/ + /*targetStateCommitment*/ bytes calldata input ) external diff --git a/test/payloads/ethereum/arb_pointer_proof_block_10128554.json b/test/payloads/ethereum/arb_pointer_proof_block_10128554.json new file mode 100644 index 0000000..104fdc6 --- /dev/null +++ b/test/payloads/ethereum/arb_pointer_proof_block_10128554.json @@ -0,0 +1,11 @@ +{ + "blockNumber": "0x9a8caa", + "blockHash": "0x839c3304f266d7ecfc5ea6038a4e873e8fc056405622a3aad7a22030526bc596", + "stateRoot": "0xadd8e77a2d10593dbf937715ff4371d69957bfd534a893808693f9ddba318460", + "account": "0xe2C96c2D3E4b54D4F1E9962b8D6eC5AD96f567b1", + "slot": "0xee72f8a02087d3307bd38f55854a433976910dab504e72b626e32d8d187b5aca", + "slotValue": "0xb0ee67f19114ef2f540e7ca5437c034501fce3d2236f3eb65f2d0550dd62b72a", + "rlpBlockHeader": "0xf90286a09b226edf0d110a499f4721aceebce53519f4d9a4e265088e9d5df7a1e84ebc5da01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347943826539cbd8d68dcf119e80b994557b4278cec9fa0add8e77a2d10593dbf937715ff4371d69957bfd534a893808693f9ddba318460a074a432ac7986d1e7ca9fd24d8bb8ed31f2df4688c29af608d222788ba2d4ade1a00c9bd88e4e3ec3adf8cf713004242c4e587e8801ebd8cada198649e7bfd618bcb901000c848164420048bc08b8010584110b810d01124011180588368089004a0314910210220800b06000443427420000000008210186208aa42104010222c46c49301a00000001060248100d0088042884800800024921040020200e05008023a4404a1a0c0832800054000880c002124f02a10210093330004400001231806820300840001843144800a220808242a04280a10005c54a91100040169201040088802a0820011980100a0008190d02032002242066a2880200009a901440082002110810800280017001040307e6090d2062131000428a254031400a2940838260000110930040803200041ad416000000648088041440b80250808462024420820080839a8caa840393870083a99ac384697791c09a626573752032352e31322d646576656c6f702d63663962623765a0e0fdf580b83e231b0b38a8ec2e1534fcb62a851165f76ab1a9935ff77298ec80880000000000000000843ea88b0aa06196940f214be90db993448f21941f6b5bf00fce50ace0136118b42dd0cafffc83140000840c99a0d2a0326ec1f16de36000ec6231e8d8c7aae59e717e805b1230a960753a3605a390c5a0e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "rlpAccountProof": "0xf90e5eb90214f90211a0d5741b9c8b034f90fb10ffce72932a0292b34018044d4ef1e9a34f0259aeee5ba0d909ddd21f79be2837b3788874b427280bc5969389d2af49edd9032ffab0a6e1a0e772cc66f39a236391b47d20b1e6171952f4f36a7f47268ef67ab9434ad6299ba09ea748226e401e5bad94fc7b6e7cb7d70407fb5f85954de215c14c4a84bbeae7a0dff82757028ebb8bd4c33844dce6478b2a6a389a9ad40630549fdbc18106803fa081328db7d620a07a4cf36a8749692a6b2482d763e23c18fde1237b1e342758c8a0d6b9907ca2e4de29c9e7cff67a9078a4336d704f4644573f8d9176959a5c082da0556e02f736004a9cd156e25dbc1347f45dee830cf8398aba03773b8cf0043130a0a8021cb5871721c1a501736b56faac7ab56093ed3415cb1cc626338aa985d242a01a431fe7110e04a01c3dbca4ad5fd2cd2492b4a9e917c1daba497177b2333a7da0bf8eb8e8256decfa13f7910e1276afc186036a5dc8c2cb0a6fa455bc9b7cd1c3a0f4fc3ebeec71afd19a78b6420f7a83a64404d22f923d5a6a6a227623996b0faaa0b4658c1c568de1059ff5d3c1672126b0a3f14e52dc790a1008778e81e1ac23afa085bdc55dffacf04275199b4557df03d67fea925aafcfe2ba1398aca159c09b7aa05155806df430d951f9d76007b69f4f987bf7ae86c56d5dca491fd21472804eada0171c0a64f56332bedad2c1989f78cb5be6efe056e06e004ac9f6466e87f9819c80b90214f90211a04e086691d2c1c22558fa80552b4a76955c6636f14b667a92191f3cb994805009a0f8167ba35342380d13a0443bd5d1538d8e6a4026e0f7e02a6d95a6c66ee4ccfba0c3898def9d5161a22b427a51d1ebea47f2615bf895bf1d4e493d915b14664277a0c916ab643e13f4de9389323de9d42379c7b3ddf036e9ec8dc539793e1c8e220da0518cb03a9f5a75ccb2582fd2408bbde225a3cc3d97ac24f15f35ac1c2e5fd0dfa083449fbcfdb1f49f9e708c8239bdbaf6ed796b2bd3cbc697acd26b1a02243b4ea039b31c5b1f8184885e7e3e837a6f8e61b00c8c3a0c174649593679ea1477fa5ba0ba154d2eb36e5b71783e8602ad62989da40aa266eaadc3fde36f31ebfb422bcda0ad1f4c00bd0ce3b4be3eac3984042a7dc1dccd547c4c20fc8a2ec34e9467d1a1a01902c1c968504a679617a1b42d53b784e33b65667029765b6eff7b0e7f216531a0ed08cb9b76aeb53b798f19f9c7b3228c42a23d7965954dad795ddf28f9aeaaaca09764f6b49e3ebe1adcc0f9bfc96b268c6212374352fa9ef6c1fb47f57f0492e3a0249547e69d02c6d6d026db0ddab0ab7d91b1c811b1ab802ab6993520bd08bfe1a0e86165ab9f8559f2214773fd3177c6317f4bb6a0fa51c4d95ba8fb0afa303847a0186fa4591f6545b4e2a027226a3ccab424f961cb2ca330215d3e574aa3391e63a00e04c0d6eac25b8b80863e1610e0755adfe20669ce748ca4e71dc85afd7a982f80b90214f90211a05604a692474080a896faac440d193f0505c4661a0338a4848f8249eab6ea201fa0526622ab09ff66238594acd1f73c036aab87146b58f54c5771c351e48f0accf1a011777da9f7db041d52eee9fef87586e18bceddd3bd2a6545c8f12933ee31c842a09cff23ab054afe5ab7cf5b93c0d7ee82ddb6f26620a5977eb85a3e3204e0039ba0b78bcd60e21722ff7940736dff916c9a59af2b232b5b376db51cc2cd4346ecf9a0e9db9460c6e82f9c435d0894604196df2aaa74bcce01ac15d68fb810a4fda5a6a03269e7ea9ce71721e6e10477092afc43f030994535a52f4ca598b15aba944eb4a01cfa67d153c3b920a75832245ca69252761824803d9f11379103933770b72193a0d4485e38acc765a5a34ad072765e04a838e7fb13e65e5cfcdd58a1ac4dfe432fa026f241828adbab8e8b84ffdc56d05a173b33ee020ee36abd834c369e73798227a0be188107d87c2ada84ba6961644ef1267557b964cb6d9f1ac3bf4692fbc3eed6a049093d270c946f9f0d45af9b164918be122e0dcc7e78e6ac4affd14908d2388aa0963c2bdfa9d65e8b3159fed84a56229a44e289391d3b63e21f22104ee6552993a04c57f9a0e6c5180c2bbeca6e8d6465992db6264a5f0ca7c3f04d6aa4e1880095a0910595e5dc577315fe8c87ef83e46612d0599fa38f9a9fdab9b61cd011bc15c0a0c84bbf02d750c3759c83457fa3647eeac0525052313850b65a3557cf2cd7f8f280b90214f90211a0546da58cbe51ed182862d3c022400ee8cf8c079a98d3950ab45837e9ab338b25a0b1f5ed8a4561cf02b23b66fdece36f3773a2eb894bf8782be4a337b91c35b4c2a0da3b0829645312ef53596663595598e3c2e3a6fb90060b39401db5aeb629549ba0bf204de3d280ea5cf78533fea732c9f8d0983990a1f9d85562597a173a92363ba087003c1f6fb5bfd1b7d47fd15f1a45fb3df754649f147412b1dee1467e76bb34a0dd6d3b12ed8a602935be01beef801aa3bb1dfda5a4546e435f30f885229b4ef6a0d8a17ebc6e89ea29533d2cdd220e6313bc11f908616b75053af6e76af0d98957a0091f8574f339c2df4ed3b2c0dd91ab75f93f3afc0c0934ffc71f6c364882972ea0dc78f7ccd5545a9c887b7b9915f94035cacc305e3c454d9a8e9b11de3acc2bcba07f23562cb8d322e4ac861ea7fb827ddf5211e93e6f1fdacdadc6129074ae2069a0548c21cd0beb7c8a014d4684d3b8cbaffc85f844a8429a935b5c866845cf4ecea0533df4189beff2fff03e8f34446067a118cf2dac3eb1a62e6263e1b828329d1fa0969113956dc5c9b47e2851a62faddefa9c2075b6d0be4674197d35efab39fecfa07033185b3fcc9a7dfe2dfe1273b3354aade90b5d7990cf816b368fe3aede9819a047c625d8214e563c2ff50dbeaf8d29243e1a2ac5983588f5ec8f332e07f53bd0a09896cf91c1d8e2cd09c0663174154bb43fec08204a46c97370d8761ecffe442480b90214f90211a0614ea66d4ac800174be8b511bb83238d32534215e4c5e67f99eeeaaac4f0c6c8a0c2f40ce488417b266f6534c4b58e09103407ba3741cfb8048dd9898fbb9b2b28a0799ad3c69b5d58e177c9f29f1040909693f1beaa545dc41aa1687f9a63b55a52a0b5b1e6748289dfbb483c147423854b92cebbbb43facff732adc522178c304f35a03e0578afb47f3578193d10a265b0fa3c1a857e9fd616ced7062931fedda38720a00f9a5173e88e704ab0eef02da091f82d496f6a50e1610ec1213757252a56b605a03b1bf5994e6f57096e1ef6d4b80537d59c1dd08019c8289d5bea947c24e93f9fa0bc8f62a3cc7e0ac7d86e3be570cdf295b5a149371a0c58fbc36e7d5911c476f8a09023fc1d532e29f411647d549488a82a284ba0373a70c92745f8d26f118304bca03d6d4beef071120a8d1dcd4866d6dff1cb3ee0e81d6eaf474f182cae166932e2a0ed7359930a8dbf2c9c73dd849e2f80213d52a5e9aaf8d8981fb353cece19539ea02eb06dfb9009a2962143cd9324c8985b68edb3eb1dfff2f2798d991ee40d2538a0db320e2b273d3fb3cc7b4f7f2524b24488fe8f952563e48bebf5eee94161c71fa02e6ff759bdcd24a91d783b5918262c144b1c6ee3104f131cdf5f8157fb752d8aa0500dfad4066d7db8eb049c226bd70fc0d201b2d1911a3d9b278380fa19f97e6da00a110cd362c39fde78c12ebd1a0af3c3d7f90a74d98753a54fade77d5e48459880b90214f90211a0415697df58127f5489b47b7869f953e596f009ff2820e4e07c3ca964bd0e92cba03a00470d4e85ad8ebf924094f4c136b11045b0320b2b9a181c54e4cdcbc3b3e0a06eb6106433e1ac263d2a561f80b8f81a27c833a65940b5c62d3bc04afbe2a4c5a0a5bc7474d8d7c266e65c9d18e8753c8aabf546c6172cc82a50a6ed5ae54ecccda0c3de33e94fd55c8c5bbed09f5e0005d11e43036657f464721ce9155186284ff1a08ebbdf3e7314e2ae79bd3abadcc506f491fd77a1b07a19fee2d4eb4a68e6696fa08611c6182c89e0e07ad55e023e9bd1e823685f1f8a2ee7542762537025f7fd42a0d642d007b7acc4e33d3b3b06a9e14ec1d61d3cea23b3ab0a47ec8661cd3f3509a042d9257eb39a185b5e7e576903dd26aee04a72af13072b86083f490b35a2f67da03ed7fd323b52935133466884d343cccce36d098552c599ee84deb98a9bd1ddfea0a35e8e4ff8698d1ba110ad85a4ff37581479c8213eec25811b79397f5a8c8731a01c00da91c2c406a37900da538f648b31c434387b500ea058ec866ea90f787583a06b79a55837d70c9b3c1522c07b14febacac92baf697e750989670351720f8063a0a76b0984e61b37186b7b4f43a37bcc5dfee7b6ce6ca9bb617ed6d59dd37166c4a0d0446fb25d970e5e906e89699ad5c5f622c27c4d8770741594f28bcbf599f2c9a0e9bde4aeac3fd5b1af43bb40d0fbae82b75e058f67a2246221fb1ebb5e24ebec80b8d3f8d1808080808080a0c010e06da0f44da6fd1762fb2c490210f5a22baf61478b142e11479f3d4741f2a07ce0f8f2de241635efd262318cb8832a39ade936435eaf077cd87d4e5fdf0674a0facc845ddae31dba0c3c448d6ce413a94a92500944b0a19ec04dabb4ba3e6cfa80a0d60f9cbb1a8a845ee720ffb68937d30ebdd3b2852c65a85ceacbb961e782b1b5a0943ff0d8ad6450b852e582777197bcef2d0f5388463ce6c431c42ae0e11f557e8080a06852d832eada7c7ef7b513b1b794cd586e95a0d9dc610f981e06462581faca1c8080b893f891a0abfd3232342bf0f233bd1b3d96e5ac6577f918939b3c08c8a0adbc2d485a975d80808080808080808080a011a01c20fb36c489c5b8fc67fff6d225dafd9476f3613ce760036fdc53e1d480a098db0456199c4bf7e088d5b74e1759164ec00425d90085e1470bee1a326be23780a055f168672904585f14c83a8770966e707179899901202f64014bca1d9fc7ce908080b868f8669d207eefac74e7cdf442ac370d23a028a12a1bed220ff723241417885234b846f8440180a050d25d32255015c4b33e12e971fed1f1dc6f7a98175df1d38777062a99356be9a06805798ebe44ed998ce93036572c1580000c5206e4461b233323a67eebfd5001", + "rlpStorageProof": "0xf8bcb873f8718080a0fecf31ff05c59132f4f3857913ac1442ca8c30a3ef31aa057e80bef0edc13b6780a096cfcf806ec0baf972c99e2dbb96bd89ce0f3f28ae5e7b1f833293a0d6686ced808080808080a0a63bce3177bdcf2bcdf20593fc382df6829cd820447b8ee8d09a0c72cfb667e48080808080b845f843a0323543e67277ca39e1a40854aa32c04a3801f9748e5b663bc20961794dd54596a1a0b0ee67f19114ef2f540e7ca5437c034501fce3d2236f3eb65f2d0550dd62b72a" +} \ No newline at end of file diff --git a/test/payloads/linea/broadcast-info.json b/test/payloads/linea/broadcast-info.json index 6cb7d0c..45154ec 100644 --- a/test/payloads/linea/broadcast-info.json +++ b/test/payloads/linea/broadcast-info.json @@ -10,7 +10,7 @@ "finalization": { "batchBlockNumber": 21834331, "zkStateRoot": "0x0b9797153d1cef2b38f6e87d2c225791b74107ae7ce28e60bf498b9d1c094f14", - "note": "Linea only stores state roots at batch boundaries. Use batchBlockNumber (not txBlockNumber) for getTargetBlockHash. Uses Sparse Merkle Tree proofs (linea_getProof)." + "note": "Linea only stores state roots at batch boundaries. Use batchBlockNumber (not txBlockNumber) for getTargetStateCommitment. Uses Sparse Merkle Tree proofs (linea_getProof)." }, "storageSlot": { "slot": "0xc7f3206d60205e1634924ee1e67de7607b9a5991744aaf3526fde997abcc5170", diff --git a/test/provers/arbitrum/ChildToParentProver.t.sol b/test/provers/arbitrum/ChildToParentProver.t.sol index 6660fd2..e8d0113 100644 --- a/test/provers/arbitrum/ChildToParentProver.t.sol +++ b/test/provers/arbitrum/ChildToParentProver.t.sol @@ -81,56 +81,56 @@ contract BroadcasterTest is Test { payload = vm.parseBytes(vm.readFile(string.concat(vm.projectRoot(), "/", path))); } - function test_getTargetBlockHash() public { + function test_getTargetStateCommitment() public { vm.selectFork(childForkId); bytes memory payload = _loadPayload("test/payloads/arbitrum/calldata_get.hex"); assertEq(payload.length, 64); uint256 input; - bytes32 targetBlockHash; + bytes32 targetStateCommitment; assembly { input := mload(add(payload, 0x20)) - targetBlockHash := mload(add(payload, 0x40)) + targetStateCommitment := mload(add(payload, 0x40)) } - bytes32 result = childToParentProver.getTargetBlockHash(abi.encode(input)); + bytes32 result = childToParentProver.getTargetStateCommitment(abi.encode(input)); - assertEq(result, targetBlockHash); + assertEq(result, targetStateCommitment); } - function test_getTargetBlockHash_broadcast() public { + function test_getTargetStateCommitment_broadcast() public { vm.selectFork(childForkId); - bytes32 targetBlockHash = 0x57845b0a97194c2869580ed8857fee67c91f2bb9cdf54368685c0ea5bf25f6c2; + bytes32 targetStateCommitment = 0x57845b0a97194c2869580ed8857fee67c91f2bb9cdf54368685c0ea5bf25f6c2; uint256 blockNumber = 9043658; - bytes32 result = childToParentProver.getTargetBlockHash(abi.encode(blockNumber)); + bytes32 result = childToParentProver.getTargetStateCommitment(abi.encode(blockNumber)); - assertEq(result, targetBlockHash); + assertEq(result, targetStateCommitment); } - function test_getTargetBlockHash_broadcaster() public { + function test_getTargetStateCommitment_broadcaster() public { vm.selectFork(childForkId); bytes memory payload = _loadPayload("test/payloads/arbitrum/broadcaster_get.hex"); assertEq(payload.length, 64); bytes32 input; - bytes32 targetBlockHash; + bytes32 targetStateCommitment; assembly { input := mload(add(payload, 0x20)) - targetBlockHash := mload(add(payload, 0x40)) + targetStateCommitment := mload(add(payload, 0x40)) } - bytes32 result = childToParentProver.getTargetBlockHash(abi.encode(input)); + bytes32 result = childToParentProver.getTargetStateCommitment(abi.encode(input)); - assertEq(result, targetBlockHash); + assertEq(result, targetStateCommitment); } - function test_reverts_getTargetBlockHash_on_target_chain() public { + function test_reverts_getTargetStateCommitment_on_target_chain() public { vm.selectFork(parentForkId); bytes memory payload = _loadPayload("test/payloads/arbitrum/calldata_get.hex"); @@ -139,27 +139,27 @@ contract BroadcasterTest is Test { assertEq(payload.length, 64); bytes32 input; - bytes32 targetBlockHash; + bytes32 targetStateCommitment; assembly { input := mload(add(payload, 0x20)) - targetBlockHash := mload(add(payload, 0x40)) + targetStateCommitment := mload(add(payload, 0x40)) } vm.expectRevert(ChildToParentProver.CallNotOnHomeChain.selector); - newChildToParentProver.getTargetBlockHash(abi.encode(input)); + newChildToParentProver.getTargetStateCommitment(abi.encode(input)); } - function test_reverts_getTargetBlockHash_reverts_not_found() public { + function test_reverts_getTargetStateCommitment_reverts_not_found() public { vm.selectFork(childForkId); uint256 input = type(uint256).max; vm.expectRevert(abi.encodeWithSelector(IBuffer.UnknownParentChainBlockHash.selector, input)); - childToParentProver.getTargetBlockHash(abi.encode(input)); + childToParentProver.getTargetStateCommitment(abi.encode(input)); } - function test_verifyTargetBlockHash() public { + function test_verifyTargetStateCommitment() public { vm.selectFork(parentForkId); bytes memory payload = _loadPayload("test/payloads/arbitrum/calldata_verify_target.hex"); @@ -169,21 +169,21 @@ contract BroadcasterTest is Test { assertGt(payload.length, 64); bytes32 homeBlockHash; - bytes32 targetBlockHash; + bytes32 targetStateCommitment; bytes memory input = Bytes.slice(payload, 64); assembly { homeBlockHash := mload(add(payload, 0x20)) - targetBlockHash := mload(add(payload, 0x40)) + targetStateCommitment := mload(add(payload, 0x40)) } - bytes32 result = childToParentProverCopy.verifyTargetBlockHash(homeBlockHash, input); + bytes32 result = childToParentProverCopy.verifyTargetStateCommitment(homeBlockHash, input); - assertEq(result, targetBlockHash); + assertEq(result, targetStateCommitment); } - function test_verifyTargetBlockHash_reverts_on_home_chain() public { + function test_verifyTargetStateCommitment_reverts_on_home_chain() public { vm.selectFork(childForkId); bytes memory payload = _loadPayload("test/payloads/arbitrum/calldata_verify_target.hex"); @@ -193,17 +193,17 @@ contract BroadcasterTest is Test { assertGt(payload.length, 64); bytes32 homeBlockHash; - bytes32 targetBlockHash; + bytes32 targetStateCommitment; bytes memory input = Bytes.slice(payload, 64); assembly { homeBlockHash := mload(add(payload, 0x20)) - targetBlockHash := mload(add(payload, 0x40)) + targetStateCommitment := mload(add(payload, 0x40)) } vm.expectRevert(ChildToParentProver.CallOnHomeChain.selector); - childToParentProverCopy.verifyTargetBlockHash(homeBlockHash, input); + childToParentProverCopy.verifyTargetStateCommitment(homeBlockHash, input); } function test_verifyStorageSlot() public { @@ -218,17 +218,17 @@ contract BroadcasterTest is Test { assertGt(payload.length, 64); - bytes32 targetBlockHash; + bytes32 targetStateCommitment; bytes32 storageSlotValue; bytes memory input = Bytes.slice(payload, 64); assembly { - targetBlockHash := mload(add(payload, 0x20)) + targetStateCommitment := mload(add(payload, 0x20)) storageSlotValue := mload(add(payload, 0x40)) } (address account, uint256 slot, bytes32 value) = - childToParentProverCopy.verifyStorageSlot(targetBlockHash, input); + childToParentProverCopy.verifyStorageSlot(targetStateCommitment, input); assertEq(account, knownAccount); assertEq(slot, knownSlot); diff --git a/test/provers/arbitrum/ParentToChildProver.t.sol b/test/provers/arbitrum/ParentToChildProver.t.sol index 9fc69bb..c26f26a 100644 --- a/test/provers/arbitrum/ParentToChildProver.t.sol +++ b/test/provers/arbitrum/ParentToChildProver.t.sol @@ -77,25 +77,25 @@ contract ArbitrumParentToChildProverTest is Test { parentForkId = vm.createFork(vm.envString("ETHEREUM_RPC_URL")); vm.selectFork(parentForkId); - // Mock Outbox holds the expected sendRoot -> targetBlockHash mapping for stability + // Mock Outbox holds the expected sendRoot -> targetStateCommitment mapping for stability mockOutbox = new ArbitrumOutputMock(); bytes32 sendRoot = 0x7995a5be000a0212a46f7f128e5ffd6f6a99fa9c72046d9e9b0668bd080712cd; - bytes32 targetBlockHashFromProof = 0xa97ce065a04d2abfec36a459db323721847718d3159d51c4256d271ee3b37e42; - mockOutbox.updateSendRoot(sendRoot, targetBlockHashFromProof); + bytes32 targetStateCommitmentFromProof = 0xa97ce065a04d2abfec36a459db323721847718d3159d51c4256d271ee3b37e42; + mockOutbox.updateSendRoot(sendRoot, targetStateCommitmentFromProof); } - function test_getTargetBlockHash() public { + function test_getTargetStateCommitment() public { vm.selectFork(parentForkId); // Test with the sendRoot from the proof data bytes32 sendRoot = 0x7995a5be000a0212a46f7f128e5ffd6f6a99fa9c72046d9e9b0668bd080712cd; ParentToChildProver mockProver = new ParentToChildProver(address(mockOutbox), rootSlot, block.chainid); - bytes32 result = mockProver.getTargetBlockHash(abi.encode(sendRoot)); + bytes32 result = mockProver.getTargetStateCommitment(abi.encode(sendRoot)); bytes32 expectedTargetBlockHash = 0xa97ce065a04d2abfec36a459db323721847718d3159d51c4256d271ee3b37e42; - assertEq(result, expectedTargetBlockHash, "getTargetBlockHash should return correct Arbitrum block hash"); + assertEq(result, expectedTargetBlockHash, "getTargetStateCommitment should return correct Arbitrum block hash"); } - function test_verifyTargetBlockHash() public { + function test_verifyTargetStateCommitment() public { vm.selectFork(parentForkId); uint256 proverHomeChainId = block.chainid; ParentToChildProver prover = new ParentToChildProver(address(outbox), rootSlot, proverHomeChainId); @@ -116,11 +116,13 @@ contract ArbitrumParentToChildProverTest is Test { bytes memory input = abi.encode(rlpBlockHeader, sendRoot, rlpAccountProof, rlpStorageProof); bytes32 expectedTargetBlockHash = 0xcb53c786e7e875d7e3b1d3a770adbe02877ee5daab2ebfa55b935798b3ee9d24; - // verifyTargetBlockHash MUST be called off the prover's home chain. + // verifyTargetStateCommitment MUST be called off the prover's home chain. vm.chainId(proverHomeChainId + 1); - bytes32 result = prover.verifyTargetBlockHash(homeBlockHash, input); - assertEq(result, expectedTargetBlockHash, "verifyTargetBlockHash should return correct Arbitrum block hash"); + bytes32 result = prover.verifyTargetStateCommitment(homeBlockHash, input); + assertEq( + result, expectedTargetBlockHash, "verifyTargetStateCommitment should return correct Arbitrum block hash" + ); } function test_verifyStorageSlot() public { @@ -135,7 +137,7 @@ contract ArbitrumParentToChildProverTest is Test { RLP.Encoder memory enc = RLP.encoder().push(bytes32(0)).push(bytes32(0)).push(bytes32(0)).push(stateRoot); bytes memory rlpBlockHeader = enc.encode(); - bytes32 targetBlockHash = keccak256(rlpBlockHeader); + bytes32 targetStateCommitment = keccak256(rlpBlockHeader); // Outbox contract and storage slot from proof.json address outboxAddress = 0x65f07C7D521164a4d5DaC6eB8Fac8DA067A3B78F; @@ -144,7 +146,7 @@ contract ArbitrumParentToChildProverTest is Test { bytes memory rlpStorageProof = _getStorageProof(); bytes memory input = abi.encode(rlpBlockHeader, outboxAddress, storageSlot, rlpAccountProof, rlpStorageProof); - (address account, uint256 slot, bytes32 value) = prover.verifyStorageSlot(targetBlockHash, input); + (address account, uint256 slot, bytes32 value) = prover.verifyStorageSlot(targetStateCommitment, input); assertEq(account, outboxAddress, "Account should match Outbox address"); assertEq(slot, storageSlot, "Slot should match roots mapping slot"); diff --git a/test/provers/linea/ParentToChildProver.t.sol b/test/provers/linea/ParentToChildProver.t.sol index e5d1936..af86bee 100644 --- a/test/provers/linea/ParentToChildProver.t.sol +++ b/test/provers/linea/ParentToChildProver.t.sol @@ -3,16 +3,31 @@ pragma solidity ^0.8.28; import {Test} from "forge-std/Test.sol"; import {console} from "forge-std/console.sol"; -import {ParentToChildProver, ILineaRollup} from "../../../src/contracts/provers/linea/ParentToChildProver.sol"; +import {ParentToChildProver} from "../../../src/contracts/provers/linea/ParentToChildProver.sol"; +import {ZkEvmV2} from "@linea-contracts/rollup/ZkEvmV2.sol"; import {stdJson} from "forge-std/StdJson.sol"; /// @notice Mock LineaRollup contract for testing -contract MockLineaRollup { - mapping(uint256 => bytes32) public stateRootHashes; - +contract MockLineaRollup is ZkEvmV2 { function setStateRootHash(uint256 blockNumber, bytes32 stateRootHash) external { stateRootHashes[blockNumber] = stateRootHash; } + + function sendMessage(address _to, uint256 _fee, bytes calldata _calldata) external payable { + if (_fee > msg.value) { + revert ValueSentTooLow(); + } + + bytes32 messageHash = keccak256(abi.encode(msg.sender, _to, _fee, msg.value - _fee, 0, _calldata)); + + emit MessageSent(msg.sender, _to, _fee, msg.value - _fee, 0, _calldata, messageHash); + + // no-op + } + + function sender() external view returns (address) { + return TRANSIENT_MESSAGE_SENDER; + } } contract LineaParentToChildProverTest is Test { @@ -73,68 +88,68 @@ contract LineaParentToChildProverTest is Test { } // ═══════════════════════════════════════════════════════════════════════════ - // getTargetBlockHash Tests (Home Chain - L1) + // getTargetStateCommitment Tests (Home Chain - L1) // ═══════════════════════════════════════════════════════════════════════════ - function test_getTargetBlockHash_success() public { + function test_getTargetStateCommitment_success() public { // Set up mock to return a state root mockLineaRollup.setStateRootHash(L2_BLOCK_NUMBER, L2_STATE_ROOT); // We're on L1 (home chain) by default in tests vm.chainId(ETH_MAINNET_CHAIN_ID); - bytes32 stateRoot = prover.getTargetBlockHash(abi.encode(L2_BLOCK_NUMBER)); + bytes32 stateRoot = prover.getTargetStateCommitment(abi.encode(L2_BLOCK_NUMBER)); assertEq(stateRoot, L2_STATE_ROOT); } - function test_getTargetBlockHash_revertsWhenNotFound() public { + function test_getTargetStateCommitment_revertsWhenNotFound() public { // State root not set (returns bytes32(0)) vm.chainId(ETH_MAINNET_CHAIN_ID); vm.expectRevert(ParentToChildProver.TargetStateRootNotFound.selector); - prover.getTargetBlockHash(abi.encode(L2_BLOCK_NUMBER)); + prover.getTargetStateCommitment(abi.encode(L2_BLOCK_NUMBER)); } - function test_getTargetBlockHash_revertsOffHomeChain() public { + function test_getTargetStateCommitment_revertsOffHomeChain() public { // Switch to Linea L2 (not home chain) vm.chainId(LINEA_MAINNET_CHAIN_ID); vm.expectRevert(ParentToChildProver.CallNotOnHomeChain.selector); - prover.getTargetBlockHash(abi.encode(L2_BLOCK_NUMBER)); + prover.getTargetStateCommitment(abi.encode(L2_BLOCK_NUMBER)); } - function test_getTargetBlockHash_zeroBlockNumber() public { + function test_getTargetStateCommitment_zeroBlockNumber() public { mockLineaRollup.setStateRootHash(0, L2_STATE_ROOT); vm.chainId(ETH_MAINNET_CHAIN_ID); - bytes32 stateRoot = prover.getTargetBlockHash(abi.encode(uint256(0))); + bytes32 stateRoot = prover.getTargetStateCommitment(abi.encode(uint256(0))); assertEq(stateRoot, L2_STATE_ROOT); } - function testFuzz_getTargetBlockHash_revertsOnUnknownBlock(uint48 blockNumber) public { + function testFuzz_getTargetStateCommitment_revertsOnUnknownBlock(uint48 blockNumber) public { // Don't set any state root vm.chainId(ETH_MAINNET_CHAIN_ID); vm.expectRevert(ParentToChildProver.TargetStateRootNotFound.selector); - prover.getTargetBlockHash(abi.encode(uint256(blockNumber))); + prover.getTargetStateCommitment(abi.encode(uint256(blockNumber))); } // ═══════════════════════════════════════════════════════════════════════════ - // verifyTargetBlockHash Tests (Non-Home Chain) + // verifyTargetStateCommitment Tests (Non-Home Chain) // ═══════════════════════════════════════════════════════════════════════════ - function test_verifyTargetBlockHash_revertsOnHomeChain() public { - // On L1 (home chain), verifyTargetBlockHash should revert + function test_verifyTargetStateCommitment_revertsOnHomeChain() public { + // On L1 (home chain), verifyTargetStateCommitment should revert vm.chainId(ETH_MAINNET_CHAIN_ID); vm.expectRevert(ParentToChildProver.CallOnHomeChain.selector); - prover.verifyTargetBlockHash(bytes32(0), bytes("")); + prover.verifyTargetStateCommitment(bytes32(0), bytes("")); } /// @dev This test requires a real storage proof from L1 LineaRollup /// For now, we test that the function reverts with invalid proofs - function test_verifyTargetBlockHash_revertsWithInvalidProof() public { + function test_verifyTargetStateCommitment_revertsWithInvalidProof() public { vm.chainId(LINEA_MAINNET_CHAIN_ID); bytes memory input = abi.encode( @@ -146,7 +161,7 @@ contract LineaParentToChildProverTest is Test { // Should revert due to invalid proof vm.expectRevert(); - prover.verifyTargetBlockHash(bytes32(uint256(1)), input); + prover.verifyTargetStateCommitment(bytes32(uint256(1)), input); } // ═══════════════════════════════════════════════════════════════════════════ diff --git a/test/provers/optimism/ChildToParentProver.t.sol b/test/provers/optimism/ChildToParentProver.t.sol index 5d00286..53e0e63 100644 --- a/test/provers/optimism/ChildToParentProver.t.sol +++ b/test/provers/optimism/ChildToParentProver.t.sol @@ -36,10 +36,10 @@ contract OptimismChildToParentProverTest is Test { payload = vm.parseBytes(vm.readFile(string.concat(vm.projectRoot(), "/", path))); } - /// @notice Test getTargetBlockHash() - reads L1Block predeploy on Optimism + /// @notice Test getTargetStateCommitment() - reads L1Block predeploy on Optimism /// @dev Uses LIVE data instead of payload files because L1Block updates constantly. /// This approach is more reliable than static payloads for Optimism. - function test_getTargetBlockHash() public { + function test_getTargetStateCommitment() public { vm.selectFork(childForkId); // Read the CURRENT L1 block hash from the predeploy @@ -52,14 +52,14 @@ contract OptimismChildToParentProverTest is Test { expectedL1Hash = abi.decode(data, (bytes32)); // Test our prover returns the same value - bytes32 result = childToParentProver.getTargetBlockHash(""); + bytes32 result = childToParentProver.getTargetStateCommitment(""); assertEq(result, expectedL1Hash, "Block hash should match L1Block predeploy"); assertTrue(result != bytes32(0), "Block hash should not be zero"); } - /// @notice Test getTargetBlockHash() reverts when called on target chain (Ethereum) - function test_reverts_getTargetBlockHash_on_target_chain() public { + /// @notice Test getTargetStateCommitment() reverts when called on target chain (Ethereum) + function test_reverts_getTargetStateCommitment_on_target_chain() public { vm.selectFork(parentForkId); bytes memory payload = _loadPayload("test/payloads/optimism/calldata_get.hex"); @@ -75,15 +75,15 @@ contract OptimismChildToParentProverTest is Test { // Should revert because we're on Ethereum, not Optimism vm.expectRevert(ChildToParentProver.CallNotOnHomeChain.selector); - newChildToParentProver.getTargetBlockHash(abi.encode(input)); + newChildToParentProver.getTargetStateCommitment(abi.encode(input)); } - /// @notice Test verifyTargetBlockHash() - uses Merkle proofs + /// @notice Test verifyTargetStateCommitment() - uses Merkle proofs /// @dev Currently skipped due to memory allocation issues during proof decoding /// The underlying Merkle proof verification logic IS tested in Arbitrum tests. /// Root cause: Likely an ABI decoding issue with the specific proof structure from Optimism. /// The ProverUtils.getSlotFromBlockHeader() function is identical for both chains. - function skip_test_verifyTargetBlockHash() public { + function skip_test_verifyTargetStateCommitment() public { vm.selectFork(parentForkId); // Run verification on Ethereum bytes memory payload = _loadPayload("test/payloads/optimism/calldata_verify_target.hex"); @@ -93,21 +93,21 @@ contract OptimismChildToParentProverTest is Test { assertGt(payload.length, 64, "Payload should be > 64 bytes"); bytes32 homeBlockHash; - bytes32 targetBlockHash; + bytes32 targetStateCommitment; bytes memory input = Bytes.slice(payload, 64); assembly { homeBlockHash := mload(add(payload, 0x20)) - targetBlockHash := mload(add(payload, 0x40)) + targetStateCommitment := mload(add(payload, 0x40)) } - bytes32 result = childToParentProverCopy.verifyTargetBlockHash(homeBlockHash, input); + bytes32 result = childToParentProverCopy.verifyTargetStateCommitment(homeBlockHash, input); - assertEq(result, targetBlockHash, "Target block hash should match"); + assertEq(result, targetStateCommitment, "Target block hash should match"); } - /// @notice Test verifyTargetBlockHash() reverts when called on home chain (Optimism) - function test_verifyTargetBlockHash_reverts_on_home_chain() public { + /// @notice Test verifyTargetStateCommitment() reverts when called on home chain (Optimism) + function test_verifyTargetStateCommitment_reverts_on_home_chain() public { vm.selectFork(childForkId); // On Optimism (home chain) bytes memory payload = _loadPayload("test/payloads/optimism/calldata_verify_target.hex"); @@ -125,13 +125,13 @@ contract OptimismChildToParentProverTest is Test { // Should revert because we're on Optimism (home chain) vm.expectRevert(ChildToParentProver.CallOnHomeChain.selector); - childToParentProverCopy.verifyTargetBlockHash(homeBlockHash, input); + childToParentProverCopy.verifyTargetStateCommitment(homeBlockHash, input); } /// @notice Test verifyStorageSlot() - verifies Ethereum storage from Optimism /// @dev Currently skipped due to memory allocation issues during proof decoding /// The underlying storage proof verification logic IS tested in Arbitrum tests. - /// Root cause: Same ABI decoding issue as skip_test_verifyTargetBlockHash. + /// Root cause: Same ABI decoding issue as skip_test_verifyTargetStateCommitment. /// The ProverUtils.getSlotFromBlockHeader() function is identical for both chains. function skip_test_verifyStorageSlot() public { vm.selectFork(parentForkId); // Run on Ethereum @@ -146,17 +146,17 @@ contract OptimismChildToParentProverTest is Test { assertGt(payload.length, 64, "Payload should be > 64 bytes"); - bytes32 targetBlockHash; + bytes32 targetStateCommitment; bytes32 storageSlotValue; bytes memory input = Bytes.slice(payload, 64); assembly { - targetBlockHash := mload(add(payload, 0x20)) + targetStateCommitment := mload(add(payload, 0x20)) storageSlotValue := mload(add(payload, 0x40)) } (address account, uint256 slot, bytes32 value) = - childToParentProverCopy.verifyStorageSlot(targetBlockHash, input); + childToParentProverCopy.verifyStorageSlot(targetStateCommitment, input); assertEq(account, knownAccount, "Account should match"); assertEq(slot, knownSlot, "Slot should match"); diff --git a/test/provers/scroll/ParentToChildProver.t.sol b/test/provers/scroll/ParentToChildProver.t.sol index ea0219b..17ee513 100644 --- a/test/provers/scroll/ParentToChildProver.t.sol +++ b/test/provers/scroll/ParentToChildProver.t.sol @@ -3,7 +3,8 @@ pragma solidity ^0.8.27; import {console, Test} from "forge-std/Test.sol"; import {stdJson} from "forge-std/StdJson.sol"; -import {ParentToChildProver, IScrollChain} from "../../../src/contracts/provers/scroll/ParentToChildProver.sol"; +import {ParentToChildProver} from "../../../src/contracts/provers/scroll/ParentToChildProver.sol"; +import {IScrollChain} from "@scroll-tech/scroll-contracts/L1/rollup/IScrollChain.sol"; import {Bytes} from "@openzeppelin/contracts/utils/Bytes.sol"; import {RLP} from "@openzeppelin/contracts/utils/RLP.sol"; @@ -20,6 +21,43 @@ contract ScrollChainMock is IScrollChain { function isBatchFinalized(uint256 batchIndex) external view override returns (bool) { return _isBatchFinalized[batchIndex]; } + + // no-ops + function lastFinalizedBatchIndex() external view returns (uint256) { + return 0; + } + + function committedBatches(uint256 batchIndex) external view returns (bytes32) { + return bytes32(0); + } + + function withdrawRoots(uint256 batchIndex) external view returns (bytes32) { + return bytes32(0); + } + + function commitBatches(uint8 version, bytes32 parentBatchHash, bytes32 lastBatchHash) external { + return; + } + + function revertBatch(bytes calldata batchHeader) external { + return; + } + + function finalizeBundlePostEuclidV2( + bytes calldata batchHeader, + uint256 totalL1MessagesPoppedOverall, + bytes32 postStateRoot, + bytes32 withdrawRoot, + bytes calldata aggrProof + ) external { + return; + } + + function commitAndFinalizeBatch(uint8 version, bytes32 parentBatchHash, FinalizeStruct calldata finalizeStruct) + external + { + return; + } } contract ScrollParentToChildProverTest is Test { @@ -59,8 +97,8 @@ contract ScrollChainMock is IScrollChain { l2ChainId = 534352; // Scroll mainnet chain ID } - /// @notice Test getTargetBlockHash returns state root when called on home chain - function test_getTargetBlockHash_success() public { + /// @notice Test getTargetStateCommitment returns state root when called on home chain + function test_getTargetStateCommitment_success() public { vm.selectFork(l1ForkId); uint256 batchIndex = 12345; @@ -69,15 +107,15 @@ contract ScrollChainMock is IScrollChain { // Set up the mock scrollChainMock.setFinalizedStateRoot(batchIndex, expectedStateRoot); - // Call getTargetBlockHash + // Call getTargetStateCommitment bytes memory input = abi.encode(batchIndex); - bytes32 stateRoot = parentToChildProver.getTargetBlockHash(input); + bytes32 stateRoot = parentToChildProver.getTargetStateCommitment(input); assertEq(stateRoot, expectedStateRoot, "State root mismatch"); } - /// @notice Test getTargetBlockHash reverts when batch is not finalized - function test_getTargetBlockHash_stateRootNotFound() public { + /// @notice Test getTargetStateCommitment reverts when batch is not finalized + function test_getTargetStateCommitment_stateRootNotFound() public { vm.selectFork(l1ForkId); uint256 batchIndex = 99999; // Non-existent batch @@ -85,11 +123,11 @@ contract ScrollChainMock is IScrollChain { bytes memory input = abi.encode(batchIndex); vm.expectRevert(ParentToChildProver.StateRootNotFound.selector); - parentToChildProver.getTargetBlockHash(input); + parentToChildProver.getTargetStateCommitment(input); } - /// @notice Test getTargetBlockHash reverts when not on home chain - function test_getTargetBlockHash_notOnHomeChain() public { + /// @notice Test getTargetStateCommitment reverts when not on home chain + function test_getTargetStateCommitment_notOnHomeChain() public { // Simulate being on a different chain (Scroll L2) vm.chainId(l2ChainId); @@ -97,7 +135,7 @@ contract ScrollChainMock is IScrollChain { bytes memory input = abi.encode(batchIndex); vm.expectRevert(ParentToChildProver.CallNotOnHomeChain.selector); - parentToChildProver.getTargetBlockHash(input); + parentToChildProver.getTargetStateCommitment(input); } /// @notice Test verifyStorageSlot with real proof data @@ -126,7 +164,7 @@ contract ScrollChainMock is IScrollChain { // Input: abi.encode(address account, uint256 slot, bytes accountProof, bytes storageProof) bytes memory input = abi.encode(account, slot, rlpAccountProof, rlpStorageProof); - // The "targetBlockHash" for Scroll is actually the state root + // The "targetStateCommitment" for Scroll is actually the state root (address actualAccount, uint256 actualSlot, bytes32 actualValue) = parentToChildProver.verifyStorageSlot(stateRoot, input); @@ -180,7 +218,7 @@ contract ScrollChainMock is IScrollChain { // Step 2: Get the state root (simulating what happens on L1) bytes memory getInput = abi.encode(batchIndex); - bytes32 retrievedStateRoot = parentToChildProver.getTargetBlockHash(getInput); + bytes32 retrievedStateRoot = parentToChildProver.getTargetStateCommitment(getInput); assertEq(retrievedStateRoot, stateRoot, "Retrieved state root should match"); @@ -194,8 +232,8 @@ contract ScrollChainMock is IScrollChain { assertEq(actualValue, expectedValue, "value mismatch"); } - /// @notice Test verifyTargetBlockHash reverts when called on home chain - function test_verifyTargetBlockHash_onHomeChain() public { + /// @notice Test verifyTargetStateCommitment reverts when called on home chain + function test_verifyTargetStateCommitment_onHomeChain() public { vm.selectFork(l1ForkId); bytes32 homeBlockHash = bytes32(uint256(1)); @@ -207,7 +245,7 @@ contract ScrollChainMock is IScrollChain { ); vm.expectRevert(ParentToChildProver.CallOnHomeChain.selector); - parentToChildProver.verifyTargetBlockHash(homeBlockHash, input); + parentToChildProver.verifyTargetStateCommitment(homeBlockHash, input); } /// @notice Test version returns 1 diff --git a/test/provers/taiko/ChildToParentProver.t.sol b/test/provers/taiko/ChildToParentProver.t.sol index 5129901..06629c1 100644 --- a/test/provers/taiko/ChildToParentProver.t.sol +++ b/test/provers/taiko/ChildToParentProver.t.sol @@ -5,7 +5,7 @@ import {Test, console} from "forge-std/Test.sol"; import {stdJson} from "forge-std/StdJson.sol"; import {ChildToParentProver} from "../../../src/contracts/provers/taiko/ChildToParentProver.sol"; -/// @notice Mock SignalService for testing getTargetBlockHash +/// @notice Mock SignalService for testing getTargetStateCommitment contract MockSignalService { struct Checkpoint { uint48 blockNumber; @@ -31,8 +31,8 @@ contract MockSignalService { /// @title ChildToParentProver Tests /// @notice Tests for the Taiko ChildToParentProver (L2 → L1 verification) /// @dev Home chain: L2 (Taiko). Target chain: L1 (Ethereum). -/// - getTargetBlockHash: Called on L2 to read L1 block hash from L2's SignalService -/// - verifyTargetBlockHash: Called on L1 to verify L1 block hash via storage proof +/// - getTargetStateCommitment: Called on L2 to read L1 block hash from L2's SignalService +/// - verifyTargetStateCommitment: Called on L1 to verify L1 block hash via storage proof /// - verifyStorageSlot: Verifies storage slots against a trusted block hash contract TaikoChildToParentProverTest is Test { using stdJson for string; @@ -80,41 +80,41 @@ contract TaikoChildToParentProverTest is Test { // Chain ID Validation Tests // ═══════════════════════════════════════════════════════════════════════════ - function test_verifyTargetBlockHash_revertsOnHomeChain() public { + function test_verifyTargetStateCommitment_revertsOnHomeChain() public { vm.chainId(L2_CHAIN_ID); bytes memory input = abi.encode(bytes(""), uint48(0), bytes(""), bytes("")); vm.expectRevert(ChildToParentProver.CallOnHomeChain.selector); - prover.verifyTargetBlockHash(bytes32(0), input); + prover.verifyTargetStateCommitment(bytes32(0), input); } - function test_getTargetBlockHash_revertsOffHomeChain() public { + function test_getTargetStateCommitment_revertsOffHomeChain() public { vm.chainId(L1_CHAIN_ID); bytes memory input = abi.encode(uint48(0)); vm.expectRevert(ChildToParentProver.CallNotOnHomeChain.selector); - prover.getTargetBlockHash(input); + prover.getTargetStateCommitment(input); } // ═══════════════════════════════════════════════════════════════════════════ - // getTargetBlockHash Tests (Called on L2 to read L1 block hash) + // getTargetStateCommitment Tests (Called on L2 to read L1 block hash) // ═══════════════════════════════════════════════════════════════════════════ - function test_getTargetBlockHash_success() public { + function test_getTargetStateCommitment_success() public { vm.chainId(L2_CHAIN_ID); // Set checkpoint in mock SignalService mockSignalService.setCheckpoint(uint48(L1_BLOCK_NUMBER), L1_BLOCK_HASH, L1_STATE_ROOT); bytes memory input = abi.encode(uint48(L1_BLOCK_NUMBER)); - bytes32 targetBlockHash = prover.getTargetBlockHash(input); + bytes32 targetStateCommitment = prover.getTargetStateCommitment(input); - assertEq(targetBlockHash, L1_BLOCK_HASH, "targetBlockHash mismatch"); + assertEq(targetStateCommitment, L1_BLOCK_HASH, "targetStateCommitment mismatch"); } - function test_getTargetBlockHash_revertsWhenNotFound() public { + function test_getTargetStateCommitment_revertsWhenNotFound() public { vm.chainId(L2_CHAIN_ID); // Don't set any checkpoint - it doesn't exist @@ -122,7 +122,7 @@ contract TaikoChildToParentProverTest is Test { // Reverts with SignalService's SS_CHECKPOINT_NOT_FOUND error vm.expectRevert(MockSignalService.SS_CHECKPOINT_NOT_FOUND.selector); - prover.getTargetBlockHash(input); + prover.getTargetStateCommitment(input); } // ═══════════════════════════════════════════════════════════════════════════ @@ -172,17 +172,17 @@ contract TaikoChildToParentProverTest is Test { } // ═══════════════════════════════════════════════════════════════════════════ - // verifyTargetBlockHash Tests (Called on L1 to verify via storage proof) + // verifyTargetStateCommitment Tests (Called on L1 to verify via storage proof) // ═══════════════════════════════════════════════════════════════════════════ - /// @notice Test verifyTargetBlockHash with mocked L2 state + /// @notice Test verifyTargetStateCommitment with mocked L2 state /// @dev This simulates being on L1 and verifying that L2's SignalService /// contains a checkpoint for a specific L1 block - function test_verifyTargetBlockHash_withMockedProof() public { + function test_verifyTargetStateCommitment_withMockedProof() public { // Simulate being on L1 (not home chain) vm.chainId(L1_CHAIN_ID); - // For verifyTargetBlockHash, we need: + // For verifyTargetStateCommitment, we need: // 1. An L2 block hash (homeBlockHash) - the trusted anchor // 2. Proof that L2's SignalService contains the L1 checkpoint @@ -200,7 +200,7 @@ contract TaikoChildToParentProverTest is Test { // This will revert because the proof is invalid, but it proves we're past chain ID check vm.expectRevert(); // Will revert on invalid RLP/proof - prover.verifyTargetBlockHash(bytes32(uint256(1)), input); + prover.verifyTargetStateCommitment(bytes32(uint256(1)), input); } // ═══════════════════════════════════════════════════════════════════════════ @@ -211,7 +211,7 @@ contract TaikoChildToParentProverTest is Test { /// @dev This test: /// 1. Deploys the prover configured for L2 as home chain /// 2. Sets L2's SignalService with an L1 checkpoint - /// 3. Uses getTargetBlockHash (on L2) to get L1 block hash + /// 3. Uses getTargetStateCommitment (on L2) to get L1 block hash /// 4. Uses verifyStorageSlot to verify L1 Broadcaster storage function test_integration_L1ToL2_verification() public { // Step 1: Configure chain as L2 (home chain) @@ -222,7 +222,7 @@ contract TaikoChildToParentProverTest is Test { // Step 3: Get L1 block hash from L2's SignalService bytes memory getInput = abi.encode(uint48(L1_BLOCK_NUMBER)); - bytes32 l1BlockHash = prover.getTargetBlockHash(getInput); + bytes32 l1BlockHash = prover.getTargetStateCommitment(getInput); assertEq(l1BlockHash, L1_BLOCK_HASH, "L1 block hash mismatch"); // Step 4: Load proof data and verify L1 storage @@ -250,7 +250,7 @@ contract TaikoChildToParentProverTest is Test { // Edge Case Tests // ═══════════════════════════════════════════════════════════════════════════ - function test_getTargetBlockHash_zeroBlockNumber() public { + function test_getTargetStateCommitment_zeroBlockNumber() public { vm.chainId(L2_CHAIN_ID); // Block 0 with no checkpoint should revert @@ -258,7 +258,7 @@ contract TaikoChildToParentProverTest is Test { // Reverts with SignalService's SS_CHECKPOINT_NOT_FOUND error vm.expectRevert(MockSignalService.SS_CHECKPOINT_NOT_FOUND.selector); - prover.getTargetBlockHash(input); + prover.getTargetStateCommitment(input); } function test_constructor_differentParameters() public { diff --git a/test/provers/taiko/ParentToChildProver.t.sol b/test/provers/taiko/ParentToChildProver.t.sol index e6374ff..a18e8f8 100644 --- a/test/provers/taiko/ParentToChildProver.t.sol +++ b/test/provers/taiko/ParentToChildProver.t.sol @@ -5,7 +5,7 @@ import {Test, console} from "forge-std/Test.sol"; import {stdJson} from "forge-std/StdJson.sol"; import {ParentToChildProver} from "../../../src/contracts/provers/taiko/ParentToChildProver.sol"; -/// @notice Mock SignalService for testing getTargetBlockHash +/// @notice Mock SignalService for testing getTargetStateCommitment contract MockSignalService { struct Checkpoint { uint48 blockNumber; @@ -31,8 +31,8 @@ contract MockSignalService { /// @title ParentToChildProver Tests /// @notice Tests for the Taiko ParentToChildProver (L1 → L2 verification) /// @dev Home chain: L1 (Ethereum). Target chain: L2 (Taiko). -/// - getTargetBlockHash: Called on L1 to read L2 block hash from L1's SignalService -/// - verifyTargetBlockHash: Called on L2 to verify L2 block hash via storage proof +/// - getTargetStateCommitment: Called on L1 to read L2 block hash from L1's SignalService +/// - verifyTargetStateCommitment: Called on L2 to verify L2 block hash via storage proof /// - verifyStorageSlot: Verifies storage slots against a trusted block hash contract TaikoParentToChildProverTest is Test { using stdJson for string; @@ -80,41 +80,41 @@ contract TaikoParentToChildProverTest is Test { // Chain ID Validation Tests // ═══════════════════════════════════════════════════════════════════════════ - function test_verifyTargetBlockHash_revertsOnHomeChain() public { + function test_verifyTargetStateCommitment_revertsOnHomeChain() public { vm.chainId(L1_CHAIN_ID); bytes memory input = abi.encode(bytes(""), uint48(0), bytes(""), bytes("")); vm.expectRevert(ParentToChildProver.CallOnHomeChain.selector); - prover.verifyTargetBlockHash(bytes32(0), input); + prover.verifyTargetStateCommitment(bytes32(0), input); } - function test_getTargetBlockHash_revertsOffHomeChain() public { + function test_getTargetStateCommitment_revertsOffHomeChain() public { vm.chainId(L2_CHAIN_ID); bytes memory input = abi.encode(uint48(0)); vm.expectRevert(ParentToChildProver.CallNotOnHomeChain.selector); - prover.getTargetBlockHash(input); + prover.getTargetStateCommitment(input); } // ═══════════════════════════════════════════════════════════════════════════ - // getTargetBlockHash Tests (Called on L1 to read L2 block hash) + // getTargetStateCommitment Tests (Called on L1 to read L2 block hash) // ═══════════════════════════════════════════════════════════════════════════ - function test_getTargetBlockHash_success() public { + function test_getTargetStateCommitment_success() public { vm.chainId(L1_CHAIN_ID); // Set checkpoint in mock SignalService mockSignalService.setCheckpoint(uint48(L2_BLOCK_NUMBER), L2_BLOCK_HASH, L2_STATE_ROOT); bytes memory input = abi.encode(uint48(L2_BLOCK_NUMBER)); - bytes32 targetBlockHash = prover.getTargetBlockHash(input); + bytes32 targetStateCommitment = prover.getTargetStateCommitment(input); - assertEq(targetBlockHash, L2_BLOCK_HASH, "targetBlockHash mismatch"); + assertEq(targetStateCommitment, L2_BLOCK_HASH, "targetStateCommitment mismatch"); } - function test_getTargetBlockHash_revertsWhenNotFound() public { + function test_getTargetStateCommitment_revertsWhenNotFound() public { vm.chainId(L1_CHAIN_ID); // Don't set any checkpoint - it doesn't exist @@ -122,7 +122,7 @@ contract TaikoParentToChildProverTest is Test { // Reverts with SignalService's SS_CHECKPOINT_NOT_FOUND error vm.expectRevert(MockSignalService.SS_CHECKPOINT_NOT_FOUND.selector); - prover.getTargetBlockHash(input); + prover.getTargetStateCommitment(input); } // ═══════════════════════════════════════════════════════════════════════════ @@ -172,17 +172,17 @@ contract TaikoParentToChildProverTest is Test { } // ═══════════════════════════════════════════════════════════════════════════ - // verifyTargetBlockHash Tests (Called on L2 to verify via storage proof) + // verifyTargetStateCommitment Tests (Called on L2 to verify via storage proof) // ═══════════════════════════════════════════════════════════════════════════ - /// @notice Test verifyTargetBlockHash with mocked L1 state + /// @notice Test verifyTargetStateCommitment with mocked L1 state /// @dev This simulates being on L2 and verifying that L1's SignalService /// contains a checkpoint for a specific L2 block - function test_verifyTargetBlockHash_withMockedProof() public { + function test_verifyTargetStateCommitment_withMockedProof() public { // Simulate being on L2 (not home chain) vm.chainId(L2_CHAIN_ID); - // For verifyTargetBlockHash, we need: + // For verifyTargetStateCommitment, we need: // 1. An L1 block hash (homeBlockHash) - the trusted anchor // 2. Proof that L1's SignalService contains the L2 checkpoint @@ -200,7 +200,7 @@ contract TaikoParentToChildProverTest is Test { // This will revert because the proof is invalid, but it proves we're past chain ID check vm.expectRevert(); // Will revert on invalid RLP/proof - prover.verifyTargetBlockHash(bytes32(uint256(1)), input); + prover.verifyTargetStateCommitment(bytes32(uint256(1)), input); } // ═══════════════════════════════════════════════════════════════════════════ @@ -211,7 +211,7 @@ contract TaikoParentToChildProverTest is Test { /// @dev This test: /// 1. Deploys the prover configured for L1 as home chain /// 2. Sets L1's SignalService with an L2 checkpoint - /// 3. Uses getTargetBlockHash (on L1) to get L2 block hash + /// 3. Uses getTargetStateCommitment (on L1) to get L2 block hash /// 4. Uses verifyStorageSlot to verify L2 Broadcaster storage function test_integration_L2ToL1_verification() public { // Step 1: Configure chain as L1 (home chain) @@ -222,7 +222,7 @@ contract TaikoParentToChildProverTest is Test { // Step 3: Get L2 block hash from L1's SignalService bytes memory getInput = abi.encode(uint48(L2_BLOCK_NUMBER)); - bytes32 l2BlockHash = prover.getTargetBlockHash(getInput); + bytes32 l2BlockHash = prover.getTargetStateCommitment(getInput); assertEq(l2BlockHash, L2_BLOCK_HASH, "L2 block hash mismatch"); // Step 4: Load proof data and verify L2 storage @@ -250,7 +250,7 @@ contract TaikoParentToChildProverTest is Test { // Edge Case Tests // ═══════════════════════════════════════════════════════════════════════════ - function test_getTargetBlockHash_zeroBlockNumber() public { + function test_getTargetStateCommitment_zeroBlockNumber() public { vm.chainId(L1_CHAIN_ID); // Block 0 with no checkpoint should revert @@ -258,7 +258,7 @@ contract TaikoParentToChildProverTest is Test { // Reverts with SignalService's SS_CHECKPOINT_NOT_FOUND error vm.expectRevert(MockSignalService.SS_CHECKPOINT_NOT_FOUND.selector); - prover.getTargetBlockHash(input); + prover.getTargetStateCommitment(input); } function test_constructor_differentParameters() public { @@ -277,7 +277,7 @@ contract TaikoParentToChildProverTest is Test { // Fuzz Tests // ═══════════════════════════════════════════════════════════════════════════ - function testFuzz_getTargetBlockHash_revertsOnUnknownBlock(uint48 blockNumber) public { + function testFuzz_getTargetStateCommitment_revertsOnUnknownBlock(uint48 blockNumber) public { vm.chainId(L1_CHAIN_ID); // Any block number without a mocked checkpoint should revert @@ -285,7 +285,7 @@ contract TaikoParentToChildProverTest is Test { // Reverts with SignalService's SS_CHECKPOINT_NOT_FOUND error vm.expectRevert(MockSignalService.SS_CHECKPOINT_NOT_FOUND.selector); - prover.getTargetBlockHash(input); + prover.getTargetStateCommitment(input); } function testFuzz_constructor_acceptsAnyParameters(address signalService, uint256 slot, uint256 chainId) public { diff --git a/test/provers/zksync/ParentChildToProver.t.sol b/test/provers/zksync/ParentChildToProver.t.sol index 629e0c2..d7c2d2d 100644 --- a/test/provers/zksync/ParentChildToProver.t.sol +++ b/test/provers/zksync/ParentChildToProver.t.sol @@ -122,7 +122,7 @@ contract ZkSyncParentToChildProverTest is Test { prover.verifyStorageSlot(expectedL2LogsRootHash, abi.encode(proof)); } - function test_getTargetBlockHash() public { + function test_getTargetStateCommitment() public { vm.selectFork(parentForkId); MockZkChain mockZkChain = new MockZkChain(); @@ -130,7 +130,7 @@ contract ZkSyncParentToChildProverTest is Test { ParentToChildProver prover = new ParentToChildProver(address(mockZkChain), 0, 300, 32657, parentChainId); - bytes32 targetL2LogsRootHash = prover.getTargetBlockHash(abi.encode(43984)); + bytes32 targetL2LogsRootHash = prover.getTargetStateCommitment(abi.encode(43984)); assertEq( targetL2LogsRootHash, 0x4cbeceb2a95a01369ab104ec6a305e37cb22d3717abb91da6880e038c3160470, @@ -138,22 +138,22 @@ contract ZkSyncParentToChildProverTest is Test { ); } - function test_getTargetBlockHash_revertsWithNotFound() public { + function test_getTargetStateCommitment_revertsWithNotFound() public { vm.selectFork(parentForkId); MockZkChain mockZkChain = new MockZkChain(); ParentToChildProver prover = new ParentToChildProver(address(mockZkChain), 0, 300, 32657, parentChainId); vm.expectRevert(ParentToChildProver.L2LogsRootHashNotFound.selector); - prover.getTargetBlockHash(abi.encode(43985)); + prover.getTargetStateCommitment(abi.encode(43985)); } - function test_getTargetBlockHash_revertsWithNotInHomeChain() public { + function test_getTargetStateCommitment_revertsWithNotInHomeChain() public { MockZkChain mockZkChain = new MockZkChain(); ParentToChildProver prover = new ParentToChildProver(address(mockZkChain), 0, 300, 32657, parentChainId); - vm.expectRevert(ParentToChildProver.NotInHomeChain.selector); - prover.getTargetBlockHash(abi.encode(43985)); + vm.expectRevert(ParentToChildProver.CallNotOnHomeChain.selector); + prover.getTargetStateCommitment(abi.encode(43985)); } }