Skip to content

Commit 873c20b

Browse files
agusduha0xDiscotech
authored andcommitted
feat: resend message (ethereum-optimism#15300)
* feat: add re emit sent msg event logic on l2 to l2 cdm * feat: add tests for the re emit feature * fix: review comments chore: run pre pr * refactor: rename to resend msg * chore: return msg hash (ethereum-optimism#361) --------- Co-authored-by: 0xDiscotech <[email protected]>
1 parent 55e4110 commit 873c20b

File tree

6 files changed

+242
-59
lines changed

6 files changed

+242
-59
lines changed

packages/contracts-bedrock/interfaces/L2/IL2ToL2CrossDomainMessenger.sol

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ interface IL2ToL2CrossDomainMessenger {
3636
/// @notice Thrown when a reentrant call is detected.
3737
error ReentrantCall();
3838

39+
/// @notice Thrown when the provided message parameters do not match any hash of a previously sent message.
40+
error InvalidMessage();
41+
3942
/// @notice Emitted whenever a message is sent to a destination
4043
/// @param destination Chain ID of the destination chain.
4144
/// @param target Target contract or wallet address.
@@ -66,6 +69,11 @@ interface IL2ToL2CrossDomainMessenger {
6669
/// @return Nonce of the next message to be sent, with added message version.
6770
function messageNonce() external view returns (uint256);
6871

72+
/// @notice Mapping of message hashes to boolean sent values. Note that a message will only be present in this
73+
/// mapping if it has been sent from this chain to a destination chain.
74+
/// @return Returns true if the message corresponding to the `_msgHash` was successfully sent.
75+
function sentMessages(bytes32) external view returns (bool);
76+
6977
/// @notice Retrieves the sender of the current cross domain message.
7078
/// @return sender_ Address of the sender of the current cross domain message.
7179
function crossDomainMessageSender() external view returns (address sender_);
@@ -86,9 +94,35 @@ interface IL2ToL2CrossDomainMessenger {
8694
/// @param _destination Chain ID of the destination chain.
8795
/// @param _target Target contract or wallet address.
8896
/// @param _message Message to trigger the target address with.
89-
/// @return msgHash_ The hash of the message being sent, which can be used for tracking whether
90-
/// the message has successfully been relayed.
91-
function sendMessage(uint256 _destination, address _target, bytes calldata _message) external returns (bytes32);
97+
/// @return messageHash_ The hash of the message being sent, used to track whether the message
98+
/// has successfully been relayed.
99+
function sendMessage(
100+
uint256 _destination,
101+
address _target,
102+
bytes calldata _message
103+
)
104+
external
105+
returns (bytes32 messageHash_);
106+
107+
/// @notice Re-emits a previously sent message event for old messages that haven't been
108+
/// relayed yet, allowing offchain infrastructure to pick them up and relay them.
109+
/// @dev Emitting a message that has already been relayed will have no effect, as it is only
110+
/// relayed once on the destination chain.
111+
/// @param _destination Chain ID of the destination chain.
112+
/// @param _nonce Nonce of the message sent
113+
/// @param _sender Address that sent the message
114+
/// @param _target Target contract or wallet address.
115+
/// @param _message Message payload to call target with.
116+
/// @return messageHash_ The hash of the message being re-sent.
117+
function resendMessage(
118+
uint256 _destination,
119+
uint256 _nonce,
120+
address _sender,
121+
address _target,
122+
bytes calldata _message
123+
)
124+
external
125+
returns (bytes32 messageHash_);
92126

93127
/// @notice Relays a message that was sent by the other CrossDomainMessenger contract. Can only
94128
/// be executed via cross-chain call from the other messenger OR if the message was

packages/contracts-bedrock/snapshots/abi/L2ToL2CrossDomainMessenger.json

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,45 @@
120120
"stateMutability": "payable",
121121
"type": "function"
122122
},
123+
{
124+
"inputs": [
125+
{
126+
"internalType": "uint256",
127+
"name": "_destination",
128+
"type": "uint256"
129+
},
130+
{
131+
"internalType": "uint256",
132+
"name": "_nonce",
133+
"type": "uint256"
134+
},
135+
{
136+
"internalType": "address",
137+
"name": "_sender",
138+
"type": "address"
139+
},
140+
{
141+
"internalType": "address",
142+
"name": "_target",
143+
"type": "address"
144+
},
145+
{
146+
"internalType": "bytes",
147+
"name": "_message",
148+
"type": "bytes"
149+
}
150+
],
151+
"name": "resendMessage",
152+
"outputs": [
153+
{
154+
"internalType": "bytes32",
155+
"name": "messageHash_",
156+
"type": "bytes32"
157+
}
158+
],
159+
"stateMutability": "nonpayable",
160+
"type": "function"
161+
},
123162
{
124163
"inputs": [
125164
{
@@ -142,13 +181,32 @@
142181
"outputs": [
143182
{
144183
"internalType": "bytes32",
145-
"name": "",
184+
"name": "messageHash_",
146185
"type": "bytes32"
147186
}
148187
],
149188
"stateMutability": "nonpayable",
150189
"type": "function"
151190
},
191+
{
192+
"inputs": [
193+
{
194+
"internalType": "bytes32",
195+
"name": "",
196+
"type": "bytes32"
197+
}
198+
],
199+
"name": "sentMessages",
200+
"outputs": [
201+
{
202+
"internalType": "bool",
203+
"name": "",
204+
"type": "bool"
205+
}
206+
],
207+
"stateMutability": "view",
208+
"type": "function"
209+
},
152210
{
153211
"inputs": [
154212
{
@@ -253,6 +311,11 @@
253311
"name": "IdOriginNotL2ToL2CrossDomainMessenger",
254312
"type": "error"
255313
},
314+
{
315+
"inputs": [],
316+
"name": "InvalidMessage",
317+
"type": "error"
318+
},
256319
{
257320
"inputs": [],
258321
"name": "MessageAlreadyRelayed",

packages/contracts-bedrock/snapshots/semver-lock.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,8 @@
8484
"sourceCodeHash": "0xaef8ea36c5b78cd12e0e62811d51db627ccf0dfd2cc5479fb707a10ef0d42048"
8585
},
8686
"src/L2/L2ToL2CrossDomainMessenger.sol:L2ToL2CrossDomainMessenger": {
87-
"initCodeHash": "0x7ad26c308c506ca65e080c8a33eceac48201221cd291030844021cf1cfde4871",
88-
"sourceCodeHash": "0xe59eec8a64d6c42d9cc2a67aa649718cad590961f92787d62435bf8cde93f7f3"
87+
"initCodeHash": "0xcf875a2073bf91cecffea3308c11f7160274c5eeabe1490065378401d6a8ff3d",
88+
"sourceCodeHash": "0x4f14cd11f43a2efe041b6e8aabccddb5d6c7826362f831c584eccf2ad7c50293"
8989
},
9090
"src/L2/OperatorFeeVault.sol:OperatorFeeVault": {
9191
"initCodeHash": "0x3d8c0d7736e8767f2f797da1c20c5fe30bd7f48a4cf75f376290481ad7c0f91f",

packages/contracts-bedrock/snapshots/storageLayout/L2ToL2CrossDomainMessenger.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,12 @@
1212
"offset": 0,
1313
"slot": "1",
1414
"type": "uint240"
15+
},
16+
{
17+
"bytes": "32",
18+
"label": "sentMessages",
19+
"offset": 0,
20+
"slot": "2",
21+
"type": "mapping(bytes32 => bool)"
1522
}
1623
]

packages/contracts-bedrock/src/L2/L2ToL2CrossDomainMessenger.sol

Lines changed: 58 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ error MessageAlreadyRelayed();
3535
/// @notice Thrown when a reentrant call is detected.
3636
error ReentrantCall();
3737

38+
/// @notice Thrown when the provided message parameters do not match any hash of a previously sent message.
39+
error InvalidMessage();
40+
3841
/// @custom:proxied true
3942
/// @custom:predeploy 0x4200000000000000000000000000000000000023
4043
/// @title L2ToL2CrossDomainMessenger
@@ -61,8 +64,8 @@ contract L2ToL2CrossDomainMessenger is ISemver, TransientReentrancyAware {
6164
uint16 public constant messageVersion = uint16(0);
6265

6366
/// @notice Semantic version.
64-
/// @custom:semver 1.0.0-beta.16
65-
string public constant version = "1.0.0-beta.16";
67+
/// @custom:semver 1.1.0
68+
string public constant version = "1.1.0";
6669

6770
/// @notice Mapping of message hashes to boolean receipt values. Note that a message will only be present in this
6871
/// mapping if it has successfully been relayed on this chain, and can therefore not be relayed again.
@@ -73,6 +76,10 @@ contract L2ToL2CrossDomainMessenger is ISemver, TransientReentrancyAware {
7376
/// message.
7477
uint240 internal msgNonce;
7578

79+
/// @notice Mapping of message hashes to boolean sent values. Note that a message will only be present in this
80+
/// mapping if it has been sent from this chain to a destination chain.
81+
mapping(bytes32 => bool) public sentMessages;
82+
7683
/// @notice Emitted whenever a message is sent to a destination
7784
/// @param destination Chain ID of the destination chain.
7885
/// @param target Target contract or wallet address.
@@ -121,24 +128,67 @@ contract L2ToL2CrossDomainMessenger is ISemver, TransientReentrancyAware {
121128
/// @param _destination Chain ID of the destination chain.
122129
/// @param _target Target contract or wallet address.
123130
/// @param _message Message payload to call target with.
124-
/// @return The hash of the message being sent, used to track whether the message has successfully been relayed.
125-
function sendMessage(uint256 _destination, address _target, bytes calldata _message) external returns (bytes32) {
131+
/// @return messageHash_ The hash of the message being sent, used to track whether the message
132+
/// has successfully been relayed.
133+
function sendMessage(
134+
uint256 _destination,
135+
address _target,
136+
bytes calldata _message
137+
)
138+
external
139+
returns (bytes32 messageHash_)
140+
{
126141
if (_destination == block.chainid) revert MessageDestinationSameChain();
127142
if (_target == Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER) revert MessageTargetL2ToL2CrossDomainMessenger();
128143

129144
uint256 nonce = messageNonce();
130-
emit SentMessage(_destination, _target, nonce, msg.sender, _message);
145+
messageHash_ = Hashing.hashL2toL2CrossDomainMessage({
146+
_destination: _destination,
147+
_source: block.chainid,
148+
_nonce: nonce,
149+
_sender: msg.sender,
150+
_target: _target,
151+
_message: _message
152+
});
131153

154+
sentMessages[messageHash_] = true;
132155
msgNonce++;
133156

134-
return Hashing.hashL2toL2CrossDomainMessage({
157+
emit SentMessage(_destination, _target, nonce, msg.sender, _message);
158+
}
159+
160+
/// @notice Re-emits a previously sent message event for old messages that haven't been
161+
/// relayed yet, allowing offchain infrastructure to pick them up and relay them.
162+
/// @dev Emitting a message that has already been relayed will have no effect, as it is only
163+
/// relayed once on the destination chain.
164+
/// @param _destination Chain ID of the destination chain.
165+
/// @param _nonce Nonce of the message sent
166+
/// @param _sender Address that sent the message
167+
/// @param _target Target contract or wallet address.
168+
/// @param _message Message payload to call target with.
169+
/// @return messageHash_ The hash of the message being re-sent.
170+
function resendMessage(
171+
uint256 _destination,
172+
uint256 _nonce,
173+
address _sender,
174+
address _target,
175+
bytes calldata _message
176+
)
177+
external
178+
returns (bytes32 messageHash_)
179+
{
180+
messageHash_ = Hashing.hashL2toL2CrossDomainMessage({
135181
_destination: _destination,
136182
_source: block.chainid,
137-
_nonce: nonce,
138-
_sender: msg.sender,
183+
_nonce: _nonce,
184+
_sender: _sender,
139185
_target: _target,
140186
_message: _message
141187
});
188+
189+
if (!sentMessages[messageHash_]) revert InvalidMessage();
190+
191+
emit SentMessage(_destination, _target, _nonce, _sender, _message);
142192
}
143193

144194
/// @notice Relays a message that was sent by the other L2ToL2CrossDomainMessenger contract. Can only be executed

0 commit comments

Comments
 (0)