Skip to content

Commit 38bc3ef

Browse files
committed
merge main
1 parent 10055d2 commit 38bc3ef

19 files changed

Lines changed: 30 additions & 417 deletions

src/contracts/Broadcaster.sol

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import {StorageSlot} from "@openzeppelin/contracts/utils/StorageSlot.sol";
1010
/// @dev Message timestamps are stored in deterministic storage slots calculated from hash(message, publisher) to prevent duplicate broadcasts.
1111
/// Each broadcast is timestamped with the block timestamp and emits an event for off-chain indexing.
1212
/// The storage layout is designed to be efficiently provable for cross-chain message verification.
13-
/// @custom:security-contact security@openzeppelin.com
1413
contract Broadcaster is IBroadcaster {
1514
error MessageAlreadyBroadcasted();
1615

@@ -47,17 +46,17 @@ contract Broadcaster is IBroadcaster {
4746
}
4847

4948
/// @dev Helper function to store a value in a storage slot.
50-
function _writeStorageSlot(bytes32 slot, uint256 value) private {
49+
function _writeStorageSlot(bytes32 slot, uint256 value) internal {
5150
StorageSlot.getUint256Slot(slot).value = value;
5251
}
5352

5453
/// @dev Helper function to load a storage slot.
55-
function _loadStorageSlot(bytes32 slot) private view returns (uint256 value) {
54+
function _loadStorageSlot(bytes32 slot) internal view returns (uint256 value) {
5655
value = StorageSlot.getUint256Slot(slot).value;
5756
}
5857

5958
/// @dev Helper function to calculate the storage slot for a message.
60-
function _computeMessageSlot(bytes32 message, address publisher) private pure returns (bytes32) {
59+
function _computeMessageSlot(bytes32 message, address publisher) internal pure returns (bytes32) {
6160
return keccak256(abi.encode(message, publisher));
6261
}
6362
}

src/contracts/Receiver.sol

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import {STATE_PROVER_POINTER_SLOT} from "./StateProverPointer.sol";
1414
/// 3. Following a proof route that can span multiple chain hops
1515
/// The verification process ensures that a message was actually broadcast on a remote chain
1616
/// at a specific timestamp without requiring trust in intermediaries.
17-
/// @custom:security-contact security@openzeppelin.com
1817
contract Receiver is IReceiver {
1918
mapping(bytes32 stateProverPointerId => IStateProver stateProverCopy) private _stateProverCopies;
2019

@@ -112,7 +111,7 @@ contract Receiver is IReceiver {
112111
}
113112

114113
function _readRemoteSlot(RemoteReadArgs calldata readArgs)
115-
private
114+
internal
116115
view
117116
returns (bytes32 remoteAccountId, uint256 slot, bytes32 slotValue)
118117
{
@@ -150,7 +149,7 @@ contract Receiver is IReceiver {
150149
remoteAccountId = accumulator(remoteAccountId, remoteAccount);
151150
}
152151

153-
function accumulator(bytes32 acc, address addr) private pure returns (bytes32) {
152+
function accumulator(bytes32 acc, address addr) internal pure returns (bytes32) {
154153
return keccak256(abi.encode(acc, addr));
155154
}
156155
}

src/contracts/StateProverPointer.sol

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ bytes32 constant STATE_PROVER_POINTER_SLOT = bytes32(uint256(keccak256("eip7888.
1313
/// @dev This contract stores the address and code hash of the current StateProver implementation.
1414
/// It enforces version monotonicity to ensure that updates always move to newer versions.
1515
/// The code hash is stored in a dedicated storage slot for efficient cross-chain verification.
16-
/// @custom:security-contact security@openzeppelin.com
1716
contract StateProverPointer is IStateProverPointer, Ownable {
1817
address internal _implementationAddress;
1918

@@ -30,7 +29,7 @@ contract StateProverPointer is IStateProverPointer, Ownable {
3029

3130
/// @notice Return the code hash of the latest version of the prover.
3231
/// @return codeHash The code hash of the current implementation stored in the pointer slot.
33-
function implementationCodeHash() external view returns (bytes32 codeHash) {
32+
function implementationCodeHash() public view returns (bytes32 codeHash) {
3433
codeHash = StorageSlot.getBytes32Slot(STATE_PROVER_POINTER_SLOT).value;
3534
}
3635

@@ -69,7 +68,7 @@ contract StateProverPointer is IStateProverPointer, Ownable {
6968
);
7069
}
7170

72-
function _setCodeHash(bytes32 _codeHash) private {
71+
function _setCodeHash(bytes32 _codeHash) internal {
7372
StorageSlot.getBytes32Slot(STATE_PROVER_POINTER_SLOT).value = _codeHash;
7473
}
7574
}

src/contracts/ZkSyncBroadcaster.sol

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ interface IL1Messenger {
2222
/// with the block timestamp and emits an event for off-chain indexing. Additionally, when a message is
2323
/// broadcast, it sends an L2->L1 message containing the original message and timestamp (ABI encoded together).
2424
/// The storage layout is designed to be efficiently provable for cross-chain message verification.
25-
/// @custom:security-contact security@openzeppelin.com
2625
contract ZkSyncBroadcaster is IBroadcaster {
2726
/// @notice Error thrown when attempting to broadcast a message that has already been broadcast by the same publisher.
2827
error MessageAlreadyBroadcasted();
@@ -72,29 +71,29 @@ contract ZkSyncBroadcaster is IBroadcaster {
7271

7372
/// @notice Returns the L1 messenger contract address.
7473
/// @return The IL1Messenger contract instance used to send messages to L1.
75-
function l1Messenger() external view returns (IL1Messenger) {
74+
function l1Messenger() public view returns (IL1Messenger) {
7675
return _l1Messenger;
7776
}
7877

7978
/// @dev Helper function to store a value in a storage slot.
8079
/// @param slot The storage slot to write to.
8180
/// @param value The value to store.
82-
function _writeStorageSlot(bytes32 slot, uint256 value) private {
81+
function _writeStorageSlot(bytes32 slot, uint256 value) internal {
8382
StorageSlot.getUint256Slot(slot).value = value;
8483
}
8584

8685
/// @dev Helper function to load a storage slot.
8786
/// @param slot The storage slot to read from.
8887
/// @return value The value stored in the slot.
89-
function _loadStorageSlot(bytes32 slot) private view returns (uint256 value) {
88+
function _loadStorageSlot(bytes32 slot) internal view returns (uint256 value) {
9089
value = StorageSlot.getUint256Slot(slot).value;
9190
}
9291

9392
/// @dev Helper function to calculate the storage slot for a message.
9493
/// @param message The message to compute the slot for.
9594
/// @param publisher The address of the publisher.
9695
/// @return The computed storage slot.
97-
function _computeMessageSlot(bytes32 message, address publisher) private pure returns (bytes32) {
96+
function _computeMessageSlot(bytes32 message, address publisher) internal pure returns (bytes32) {
9897
return keccak256(abi.encode(message, publisher));
9998
}
10099
}

src/contracts/libraries/ProverUtils.sol

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import {RLP} from "@openzeppelin/contracts/utils/RLP.sol";
66
import {Memory} from "@openzeppelin/contracts/utils/Memory.sol";
77

88
/// @notice Base contract for IStateProver contracts. Contains helpers for verifying block headers and MPT proofs.
9-
/// @custom:security-contact security@openzeppelin.com
109
library ProverUtils {
1110
using Memory for bytes;
1211
using RLP for Memory.Slice;

src/contracts/provers/arbitrum/ChildToParentProver.sol

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import {SlotDerivation} from "@openzeppelin/contracts/utils/SlotDerivation.sol";
1010
/// @dev verifyTargetStateCommitment and getTargetStateCommitment get block hashes from the block hash buffer at 0x0000000048C4Ed10cF14A02B9E0AbDDA5227b071.
1111
/// See https://github.com/OffchainLabs/block-hash-pusher/blob/a1e26f2e42e6306d1e7f03c5d20fa6aa64ff7a12 for more details.
1212
/// verifyStorageSlot is implemented to work against any parent chain with a standard Ethereum block header and state trie.
13-
/// @custom:security-contact security@openzeppelin.com
1413
contract ChildToParentProver is IStateProver {
1514
/// @dev Address of the block hash buffer contract
1615
/// See https://github.com/OffchainLabs/block-hash-pusher/blob/a1e26f2e42e6306d1e7f03c5d20fa6aa64ff7a12/.env.example#L12

src/contracts/provers/arbitrum/ParentToChildProver.sol

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import {SlotDerivation} from "@openzeppelin/contracts/utils/SlotDerivation.sol";
99
/// @notice Arbitrum implementation of a parent to child IStateProver.
1010
/// @dev verifyTargetStateCommitment and getTargetStateCommitment get block hashes from the child chain's Outbox contract.
1111
/// verifyStorageSlot is implemented to work against any Arbitrum child chain with a standard Ethereum block header and state trie.
12-
/// @custom:security-contact security@openzeppelin.com
1312
contract ParentToChildProver is IStateProver {
1413
/// @dev Address of the child chain's Outbox contract
1514
address public immutable outbox;

src/contracts/provers/linea/ChildToParentProver.sol

Lines changed: 12 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
// SPDX-License-Identifier: MIT
22
pragma solidity 0.8.30;
33

4-
import {SparseMerkleProof} from "../../libraries/linea/SparseMerkleProof.sol";
54
import {ProverUtils} from "../../libraries/ProverUtils.sol";
65
import {IStateProver} from "../../interfaces/IStateProver.sol";
76
import {IBuffer} from "../../block-hash-pusher/interfaces/IBuffer.sol";
@@ -10,7 +9,6 @@ import {SlotDerivation} from "@openzeppelin/contracts/utils/SlotDerivation.sol";
109
/// @notice Linea implementation of a child to parent IStateProver.
1110
/// @dev verifyTargetStateCommitment and getTargetStateCommitment get block hashes from the block hash buffer.
1211
/// verifyStorageSlot is implemented to work against any parent chain with a standard Ethereum block header and state trie.
13-
/// @custom:security-contact security@openzeppelin.com
1412
contract ChildToParentProver is IStateProver {
1513
/// @dev Address of the block hash buffer contract.
1614
address public immutable blockHashBuffer;
@@ -23,25 +21,16 @@ contract ChildToParentProver is IStateProver {
2321

2422
error CallNotOnHomeChain();
2523
error CallOnHomeChain();
26-
error InvalidAccountProof();
27-
error InvalidStorageProof();
28-
error StorageValueMismatch();
29-
error AccountKeyMismatch();
30-
error AccountValueMismatch();
31-
error StorageKeyMismatch();
3224
error InvalidTargetStateCommitment();
3325

3426
constructor(address _blockHashBuffer, uint256 _homeChainId) {
3527
blockHashBuffer = _blockHashBuffer;
3628
homeChainId = _homeChainId;
3729
}
3830

39-
/// @notice Get a parent chain block hash from the buffer at `blockHashBuffer` using a Linea SMT proof
40-
/// @dev Linea uses Sparse Merkle Trees with MiMC hashing.
41-
/// Proofs must be generated using linea_getProof RPC method.
42-
/// @param homeBlockHash The state root of the home chain (Linea SMT state root).
43-
/// @param input ABI encoded (uint256 targetBlockNumber, uint256 accountLeafIndex, bytes[] accountProof,
44-
/// bytes accountValue, uint256 storageLeafIndex, bytes[] storageProof, bytes32 claimedStorageValue)
31+
/// @notice Get a parent chain block hash from the buffer at `blockHashBuffer` using a storage proof
32+
/// @param homeBlockHash The block hash of the home chain.
33+
/// @param input ABI encoded (bytes blockHeader, uint256 targetBlockNumber, bytes accountProof, bytes storageProof)
4534
function verifyTargetStateCommitment(bytes32 homeBlockHash, bytes calldata input)
4635
external
4736
view
@@ -50,62 +39,18 @@ contract ChildToParentProver is IStateProver {
5039
if (block.chainid == homeChainId) {
5140
revert CallOnHomeChain();
5241
}
42+
// decode the input
43+
(bytes memory rlpBlockHeader, uint256 targetBlockNumber, bytes memory accountProof, bytes memory storageProof) =
44+
abi.decode(input, (bytes, uint256, bytes, bytes));
5345

54-
uint256 targetBlockNumber;
55-
uint256 accountLeafIndex;
56-
bytes[] memory accountProof;
57-
bytes memory accountValue;
58-
uint256 storageLeafIndex;
59-
bytes[] memory storageProof;
60-
bytes32 claimedStorageValue;
61-
62-
(
63-
targetBlockNumber,
64-
accountLeafIndex,
65-
accountProof,
66-
accountValue,
67-
storageLeafIndex,
68-
storageProof,
69-
claimedStorageValue
70-
) = abi.decode(input, (uint256, uint256, bytes[], bytes, uint256, bytes[], bytes32));
71-
46+
// calculate the slot based on the provided block number
47+
// see: https://github.com/OffchainLabs/block-hash-pusher/blob/a1e26f2e42e6306d1e7f03c5d20fa6aa64ff7a12/contracts/Buffer.sol#L32
7248
uint256 slot = uint256(SlotDerivation.deriveMapping(bytes32(BLOCK_HASH_MAPPING_SLOT), targetBlockNumber));
7349

74-
bool accountValid = SparseMerkleProof.verifyProof(accountProof, accountLeafIndex, homeBlockHash);
75-
if (!accountValid) {
76-
revert InvalidAccountProof();
77-
}
78-
79-
SparseMerkleProof.Leaf memory accountLeaf = SparseMerkleProof.getLeaf(accountProof[accountProof.length - 1]);
80-
bytes32 expectedAccountHKey = SparseMerkleProof.hashAccountKey(blockHashBuffer);
81-
if (accountLeaf.hKey != expectedAccountHKey) {
82-
revert AccountKeyMismatch();
83-
}
84-
85-
bytes32 expectedAccountHValue = SparseMerkleProof.hashAccountValue(accountValue);
86-
if (accountLeaf.hValue != expectedAccountHValue) {
87-
revert AccountValueMismatch();
88-
}
89-
90-
SparseMerkleProof.Account memory accountData = SparseMerkleProof.getAccount(accountValue);
91-
92-
bool storageValid = SparseMerkleProof.verifyProof(storageProof, storageLeafIndex, accountData.storageRoot);
93-
if (!storageValid) {
94-
revert InvalidStorageProof();
95-
}
96-
97-
SparseMerkleProof.Leaf memory storageLeaf = SparseMerkleProof.getLeaf(storageProof[storageProof.length - 1]);
98-
bytes32 expectedStorageHKey = SparseMerkleProof.hashStorageKey(bytes32(slot));
99-
if (storageLeaf.hKey != expectedStorageHKey) {
100-
revert StorageKeyMismatch();
101-
}
102-
103-
bytes32 expectedHValue = SparseMerkleProof.hashStorageValue(claimedStorageValue);
104-
if (storageLeaf.hValue != expectedHValue) {
105-
revert StorageValueMismatch();
106-
}
107-
108-
targetStateCommitment = claimedStorageValue;
50+
// verify proofs and get the block hash
51+
targetStateCommitment = ProverUtils.getSlotFromBlockHeader(
52+
homeBlockHash, rlpBlockHeader, blockHashBuffer, slot, accountProof, storageProof
53+
);
10954
require(targetStateCommitment != bytes32(0), InvalidTargetStateCommitment());
11055
}
11156

src/contracts/provers/linea/ParentToChildProver.sol

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ import {ZkEvmV2} from "@linea-contracts/rollup/ZkEvmV2.sol";
1616
///
1717
/// Note: Linea uses Sparse Merkle Tree (SMT) with MiMC hashing, NOT Merkle-Patricia Trie (MPT).
1818
/// The state root stored on L1 is the SMT root, which requires linea_getProof for verification.
19-
/// @custom:security-contact security@openzeppelin.com
2019
contract ParentToChildProver is IStateProver {
2120
/// @dev Address of the LineaRollup contract on L1
2221
address public immutable lineaRollup;

src/contracts/provers/optimism/ChildToParentProver.sol

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ interface IL1Block {
1616
/// Historical messages CAN be verified by generating fresh proofs on-demand.
1717
/// Pre-generated proofs become stale when L1Block updates (~5 minutes).
1818
/// Operational difference from Arbitrum: proofs must be generated just-in-time rather than pre-cached.
19-
/// @custom:security-contact security@openzeppelin.com
2019
contract ChildToParentProver is IStateProver {
2120
address public constant L1_BLOCK_PREDEPLOY = 0x4200000000000000000000000000000000000015;
2221
uint256 public constant L1_BLOCK_HASH_SLOT = 2; // hash is at slot 2

0 commit comments

Comments
 (0)