From e71fb1880647143365dd00891b648bc6327d681e Mon Sep 17 00:00:00 2001 From: Hamdi Allam Date: Mon, 12 May 2025 11:40:50 -0400 Subject: [PATCH 1/3] sent message ctx l2tol2cdm.sentmessage.ctx --- .../L2/IL2ToL2CrossDomainMessenger.sol | 12 ++++- .../src/L2/L2ToL2CrossDomainMessenger.sol | 53 ++++++++++++------- .../src/libraries/Hashing.sol | 6 ++- .../test/L2/L2ToL2CrossDomainMessenger.t.sol | 48 ++++++++++------- 4 files changed, 77 insertions(+), 42 deletions(-) diff --git a/packages/contracts-bedrock/interfaces/L2/IL2ToL2CrossDomainMessenger.sol b/packages/contracts-bedrock/interfaces/L2/IL2ToL2CrossDomainMessenger.sol index 0d888652308..53c5a5fc069 100644 --- a/packages/contracts-bedrock/interfaces/L2/IL2ToL2CrossDomainMessenger.sol +++ b/packages/contracts-bedrock/interfaces/L2/IL2ToL2CrossDomainMessenger.sol @@ -45,8 +45,14 @@ interface IL2ToL2CrossDomainMessenger { /// @param messageNonce Nonce associated with the messsage sent /// @param sender Address initiating this message call /// @param message Message payload to call target with. + /// @param context Context of the message. 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, + bytes context ); /// @notice Emitted whenever a message is successfully relayed on this chain. @@ -116,13 +122,15 @@ interface IL2ToL2CrossDomainMessenger { /// @param _sender Address that sent the message /// @param _target Target contract or wallet address. /// @param _message Message payload to call target with. + /// @param _context Context of the message. /// @return messageHash_ The hash of the message being re-sent. function resendMessage( uint256 _destination, uint256 _nonce, address _sender, address _target, - bytes calldata _message + bytes calldata _message, + bytes calldata _context ) external returns (bytes32 messageHash_); diff --git a/packages/contracts-bedrock/src/L2/L2ToL2CrossDomainMessenger.sol b/packages/contracts-bedrock/src/L2/L2ToL2CrossDomainMessenger.sol index f436c629117..3a1d3759141 100644 --- a/packages/contracts-bedrock/src/L2/L2ToL2CrossDomainMessenger.sol +++ b/packages/contracts-bedrock/src/L2/L2ToL2CrossDomainMessenger.sol @@ -55,17 +55,12 @@ contract L2ToL2CrossDomainMessenger is ISemver, TransientReentrancyAware { bytes32 internal constant CROSS_DOMAIN_MESSAGE_SOURCE_SLOT = 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. uint16 public constant messageVersion = uint16(0); /// @notice Semantic version. - /// @custom:semver 1.2.0 - string public constant version = "1.2.0"; + /// @custom:semver 1.3.0 + string public constant version = "1.3.0"; /// @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. @@ -86,8 +81,14 @@ contract L2ToL2CrossDomainMessenger is ISemver, TransientReentrancyAware { /// @param messageNonce Nonce associated with the message sent /// @param sender Address initiating this message call /// @param message Message payload to call target with. + /// @param context Context of the message. 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, + bytes context ); /// @notice Emitted whenever a message is successfully relayed on this chain. @@ -145,19 +146,22 @@ contract L2ToL2CrossDomainMessenger is ISemver, TransientReentrancyAware { if (_target == Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER) revert MessageTargetL2ToL2CrossDomainMessenger(); uint256 nonce = messageNonce(); + bytes memory context = ""; + messageHash_ = Hashing.hashL2toL2CrossDomainMessage({ _destination: _destination, _source: block.chainid, _nonce: nonce, _sender: msg.sender, _target: _target, - _message: _message + _message: _message, + _context: context }); sentMessages[messageHash_] = true; msgNonce++; - emit SentMessage(_destination, _target, nonce, msg.sender, _message); + emit SentMessage(_destination, _target, nonce, msg.sender, _message, context); } /// @notice Re-emits a previously sent message event for old messages that haven't been @@ -169,13 +173,15 @@ contract L2ToL2CrossDomainMessenger is ISemver, TransientReentrancyAware { /// @param _sender Address that sent the message /// @param _target Target contract or wallet address. /// @param _message Message payload to call target with. + /// @param _context The context of the message. /// @return messageHash_ The hash of the message being re-sent. function resendMessage( uint256 _destination, uint256 _nonce, address _sender, address _target, - bytes calldata _message + bytes calldata _message, + bytes calldata _context ) external returns (bytes32 messageHash_) @@ -186,12 +192,13 @@ contract L2ToL2CrossDomainMessenger is ISemver, TransientReentrancyAware { _nonce: _nonce, _sender: _sender, _target: _target, - _message: _message + _message: _message, + _context: _context }); if (!sentMessages[messageHash_]) revert InvalidMessage(); - emit SentMessage(_destination, _target, _nonce, _sender, _message); + emit SentMessage(_destination, _target, _nonce, _sender, _message, _context); } /// @notice Relays a message that was sent by the other L2ToL2CrossDomainMessenger contract. Can only be executed @@ -218,8 +225,8 @@ 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) = - _decodeSentMessagePayload(_sentMessage); + (uint256 destination, address target, uint256 nonce, address sender, bytes memory message, bytes memory context) + = _decodeSentMessagePayload(_sentMessage); // Assert invariants on the message if (destination != block.chainid) revert MessageDestinationNotRelayChain(); @@ -231,7 +238,8 @@ contract L2ToL2CrossDomainMessenger is ISemver, TransientReentrancyAware { _nonce: nonce, _sender: sender, _target: target, - _message: message + _message: message, + _context: context }); if (successfulMessages[messageHash]) { @@ -287,16 +295,23 @@ contract L2ToL2CrossDomainMessenger is ISemver, TransientReentrancyAware { 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_, + bytes memory context_ + ) { // Validate Selector (also reverts if LOG0 with no topics) bytes32 selector = abi.decode(_payload[:32], (bytes32)); - if (selector != SENT_MESSAGE_EVENT_SELECTOR) revert EventPayloadNotSentMessage(); + if (selector != SentMessage.selector) revert EventPayloadNotSentMessage(); // Topics (destination_, target_, nonce_) = abi.decode(_payload[32:128], (uint256, address, uint256)); // Data - (sender_, message_) = abi.decode(_payload[128:], (address, bytes)); + (sender_, message_, context_) = abi.decode(_payload[128:], (address, bytes, bytes)); } } diff --git a/packages/contracts-bedrock/src/libraries/Hashing.sol b/packages/contracts-bedrock/src/libraries/Hashing.sol index 782bbbe4f99..f2d0e23726b 100644 --- a/packages/contracts-bedrock/src/libraries/Hashing.sol +++ b/packages/contracts-bedrock/src/libraries/Hashing.sol @@ -131,6 +131,7 @@ library Hashing { /// @param _sender Address of the user who originally sent the message. /// @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 _context The context of the message. /// @return Hash of the encoded message parameters, used to uniquely identify the message. function hashL2toL2CrossDomainMessage( uint256 _destination, @@ -138,13 +139,14 @@ library Hashing { uint256 _nonce, address _sender, address _target, - bytes memory _message + bytes memory _message, + bytes memory _context ) internal pure returns (bytes32) { - return keccak256(abi.encode(_destination, _source, _nonce, _sender, _target, _message)); + return keccak256(abi.encode(_destination, _source, _nonce, _sender, _target, _message, _context)); } /// @notice Hashes a Super Root proof into a Super Root. diff --git a/packages/contracts-bedrock/test/L2/L2ToL2CrossDomainMessenger.t.sol b/packages/contracts-bedrock/test/L2/L2ToL2CrossDomainMessenger.t.sol index df5b716d6fe..333c31a85c1 100644 --- a/packages/contracts-bedrock/test/L2/L2ToL2CrossDomainMessenger.t.sol +++ b/packages/contracts-bedrock/test/L2/L2ToL2CrossDomainMessenger.t.sol @@ -91,6 +91,9 @@ contract L2ToL2CrossDomainMessengerTest is Test { // Get the current message nonce uint256 messageNonce = l2ToL2CrossDomainMessenger.messageNonce(); + // Context is unused + bytes memory context = ""; + // Look for correct emitted event vm.recordLogs(); @@ -99,7 +102,7 @@ contract L2ToL2CrossDomainMessengerTest is Test { assertEq( msgHash, Hashing.hashL2toL2CrossDomainMessage( - _destination, block.chainid, messageNonce, address(this), _target, _message + _destination, block.chainid, messageNonce, address(this), _target, _message, "" ) ); @@ -114,7 +117,7 @@ contract L2ToL2CrossDomainMessengerTest is Test { assertEq(logs[0].topics[3], bytes32(messageNonce)); // data - assertEq(logs[0].data, abi.encode(address(this), _message)); + assertEq(logs[0].data, abi.encode(address(this), _message, context)); // Check that the message nonce has been incremented and the message hash has been stored assertEq(l2ToL2CrossDomainMessenger.messageNonce(), messageNonce + 1); @@ -188,20 +191,22 @@ contract L2ToL2CrossDomainMessengerTest is Test { uint256 _nonce, address _sender, address _target, - bytes calldata _message + bytes calldata _message, + bytes calldata _context ) external { // Get the message hash and ensure it has not been sent yet - bytes32 msgHash = - Hashing.hashL2toL2CrossDomainMessage(_destination, block.chainid, _nonce, _sender, _target, _message); + bytes32 msgHash = Hashing.hashL2toL2CrossDomainMessage( + _destination, block.chainid, _nonce, _sender, _target, _message, _context + ); vm.assume(l2ToL2CrossDomainMessenger.sentMessages(msgHash) == false); // Expect a revert with the InvalidMessage selector vm.expectRevert(InvalidMessage.selector); // Call the resendMessage function - l2ToL2CrossDomainMessenger.resendMessage(_destination, _nonce, _sender, _target, _message); + l2ToL2CrossDomainMessenger.resendMessage(_destination, _nonce, _sender, _target, _message, _context); } /// @dev Tests that `resendMessage` succeeds and emits the same SentMessage event as the one @@ -223,6 +228,9 @@ contract L2ToL2CrossDomainMessengerTest is Test { // Get the current message nonce uint256 messageNonce = l2ToL2CrossDomainMessenger.messageNonce(); + // Context is unused + bytes memory context = ""; + // Look for correct emitted event vm.recordLogs(); @@ -231,7 +239,9 @@ contract L2ToL2CrossDomainMessengerTest is Test { bytes32 msgHash = l2ToL2CrossDomainMessenger.sendMessage(_destination, _target, _message); assertEq( msgHash, - Hashing.hashL2toL2CrossDomainMessage(_destination, block.chainid, messageNonce, _sender, _target, _message) + Hashing.hashL2toL2CrossDomainMessage( + _destination, block.chainid, messageNonce, _sender, _target, _message, context + ) ); // Check that the event was emitted with the correct parameters @@ -245,7 +255,7 @@ contract L2ToL2CrossDomainMessengerTest is Test { assertEq(logs[0].topics[3], bytes32(messageNonce)); // data - assertEq(logs[0].data, abi.encode(_sender, _message)); + assertEq(logs[0].data, abi.encode(_sender, _message, context)); // Check that the message nonce has been incremented and the message hash has been stored assertEq(l2ToL2CrossDomainMessenger.messageNonce(), messageNonce + 1); @@ -253,7 +263,7 @@ contract L2ToL2CrossDomainMessengerTest is Test { // Call the `resendMessage` function bytes32 resendMsgHash = - l2ToL2CrossDomainMessenger.resendMessage(_destination, messageNonce, _sender, _target, _message); + l2ToL2CrossDomainMessenger.resendMessage(_destination, messageNonce, _sender, _target, _message, context); // Check that the event was emitted with the correct parameters logs = vm.getRecordedLogs(); @@ -335,7 +345,7 @@ contract L2ToL2CrossDomainMessengerTest is Test { address target = address(this); bytes memory message = abi.encodeCall(this.mockTarget, (_source, _sender)); - bytes32 msgHash = keccak256(abi.encode(block.chainid, _source, _nonce, _sender, target, message)); + bytes32 msgHash = keccak256(abi.encode(block.chainid, _source, _nonce, _sender, target, message, "")); // Look for correct emitted event vm.expectEmit(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER); @@ -349,7 +359,7 @@ contract L2ToL2CrossDomainMessengerTest is Test { 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 + abi.encode(_sender, message, "") // data (no context) ); // Ensure the CrossL2Inbox validates this message @@ -406,7 +416,7 @@ contract L2ToL2CrossDomainMessengerTest is Test { 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 + abi.encode(_sender, message, "") // data (no context) ); // Ensure the CrossL2Inbox validates this message @@ -442,7 +452,7 @@ contract L2ToL2CrossDomainMessengerTest is Test { Identifier memory id = Identifier(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER, 1, 1, 1, _source); bytes memory sentMessage = abi.encodePacked( abi.encode(L2ToL2CrossDomainMessenger.SentMessage.selector, block.chainid, address(0), _nonce), // topics - abi.encode(_sender, "") // data + abi.encode(_sender, "", "") // data (no message, no context) ); l2ToL2CrossDomainMessenger.relayMessage(id, sentMessage); @@ -481,7 +491,7 @@ contract L2ToL2CrossDomainMessengerTest is Test { Identifier(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER, _blockNum, _logIndex, _time, _source1); bytes memory sentMessage = abi.encodePacked( abi.encode(L2ToL2CrossDomainMessenger.SentMessage.selector, block.chainid, target, _nonce), // topics - abi.encode(_sender1, message) // data + abi.encode(_sender1, message, "") // data (no context) ); // Ensure the CrossL2Inbox validates this message @@ -530,7 +540,7 @@ contract L2ToL2CrossDomainMessengerTest is Test { Identifier memory id = Identifier(_origin, _blockNum, _logIndex, _time, _source); bytes memory sentMessage = abi.encodePacked( abi.encode(L2ToL2CrossDomainMessenger.SentMessage.selector, block.chainid, _target, _nonce), // topics - abi.encode(_sender, _message) // data + abi.encode(_sender, _message, "") // data (no context) ); // Call @@ -563,7 +573,7 @@ contract L2ToL2CrossDomainMessengerTest is Test { 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 + abi.encode(_sender, _message, "") // data (no context) ); // Ensure the CrossL2Inbox validates this message @@ -612,7 +622,7 @@ contract L2ToL2CrossDomainMessengerTest is Test { emit L2ToL2CrossDomainMessenger.RelayedMessage( _source, _nonce, - keccak256(abi.encode(block.chainid, _source, _nonce, _sender, _target, _message)), + keccak256(abi.encode(block.chainid, _source, _nonce, _sender, _target, _message, "")), keccak256(_message) ); @@ -620,7 +630,7 @@ contract L2ToL2CrossDomainMessengerTest is Test { 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 + abi.encode(_sender, _message, "") // data (no context) ); // Ensure the CrossL2Inbox validates this message @@ -670,7 +680,7 @@ contract L2ToL2CrossDomainMessengerTest is Test { bytes memory sentMessage = abi.encodePacked( abi.encode(L2ToL2CrossDomainMessenger.SentMessage.selector, block.chainid, _target, _nonce), // topics - abi.encode(_sender, _message) // data + abi.encode(_sender, _message, "") // data (no context) ); // Ensure the CrossL2Inbox validates this message From d507b00c1918f670c540386b2d7b47c005b4b36c Mon Sep 17 00:00:00 2001 From: Hamdi Allam Date: Mon, 12 May 2025 11:47:04 -0400 Subject: [PATCH 2/3] pre-pr --- .../snapshots/abi/L2ToL2CrossDomainMessenger.json | 11 +++++++++++ packages/contracts-bedrock/snapshots/semver-lock.json | 4 ++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/packages/contracts-bedrock/snapshots/abi/L2ToL2CrossDomainMessenger.json b/packages/contracts-bedrock/snapshots/abi/L2ToL2CrossDomainMessenger.json index c19fa9a2594..0750b72d2f4 100644 --- a/packages/contracts-bedrock/snapshots/abi/L2ToL2CrossDomainMessenger.json +++ b/packages/contracts-bedrock/snapshots/abi/L2ToL2CrossDomainMessenger.json @@ -146,6 +146,11 @@ "internalType": "bytes", "name": "_message", "type": "bytes" + }, + { + "internalType": "bytes", + "name": "_context", + "type": "bytes" } ], "name": "resendMessage", @@ -302,6 +307,12 @@ "internalType": "bytes", "name": "message", "type": "bytes" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "context", + "type": "bytes" } ], "name": "SentMessage", diff --git a/packages/contracts-bedrock/snapshots/semver-lock.json b/packages/contracts-bedrock/snapshots/semver-lock.json index 13704cba7a1..f9e16eea018 100644 --- a/packages/contracts-bedrock/snapshots/semver-lock.json +++ b/packages/contracts-bedrock/snapshots/semver-lock.json @@ -84,8 +84,8 @@ "sourceCodeHash": "0x83396cbd12a0c5c02e09a4d99c4b62ab4e9d9eb762745e63283e2e818a78a39c" }, "src/L2/L2ToL2CrossDomainMessenger.sol:L2ToL2CrossDomainMessenger": { - "initCodeHash": "0xd997db3cb7c84c8c851719bcf561ef35eb660262b2f4093dd6a3d86c5426240f", - "sourceCodeHash": "0x33393a279f32ecd6641adf267b59413d86595357b4f50b8a09afb62b8fc7639c" + "initCodeHash": "0xdc587a5d2d6a105dd7fc3c0d41b2872e72714ecd274943dea9cfd44c0f0a0ec2", + "sourceCodeHash": "0xc6112743ecc38f8924c99e25f0ef91d793bc58eeb18773dad4382d317340daf6" }, "src/L2/OperatorFeeVault.sol:OperatorFeeVault": { "initCodeHash": "0x3d8c0d7736e8767f2f797da1c20c5fe30bd7f48a4cf75f376290481ad7c0f91f", From b22f083287305098012fb6601c747ead8e71ae4a Mon Sep 17 00:00:00 2001 From: Hamdi Allam Date: Mon, 12 May 2025 11:59:15 -0400 Subject: [PATCH 3/3] fix ExecutingMessage test --- .../contracts-bedrock/test/L2/ExecutingMessageEmitted.t.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/contracts-bedrock/test/L2/ExecutingMessageEmitted.t.sol b/packages/contracts-bedrock/test/L2/ExecutingMessageEmitted.t.sol index 8f1974b79a5..3ec0bc23b38 100644 --- a/packages/contracts-bedrock/test/L2/ExecutingMessageEmitted.t.sol +++ b/packages/contracts-bedrock/test/L2/ExecutingMessageEmitted.t.sol @@ -16,7 +16,7 @@ import { ISuperchainTokenBridge } from "interfaces/L2/ISuperchainTokenBridge.sol /// @notice Integration test that checks that the `ExecutingMessage` event is emitted on crosschain mints. contract ExecutingMessageEmittedTest is CommonTest { bytes32 internal constant SENT_MESSAGE_EVENT_SELECTOR = - 0x382409ac69001e11931a28435afef442cbfd20d9891907e8fa373ba7d351f320; + 0x687289caffce8cccd179ad6b3eebf5b30d65912f573a6b50d0525642b073297e; event ExecutingMessage(bytes32 indexed msgHash, Identifier id); @@ -84,7 +84,7 @@ contract ExecutingMessageEmittedTest is CommonTest { bytes memory message = abi.encodeCall(ISuperchainTokenBridge.relayERC20, (_token, _sender, _to, _amount)); bytes memory sentMessage = abi.encodePacked( abi.encode(SENT_MESSAGE_EVENT_SELECTOR, block.chainid, SUPERCHAIN_TOKEN_BRIDGE, _nonce), // topics - abi.encode(_sender, message) // data + abi.encode(_sender, message, "") // data (no context) ); // Mock `crossDomainMessageContext` call for it to succeed