Commit a96b2282 authored by Hamdi Allam's avatar Hamdi Allam Committed by GitHub

sent message event and l2tol2cdm relayMessage entrypoint (#11592)

parent d141b53e
...@@ -112,8 +112,8 @@ ...@@ -112,8 +112,8 @@
"sourceCodeHash": "0xd08a2e6514dbd44e16aa312a1b27b2841a9eab5622cbd05a39c30f543fad673c" "sourceCodeHash": "0xd08a2e6514dbd44e16aa312a1b27b2841a9eab5622cbd05a39c30f543fad673c"
}, },
"src/L2/L2ToL2CrossDomainMessenger.sol": { "src/L2/L2ToL2CrossDomainMessenger.sol": {
"initCodeHash": "0x652e07372d45f0f861aa65b4a73db55871291b875ced02df893a405419de723a", "initCodeHash": "0x6f19eb8ff0950156b65cd92872240c0153ac5f3b6f0861d57bf561fdbcacbeac",
"sourceCodeHash": "0xc3e73c2d9abf3c7853d2505a83e475d58e96ab5fc5ad7770d04dea5feb9e5717" "sourceCodeHash": "0xfea53344596d735eff3be945ed1300dc75a6f8b7b2c02c0043af5b0036f5f239"
}, },
"src/L2/OptimismSuperchainERC20.sol": { "src/L2/OptimismSuperchainERC20.sol": {
"initCodeHash": "0xe3dbb0851669708901a4c6bb7ad7d55f9896deeec02cbe53ac58d689ff95b88b", "initCodeHash": "0xe3dbb0851669708901a4c6bb7ad7d55f9896deeec02cbe53ac58d689ff95b88b",
......
...@@ -54,33 +54,40 @@ ...@@ -54,33 +54,40 @@
{ {
"inputs": [ "inputs": [
{ {
"internalType": "uint256", "components": [
"name": "_destination", {
"type": "uint256" "internalType": "address",
}, "name": "origin",
{ "type": "address"
"internalType": "uint256", },
"name": "_source", {
"type": "uint256" "internalType": "uint256",
}, "name": "blockNumber",
{ "type": "uint256"
"internalType": "uint256", },
"name": "_nonce", {
"type": "uint256" "internalType": "uint256",
}, "name": "logIndex",
{ "type": "uint256"
"internalType": "address", },
"name": "_sender", {
"type": "address" "internalType": "uint256",
}, "name": "timestamp",
{ "type": "uint256"
"internalType": "address", },
"name": "_target", {
"type": "address" "internalType": "uint256",
"name": "chainId",
"type": "uint256"
}
],
"internalType": "struct ICrossL2Inbox.Identifier",
"name": "_id",
"type": "tuple"
}, },
{ {
"internalType": "bytes", "internalType": "bytes",
"name": "_message", "name": "_sentMessage",
"type": "bytes" "type": "bytes"
} }
], ],
...@@ -111,7 +118,7 @@ ...@@ -111,7 +118,7 @@
"outputs": [ "outputs": [
{ {
"internalType": "bytes32", "internalType": "bytes32",
"name": "msgHash_", "name": "",
"type": "bytes32" "type": "bytes32"
} }
], ],
...@@ -153,6 +160,18 @@ ...@@ -153,6 +160,18 @@
{ {
"anonymous": false, "anonymous": false,
"inputs": [ "inputs": [
{
"indexed": true,
"internalType": "uint256",
"name": "source",
"type": "uint256"
},
{
"indexed": true,
"internalType": "uint256",
"name": "messageNonce",
"type": "uint256"
},
{ {
"indexed": true, "indexed": true,
"internalType": "bytes32", "internalType": "bytes32",
...@@ -166,6 +185,18 @@ ...@@ -166,6 +185,18 @@
{ {
"anonymous": false, "anonymous": false,
"inputs": [ "inputs": [
{
"indexed": true,
"internalType": "uint256",
"name": "source",
"type": "uint256"
},
{
"indexed": true,
"internalType": "uint256",
"name": "messageNonce",
"type": "uint256"
},
{ {
"indexed": true, "indexed": true,
"internalType": "bytes32", "internalType": "bytes32",
...@@ -176,9 +207,51 @@ ...@@ -176,9 +207,51 @@
"name": "RelayedMessage", "name": "RelayedMessage",
"type": "event" "type": "event"
}, },
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "uint256",
"name": "destination",
"type": "uint256"
},
{
"indexed": true,
"internalType": "address",
"name": "target",
"type": "address"
},
{
"indexed": true,
"internalType": "uint256",
"name": "messageNonce",
"type": "uint256"
},
{
"indexed": false,
"internalType": "address",
"name": "sender",
"type": "address"
},
{
"indexed": false,
"internalType": "bytes",
"name": "message",
"type": "bytes"
}
],
"name": "SentMessage",
"type": "event"
},
{
"inputs": [],
"name": "EventPayloadNotSentMessage",
"type": "error"
},
{ {
"inputs": [], "inputs": [],
"name": "CrossL2InboxOriginNotL2ToL2CrossDomainMessenger", "name": "IdOriginNotL2ToL2CrossDomainMessenger",
"type": "error" "type": "error"
}, },
{ {
...@@ -215,10 +288,5 @@ ...@@ -215,10 +288,5 @@
"inputs": [], "inputs": [],
"name": "ReentrantCall", "name": "ReentrantCall",
"type": "error" "type": "error"
},
{
"inputs": [],
"name": "RelayMessageCallerNotCrossL2Inbox",
"type": "error"
} }
] ]
\ No newline at end of file
...@@ -5,6 +5,7 @@ import { Encoding } from "src/libraries/Encoding.sol"; ...@@ -5,6 +5,7 @@ import { Encoding } from "src/libraries/Encoding.sol";
import { Hashing } from "src/libraries/Hashing.sol"; import { Hashing } from "src/libraries/Hashing.sol";
import { Predeploys } from "src/libraries/Predeploys.sol"; import { Predeploys } from "src/libraries/Predeploys.sol";
import { CrossL2Inbox } from "src/L2/CrossL2Inbox.sol"; import { CrossL2Inbox } from "src/L2/CrossL2Inbox.sol";
import { ICrossL2Inbox } from "src/L2/interfaces/ICrossL2Inbox.sol";
import { IL2ToL2CrossDomainMessenger } from "src/L2/interfaces/IL2ToL2CrossDomainMessenger.sol"; import { IL2ToL2CrossDomainMessenger } from "src/L2/interfaces/IL2ToL2CrossDomainMessenger.sol";
import { ISemver } from "src/universal/interfaces/ISemver.sol"; import { ISemver } from "src/universal/interfaces/ISemver.sol";
import { SafeCall } from "src/libraries/SafeCall.sol"; import { SafeCall } from "src/libraries/SafeCall.sol";
...@@ -13,14 +14,14 @@ import { TransientReentrancyAware } from "src/libraries/TransientContext.sol"; ...@@ -13,14 +14,14 @@ import { TransientReentrancyAware } from "src/libraries/TransientContext.sol";
/// @notice Thrown when a non-written slot in transient storage is attempted to be read from. /// @notice Thrown when a non-written slot in transient storage is attempted to be read from.
error NotEntered(); error NotEntered();
/// @notice Thrown when attempting to send a message to the chain that the message is being sent from. /// @notice Thrown when attempting to relay a message where payload origin is not L2ToL2CrossDomainMessenger.
error MessageDestinationSameChain(); error IdOriginNotL2ToL2CrossDomainMessenger();
/// @notice Thrown when attempting to relay a message and the function caller (msg.sender) is not CrossL2Inbox. /// @notice Thrown when the payload provided to the relay is not a SentMessage event.
error RelayMessageCallerNotCrossL2Inbox(); error EventPayloadNotSentMessage();
/// @notice Thrown when attempting to relay a message where CrossL2Inbox's origin is not L2ToL2CrossDomainMessenger. /// @notice Thrown when attempting to send a message to the chain that the message is being sent from.
error CrossL2InboxOriginNotL2ToL2CrossDomainMessenger(); error MessageDestinationSameChain();
/// @notice Thrown when attempting to relay a message whose destination chain is not the chain relaying it. /// @notice Thrown when attempting to relay a message whose destination chain is not the chain relaying it.
error MessageDestinationNotRelayChain(); error MessageDestinationNotRelayChain();
...@@ -54,12 +55,17 @@ contract L2ToL2CrossDomainMessenger is IL2ToL2CrossDomainMessenger, ISemver, Tra ...@@ -54,12 +55,17 @@ contract L2ToL2CrossDomainMessenger is IL2ToL2CrossDomainMessenger, ISemver, Tra
bytes32 internal constant CROSS_DOMAIN_MESSAGE_SOURCE_SLOT = bytes32 internal constant CROSS_DOMAIN_MESSAGE_SOURCE_SLOT =
0x711dfa3259c842fffc17d6e1f1e0fc5927756133a2345ca56b4cb8178589fee7; 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.
bytes32 internal constant SENT_MESSAGE_EVENT_SELECTOR =
0x382409ac69001e11931a28435afef442cbfd20d9891907e8fa373ba7d351f320;
/// @notice Current message version identifier. /// @notice Current message version identifier.
uint16 public constant messageVersion = uint16(0); uint16 public constant messageVersion = uint16(0);
/// @notice Semantic version. /// @notice Semantic version.
/// @custom:semver 1.0.0-beta.6 /// @custom:semver 1.0.0-beta.7
string public constant version = "1.0.0-beta.6"; string public constant version = "1.0.0-beta.7";
/// @notice Mapping of message hashes to boolean receipt values. Note that a message will only be present in this /// @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. /// mapping if it has successfully been relayed on this chain, and can therefore not be relayed again.
...@@ -70,13 +76,27 @@ contract L2ToL2CrossDomainMessenger is IL2ToL2CrossDomainMessenger, ISemver, Tra ...@@ -70,13 +76,27 @@ contract L2ToL2CrossDomainMessenger is IL2ToL2CrossDomainMessenger, ISemver, Tra
/// message. /// message.
uint240 internal msgNonce; uint240 internal msgNonce;
/// @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 sender Address initiating this message call
/// @param message Message payload to call target with.
event SentMessage(
uint256 indexed destination, address indexed target, uint256 indexed messageNonce, address sender, bytes message
);
/// @notice Emitted whenever a message is successfully relayed on this chain. /// @notice Emitted whenever a message is successfully relayed on this chain.
/// @param messageHash Hash of the message that was relayed. /// @param source Chain ID of the source chain.
event RelayedMessage(bytes32 indexed messageHash); /// @param messageNonce Nonce associated with the messsage sent
/// @param messageHash Hash of the message that was relayed.
event RelayedMessage(uint256 indexed source, uint256 indexed messageNonce, bytes32 indexed messageHash);
/// @notice Emitted whenever a message fails to be relayed on this chain. /// @notice Emitted whenever a message fails to be relayed on this chain.
/// @param messageHash Hash of the message that failed to be relayed. /// @param source Chain ID of the source chain.
event FailedRelayedMessage(bytes32 indexed messageHash); /// @param messageNonce Nonce associated with the messsage sent
/// @param messageHash Hash of the message that failed to be relayed.
event FailedRelayedMessage(uint256 indexed source, uint256 indexed messageNonce, bytes32 indexed messageHash);
/// @notice Retrieves the sender of the current cross domain message. If not entered, reverts. /// @notice Retrieves the sender of the current cross domain message. If not entered, reverts.
/// @return sender_ Address of the sender of the current cross domain message. /// @return sender_ Address of the sender of the current cross domain message.
...@@ -100,90 +120,81 @@ contract L2ToL2CrossDomainMessenger is IL2ToL2CrossDomainMessenger, ISemver, Tra ...@@ -100,90 +120,81 @@ contract L2ToL2CrossDomainMessenger is IL2ToL2CrossDomainMessenger, ISemver, Tra
/// @param _destination Chain ID of the destination chain. /// @param _destination Chain ID of the destination chain.
/// @param _target Target contract or wallet address. /// @param _target Target contract or wallet address.
/// @param _message Message payload to call target with. /// @param _message Message payload to call target with.
/// @return msgHash_ The hash of the message being sent, which can be used for tracking whether /// @return The hash of the message being sent, used to track whether the message has successfully been relayed.
/// the message has successfully been relayed. function sendMessage(uint256 _destination, address _target, bytes calldata _message) external returns (bytes32) {
function sendMessage(
uint256 _destination,
address _target,
bytes calldata _message
)
external
returns (bytes32 msgHash_)
{
if (_destination == block.chainid) revert MessageDestinationSameChain(); if (_destination == block.chainid) revert MessageDestinationSameChain();
if (_target == Predeploys.CROSS_L2_INBOX) revert MessageTargetCrossL2Inbox(); if (_target == Predeploys.CROSS_L2_INBOX) revert MessageTargetCrossL2Inbox();
if (_target == Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER) revert MessageTargetL2ToL2CrossDomainMessenger(); if (_target == Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER) revert MessageTargetL2ToL2CrossDomainMessenger();
(uint256 source, uint256 nonce, address sender) = (block.chainid, messageNonce(), msg.sender); uint256 nonce = messageNonce();
bytes memory data = abi.encodeCall( emit SentMessage(_destination, _target, nonce, msg.sender, _message);
L2ToL2CrossDomainMessenger.relayMessage, (_destination, source, nonce, sender, _target, _message)
); msgNonce++;
msgHash_ = Hashing.hashL2toL2CrossDomainMessengerRelayMessage({
return Hashing.hashL2toL2CrossDomainMessage({
_destination: _destination, _destination: _destination,
_source: source, _source: block.chainid,
_nonce: nonce, _nonce: nonce,
_sender: sender, _sender: msg.sender,
_target: _target, _target: _target,
_message: _message _message: _message
}); });
assembly {
log0(add(data, 0x20), mload(data))
}
msgNonce++;
} }
/// @notice Relays a message that was sent by the other CrossDomainMessenger contract. Can only be executed via /// @notice Relays a message that was sent by the other L2ToL2CrossDomainMessenger contract. Can only be executed
/// cross-chain call from the other messenger OR if the message was already received once and is currently /// via cross chain call from the other messenger OR if the message was already received once and is
/// being replayed. /// currently being replayed.
/// @param _destination Chain ID of the destination chain. /// @param _id Identifier of the SentMessage event to be relayed
/// @param _source Chain ID of the source chain. /// @param _sentMessage Message payload of the `SentMessage` event
/// @param _nonce Nonce of the message being relayed.
/// @param _sender Address of the user who sent the message.
/// @param _target Address that the message is targeted at.
/// @param _message Message payload to call target with.
function relayMessage( function relayMessage(
uint256 _destination, ICrossL2Inbox.Identifier calldata _id,
uint256 _source, bytes calldata _sentMessage
uint256 _nonce,
address _sender,
address _target,
bytes memory _message
) )
external external
payable payable
nonReentrant nonReentrant
{ {
if (msg.sender != Predeploys.CROSS_L2_INBOX) revert RelayMessageCallerNotCrossL2Inbox(); // Ensure the log came from the messenger. Since the log origin is the CDM, there isn't a scenario where
if (CrossL2Inbox(Predeploys.CROSS_L2_INBOX).origin() != Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER) { // this can be invoked from the CrossL2Inbox as the SentMessage log is not calldata for this function
revert CrossL2InboxOriginNotL2ToL2CrossDomainMessenger(); if (_id.origin != Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER) {
} revert IdOriginNotL2ToL2CrossDomainMessenger();
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();
} }
bytes32 messageHash = Hashing.hashL2toL2CrossDomainMessengerRelayMessage({ // Signal that this is a cross chain call that needs to have the identifier validated
_destination: _destination, CrossL2Inbox(Predeploys.CROSS_L2_INBOX).validateMessage(_id, keccak256(_sentMessage));
_source: _source,
_nonce: _nonce, // Decode the payload
_sender: _sender, (uint256 destination, address target, uint256 nonce, address sender, bytes memory message) =
_target: _target, _decodeSentMessagePayload(_sentMessage);
_message: _message
// 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();
uint256 source = _id.chainId;
bytes32 messageHash = Hashing.hashL2toL2CrossDomainMessage({
_destination: destination,
_source: source,
_nonce: nonce,
_sender: sender,
_target: target,
_message: message
}); });
if (successfulMessages[messageHash]) { if (successfulMessages[messageHash]) {
revert MessageAlreadyRelayed(); revert MessageAlreadyRelayed();
} }
_storeMessageMetadata(_source, _sender); _storeMessageMetadata(source, sender);
bool success = SafeCall.call(_target, msg.value, _message); bool success = SafeCall.call(target, msg.value, message);
if (success) { if (success) {
successfulMessages[messageHash] = true; successfulMessages[messageHash] = true;
emit RelayedMessage(messageHash); emit RelayedMessage(source, nonce, messageHash);
} else { } else {
emit FailedRelayedMessage(messageHash); emit FailedRelayedMessage(source, nonce, messageHash);
} }
_storeMessageMetadata(0, address(0)); _storeMessageMetadata(0, address(0));
...@@ -205,4 +216,20 @@ contract L2ToL2CrossDomainMessenger is IL2ToL2CrossDomainMessenger, ISemver, Tra ...@@ -205,4 +216,20 @@ contract L2ToL2CrossDomainMessenger is IL2ToL2CrossDomainMessenger, ISemver, Tra
tstore(CROSS_DOMAIN_MESSAGE_SOURCE_SLOT, _source) tstore(CROSS_DOMAIN_MESSAGE_SOURCE_SLOT, _source)
} }
} }
function _decodeSentMessagePayload(bytes calldata _payload)
internal
pure
returns (uint256 destination_, address target_, uint256 nonce_, address sender_, bytes memory message_)
{
// Validate Selector (also reverts if LOG0 with no topics)
bytes32 selector = abi.decode(_payload[:32], (bytes32));
if (selector != SENT_MESSAGE_EVENT_SELECTOR) revert EventPayloadNotSentMessage();
// Topics
(destination_, target_, nonce_) = abi.decode(_payload[32:128], (uint256, address, uint256));
// Data
(sender_, message_) = abi.decode(_payload[128:], (address, bytes));
}
} }
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity ^0.8.0; pragma solidity ^0.8.0;
import { ICrossL2Inbox } from "src/L2/interfaces/ICrossL2Inbox.sol";
/// @title IL2ToL2CrossDomainMessenger /// @title IL2ToL2CrossDomainMessenger
/// @notice Interface for the L2ToL2CrossDomainMessenger contract. /// @notice Interface for the L2ToL2CrossDomainMessenger contract.
interface IL2ToL2CrossDomainMessenger { interface IL2ToL2CrossDomainMessenger {
...@@ -45,20 +47,7 @@ interface IL2ToL2CrossDomainMessenger { ...@@ -45,20 +47,7 @@ interface IL2ToL2CrossDomainMessenger {
/// @notice Relays a message that was sent by the other CrossDomainMessenger contract. Can only /// @notice Relays a message that was sent by the other CrossDomainMessenger contract. Can only
/// be executed via cross-chain call from the other messenger OR if the message was /// be executed via cross-chain call from the other messenger OR if the message was
/// already received once and is currently being replayed. /// already received once and is currently being replayed.
/// @param _destination Chain ID of the destination chain. /// @param _id Identifier of the SentMessage event to be relayed
/// @param _nonce Nonce of the message being relayed. /// @param _sentMessage Message payload of the `SentMessage` event
/// @param _sender Address of the user who sent the message. function relayMessage(ICrossL2Inbox.Identifier calldata _id, bytes calldata _sentMessage) external payable;
/// @param _source Chain ID of the source chain.
/// @param _target Address that the message is targeted at.
/// @param _message Message to send to the target.
function relayMessage(
uint256 _destination,
uint256 _source,
uint256 _nonce,
address _sender,
address _target,
bytes calldata _message
)
external
payable;
} }
...@@ -122,8 +122,8 @@ library Hashing { ...@@ -122,8 +122,8 @@ library Hashing {
); );
} }
/// @notice Generates a unique hash for a message to be relayed across chains. This hash is /// @notice Generates a unique hash for cross l2 messages. This hash is used to identify
/// used to identify the message and ensure it is not relayed more than once. /// the message and ensure it is not relayed more than once.
/// @param _destination Chain ID of the destination chain. /// @param _destination Chain ID of the destination chain.
/// @param _source Chain ID of the source chain. /// @param _source Chain ID of the source chain.
/// @param _nonce Unique nonce associated with the message to prevent replay attacks. /// @param _nonce Unique nonce associated with the message to prevent replay attacks.
...@@ -131,7 +131,7 @@ library Hashing { ...@@ -131,7 +131,7 @@ library Hashing {
/// @param _target Address of the contract or wallet that the message is targeting on the destination chain. /// @param _target Address of the contract or wallet that the message is targeting on the destination chain.
/// @param _message The message payload to be relayed to the target on the destination chain. /// @param _message The message payload to be relayed to the target on the destination chain.
/// @return Hash of the encoded message parameters, used to uniquely identify the message. /// @return Hash of the encoded message parameters, used to uniquely identify the message.
function hashL2toL2CrossDomainMessengerRelayMessage( function hashL2toL2CrossDomainMessage(
uint256 _destination, uint256 _destination,
uint256 _source, uint256 _source,
uint256 _nonce, uint256 _nonce,
......
...@@ -10,19 +10,20 @@ import { Predeploys } from "src/libraries/Predeploys.sol"; ...@@ -10,19 +10,20 @@ import { Predeploys } from "src/libraries/Predeploys.sol";
import { Hashing } from "src/libraries/Hashing.sol"; import { Hashing } from "src/libraries/Hashing.sol";
// Target contract // Target contract
import { CrossL2Inbox } from "src/L2/CrossL2Inbox.sol";
import { ICrossL2Inbox } from "src/L2/interfaces/ICrossL2Inbox.sol";
import { import {
L2ToL2CrossDomainMessenger, L2ToL2CrossDomainMessenger,
NotEntered, NotEntered,
MessageDestinationSameChain, MessageDestinationSameChain,
RelayMessageCallerNotCrossL2Inbox, IdOriginNotL2ToL2CrossDomainMessenger,
CrossL2InboxOriginNotL2ToL2CrossDomainMessenger, EventPayloadNotSentMessage,
MessageDestinationNotRelayChain, MessageDestinationNotRelayChain,
MessageTargetCrossL2Inbox, MessageTargetCrossL2Inbox,
MessageTargetL2ToL2CrossDomainMessenger, MessageTargetL2ToL2CrossDomainMessenger,
MessageAlreadyRelayed, MessageAlreadyRelayed,
ReentrantCall ReentrantCall
} from "src/L2/L2ToL2CrossDomainMessenger.sol"; } from "src/L2/L2ToL2CrossDomainMessenger.sol";
import { CrossL2Inbox } from "src/L2/CrossL2Inbox.sol";
/// @title L2ToL2CrossDomainMessengerWithModifiableTransientStorage /// @title L2ToL2CrossDomainMessengerWithModifiableTransientStorage
/// @dev L2ToL2CrossDomainMessenger contract with methods to modify the transient storage. /// @dev L2ToL2CrossDomainMessenger contract with methods to modify the transient storage.
...@@ -91,11 +92,10 @@ contract L2ToL2CrossDomainMessengerTest is Test { ...@@ -91,11 +92,10 @@ contract L2ToL2CrossDomainMessengerTest is Test {
vm.recordLogs(); vm.recordLogs();
// Call the sendMessage function // Call the sendMessage function
bytes32 msgHash = bytes32 msgHash = l2ToL2CrossDomainMessenger.sendMessage(_destination, _target, _message);
l2ToL2CrossDomainMessenger.sendMessage({ _destination: _destination, _target: _target, _message: _message });
assertEq( assertEq(
msgHash, msgHash,
Hashing.hashL2toL2CrossDomainMessengerRelayMessage( Hashing.hashL2toL2CrossDomainMessage(
_destination, block.chainid, messageNonce, address(this), _target, _message _destination, block.chainid, messageNonce, address(this), _target, _message
) )
); );
...@@ -103,13 +103,15 @@ contract L2ToL2CrossDomainMessengerTest is Test { ...@@ -103,13 +103,15 @@ contract L2ToL2CrossDomainMessengerTest is Test {
// Check that the event was emitted with the correct parameters // Check that the event was emitted with the correct parameters
Vm.Log[] memory logs = vm.getRecordedLogs(); Vm.Log[] memory logs = vm.getRecordedLogs();
assertEq(logs.length, 1); assertEq(logs.length, 1);
assertEq(
logs[0].data, // topics
abi.encodeCall( assertEq(logs[0].topics[0], L2ToL2CrossDomainMessenger.SentMessage.selector);
L2ToL2CrossDomainMessenger.relayMessage, assertEq(logs[0].topics[1], bytes32(_destination));
(_destination, block.chainid, messageNonce, address(this), _target, _message) assertEq(logs[0].topics[2], bytes32(uint256(uint160(_target))));
) assertEq(logs[0].topics[3], bytes32(messageNonce));
);
// data
assertEq(logs[0].data, abi.encode(address(this), _message));
// Check that the message nonce has been incremented // Check that the message nonce has been incremented
assertEq(l2ToL2CrossDomainMessenger.messageNonce(), messageNonce + 1); assertEq(l2ToL2CrossDomainMessenger.messageNonce(), messageNonce + 1);
...@@ -198,16 +200,15 @@ contract L2ToL2CrossDomainMessengerTest is Test { ...@@ -198,16 +200,15 @@ contract L2ToL2CrossDomainMessengerTest is Test {
address _sender, address _sender,
address _target, address _target,
bytes calldata _message, bytes calldata _message,
uint256 _value uint256 _value,
uint256 _blockNum,
uint256 _logIndex,
uint256 _time
) )
external external
{ {
// Ensure that the target contract is not a Forge contract.
assumeNotForgeAddress(_target);
// Ensure that the target contract is not CrossL2Inbox or L2ToL2CrossDomainMessenger // Ensure that the target contract is not CrossL2Inbox or L2ToL2CrossDomainMessenger
vm.assume(_target != Predeploys.CROSS_L2_INBOX); vm.assume(_target != Predeploys.CROSS_L2_INBOX && _target != Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER);
vm.assume(_target != Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER);
// Ensure that the target call is payable if value is sent // Ensure that the target call is payable if value is sent
if (_value > 0) assumePayable(_target); if (_value > 0) assumePayable(_target);
...@@ -215,51 +216,68 @@ contract L2ToL2CrossDomainMessengerTest is Test { ...@@ -215,51 +216,68 @@ contract L2ToL2CrossDomainMessengerTest is Test {
// Ensure that the target contract does not revert // Ensure that the target contract does not revert
vm.mockCall({ callee: _target, msgValue: _value, data: _message, returnData: abi.encode(true) }); vm.mockCall({ callee: _target, msgValue: _value, data: _message, returnData: abi.encode(true) });
// Mock the CrossL2Inbox origin to return the L2ToL2CrossDomainMessenger contract // Construct the SentMessage payload & identifier
ICrossL2Inbox.Identifier memory id =
ICrossL2Inbox.Identifier(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER, _blockNum, _logIndex, _time, _source);
bytes memory sentMessage = abi.encodePacked(
abi.encode(L2ToL2CrossDomainMessenger.SentMessage.selector, block.chainid, _target, _nonce), // topics
abi.encode(_sender, _message) // data
);
// Ensure the CrossL2Inbox validates this message
vm.mockCall({ vm.mockCall({
callee: Predeploys.CROSS_L2_INBOX, callee: Predeploys.CROSS_L2_INBOX,
data: abi.encodeWithSelector(CrossL2Inbox.origin.selector), data: abi.encodeWithSelector(CrossL2Inbox.validateMessage.selector, id, sentMessage),
returnData: abi.encode(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER) returnData: ""
}); });
// Look for correct emitted event // Look for correct emitted event
vm.expectEmit(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER); vm.expectEmit(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER);
emit L2ToL2CrossDomainMessenger.RelayedMessage( emit L2ToL2CrossDomainMessenger.RelayedMessage(
keccak256(abi.encode(block.chainid, _source, _nonce, _sender, _target, _message)) _source, _nonce, keccak256(abi.encode(block.chainid, _source, _nonce, _sender, _target, _message))
); );
// Ensure the target contract is called with the correct parameters // relay the message
vm.expectCall({ callee: _target, msgValue: _value, data: _message }); hoax(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER, _value);
l2ToL2CrossDomainMessenger.relayMessage{ value: _value }(id, sentMessage);
// Ensure caller is CrossL2Inbox to prevent a revert from the caller check and that it has sufficient value
hoax(Predeploys.CROSS_L2_INBOX, _value);
// Call the relayMessage function
l2ToL2CrossDomainMessenger.relayMessage{ value: _value }({
_destination: block.chainid, // ensure the destination is the chain of L2ToL2CrossDomainMessenger
_source: _source,
_nonce: _nonce,
_sender: _sender,
_target: _target,
_message: _message
});
// Check that successfulMessages mapping updates the message hash correctly
assertEq( assertEq(
l2ToL2CrossDomainMessenger.successfulMessages( l2ToL2CrossDomainMessenger.successfulMessages(
keccak256(abi.encode(block.chainid, _source, _nonce, _sender, _target, _message)) keccak256(abi.encode(block.chainid, _source, _nonce, _sender, _target, _message))
), ),
true true
); );
}
// Check that entered slot is cleared after the function call function testFuzz_relayMessage_eventPayloadNotSentMessage_reverts(
assertEq(l2ToL2CrossDomainMessenger.entered(), false); uint256 _source,
uint256 _nonce,
bytes32 _msgHash,
uint256 _value,
uint256 _blockNum,
uint256 _logIndex,
uint256 _time
)
external
{
// Expect a revert with the EventPayloadNotSentMessage selector
vm.expectRevert(EventPayloadNotSentMessage.selector);
// Check that metadata is cleared after the function call. We need to set the `entered` slot to non-zero value // Point to a different remote log that the inbox validates
// to prevent NotEntered revert when calling the crossDomainMessageSender and crossDomainMessageSource functions ICrossL2Inbox.Identifier memory id =
l2ToL2CrossDomainMessenger.setEntered(1); ICrossL2Inbox.Identifier(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER, _blockNum, _logIndex, _time, _source);
assertEq(l2ToL2CrossDomainMessenger.crossDomainMessageSource(), 0); bytes memory sentMessage =
assertEq(l2ToL2CrossDomainMessenger.crossDomainMessageSender(), address(0)); abi.encode(L2ToL2CrossDomainMessenger.RelayedMessage.selector, _source, _nonce, _msgHash);
// Ensure the CrossL2Inbox validates this message
vm.mockCall({
callee: Predeploys.CROSS_L2_INBOX,
data: abi.encodeWithSelector(CrossL2Inbox.validateMessage.selector, id, sentMessage),
returnData: ""
});
// Call
hoax(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER, _value);
l2ToL2CrossDomainMessenger.relayMessage{ value: _value }(id, sentMessage);
} }
/// @dev Mock target function that checks the source and sender of the message in transient storage. /// @dev Mock target function that checks the source and sender of the message in transient storage.
...@@ -281,7 +299,10 @@ contract L2ToL2CrossDomainMessengerTest is Test { ...@@ -281,7 +299,10 @@ contract L2ToL2CrossDomainMessengerTest is Test {
uint256 _source, uint256 _source,
uint256 _nonce, uint256 _nonce,
address _sender, address _sender,
uint256 _value uint256 _value,
uint256 _blockNum,
uint256 _logIndex,
uint256 _time
) )
external external
{ {
...@@ -289,46 +310,39 @@ contract L2ToL2CrossDomainMessengerTest is Test { ...@@ -289,46 +310,39 @@ contract L2ToL2CrossDomainMessengerTest is Test {
// contract has a non-zero balance. Thus, we set this contract's balance to zero and we hoax afterwards. // contract has a non-zero balance. Thus, we set this contract's balance to zero and we hoax afterwards.
vm.deal(address(this), 0); vm.deal(address(this), 0);
// Mock the CrossL2Inbox origin to return the L2ToL2CrossDomainMessenger contract
vm.mockCall({
callee: Predeploys.CROSS_L2_INBOX,
data: abi.encodeWithSelector(CrossL2Inbox.origin.selector),
returnData: abi.encode(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER)
});
// Set the target and message for the reentrant call // Set the target and message for the reentrant call
address target = address(this); address target = address(this);
bytes memory message = abi.encodeWithSelector(this.mockTarget.selector, _source, _sender); bytes memory message = abi.encodeWithSelector(this.mockTarget.selector, _source, _sender);
bytes32 msgHash = keccak256(abi.encode(block.chainid, _source, _nonce, _sender, target, message));
// Look for correct emitted event // Look for correct emitted event
vm.expectEmit(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER); vm.expectEmit(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER);
emit L2ToL2CrossDomainMessenger.RelayedMessage( emit L2ToL2CrossDomainMessenger.RelayedMessage(_source, _nonce, msgHash);
keccak256(abi.encode(block.chainid, _source, _nonce, _sender, target, message))
);
// Ensure the target contract is called with the correct parameters // Ensure the target contract is called with the correct parameters
vm.expectCall({ callee: target, msgValue: _value, data: message }); vm.expectCall({ callee: target, msgValue: _value, data: message });
// Ensure caller is CrossL2Inbox to prevent a revert from the caller check and that it has sufficient value // Construct and relay the message
hoax(Predeploys.CROSS_L2_INBOX, _value); ICrossL2Inbox.Identifier memory id =
ICrossL2Inbox.Identifier(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER, _blockNum, _logIndex, _time, _source);
// Call the relayMessage function bytes memory sentMessage = abi.encodePacked(
l2ToL2CrossDomainMessenger.relayMessage{ value: _value }({ abi.encode(L2ToL2CrossDomainMessenger.SentMessage.selector, block.chainid, target, _nonce), // topics
_destination: block.chainid, // ensure the destination is the chain of L2ToL2CrossDomainMessenger abi.encode(_sender, message) // data
_source: _source, );
_nonce: _nonce,
_sender: _sender, // Ensure the CrossL2Inbox validates this message
_target: target, vm.mockCall({
_message: message callee: Predeploys.CROSS_L2_INBOX,
data: abi.encodeWithSelector(CrossL2Inbox.validateMessage.selector, id, sentMessage),
returnData: ""
}); });
hoax(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER, _value);
l2ToL2CrossDomainMessenger.relayMessage{ value: _value }(id, sentMessage);
// Check that successfulMessages mapping updates the message hash correctly // Check that successfulMessages mapping updates the message hash correctly
assertEq( assertEq(l2ToL2CrossDomainMessenger.successfulMessages(msgHash), true);
l2ToL2CrossDomainMessenger.successfulMessages(
keccak256(abi.encode(block.chainid, _source, _nonce, _sender, target, message))
),
true
);
// Check that entered slot is cleared after the function call // Check that entered slot is cleared after the function call
assertEq(l2ToL2CrossDomainMessenger.entered(), false); assertEq(l2ToL2CrossDomainMessenger.entered(), false);
...@@ -353,14 +367,14 @@ contract L2ToL2CrossDomainMessengerTest is Test { ...@@ -353,14 +367,14 @@ contract L2ToL2CrossDomainMessengerTest is Test {
vm.expectRevert(ReentrantCall.selector); vm.expectRevert(ReentrantCall.selector);
l2ToL2CrossDomainMessenger.relayMessage({ ICrossL2Inbox.Identifier memory id =
_destination: block.chainid, ICrossL2Inbox.Identifier(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER, 1, 1, 1, _source);
_source: _source, bytes memory sentMessage = abi.encodePacked(
_nonce: _nonce, abi.encode(L2ToL2CrossDomainMessenger.SentMessage.selector, block.chainid, address(0), _nonce), // topics
_sender: _sender, abi.encode(_sender, "") // data
_target: address(0), );
_message: ""
}); l2ToL2CrossDomainMessenger.relayMessage(id, sentMessage);
// Ensure the function still reverts if `expectRevert` succeeds // Ensure the function still reverts if `expectRevert` succeeds
revert(); revert();
...@@ -373,7 +387,10 @@ contract L2ToL2CrossDomainMessengerTest is Test { ...@@ -373,7 +387,10 @@ contract L2ToL2CrossDomainMessengerTest is Test {
uint256 _source2, // sender passed to `relayMessage` by the reentrant call. uint256 _source2, // sender passed to `relayMessage` by the reentrant call.
address _sender2, // sender passed to `relayMessage` by the reentrant call. address _sender2, // sender passed to `relayMessage` by the reentrant call.
uint256 _nonce, uint256 _nonce,
uint256 _value uint256 _value,
uint256 _blockNum,
uint256 _logIndex,
uint256 _time
) )
external external
{ {
...@@ -381,13 +398,6 @@ contract L2ToL2CrossDomainMessengerTest is Test { ...@@ -381,13 +398,6 @@ contract L2ToL2CrossDomainMessengerTest is Test {
// contract has a non-zero balance. Thus, we set this contract's balance to zero and we hoax afterwards. // contract has a non-zero balance. Thus, we set this contract's balance to zero and we hoax afterwards.
vm.deal(address(this), 0); vm.deal(address(this), 0);
// Mock the CrossL2Inbox origin to return the L2ToL2CrossDomainMessenger contract
vm.mockCall({
callee: Predeploys.CROSS_L2_INBOX,
data: abi.encodeWithSelector(CrossL2Inbox.origin.selector),
returnData: abi.encode(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER)
});
// Set the target and message for the reentrant call // Set the target and message for the reentrant call
address target = address(this); address target = address(this);
bytes memory message = abi.encodeWithSelector(this.mockTargetReentrant.selector, _source2, _nonce, _sender2); bytes memory message = abi.encodeWithSelector(this.mockTargetReentrant.selector, _source2, _nonce, _sender2);
...@@ -395,25 +405,30 @@ contract L2ToL2CrossDomainMessengerTest is Test { ...@@ -395,25 +405,30 @@ contract L2ToL2CrossDomainMessengerTest is Test {
// Look for correct emitted event // Look for correct emitted event
vm.expectEmit(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER); vm.expectEmit(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER);
emit L2ToL2CrossDomainMessenger.FailedRelayedMessage( emit L2ToL2CrossDomainMessenger.FailedRelayedMessage(
keccak256(abi.encode(block.chainid, _source1, _nonce, _sender1, target, message)) _source1, _nonce, keccak256(abi.encode(block.chainid, _source1, _nonce, _sender1, target, message))
); );
// Ensure the target contract is called with the correct parameters // Ensure the target contract is called with the correct parameters
vm.expectCall({ callee: target, msgValue: _value, data: message }); vm.expectCall({ callee: target, msgValue: _value, data: message });
// Ensure caller is CrossL2Inbox to prevent a revert from the caller check and that it has sufficient value // Construct and relay the message
hoax(Predeploys.CROSS_L2_INBOX, _value); ICrossL2Inbox.Identifier memory id =
ICrossL2Inbox.Identifier(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER, _blockNum, _logIndex, _time, _source1);
// Call the relayMessage function bytes memory sentMessage = abi.encodePacked(
l2ToL2CrossDomainMessenger.relayMessage{ value: _value }({ abi.encode(L2ToL2CrossDomainMessenger.SentMessage.selector, block.chainid, target, _nonce), // topics
_destination: block.chainid, // ensure the destination is the chain of L2ToL2CrossDomainMessenger abi.encode(_sender1, message) // data
_source: _source1, );
_nonce: _nonce,
_sender: _sender1, // Ensure the CrossL2Inbox validates this message
_target: target, vm.mockCall({
_message: message callee: Predeploys.CROSS_L2_INBOX,
data: abi.encodeWithSelector(CrossL2Inbox.validateMessage.selector, id, sentMessage),
returnData: ""
}); });
hoax(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER, _value);
l2ToL2CrossDomainMessenger.relayMessage{ value: _value }(id, sentMessage);
// Check that entered slot is cleared after the function call // Check that entered slot is cleared after the function call
assertEq(l2ToL2CrossDomainMessenger.entered(), false); assertEq(l2ToL2CrossDomainMessenger.entered(), false);
...@@ -424,70 +439,36 @@ contract L2ToL2CrossDomainMessengerTest is Test { ...@@ -424,70 +439,36 @@ contract L2ToL2CrossDomainMessengerTest is Test {
assertEq(l2ToL2CrossDomainMessenger.crossDomainMessageSender(), address(0)); assertEq(l2ToL2CrossDomainMessenger.crossDomainMessageSender(), address(0));
} }
/// @dev Tests that the `relayMessage` function reverts when the caller is not the CrossL2Inbox contract. /// @dev Tests that the `relayMessage` function reverts when log identifier is not the cdm
function testFuzz_relayMessage_callerNotCrossL2Inbox_reverts( function testFuzz_relayMessage_idOriginNotL2ToL2CrossDomainMessenger_reverts(
uint256 _destination,
uint256 _source, uint256 _source,
uint256 _nonce, uint256 _nonce,
address _sender, address _sender,
address _target, address _target,
bytes calldata _message, bytes calldata _message,
uint256 _value uint256 _value,
address _origin,
uint256 _blockNum,
uint256 _logIndex,
uint256 _time
) )
external external
{ {
// Add sufficient value to the contract to relay the message with // Incorrect identifier origin
vm.deal(address(this), _value); vm.assume(_origin != Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER);
// Expect a revert with the RelayMessageCallerNotCrossL2Inbox selector // Expect a revert with the IdOriginNotL2ToL2CrossDomainMessenger
vm.expectRevert(RelayMessageCallerNotCrossL2Inbox.selector); vm.expectRevert(IdOriginNotL2ToL2CrossDomainMessenger.selector);
// Call `relayMessage` with the current contract as the caller to provoke revert
l2ToL2CrossDomainMessenger.relayMessage{ value: _value }({
_destination: _destination,
_source: _source,
_nonce: _nonce,
_sender: _sender,
_target: _target,
_message: _message
});
}
/// @dev Tests that the `relayMessage` function reverts when CrossL2Inbox's origin is not ICrossL2Inbox.Identifier memory id = ICrossL2Inbox.Identifier(_origin, _blockNum, _logIndex, _time, _source);
/// L2ToL2CrossDomainMessenger. bytes memory sentMessage = abi.encodePacked(
function testFuzz_relayMessage_crossL2InboxOriginNotL2ToL2CrossDomainMessenger_reverts( abi.encode(L2ToL2CrossDomainMessenger.SentMessage.selector, block.chainid, _target, _nonce), // topics
uint256 _destination, abi.encode(_sender, _message) // data
uint256 _source, );
uint256 _nonce,
address _sender,
address _target,
bytes calldata _message,
uint256 _value
)
external
{
// Set address(0) as the origin of the CrossL2Inbox contract, which is not the L2ToL2CrossDomainMessenger
vm.mockCall({
callee: Predeploys.CROSS_L2_INBOX,
data: abi.encodeWithSelector(CrossL2Inbox.origin.selector),
returnData: abi.encode(address(0))
});
// Ensure caller is CrossL2Inbox to prevent a revert from the caller check and that it has sufficient value
hoax(Predeploys.CROSS_L2_INBOX, _value);
// Expect a revert with the CrossL2InboxOriginNotL2ToL2CrossDomainMessenger selector
vm.expectRevert(CrossL2InboxOriginNotL2ToL2CrossDomainMessenger.selector);
// Call `relayMessage` with invalid CrossL2Inbox origin to provoke revert // Call
l2ToL2CrossDomainMessenger.relayMessage{ value: _value }({ hoax(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER, _value);
_destination: _destination, l2ToL2CrossDomainMessenger.relayMessage{ value: _value }(id, sentMessage);
_source: _source,
_nonce: _nonce,
_sender: _sender,
_target: _target,
_message: _message
});
} }
/// @dev Tests that the `relayMessage` function reverts when the destination is not the relay chain. /// @dev Tests that the `relayMessage` function reverts when the destination is not the relay chain.
...@@ -498,35 +479,36 @@ contract L2ToL2CrossDomainMessengerTest is Test { ...@@ -498,35 +479,36 @@ contract L2ToL2CrossDomainMessengerTest is Test {
address _sender, address _sender,
address _target, address _target,
bytes calldata _message, bytes calldata _message,
uint256 _value uint256 _value,
uint256 _blockNum,
uint256 _logIndex,
uint256 _time
) )
external external
{ {
// Ensure the destination is not this chain // Ensure the destination is not this chain
vm.assume(_destination != block.chainid); vm.assume(_destination != block.chainid);
// Mock the CrossL2Inbox origin to return the L2ToL2CrossDomainMessenger contract // Expect a revert with the MessageDestinationNotRelayChain selector
vm.expectRevert(MessageDestinationNotRelayChain.selector);
ICrossL2Inbox.Identifier memory id =
ICrossL2Inbox.Identifier(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER, _blockNum, _logIndex, _time, _source);
bytes memory sentMessage = abi.encodePacked(
abi.encode(L2ToL2CrossDomainMessenger.SentMessage.selector, _destination, _target, _nonce), // topics
abi.encode(_sender, _message) // data
);
// Ensure the CrossL2Inbox validates this message
vm.mockCall({ vm.mockCall({
callee: Predeploys.CROSS_L2_INBOX, callee: Predeploys.CROSS_L2_INBOX,
data: abi.encodeWithSelector(CrossL2Inbox.origin.selector), data: abi.encodeWithSelector(CrossL2Inbox.validateMessage.selector, id, sentMessage),
returnData: abi.encode(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER) returnData: ""
}); });
// Ensure caller is CrossL2Inbox to prevent a revert from the caller check and that it has sufficient value
hoax(Predeploys.CROSS_L2_INBOX, _value);
// Expect a revert with the MessageDestinationNotRelayChain selector
vm.expectRevert(MessageDestinationNotRelayChain.selector);
// Call `relayMessage` // Call `relayMessage`
l2ToL2CrossDomainMessenger.relayMessage{ value: _value }({ hoax(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER, _value);
_destination: _destination, l2ToL2CrossDomainMessenger.relayMessage{ value: _value }(id, sentMessage);
_source: _source,
_nonce: _nonce,
_sender: _sender,
_target: _target,
_message: _message
});
} }
/// @dev Tests that the `relayMessage` function reverts when the message target is CrossL2Inbox. /// @dev Tests that the `relayMessage` function reverts when the message target is CrossL2Inbox.
...@@ -535,33 +517,37 @@ contract L2ToL2CrossDomainMessengerTest is Test { ...@@ -535,33 +517,37 @@ contract L2ToL2CrossDomainMessengerTest is Test {
uint256 _nonce, uint256 _nonce,
address _sender, address _sender,
bytes calldata _message, bytes calldata _message,
uint256 _value uint256 _value,
uint256 _blockNum,
uint256 _logIndex,
uint256 _time
) )
external external
{ {
// Mock the CrossL2Inbox origin to return the L2ToL2CrossDomainMessenger contract
vm.mockCall({
callee: Predeploys.CROSS_L2_INBOX,
data: abi.encodeWithSelector(CrossL2Inbox.origin.selector),
returnData: abi.encode(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER)
});
// Ensure caller is CrossL2Inbox to prevent a revert from the caller check and that it has sufficient value
hoax(Predeploys.CROSS_L2_INBOX, _value);
// Expect a revert with the MessageTargetCrossL2Inbox selector // Expect a revert with the MessageTargetCrossL2Inbox selector
vm.expectRevert(MessageTargetCrossL2Inbox.selector); vm.expectRevert(MessageTargetCrossL2Inbox.selector);
// Call `relayMessage` with CrossL2Inbox as the target to provoke revert. The current chain is the destination // Call `relayMessage` with CrossL2Inbox as the target to provoke revert. The current chain is the destination
// to prevent revert due to invalid destination // to prevent revert due to invalid destination
l2ToL2CrossDomainMessenger.relayMessage{ value: _value }({ ICrossL2Inbox.Identifier memory id =
_destination: block.chainid, ICrossL2Inbox.Identifier(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER, _blockNum, _logIndex, _time, _source);
_source: _source, bytes memory sentMessage = abi.encodePacked(
_nonce: _nonce, abi.encode(
_sender: _sender, L2ToL2CrossDomainMessenger.SentMessage.selector, block.chainid, Predeploys.CROSS_L2_INBOX, _nonce
_target: Predeploys.CROSS_L2_INBOX, ), // topics
_message: _message abi.encode(_sender, _message) // data
);
// Ensure the CrossL2Inbox validates this message
vm.mockCall({
callee: Predeploys.CROSS_L2_INBOX,
data: abi.encodeWithSelector(CrossL2Inbox.validateMessage.selector, id, sentMessage),
returnData: ""
}); });
// Call
hoax(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER, _value);
l2ToL2CrossDomainMessenger.relayMessage{ value: _value }(id, sentMessage);
} }
/// @dev Tests that the `relayMessage` function reverts when the message target is L2ToL2CrossDomainMessenger. /// @dev Tests that the `relayMessage` function reverts when the message target is L2ToL2CrossDomainMessenger.
...@@ -570,33 +556,39 @@ contract L2ToL2CrossDomainMessengerTest is Test { ...@@ -570,33 +556,39 @@ contract L2ToL2CrossDomainMessengerTest is Test {
uint256 _nonce, uint256 _nonce,
address _sender, address _sender,
bytes calldata _message, bytes calldata _message,
uint256 _value uint256 _value,
uint256 _blockNum,
uint256 _logIndex,
uint256 _time
) )
external external
{ {
// Mock the CrossL2Inbox origin to return the L2ToL2CrossDomainMessenger contract
vm.mockCall({
callee: Predeploys.CROSS_L2_INBOX,
data: abi.encodeWithSelector(CrossL2Inbox.origin.selector),
returnData: abi.encode(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER)
});
// Ensure caller is CrossL2Inbox to prevent a revert from the caller check and that it has sufficient value
hoax(Predeploys.CROSS_L2_INBOX, _value);
// Expect a revert with the MessageTargetL2ToL2CrossDomainMessenger selector // Expect a revert with the MessageTargetL2ToL2CrossDomainMessenger selector
vm.expectRevert(MessageTargetL2ToL2CrossDomainMessenger.selector); vm.expectRevert(MessageTargetL2ToL2CrossDomainMessenger.selector);
// Call `relayMessage` with L2ToL2CrossDomainMessenger as the target to provoke revert. The current chain is the // Call `relayMessage` with L2ToL2CrossDomainMessenger as the target to provoke revert. The current chain is the
// destination to prevent revert due to invalid destination // destination to prevent revert due to invalid destination
l2ToL2CrossDomainMessenger.relayMessage{ value: _value }({ ICrossL2Inbox.Identifier memory id =
_destination: block.chainid, ICrossL2Inbox.Identifier(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER, _blockNum, _logIndex, _time, _source);
_source: _source, bytes memory sentMessage = abi.encodePacked(
_nonce: _nonce, abi.encode(
_sender: _sender, L2ToL2CrossDomainMessenger.SentMessage.selector,
_target: Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER, block.chainid,
_message: _message Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER,
_nonce
), // topics
abi.encode(_sender, _message) // data
);
// Ensure the CrossL2Inbox validates this message
vm.mockCall({
callee: Predeploys.CROSS_L2_INBOX,
data: abi.encodeWithSelector(CrossL2Inbox.validateMessage.selector, id, sentMessage),
returnData: ""
}); });
hoax(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER, _value);
l2ToL2CrossDomainMessenger.relayMessage{ value: _value }(id, sentMessage);
} }
/// @dev Tests that the `relayMessage` function reverts when the message has already been relayed. /// @dev Tests that the `relayMessage` function reverts when the message has already been relayed.
...@@ -606,7 +598,10 @@ contract L2ToL2CrossDomainMessengerTest is Test { ...@@ -606,7 +598,10 @@ contract L2ToL2CrossDomainMessengerTest is Test {
address _sender, address _sender,
address _target, address _target,
bytes calldata _message, bytes calldata _message,
uint256 _value uint256 _value,
uint256 _blockNum,
uint256 _logIndex,
uint256 _time
) )
external external
{ {
...@@ -622,48 +617,37 @@ contract L2ToL2CrossDomainMessengerTest is Test { ...@@ -622,48 +617,37 @@ contract L2ToL2CrossDomainMessengerTest is Test {
// Ensure that the target contract does not revert // Ensure that the target contract does not revert
vm.mockCall({ callee: _target, msgValue: _value, data: _message, returnData: abi.encode(true) }); vm.mockCall({ callee: _target, msgValue: _value, data: _message, returnData: abi.encode(true) });
// Mock the CrossL2Inbox origin to return the L2ToL2CrossDomainMessenger contract
vm.mockCall({
callee: Predeploys.CROSS_L2_INBOX,
data: abi.encodeWithSelector(CrossL2Inbox.origin.selector),
returnData: abi.encode(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER)
});
// Ensure caller is CrossL2Inbox to prevent a revert from the caller check and that it has sufficient value
hoax(Predeploys.CROSS_L2_INBOX, _value);
// Look for correct emitted event for first call. // Look for correct emitted event for first call.
vm.expectEmit(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER); vm.expectEmit(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER);
emit L2ToL2CrossDomainMessenger.RelayedMessage( emit L2ToL2CrossDomainMessenger.RelayedMessage(
keccak256(abi.encode(block.chainid, _source, _nonce, _sender, _target, _message)) _source, _nonce, keccak256(abi.encode(block.chainid, _source, _nonce, _sender, _target, _message))
); );
// First call to `relayMessage` should succeed. The current chain is the destination to prevent revert due to ICrossL2Inbox.Identifier memory id =
// invalid destination ICrossL2Inbox.Identifier(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER, _blockNum, _logIndex, _time, _source);
l2ToL2CrossDomainMessenger.relayMessage{ value: _value }({ bytes memory sentMessage = abi.encodePacked(
_destination: block.chainid, abi.encode(L2ToL2CrossDomainMessenger.SentMessage.selector, block.chainid, _target, _nonce), // topics
_source: _source, abi.encode(_sender, _message) // data
_nonce: _nonce, );
_sender: _sender,
_target: _target, // Ensure the CrossL2Inbox validates this message
_message: _message vm.mockCall({
callee: Predeploys.CROSS_L2_INBOX,
data: abi.encodeWithSelector(CrossL2Inbox.validateMessage.selector, id, sentMessage),
returnData: ""
}); });
// Ensure caller is CrossL2Inbox to prevent a revert from the caller check and that it has sufficient value // First call to `relayMessage` should succeed. The current chain is the destination to prevent revert due to
hoax(Predeploys.CROSS_L2_INBOX, _value); // invalid destination
hoax(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER, _value);
l2ToL2CrossDomainMessenger.relayMessage{ value: _value }(id, sentMessage);
// Second call should fail with MessageAlreadyRelayed selector // Second call should fail with MessageAlreadyRelayed selector
vm.expectRevert(MessageAlreadyRelayed.selector); vm.expectRevert(MessageAlreadyRelayed.selector);
// Call `relayMessage` again. The current chain is the destination to prevent revert due to invalid destination // Call `relayMessage` again. The current chain is the destination to prevent revert due to invalid destination
l2ToL2CrossDomainMessenger.relayMessage{ value: _value }({ hoax(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER, _value);
_destination: block.chainid, l2ToL2CrossDomainMessenger.relayMessage{ value: _value }(id, sentMessage);
_source: _source,
_nonce: _nonce,
_sender: _sender,
_target: _target,
_message: _message
});
} }
/// @dev Tests that the `relayMessage` function reverts when the target call fails. /// @dev Tests that the `relayMessage` function reverts when the target call fails.
...@@ -673,7 +657,10 @@ contract L2ToL2CrossDomainMessengerTest is Test { ...@@ -673,7 +657,10 @@ contract L2ToL2CrossDomainMessengerTest is Test {
address _sender, address _sender,
address _target, address _target,
bytes calldata _message, bytes calldata _message,
uint256 _value uint256 _value,
uint256 _blockNum,
uint256 _logIndex,
uint256 _time
) )
external external
{ {
...@@ -686,30 +673,28 @@ contract L2ToL2CrossDomainMessengerTest is Test { ...@@ -686,30 +673,28 @@ contract L2ToL2CrossDomainMessengerTest is Test {
// Ensure that the target contract reverts // Ensure that the target contract reverts
vm.mockCallRevert({ callee: _target, msgValue: _value, data: _message, revertData: abi.encode(false) }); vm.mockCallRevert({ callee: _target, msgValue: _value, data: _message, revertData: abi.encode(false) });
// Mock the CrossL2Inbox origin to return the L2ToL2CrossDomainMessenger contract
vm.mockCall({
callee: Predeploys.CROSS_L2_INBOX,
data: abi.encodeWithSelector(CrossL2Inbox.origin.selector),
returnData: abi.encode(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER)
});
// Ensure caller is CrossL2Inbox to prevent a revert from the caller check and that it has sufficient value
hoax(Predeploys.CROSS_L2_INBOX, _value);
// Look for correct emitted event // Look for correct emitted event
vm.expectEmit(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER); vm.expectEmit(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER);
emit L2ToL2CrossDomainMessenger.FailedRelayedMessage( emit L2ToL2CrossDomainMessenger.FailedRelayedMessage(
keccak256(abi.encode(block.chainid, _source, _nonce, _sender, _target, _message)) _source, _nonce, keccak256(abi.encode(block.chainid, _source, _nonce, _sender, _target, _message))
); );
l2ToL2CrossDomainMessenger.relayMessage{ value: _value }({ ICrossL2Inbox.Identifier memory id =
_destination: block.chainid, ICrossL2Inbox.Identifier(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER, _blockNum, _logIndex, _time, _source);
_source: _source, bytes memory sentMessage = abi.encodePacked(
_nonce: _nonce, abi.encode(L2ToL2CrossDomainMessenger.SentMessage.selector, block.chainid, _target, _nonce), // topics
_sender: _sender, abi.encode(_sender, _message) // data
_target: _target, );
_message: _message
// Ensure the CrossL2Inbox validates this message
vm.mockCall({
callee: Predeploys.CROSS_L2_INBOX,
data: abi.encodeWithSelector(CrossL2Inbox.validateMessage.selector, id, sentMessage),
returnData: ""
}); });
hoax(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER, _value);
l2ToL2CrossDomainMessenger.relayMessage{ value: _value }(id, sentMessage);
} }
/// @dev Tests that the `crossDomainMessageSender` function returns the correct value. /// @dev Tests that the `crossDomainMessageSender` function returns the correct value.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment