Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 2 additions & 22 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions snapshots/verifyBroadcastMessage.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
{
"EthereumToOptimism": "1639239",
"EthereumToOptimism": "1642677",
"EthereumToTaikoL2": "1052244",
"LineaL2ToEthereum": "2551806",
"ScrollL2ToEthereum": "1361970",
"ScrollToOptimism": "1348710",
"ScrollToOptimism": "1352078",
"TaikoL2ToEthereum": "1022307",
"ZkSyncL2ToEthereum": "125693"
}
61 changes: 61 additions & 0 deletions src/contracts/block-hash-pusher/optimism/OptimismBuffer.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.30;

import {BaseBuffer} from "../BaseBuffer.sol";
import {IBuffer} from "../interfaces/IBuffer.sol";
import {ICrossDomainMessenger} from "@eth-optimism/contracts/libraries/bridge/ICrossDomainMessenger.sol";

/// @title OptimismBuffer
/// @notice Implementation of BaseBuffer for storing Ethereum L1 block hashes on Optimism L2.
/// @dev This contract extends BaseBuffer with access control specific to Optimism's L1->L2 messaging.
/// The pusher address on L1 must send the message via L1CrossDomainMessengerProxy to the buffer address on L2.
/// The L2CrossDomainMessenger contract on L2 is responsible for relaying the message to the buffer contract on L2.
/// @custom:security-contact security@openzeppelin.com
contract OptimismBuffer is BaseBuffer {
/// @dev The address of the L2CrossDomainMessenger contract on L2.
address private immutable _l2CrossDomainMessenger;

/// @dev The address of the pusher contract on L1.
address private immutable _pusher;

/// @notice Thrown when attempting to set an invalid L2CrossDomainMessenger address.
error InvalidL2CrossDomainMessengerAddress();

/// @notice Thrown when attempting to set an invalid pusher address.
error InvalidPusherAddress();

/// @notice Thrown when the domain message sender does not match the pusher address.
error DomainMessageSenderMismatch();

/// @notice Thrown when the sender is not the L2CrossDomainMessenger contract.
error InvalidSender();

constructor(address l2CrossDomainMessenger_, address pusher_) {
require(l2CrossDomainMessenger_ != address(0), InvalidL2CrossDomainMessengerAddress());
require(pusher_ != address(0), InvalidPusherAddress());

_l2CrossDomainMessenger = l2CrossDomainMessenger_;
_pusher = pusher_;
}

/// @inheritdoc IBuffer
function receiveHashes(uint256 firstBlockNumber, bytes32[] calldata blockHashes) external {
ICrossDomainMessenger l2CrossDomainMessengerCached = ICrossDomainMessenger(l2CrossDomainMessenger());

require(msg.sender == address(l2CrossDomainMessengerCached), InvalidSender());
require(l2CrossDomainMessengerCached.xDomainMessageSender() == _pusher, DomainMessageSenderMismatch());

_receiveHashes(firstBlockNumber, blockHashes);
}

/// @inheritdoc IBuffer
function pusher() public view returns (address) {
return _pusher;
}

/// @notice The address of the L2CrossDomainMessenger contract on L2.
/// @return The address of the L2CrossDomainMessenger contract on L2.
function l2CrossDomainMessenger() public view returns (address) {
return _l2CrossDomainMessenger;
}
}
57 changes: 57 additions & 0 deletions src/contracts/block-hash-pusher/optimism/OptimismPusher.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.30;

import {BlockHashArrayBuilder} from "../BlockHashArrayBuilder.sol";
import {IBuffer} from "../interfaces/IBuffer.sol";
import {IPusher} from "../interfaces/IPusher.sol";
import {ICrossDomainMessenger} from "@eth-optimism/contracts/libraries/bridge/ICrossDomainMessenger.sol";

/// @title OPtimismPusher
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Typo in NatSpec title: "OPtimismPusher" → "OptimismPusher".

