github.com/ethereum-optimism/optimism@v1.7.2/packages/contracts-bedrock/src/universal/CrossDomainMessenger.sol (about) 1 // SPDX-License-Identifier: MIT 2 pragma solidity 0.8.15; 3 4 import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; 5 import { SafeCall } from "src/libraries/SafeCall.sol"; 6 import { Hashing } from "src/libraries/Hashing.sol"; 7 import { Encoding } from "src/libraries/Encoding.sol"; 8 import { Constants } from "src/libraries/Constants.sol"; 9 10 /// @custom:legacy 11 /// @title CrossDomainMessengerLegacySpacer0 12 /// @notice Contract only exists to add a spacer to the CrossDomainMessenger where the 13 /// libAddressManager variable used to exist. Must be the first contract in the inheritance 14 /// tree of the CrossDomainMessenger. 15 contract CrossDomainMessengerLegacySpacer0 { 16 /// @custom:legacy 17 /// @custom:spacer libAddressManager 18 /// @notice Spacer for backwards compatibility. 19 address private spacer_0_0_20; 20 } 21 22 /// @custom:legacy 23 /// @title CrossDomainMessengerLegacySpacer1 24 /// @notice Contract only exists to add a spacer to the CrossDomainMessenger where the 25 /// PausableUpgradable and OwnableUpgradeable variables used to exist. Must be 26 /// the third contract in the inheritance tree of the CrossDomainMessenger. 27 contract CrossDomainMessengerLegacySpacer1 { 28 /// @custom:legacy 29 /// @custom:spacer ContextUpgradable's __gap 30 /// @notice Spacer for backwards compatibility. Comes from OpenZeppelin 31 /// ContextUpgradable. 32 uint256[50] private spacer_1_0_1600; 33 34 /// @custom:legacy 35 /// @custom:spacer OwnableUpgradeable's _owner 36 /// @notice Spacer for backwards compatibility. 37 /// Come from OpenZeppelin OwnableUpgradeable. 38 address private spacer_51_0_20; 39 40 /// @custom:legacy 41 /// @custom:spacer OwnableUpgradeable's __gap 42 /// @notice Spacer for backwards compatibility. Comes from OpenZeppelin 43 /// OwnableUpgradeable. 44 uint256[49] private spacer_52_0_1568; 45 46 /// @custom:legacy 47 /// @custom:spacer PausableUpgradable's _paused 48 /// @notice Spacer for backwards compatibility. Comes from OpenZeppelin 49 /// PausableUpgradable. 50 bool private spacer_101_0_1; 51 52 /// @custom:legacy 53 /// @custom:spacer PausableUpgradable's __gap 54 /// @notice Spacer for backwards compatibility. Comes from OpenZeppelin 55 /// PausableUpgradable. 56 uint256[49] private spacer_102_0_1568; 57 58 /// @custom:legacy 59 /// @custom:spacer ReentrancyGuardUpgradeable's `_status` field. 60 /// @notice Spacer for backwards compatibility. 61 uint256 private spacer_151_0_32; 62 63 /// @custom:legacy 64 /// @custom:spacer ReentrancyGuardUpgradeable's __gap 65 /// @notice Spacer for backwards compatibility. 66 uint256[49] private spacer_152_0_1568; 67 68 /// @custom:legacy 69 /// @custom:spacer blockedMessages 70 /// @notice Spacer for backwards compatibility. 71 mapping(bytes32 => bool) private spacer_201_0_32; 72 73 /// @custom:legacy 74 /// @custom:spacer relayedMessages 75 /// @notice Spacer for backwards compatibility. 76 mapping(bytes32 => bool) private spacer_202_0_32; 77 } 78 79 /// @custom:upgradeable 80 /// @title CrossDomainMessenger 81 /// @notice CrossDomainMessenger is a base contract that provides the core logic for the L1 and L2 82 /// cross-chain messenger contracts. It's designed to be a universal interface that only 83 /// needs to be extended slightly to provide low-level message passing functionality on each 84 /// chain it's deployed on. Currently only designed for message passing between two paired 85 /// chains and does not support one-to-many interactions. 86 /// Any changes to this contract MUST result in a semver bump for contracts that inherit it. 87 abstract contract CrossDomainMessenger is 88 CrossDomainMessengerLegacySpacer0, 89 Initializable, 90 CrossDomainMessengerLegacySpacer1 91 { 92 /// @notice Current message version identifier. 93 uint16 public constant MESSAGE_VERSION = 1; 94 95 /// @notice Constant overhead added to the base gas for a message. 96 uint64 public constant RELAY_CONSTANT_OVERHEAD = 200_000; 97 98 /// @notice Numerator for dynamic overhead added to the base gas for a message. 99 uint64 public constant MIN_GAS_DYNAMIC_OVERHEAD_NUMERATOR = 64; 100 101 /// @notice Denominator for dynamic overhead added to the base gas for a message. 102 uint64 public constant MIN_GAS_DYNAMIC_OVERHEAD_DENOMINATOR = 63; 103 104 /// @notice Extra gas added to base gas for each byte of calldata in a message. 105 uint64 public constant MIN_GAS_CALLDATA_OVERHEAD = 16; 106 107 /// @notice Gas reserved for performing the external call in `relayMessage`. 108 uint64 public constant RELAY_CALL_OVERHEAD = 40_000; 109 110 /// @notice Gas reserved for finalizing the execution of `relayMessage` after the safe call. 111 uint64 public constant RELAY_RESERVED_GAS = 40_000; 112 113 /// @notice Gas reserved for the execution between the `hasMinGas` check and the external 114 /// call in `relayMessage`. 115 uint64 public constant RELAY_GAS_CHECK_BUFFER = 5_000; 116 117 /// @notice Mapping of message hashes to boolean receipt values. Note that a message will only 118 /// be present in this mapping if it has successfully been relayed on this chain, and 119 /// can therefore not be relayed again. 120 mapping(bytes32 => bool) public successfulMessages; 121 122 /// @notice Address of the sender of the currently executing message on the other chain. If the 123 /// value of this variable is the default value (0x00000000...dead) then no message is 124 /// currently being executed. Use the xDomainMessageSender getter which will throw an 125 /// error if this is the case. 126 address internal xDomainMsgSender; 127 128 /// @notice Nonce for the next message to be sent, without the message version applied. Use the 129 /// messageNonce getter which will insert the message version into the nonce to give you 130 /// the actual nonce to be used for the message. 131 uint240 internal msgNonce; 132 133 /// @notice Mapping of message hashes to a boolean if and only if the message has failed to be 134 /// executed at least once. A message will not be present in this mapping if it 135 /// successfully executed on the first attempt. 136 mapping(bytes32 => bool) public failedMessages; 137 138 /// @notice CrossDomainMessenger contract on the other chain. 139 /// @custom:network-specific 140 CrossDomainMessenger public otherMessenger; 141 142 /// @notice Reserve extra slots in the storage layout for future upgrades. 143 /// A gap size of 43 was chosen here, so that the first slot used in a child contract 144 /// would be 1 plus a multiple of 50. 145 uint256[43] private __gap; 146 147 /// @notice Emitted whenever a message is sent to the other chain. 148 /// @param target Address of the recipient of the message. 149 /// @param sender Address of the sender of the message. 150 /// @param message Message to trigger the recipient address with. 151 /// @param messageNonce Unique nonce attached to the message. 152 /// @param gasLimit Minimum gas limit that the message can be executed with. 153 event SentMessage(address indexed target, address sender, bytes message, uint256 messageNonce, uint256 gasLimit); 154 155 /// @notice Additional event data to emit, required as of Bedrock. Cannot be merged with the 156 /// SentMessage event without breaking the ABI of this contract, this is good enough. 157 /// @param sender Address of the sender of the message. 158 /// @param value ETH value sent along with the message to the recipient. 159 event SentMessageExtension1(address indexed sender, uint256 value); 160 161 /// @notice Emitted whenever a message is successfully relayed on this chain. 162 /// @param msgHash Hash of the message that was relayed. 163 event RelayedMessage(bytes32 indexed msgHash); 164 165 /// @notice Emitted whenever a message fails to be relayed on this chain. 166 /// @param msgHash Hash of the message that failed to be relayed. 167 event FailedRelayedMessage(bytes32 indexed msgHash); 168 169 /// @notice Sends a message to some target address on the other chain. Note that if the call 170 /// always reverts, then the message will be unrelayable, and any ETH sent will be 171 /// permanently locked. The same will occur if the target on the other chain is 172 /// considered unsafe (see the _isUnsafeTarget() function). 173 /// @param _target Target contract or wallet address. 174 /// @param _message Message to trigger the target address with. 175 /// @param _minGasLimit Minimum gas limit that the message can be executed with. 176 function sendMessage(address _target, bytes calldata _message, uint32 _minGasLimit) external payable { 177 // Triggers a message to the other messenger. Note that the amount of gas provided to the 178 // message is the amount of gas requested by the user PLUS the base gas value. We want to 179 // guarantee the property that the call to the target contract will always have at least 180 // the minimum gas limit specified by the user. 181 _sendMessage({ 182 _to: address(otherMessenger), 183 _gasLimit: baseGas(_message, _minGasLimit), 184 _value: msg.value, 185 _data: abi.encodeWithSelector( 186 this.relayMessage.selector, messageNonce(), msg.sender, _target, msg.value, _minGasLimit, _message 187 ) 188 }); 189 190 emit SentMessage(_target, msg.sender, _message, messageNonce(), _minGasLimit); 191 emit SentMessageExtension1(msg.sender, msg.value); 192 193 unchecked { 194 ++msgNonce; 195 } 196 } 197 198 /// @notice Relays a message that was sent by the other CrossDomainMessenger contract. Can only 199 /// be executed via cross-chain call from the other messenger OR if the message was 200 /// already received once and is currently being replayed. 201 /// @param _nonce Nonce of the message being relayed. 202 /// @param _sender Address of the user who sent the message. 203 /// @param _target Address that the message is targeted at. 204 /// @param _value ETH value to send with the message. 205 /// @param _minGasLimit Minimum amount of gas that the message can be executed with. 206 /// @param _message Message to send to the target. 207 function relayMessage( 208 uint256 _nonce, 209 address _sender, 210 address _target, 211 uint256 _value, 212 uint256 _minGasLimit, 213 bytes calldata _message 214 ) 215 external 216 payable 217 { 218 // On L1 this function will check the Portal for its paused status. 219 // On L2 this function should be a no-op, because paused will always return false. 220 require(paused() == false, "CrossDomainMessenger: paused"); 221 222 (, uint16 version) = Encoding.decodeVersionedNonce(_nonce); 223 require(version < 2, "CrossDomainMessenger: only version 0 or 1 messages are supported at this time"); 224 225 // If the message is version 0, then it's a migrated legacy withdrawal. We therefore need 226 // to check that the legacy version of the message has not already been relayed. 227 if (version == 0) { 228 bytes32 oldHash = Hashing.hashCrossDomainMessageV0(_target, _sender, _message, _nonce); 229 require(successfulMessages[oldHash] == false, "CrossDomainMessenger: legacy withdrawal already relayed"); 230 } 231 232 // We use the v1 message hash as the unique identifier for the message because it commits 233 // to the value and minimum gas limit of the message. 234 bytes32 versionedHash = 235 Hashing.hashCrossDomainMessageV1(_nonce, _sender, _target, _value, _minGasLimit, _message); 236 237 if (_isOtherMessenger()) { 238 // These properties should always hold when the message is first submitted (as 239 // opposed to being replayed). 240 assert(msg.value == _value); 241 assert(!failedMessages[versionedHash]); 242 } else { 243 require(msg.value == 0, "CrossDomainMessenger: value must be zero unless message is from a system address"); 244 245 require(failedMessages[versionedHash], "CrossDomainMessenger: message cannot be replayed"); 246 } 247 248 require( 249 _isUnsafeTarget(_target) == false, "CrossDomainMessenger: cannot send message to blocked system address" 250 ); 251 252 require(successfulMessages[versionedHash] == false, "CrossDomainMessenger: message has already been relayed"); 253 254 // If there is not enough gas left to perform the external call and finish the execution, 255 // return early and assign the message to the failedMessages mapping. 256 // We are asserting that we have enough gas to: 257 // 1. Call the target contract (_minGasLimit + RELAY_CALL_OVERHEAD + RELAY_GAS_CHECK_BUFFER) 258 // 1.a. The RELAY_CALL_OVERHEAD is included in `hasMinGas`. 259 // 2. Finish the execution after the external call (RELAY_RESERVED_GAS). 260 // 261 // If `xDomainMsgSender` is not the default L2 sender, this function 262 // is being re-entered. This marks the message as failed to allow it to be replayed. 263 if ( 264 !SafeCall.hasMinGas(_minGasLimit, RELAY_RESERVED_GAS + RELAY_GAS_CHECK_BUFFER) 265 || xDomainMsgSender != Constants.DEFAULT_L2_SENDER 266 ) { 267 failedMessages[versionedHash] = true; 268 emit FailedRelayedMessage(versionedHash); 269 270 // Revert in this case if the transaction was triggered by the estimation address. This 271 // should only be possible during gas estimation or we have bigger problems. Reverting 272 // here will make the behavior of gas estimation change such that the gas limit 273 // computed will be the amount required to relay the message, even if that amount is 274 // greater than the minimum gas limit specified by the user. 275 if (tx.origin == Constants.ESTIMATION_ADDRESS) { 276 revert("CrossDomainMessenger: failed to relay message"); 277 } 278 279 return; 280 } 281 282 xDomainMsgSender = _sender; 283 bool success = SafeCall.call(_target, gasleft() - RELAY_RESERVED_GAS, _value, _message); 284 xDomainMsgSender = Constants.DEFAULT_L2_SENDER; 285 286 if (success) { 287 // This check is identical to one above, but it ensures that the same message cannot be relayed 288 // twice, and adds a layer of protection against rentrancy. 289 assert(successfulMessages[versionedHash] == false); 290 successfulMessages[versionedHash] = true; 291 emit RelayedMessage(versionedHash); 292 } else { 293 failedMessages[versionedHash] = true; 294 emit FailedRelayedMessage(versionedHash); 295 296 // Revert in this case if the transaction was triggered by the estimation address. This 297 // should only be possible during gas estimation or we have bigger problems. Reverting 298 // here will make the behavior of gas estimation change such that the gas limit 299 // computed will be the amount required to relay the message, even if that amount is 300 // greater than the minimum gas limit specified by the user. 301 if (tx.origin == Constants.ESTIMATION_ADDRESS) { 302 revert("CrossDomainMessenger: failed to relay message"); 303 } 304 } 305 } 306 307 /// @notice Retrieves the address of the contract or wallet that initiated the currently 308 /// executing message on the other chain. Will throw an error if there is no message 309 /// currently being executed. Allows the recipient of a call to see who triggered it. 310 /// @return Address of the sender of the currently executing message on the other chain. 311 function xDomainMessageSender() external view returns (address) { 312 require( 313 xDomainMsgSender != Constants.DEFAULT_L2_SENDER, "CrossDomainMessenger: xDomainMessageSender is not set" 314 ); 315 316 return xDomainMsgSender; 317 } 318 319 /// @notice Retrieves the address of the paired CrossDomainMessenger contract on the other chain 320 /// Public getter is legacy and will be removed in the future. Use `otherMessenger()` instead. 321 /// @return CrossDomainMessenger contract on the other chain. 322 /// @custom:legacy 323 function OTHER_MESSENGER() public view returns (CrossDomainMessenger) { 324 return otherMessenger; 325 } 326 327 /// @notice Retrieves the next message nonce. Message version will be added to the upper two 328 /// bytes of the message nonce. Message version allows us to treat messages as having 329 /// different structures. 330 /// @return Nonce of the next message to be sent, with added message version. 331 function messageNonce() public view returns (uint256) { 332 return Encoding.encodeVersionedNonce(msgNonce, MESSAGE_VERSION); 333 } 334 335 /// @notice Computes the amount of gas required to guarantee that a given message will be 336 /// received on the other chain without running out of gas. Guaranteeing that a message 337 /// will not run out of gas is important because this ensures that a message can always 338 /// be replayed on the other chain if it fails to execute completely. 339 /// @param _message Message to compute the amount of required gas for. 340 /// @param _minGasLimit Minimum desired gas limit when message goes to target. 341 /// @return Amount of gas required to guarantee message receipt. 342 function baseGas(bytes calldata _message, uint32 _minGasLimit) public pure returns (uint64) { 343 return 344 // Constant overhead 345 RELAY_CONSTANT_OVERHEAD 346 // Calldata overhead 347 + (uint64(_message.length) * MIN_GAS_CALLDATA_OVERHEAD) 348 // Dynamic overhead (EIP-150) 349 + ((_minGasLimit * MIN_GAS_DYNAMIC_OVERHEAD_NUMERATOR) / MIN_GAS_DYNAMIC_OVERHEAD_DENOMINATOR) 350 // Gas reserved for the worst-case cost of 3/5 of the `CALL` opcode's dynamic gas 351 // factors. (Conservative) 352 + RELAY_CALL_OVERHEAD 353 // Relay reserved gas (to ensure execution of `relayMessage` completes after the 354 // subcontext finishes executing) (Conservative) 355 + RELAY_RESERVED_GAS 356 // Gas reserved for the execution between the `hasMinGas` check and the `CALL` 357 // opcode. (Conservative) 358 + RELAY_GAS_CHECK_BUFFER; 359 } 360 361 /// @notice Initializer. 362 /// @param _otherMessenger CrossDomainMessenger contract on the other chain. 363 function __CrossDomainMessenger_init(CrossDomainMessenger _otherMessenger) internal onlyInitializing { 364 // We only want to set the xDomainMsgSender to the default value if it hasn't been initialized yet, 365 // meaning that this is a fresh contract deployment. 366 // This prevents resetting the xDomainMsgSender to the default value during an upgrade, which would enable 367 // a reentrant withdrawal to sandwhich the upgrade replay a withdrawal twice. 368 if (xDomainMsgSender == address(0)) { 369 xDomainMsgSender = Constants.DEFAULT_L2_SENDER; 370 } 371 otherMessenger = _otherMessenger; 372 } 373 374 /// @notice Sends a low-level message to the other messenger. Needs to be implemented by child 375 /// contracts because the logic for this depends on the network where the messenger is 376 /// being deployed. 377 /// @param _to Recipient of the message on the other chain. 378 /// @param _gasLimit Minimum gas limit the message can be executed with. 379 /// @param _value Amount of ETH to send with the message. 380 /// @param _data Message data. 381 function _sendMessage(address _to, uint64 _gasLimit, uint256 _value, bytes memory _data) internal virtual; 382 383 /// @notice Checks whether the message is coming from the other messenger. Implemented by child 384 /// contracts because the logic for this depends on the network where the messenger is 385 /// being deployed. 386 /// @return Whether the message is coming from the other messenger. 387 function _isOtherMessenger() internal view virtual returns (bool); 388 389 /// @notice Checks whether a given call target is a system address that could cause the 390 /// messenger to peform an unsafe action. This is NOT a mechanism for blocking user 391 /// addresses. This is ONLY used to prevent the execution of messages to specific 392 /// system addresses that could cause security issues, e.g., having the 393 /// CrossDomainMessenger send messages to itself. 394 /// @param _target Address of the contract to check. 395 /// @return Whether or not the address is an unsafe system address. 396 function _isUnsafeTarget(address _target) internal view virtual returns (bool); 397 398 /// @notice This function should return true if the contract is paused. 399 /// On L1 this function will check the SuperchainConfig for its paused status. 400 /// On L2 this function should be a no-op. 401 /// @return Whether or not the contract is paused. 402 function paused() public view virtual returns (bool) { 403 return false; 404 } 405 }