Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: entrypoints #144

Draft
wants to merge 18 commits into
base: develop
Choose a base branch
from
Draft
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
Original file line number Diff line number Diff line change
Expand Up @@ -45,19 +45,28 @@ interface IL2ToL2CrossDomainMessenger {
/// @notice Thrown when attempting to use a chain ID that is not in the dependency set.
error InvalidChainId();

/// @notice Thrown when relaying a message from an address different than the specified entrypoint.
error InvalidEntrypoint();

/// @notice Emitted whenever a message is sent to a destination
/// @param destination Chain ID of the destination chain.
/// @param target Target contract or wallet address.
/// @param messageNonce Nonce associated with the messsage sent
/// @param messageNonce Nonce associated with the message sent
/// @param sender Address initiating this message call
/// @param message Message payload to call target with.
/// @param entrypoint Address of the entrypoint on the destination chain.
event SentMessage(
uint256 indexed destination, address indexed target, uint256 indexed messageNonce, address sender, bytes message
uint256 indexed destination,
address indexed target,
uint256 indexed messageNonce,
address sender,
bytes message,
address entrypoint
);

/// @notice Emitted whenever a message is successfully relayed on this chain.
/// @param source Chain ID of the source chain.
/// @param messageNonce Nonce associated with the messsage sent
/// @param messageNonce Nonce associated with the message sent
/// @param messageHash Hash of the message that was relayed.
event RelayedMessage(uint256 indexed source, uint256 indexed messageNonce, bytes32 indexed messageHash);

Expand Down Expand Up @@ -88,15 +97,33 @@ interface IL2ToL2CrossDomainMessenger {
/// @return source_ Chain ID of the source of the current cross domain message.
function crossDomainMessageContext() external view returns (address sender_, uint256 source_);

/// @notice Sends a message to some target address on a destination chain. Note that if the call
/// always reverts, then the message will be unrelayable, and any ETH sent will be
/// permanently locked. The same will occur if the target on the other chain is
/// considered unsafe (see the _isUnsafeTarget() function).
/// @notice Sends a message to some target address on a destination chain with an entrypoint address as an
/// authorized relayer. Note that if the call always reverts, then the message will be unrelayable and any
/// ETH sent will be permanently locked. The same will occur if the target on the other chain is considered
/// unsafe (see the _isUnsafeTarget() function). The entrypoint must have the capability to call
/// `relayMessage` for successful relaying.
/// @param _destination Chain ID of the destination chain.
/// @param _target Target contract or wallet address.
/// @param _message Message payload to call target with.
/// @param _entrypoint Address of the entrypoint on the destination chain. If it is address(0) then the
/// message can be relayed by any address on the destination chain.
/// @return The hash of the message being sent, used to track whether the message has successfully been relayed.
function sendMessage(
uint256 _destination,
address _target,
bytes calldata _message,
address _entrypoint
)
external
returns (bytes32);

/// @notice Sends a message to some target address on a destination chain. Note that if the call always reverts,
/// then the message will be unrelayable and any ETH sent will be permanently locked. The same will occur
/// if the target on the other chain is considered unsafe (see the _isUnsafeTarget() function).
/// @param _destination Chain ID of the destination chain.
/// @param _target Target contract or wallet address.
/// @param _message Message to trigger the target address with.
/// @return msgHash_ The hash of the message being sent, which can be used for tracking whether
/// the message has successfully been relayed.
/// @param _message Message payload to call target with.
/// @return The hash of the message being sent, used to track whether the message has successfully been relayed.
function sendMessage(uint256 _destination, address _target, bytes calldata _message) external returns (bytes32);

/// @notice Relays a message that was sent by the other CrossDomainMessenger contract. Can only
Expand Down
8 changes: 7 additions & 1 deletion packages/contracts-bedrock/scripts/checks/interfaces/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,13 @@ func abiItemLess(a, b map[string]interface{}) bool {

aName := getString(a, "name")
bName := getString(b, "name")
return aName < bName
if aName != bName {
return aName < bName
}

aInputs := a["inputs"].([]interface{})
bInputs := b["inputs"].([]interface{})
return len(aInputs) < len(bInputs)
}

func getString(m map[string]interface{}, key string) string {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,40 @@
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "_destination",
"type": "uint256"
},
{
"internalType": "address",
"name": "_target",
"type": "address"
},
{
"internalType": "bytes",
"name": "_message",
"type": "bytes"
},
{
"internalType": "address",
"name": "_entrypoint",
"type": "address"
}
],
"name": "sendMessage",
"outputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
Expand Down Expand Up @@ -232,6 +266,12 @@
"internalType": "bytes",
"name": "message",
"type": "bytes"
},
{
"indexed": false,
"internalType": "address",
"name": "entrypoint",
"type": "address"
}
],
"name": "SentMessage",
Expand All @@ -252,6 +292,11 @@
"name": "InvalidChainId",
"type": "error"
},
{
"inputs": [],
"name": "InvalidEntrypoint",
"type": "error"
},
{
"inputs": [],
"name": "MessageAlreadyRelayed",
Expand Down
4 changes: 2 additions & 2 deletions packages/contracts-bedrock/snapshots/semver-lock.json
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,8 @@
"sourceCodeHash": "0xaef8ea36c5b78cd12e0e62811d51db627ccf0dfd2cc5479fb707a10ef0d42048"
},
"src/L2/L2ToL2CrossDomainMessenger.sol": {
"initCodeHash": "0x45564b97c63419cc12eadc60425c6d001857a3eea688ecaf1439ae7ede6aa9aa",
"sourceCodeHash": "0xed64736338b43a42f6bc6a88cca734403e1bb9ceafa55e4738605dfdedd1a99f"
"initCodeHash": "0xeae09cf9c04e4edfa7bcb1282d1f2d0ed753fc24f122ab51b4ca9fd6c5230d2f",
"sourceCodeHash": "0x5f1452f6c299e4589fbb4ad7a2d96e7497724630888d7f17dfdc6297af5e8b64"
},
"src/L2/OptimismSuperchainERC20.sol": {
"initCodeHash": "0xdac32a1057a6bc8a8d2ffdce1db8f34950cd0ffd1454d2133865736d21869192",
Expand Down
127 changes: 99 additions & 28 deletions packages/contracts-bedrock/src/L2/L2ToL2CrossDomainMessenger.sol
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ error TargetCallFailed();
/// @notice Thrown when attempting to use a chain ID that is not in the dependency set.
error InvalidChainId();

/// @notice Thrown when relaying a message from an address different from the specified entrypoint.
error InvalidEntrypoint();

/// @custom:proxied true
/// @custom:predeploy 0x4200000000000000000000000000000000000023
/// @title L2ToL2CrossDomainMessenger
Expand All @@ -64,16 +67,16 @@ contract L2ToL2CrossDomainMessenger is ISemver, TransientReentrancyAware {
0x711dfa3259c842fffc17d6e1f1e0fc5927756133a2345ca56b4cb8178589fee7;

/// @notice Event selector for the SentMessage event. Will be removed in favor of reading
// the `selector` property directly once crytic/slithe/#2566 is fixed.
// the `selector` property directly once crytic/slither/#2566 is fixed.
bytes32 internal constant SENT_MESSAGE_EVENT_SELECTOR =
0x382409ac69001e11931a28435afef442cbfd20d9891907e8fa373ba7d351f320;
0xc3406924bd47cc346b038460ab86a034281a1fa54246e7996ce88ad205efcf4f;

/// @notice Current message version identifier.
uint16 public constant messageVersion = uint16(0);

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

/// @notice Mapping of message hashes to boolean receipt values. Note that a message will only be present in this
/// mapping if it has successfully been relayed on this chain, and can therefore not be relayed again.
Expand All @@ -87,16 +90,22 @@ contract L2ToL2CrossDomainMessenger is ISemver, TransientReentrancyAware {
/// @notice Emitted whenever a message is sent to a destination
/// @param destination Chain ID of the destination chain.
/// @param target Target contract or wallet address.
/// @param messageNonce Nonce associated with the messsage sent
/// @param messageNonce Nonce associated with the message sent
/// @param sender Address initiating this message call
/// @param message Message payload to call target with.
/// @param entrypoint Address of the entrypoint on the destination chain.
event SentMessage(
uint256 indexed destination, address indexed target, uint256 indexed messageNonce, address sender, bytes message
uint256 indexed destination,
address indexed target,
uint256 indexed messageNonce,
address sender,
bytes message,
address entrypoint
);

/// @notice Emitted whenever a message is successfully relayed on this chain.
/// @param source Chain ID of the source chain.
/// @param messageNonce Nonce associated with the messsage sent
/// @param messageNonce Nonce associated with the message sent
/// @param messageHash Hash of the message that was relayed.
event RelayedMessage(uint256 indexed source, uint256 indexed messageNonce, bytes32 indexed messageHash);

Expand Down Expand Up @@ -126,6 +135,29 @@ contract L2ToL2CrossDomainMessenger is ISemver, TransientReentrancyAware {
}
}

/// @notice Sends a message to some target address on a destination chain with an entrypoint address as an
/// authorized relayer. Note that if the call always reverts, then the message will be unrelayable and any
/// ETH sent will be permanently locked. The same will occur if the target on the other chain is considered
/// unsafe (see the _isUnsafeTarget() function). The entrypoint must have the capability to call
/// `relayMessage` for successful relaying.
/// @param _destination Chain ID of the destination chain.
/// @param _target Target contract or wallet address.
/// @param _message Message payload to call target with.
/// @param _entrypoint Address of the entrypoint on the destination chain. If it is address(0) then the
/// message can be relayed by any address on the destination chain.
/// @return The hash of the message being sent, used to track whether the message has successfully been relayed.
function sendMessage(
uint256 _destination,
address _target,
bytes calldata _message,
address _entrypoint
)
external
returns (bytes32)
{
return _sendMessage(_destination, _target, _message, _entrypoint);
}

/// @notice Sends a message to some target address on a destination chain. Note that if the call always reverts,
/// then the message will be unrelayable and any ETH sent will be permanently locked. The same will occur
/// if the target on the other chain is considered unsafe (see the _isUnsafeTarget() function).
Expand All @@ -134,24 +166,8 @@ contract L2ToL2CrossDomainMessenger is ISemver, TransientReentrancyAware {
/// @param _message Message payload to call target with.
/// @return The hash of the message being sent, used to track whether the message has successfully been relayed.
function sendMessage(uint256 _destination, address _target, bytes calldata _message) external returns (bytes32) {
if (_destination == block.chainid) revert MessageDestinationSameChain();
if (_target == Predeploys.CROSS_L2_INBOX) revert MessageTargetCrossL2Inbox();
if (_target == Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER) revert MessageTargetL2ToL2CrossDomainMessenger();
if (!IDependencySet(Predeploys.L1_BLOCK_ATTRIBUTES).isInDependencySet(_destination)) revert InvalidChainId();

uint256 nonce = messageNonce();
emit SentMessage(_destination, _target, nonce, msg.sender, _message);

msgNonce++;

return Hashing.hashL2toL2CrossDomainMessage({
_destination: _destination,
_source: block.chainid,
_nonce: nonce,
_sender: msg.sender,
_target: _target,
_message: _message
});
// If entrypoint is not specified, then anyone can relay the message
return _sendMessage(_destination, _target, _message, address(0));
}

/// @notice Relays a message that was sent by the other L2ToL2CrossDomainMessenger contract. Can only be executed
Expand All @@ -170,13 +186,14 @@ contract L2ToL2CrossDomainMessenger is ISemver, TransientReentrancyAware {
ICrossL2Inbox(Predeploys.CROSS_L2_INBOX).validateMessage(_id, keccak256(_sentMessage));

// Decode the payload
(uint256 destination, address target, uint256 nonce, address sender, bytes memory message) =
(uint256 destination, address target, uint256 nonce, address sender, bytes memory message, address entrypoint) =
_decodeSentMessagePayload(_sentMessage);

// Assert invariants on the message
if (destination != block.chainid) revert MessageDestinationNotRelayChain();
if (target == Predeploys.CROSS_L2_INBOX) revert MessageTargetCrossL2Inbox();
if (target == Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER) revert MessageTargetL2ToL2CrossDomainMessenger();
if (entrypoint != address(0) && entrypoint != msg.sender) revert InvalidEntrypoint();

uint256 source = _id.chainId;
bytes32 messageHash = Hashing.hashL2toL2CrossDomainMessage({
Expand Down Expand Up @@ -223,10 +240,25 @@ contract L2ToL2CrossDomainMessenger is ISemver, TransientReentrancyAware {
}
}

/// @notice Decodes the payload of a SentMessage event.
/// @param _payload Payload of the SentMessage event.
/// @return destination_ Chain ID of the destination chain.
/// @return target_ Target contract or wallet address.
/// @return nonce_ Nonce associated with the message sent.
/// @return sender_ Address initiating this message call.
/// @return message_ Message payload to call target with.
/// @return entrypoint_ Address of the entrypoint on the destination chain.
function _decodeSentMessagePayload(bytes calldata _payload)
internal
pure
returns (uint256 destination_, address target_, uint256 nonce_, address sender_, bytes memory message_)
returns (
uint256 destination_,
address target_,
uint256 nonce_,
address sender_,
bytes memory message_,
address entrypoint_
)
{
// Validate Selector (also reverts if LOG0 with no topics)
bytes32 selector = abi.decode(_payload[:32], (bytes32));
Expand All @@ -236,6 +268,45 @@ contract L2ToL2CrossDomainMessenger is ISemver, TransientReentrancyAware {
(destination_, target_, nonce_) = abi.decode(_payload[32:128], (uint256, address, uint256));

// Data
(sender_, message_) = abi.decode(_payload[128:], (address, bytes));
(sender_, message_, entrypoint_) = abi.decode(_payload[128:], (address, bytes, address));
}

/// @notice Sends a message to a target address on a destination chain.
/// This function checks that the destination is not the same as the current chain and that the target
/// is not the CrossL2Inbox or the L2ToL2CrossDomainMessenger itself. It emits a SentMessage event
/// and increments the message nonce.
/// @param _destination Chain ID of the destination chain.
/// @param _target Target contract or wallet address.
/// @param _message Message payload to call target with.
/// @param _entrypoint Address of the entrypoint on the destination chain or address(0) if there is none -- meaning
/// that anyone can relay the message.
/// @return The hash of the message being sent, used to track whether the message has successfully been relayed.
function _sendMessage(
uint256 _destination,
address _target,
bytes calldata _message,
address _entrypoint
)
internal
returns (bytes32)
{
if (_destination == block.chainid) revert MessageDestinationSameChain();
if (_target == Predeploys.CROSS_L2_INBOX) revert MessageTargetCrossL2Inbox();
if (_target == Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER) revert MessageTargetL2ToL2CrossDomainMessenger();
if (!IDependencySet(Predeploys.L1_BLOCK_ATTRIBUTES).isInDependencySet(_destination)) revert InvalidChainId();

uint256 nonce = messageNonce();
emit SentMessage(_destination, _target, nonce, msg.sender, _message, _entrypoint);

msgNonce++;

return Hashing.hashL2toL2CrossDomainMessage({
_destination: _destination,
_source: block.chainid,
_nonce: nonce,
_sender: msg.sender,
_target: _target,
_message: _message
});
}
}
Loading