📝 Proposed fix
-/// `@title` OPtimismPusher
+/// `@title` OptimismPusher
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/// @title OPtimismPusher
/// `@title` OptimismPusher
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/contracts/block-hash-pusher/optimism/OptimismPusher.sol` at line 9,
Update the NatSpec title comment from "OPtimismPusher" to the correct
"OptimismPusher" in the OptimismPusher contract header comment; locate the
top-of-file NatSpec/title line (for the OptimismPusher contract) and correct the
capitalization/typo so it matches the contract name OptimismPusher.

/// @notice Implementation of IPusher for pushing block hashes to Optimism L2.
/// @dev This contract sends block hashes from Ethereum L1 to a OptimismBuffer contract on Optimism L2
/// via the Optimism L1CrossDomainMessenger's `sendMessage` function. The pusher must be configured
/// with the correct L1CrossDomainMessengerProxy address.
/// @custom:security-contact security@openzeppelin.com
contract OptimismPusher is BlockHashArrayBuilder, IPusher {
/// @dev The address of the Optimism L1CrossDomainMessengerProxy contract on L1.
address private immutable _l1CrossDomainMessengerProxy;

/// @notice Parameters for the L2 transaction that will be executed on Optimism.
/// @param gasLimit The gas limit for the L2 transaction.
struct OptimismL2Transaction {
uint32 gasLimit;
}

/// @notice Thrown when attempting to set an invalid L1CrossDomainMessengerProxy address.
error InvalidL1CrossDomainMessengerProxyAddress();

constructor(address l1CrossDomainMessengerProxy_) {
require(l1CrossDomainMessengerProxy_ != address(0), InvalidL1CrossDomainMessengerProxyAddress());

_l1CrossDomainMessengerProxy = l1CrossDomainMessengerProxy_;
}

/// @inheritdoc IPusher
function pushHashes(address buffer, uint256 firstBlockNumber, uint256 batchSize, bytes calldata l2TransactionData)
external
payable
{
require(buffer != address(0), InvalidBuffer(buffer));
require(msg.value == 0, IncorrectMsgValue(0, msg.value));

bytes32[] memory blockHashes = _buildBlockHashArray(firstBlockNumber, batchSize);
bytes memory l2Calldata = abi.encodeCall(IBuffer.receiveHashes, (firstBlockNumber, blockHashes));

OptimismL2Transaction memory l2Transaction = abi.decode(l2TransactionData, (OptimismL2Transaction));

ICrossDomainMessenger(l1CrossDomainMessengerProxy()).sendMessage(buffer, l2Calldata, l2Transaction.gasLimit);

emit BlockHashesPushed(firstBlockNumber, firstBlockNumber + batchSize - 1);
}

/// @notice The address of the Optimism L1CrossDomainMessengerProxy contract on L1.
/// @return The address of the Optimism L1CrossDomainMessengerProxy contract on L1.
function l1CrossDomainMessengerProxy() public view returns (address) {
return _l1CrossDomainMessengerProxy;
}
}
2 changes: 1 addition & 1 deletion src/contracts/provers/linea/ChildToParentProver.sol
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ contract ChildToParentProver is IStateProver {
abi.decode(input, (bytes, uint256, bytes, bytes));

// calculate the slot based on the provided block number
// see: https://github.com/OffchainLabs/block-hash-pusher/blob/a1e26f2e42e6306d1e7f03c5d20fa6aa64ff7a12/contracts/Buffer.sol#L32
// see: https://github.com/openintentsframework/broadcaster/blob/8d02f8e8e39de27de8f0ded481d3c4e5a129351f/src/contracts/block-hash-pusher/BaseBuffer.sol#L24
uint256 slot = uint256(SlotDerivation.deriveMapping(bytes32(BLOCK_HASH_MAPPING_SLOT), targetBlockNumber));

// verify proofs and get the block hash
Expand Down
Loading