Skip to content

Commit

Permalink
feat/CrossL2Inbox: create (#10296)
Browse files Browse the repository at this point in the history
* contracts-bedrock: create CrossL2Inbox

* contracts-bedrock: create ICrossL2Inbox

* contracts-bedrock: create tests for CrossL2Inbox

* contracts-bedrock: update CrossL2Inbox sol version to ^0.8.24

* contracts-bedrock: rename test to .t.sol ext

* contracts-bedrock: make snapshots

* contracts-bedrock: update semver-lock

* contracts-bedrock: drop snapshots for CrossL2InboxTest

* contracts-bedrock: update license for tests CrossL2Inbox

* contracts-bedrock: add CrossL2Inbox to predeploys

* contracts-bedrock: pin sol version of CrossL2Inbox to 0.8.25

* contracts-bedrock: update semver-lock for CrossL2Inbox

* contracts-bedrock: add CROSS_L2_INBOX to predeploys

* contracts-bedrock: make slots internal in CrossL2Inbox

* contracts-bedrock: add custom errors to CrossL2Inbox

* contracts-bedrock: refactor tests for CrossL2Inbox

* contracts-bedrock: use TransientContext in CrossL2Inbox

* contracts-bedrock: fix L2Genesis test

* contracts-bedrock: minor tweaks to documentation in tests for CrossL2Inbox

* contracts-bedrock: relabel BLOCKNUMBER_SLOT to BLOCK_NUMBER_SLOT in CrossL2Inbox

* contracts-bedrock: update snapshots for CrossL2Inbox

* contracts-bedrock: update semver-lock for CrossL2Inbox

* contracts-bedrock: improve documentation for CrossL2Inbox

* contracts-bedrock: update semver-lock for CrossL2Inbox

* contracts-bedrock: fix tests for CrossL2Inbox

* contracts-bedrock: update modifier in CrossL2Inbox

* contracts-bedrock: drop arguments in custom errors for CrossL2Inbox

* contracts-bedrock: update snapshots for CrossL2Inbox

* contracts-bedrock: update semver-lock for CrossL2Inbox

* contracts-bedrock: fix tests for CrossL2Inbox

* contracts-bedrock: remove redundant lines in CrossL2Inbox

* contracts-bedrock: add tests for CrossL2Inbox

* contracts-bedrock: update semver-lock for CrossL2Inbox

* contracts-bedrock: minor improvements to tests for CrossL2Inbox

* contracts-bedrock: remove ENTERED_SLOT in CrossL2Inbox

* contracts-bedrock: remove CrossL2Inbox from isSupportedPredeploy

* contracts-bedrock: update semver-lock for CrossL2Inbox

* contracts-bedrock: update CrossL2Inbox with eip3074 specs

* contracts-bedrock: update tests for CrossL2Inbox with eip3074 specs

* contracts-bedrock: improve doc in tests for CrossL2Inbox

* contracts-bedrock: update snapshots for CrossL2Inbox

* contracts-bedrock: update semver-lock for CrossL2Inbox
  • Loading branch information
0xfuturistic authored Apr 27, 2024
1 parent 7e93729 commit 826a7bd
Show file tree
Hide file tree
Showing 7 changed files with 749 additions and 0 deletions.
4 changes: 4 additions & 0 deletions packages/contracts-bedrock/semver-lock.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@
"initCodeHash": "0x2744d34573be83206d1b75d049d18a7bb37f9058e68c0803e5008c46b0dc2474",
"sourceCodeHash": "0xd787bd2a192ba5025fa0a8de2363af66a8de20de226e411bdb576adb64636cd0"
},
"src/L2/CrossL2Inbox.sol": {
"initCodeHash": "0x2455cab02c32f22521bfeb8a6e404c5ea2eca32e1bfdce8f34013e32d52ffc13",
"sourceCodeHash": "0x864d244dd0b01cb01e5f50425bedce46e72005bdb8bead198ac885481547f41d"
},
"src/L2/GasPriceOracle.sol": {
"initCodeHash": "0xfd456e91d8c9714590a4f0a2c1046ba70e102f1c629ead886c4eebc3f921c3c3",
"sourceCodeHash": "0xde06becce9514f46ba78b4cb0732c7a714d49ba8f131258d56a5f5b22b51be7e"
Expand Down
169 changes: 169 additions & 0 deletions packages/contracts-bedrock/snapshots/abi/CrossL2Inbox.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
[
{
"inputs": [],
"name": "blockNumber",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "chainId",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"components": [
{
"internalType": "address",
"name": "origin",
"type": "address"
},
{
"internalType": "uint256",
"name": "blockNumber",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "logIndex",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "timestamp",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "chainId",
"type": "uint256"
}
],
"internalType": "struct ICrossL2Inbox.Identifier",
"name": "_id",
"type": "tuple"
},
{
"internalType": "address",
"name": "_target",
"type": "address"
},
{
"internalType": "bytes",
"name": "_message",
"type": "bytes"
}
],
"name": "executeMessage",
"outputs": [],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [],
"name": "logIndex",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "origin",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "timestamp",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "version",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"stateMutability": "view",
"type": "function"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "bytes",
"name": "encodedId",
"type": "bytes"
},
{
"indexed": false,
"internalType": "bytes",
"name": "message",
"type": "bytes"
}
],
"name": "ExecutingMessage",
"type": "event"
},
{
"inputs": [],
"name": "InvalidChainId",
"type": "error"
},
{
"inputs": [],
"name": "InvalidTimestamp",
"type": "error"
},
{
"inputs": [],
"name": "NotEntered",
"type": "error"
},
{
"inputs": [],
"name": "TargetCallFailed",
"type": "error"
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[]
161 changes: 161 additions & 0 deletions packages/contracts-bedrock/src/L2/CrossL2Inbox.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.25;

import { Predeploys } from "src/libraries/Predeploys.sol";
import { TransientContext, TransientReentrancyAware } from "src/libraries/TransientContext.sol";
import { ISemver } from "src/universal/ISemver.sol";
import { ICrossL2Inbox } from "src/L2/ICrossL2Inbox.sol";

/// @title IDependencySet
/// @notice Interface for L1Block with only `isInDependencySet(uint256)` method.
interface IDependencySet {
/// @notice Returns true iff the chain associated with input chain ID is in the interop dependency set.
/// Every chain is in the interop dependency set of itself.
/// @param _chainId Input chain ID.
/// @return True if the input chain ID corresponds to a chain in the interop dependency set, and false otherwise.
function isInDependencySet(uint256 _chainId) external view returns (bool);
}

/// @notice Thrown when a non-written transient storage slot is attempted to be read from.
error NotEntered();

/// @notice Thrown when trying to execute a cross chain message with an invalid Identifier timestamp.
error InvalidTimestamp();

/// @notice Thrown when trying to execute a cross chain message with an invalid Identifier chain ID.
error InvalidChainId();

/// @notice Thrown when trying to execute a cross chain message and the target call fails.
error TargetCallFailed();

/// @custom:proxied
/// @custom:predeploy 0x4200000000000000000000000000000000000022
/// @title CrossL2Inbox
/// @notice The CrossL2Inbox is responsible for executing a cross chain message on the destination
/// chain. It is permissionless to execute a cross chain message on behalf of any user.
contract CrossL2Inbox is ICrossL2Inbox, ISemver, TransientReentrancyAware {
/// @notice Transient storage slot that the origin for an Identifier is stored at.
/// Equal to bytes32(uint256(keccak256("crossl2inbox.identifier.origin")) - 1)
bytes32 internal constant ORIGIN_SLOT = 0xd2b7c5071ec59eb3ff0017d703a8ea513a7d0da4779b0dbefe845808c300c815;

/// @notice Transient storage slot that the blockNumber for an Identifier is stored at.
/// Equal to bytes32(uint256(keccak256("crossl2inbox.identifier.blocknumber")) - 1)
bytes32 internal constant BLOCK_NUMBER_SLOT = 0x5a1da0738b7fdc60047c07bb519beb02aa32a8619de57e6258da1f1c2e020ccc;

/// @notice Transient storage slot that the logIndex for an Identifier is stored at.
/// Equal to bytes32(uint256(keccak256("crossl2inbox.identifier.logindex")) - 1)
bytes32 internal constant LOG_INDEX_SLOT = 0xab8acc221aecea88a685fabca5b88bf3823b05f335b7b9f721ca7fe3ffb2c30d;

/// @notice Transient storage slot that the timestamp for an Identifier is stored at.
/// Equal to bytes32(uint256(keccak256("crossl2inbox.identifier.timestamp")) - 1)
bytes32 internal constant TIMESTAMP_SLOT = 0x2e148a404a50bb94820b576997fd6450117132387be615e460fa8c5e11777e02;

/// @notice Transient storage slot that the chainId for an Identifier is stored at.
/// Equal to bytes32(uint256(keccak256("crossl2inbox.identifier.chainid")) - 1)
bytes32 internal constant CHAINID_SLOT = 0x6e0446e8b5098b8c8193f964f1b567ec3a2bdaeba33d36acb85c1f1d3f92d313;

/// @notice Semantic version.
/// @custom:semver 1.0.0
string public constant version = "1.0.0";

/// @notice Emitted when a cross chain message is being executed.
/// @param encodedId Encoded Identifier of the message.
/// @param message Message payload being executed.
event ExecutingMessage(bytes encodedId, bytes message);

/// @notice Enforces that cross domain message sender and source are set. Reverts if not.
/// Used to differentiate between 0 and nil in transient storage.
modifier notEntered() {
if (TransientContext.callDepth() == 0) revert NotEntered();
_;
}

/// @notice Returns the origin address of the Identifier. If not entered, reverts.
/// @return Origin address of the Identifier.
function origin() external view notEntered returns (address) {
return address(uint160(TransientContext.get(ORIGIN_SLOT)));
}

/// @notice Returns the block number of the Identifier. If not entered, reverts.
/// @return Block number of the Identifier.
function blockNumber() external view notEntered returns (uint256) {
return TransientContext.get(BLOCK_NUMBER_SLOT);
}

/// @notice Returns the log index of the Identifier. If not entered, reverts.
/// @return Log index of the Identifier.
function logIndex() external view notEntered returns (uint256) {
return TransientContext.get(LOG_INDEX_SLOT);
}

/// @notice Returns the timestamp of the Identifier. If not entered, reverts.
/// @return Timestamp of the Identifier.
function timestamp() external view notEntered returns (uint256) {
return TransientContext.get(TIMESTAMP_SLOT);
}

/// @notice Returns the chain ID of the Identifier. If not entered, reverts.
/// @return _chainId The chain ID of the Identifier.
function chainId() external view notEntered returns (uint256) {
return TransientContext.get(CHAINID_SLOT);
}

/// @notice Executes a cross chain message on the destination chain.
/// @param _id Identifier of the message.
/// @param _target Target address to call.
/// @param _message Message payload to call target with.
function executeMessage(
Identifier calldata _id,
address _target,
bytes memory _message
)
external
payable
reentrantAware
{
if (_id.timestamp > block.timestamp) revert InvalidTimestamp();
if (!IDependencySet(Predeploys.L1_BLOCK_ATTRIBUTES).isInDependencySet(_id.chainId)) {
revert InvalidChainId();
}

// Store the Identifier in transient storage.
_storeIdentifier(_id);

// Call the target account with the message payload.
bool success = _callWithAllGas(_target, _message);

// Revert if the target call failed.
if (!success) revert TargetCallFailed();

emit ExecutingMessage(abi.encode(_id), _message);
}

/// @notice Stores the Identifier in transient storage.
/// @param _id Identifier to store.
function _storeIdentifier(Identifier calldata _id) internal {
TransientContext.set(ORIGIN_SLOT, uint160(_id.origin));
TransientContext.set(BLOCK_NUMBER_SLOT, _id.blockNumber);
TransientContext.set(LOG_INDEX_SLOT, _id.logIndex);
TransientContext.set(TIMESTAMP_SLOT, _id.timestamp);
TransientContext.set(CHAINID_SLOT, _id.chainId);
}

/// @notice Calls the target address with the message payload and all available gas.
/// @param _target Target address to call.
/// @param _message Message payload to call target with.
/// @return _success True if the call was successful, and false otherwise.
function _callWithAllGas(address _target, bytes memory _message) internal returns (bool _success) {
assembly {
_success :=
call(
gas(), // gas
_target, // recipient
callvalue(), // ether value
add(_message, 32), // inloc
mload(_message), // inlen
0, // outloc
0 // outlen
)
}
}
}
47 changes: 47 additions & 0 deletions packages/contracts-bedrock/src/L2/ICrossL2Inbox.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/// @title ICrossL2Inbox
/// @notice Interface for the CrossL2Inbox contract.
interface ICrossL2Inbox {
/// @notice The struct for a pointer to a message payload in a remote (or local) chain.
struct Identifier {
address origin;
uint256 blockNumber;
uint256 logIndex;
uint256 timestamp;
uint256 chainId;
}

/// @notice Returns the origin address of the Identifier.
/// @return _origin The origin address of the Identifier.
function origin() external view returns (address _origin);

/// @notice Returns the block number of the Identifier.
/// @return _blockNumber The block number of the Identifier.
function blockNumber() external view returns (uint256 _blockNumber);

/// @notice Returns the log index of the Identifier.
/// @return _logIndex The log index of the Identifier.
function logIndex() external view returns (uint256 _logIndex);

/// @notice Returns the timestamp of the Identifier.
/// @return _timestamp The timestamp of the Identifier.
function timestamp() external view returns (uint256 _timestamp);

/// @notice Returns the chain ID of the Identifier.
/// @return _chainId The chain ID of the Identifier.
function chainId() external view returns (uint256 _chainId);

/// @notice Executes a cross chain message on the destination chain.
/// @param _id An Identifier pointing to the initiating message.
/// @param _target Account that is called with _msg.
/// @param _msg The message payload, matching the initiating message.
function executeMessage(
ICrossL2Inbox.Identifier calldata _id,
address _target,
bytes calldata _msg
)
external
payable;
}
Loading

0 comments on commit 826a7bd

Please sign in to comment.