github.com/ethereum-optimism/optimism@v1.7.2/packages/contracts-bedrock/test/universal/CrossDomainMessenger.t.sol (about)

     1  // SPDX-License-Identifier: MIT
     2  pragma solidity 0.8.15;
     3  
     4  // Testing utilities
     5  import { Test } from "forge-std/Test.sol";
     6  import { Bridge_Initializer } from "test/setup/Bridge_Initializer.sol";
     7  import { CallerCaller, Reverter } from "test/mocks/Callers.sol";
     8  
     9  // Libraries
    10  import { Predeploys } from "src/libraries/Predeploys.sol";
    11  import { Hashing } from "src/libraries/Hashing.sol";
    12  import { Encoding } from "src/libraries/Encoding.sol";
    13  
    14  import { L1CrossDomainMessenger } from "src/L1/L1CrossDomainMessenger.sol";
    15  
    16  // CrossDomainMessenger_Test is for testing functionality which is common to both the L1 and L2
    17  // CrossDomainMessenger contracts. For simplicity, we use the L1 Messenger as the test contract.
    18  contract CrossDomainMessenger_BaseGas_Test is Bridge_Initializer {
    19      /// @dev Ensure that baseGas passes for the max value of _minGasLimit,
    20      ///      this is about 4 Billion.
    21      function test_baseGas_succeeds() external view {
    22          l1CrossDomainMessenger.baseGas(hex"ff", type(uint32).max);
    23      }
    24  
    25      /// @dev Fuzz for other values which might cause a revert in baseGas.
    26      function testFuzz_baseGas_succeeds(uint32 _minGasLimit) external view {
    27          l1CrossDomainMessenger.baseGas(hex"ff", _minGasLimit);
    28      }
    29  
    30      /// @notice The baseGas function should always return a value greater than
    31      ///         or equal to the minimum gas limit value on the OptimismPortal.
    32      ///         This guarantees that the messengers will always pass sufficient
    33      ///         gas to the OptimismPortal.
    34      function testFuzz_baseGas_portalMinGasLimit_succeeds(bytes memory _data, uint32 _minGasLimit) external {
    35          vm.assume(_data.length <= type(uint64).max);
    36          uint64 baseGas = l1CrossDomainMessenger.baseGas(_data, _minGasLimit);
    37          uint64 minGasLimit = optimismPortal.minimumGasLimit(uint64(_data.length));
    38          assertTrue(baseGas >= minGasLimit);
    39      }
    40  }
    41  
    42  /// @title ExternalRelay
    43  /// @notice A mock external contract called via the SafeCall inside
    44  ///         the CrossDomainMessenger's `relayMessage` function.
    45  contract ExternalRelay is Test {
    46      address internal op;
    47      address internal fuzzedSender;
    48      L1CrossDomainMessenger internal l1CrossDomainMessenger;
    49  
    50      event FailedRelayedMessage(bytes32 indexed msgHash);
    51  
    52      constructor(L1CrossDomainMessenger _l1Messenger, address _op) {
    53          l1CrossDomainMessenger = _l1Messenger;
    54          op = _op;
    55      }
    56  
    57      /// @notice Internal helper function to relay a message and perform assertions.
    58      function _internalRelay(address _innerSender) internal {
    59          address initialSender = l1CrossDomainMessenger.xDomainMessageSender();
    60  
    61          bytes memory callMessage = getCallData();
    62  
    63          bytes32 hash = Hashing.hashCrossDomainMessage({
    64              _nonce: Encoding.encodeVersionedNonce({ _nonce: 0, _version: 1 }),
    65              _sender: _innerSender,
    66              _target: address(this),
    67              _value: 0,
    68              _gasLimit: 0,
    69              _data: callMessage
    70          });
    71  
    72          vm.expectEmit(true, true, true, true);
    73          emit FailedRelayedMessage(hash);
    74  
    75          vm.prank(address(op));
    76          l1CrossDomainMessenger.relayMessage({
    77              _nonce: Encoding.encodeVersionedNonce({ _nonce: 0, _version: 1 }),
    78              _sender: _innerSender,
    79              _target: address(this),
    80              _value: 0,
    81              _minGasLimit: 0,
    82              _message: callMessage
    83          });
    84  
    85          assertTrue(l1CrossDomainMessenger.failedMessages(hash));
    86          assertFalse(l1CrossDomainMessenger.successfulMessages(hash));
    87          assertEq(initialSender, l1CrossDomainMessenger.xDomainMessageSender());
    88      }
    89  
    90      /// @notice externalCallWithMinGas is called by the CrossDomainMessenger.
    91      function externalCallWithMinGas() external payable {
    92          for (uint256 i = 0; i < 10; i++) {
    93              address _innerSender;
    94              unchecked {
    95                  _innerSender = address(uint160(uint256(uint160(fuzzedSender)) + i));
    96              }
    97              _internalRelay(_innerSender);
    98          }
    99      }
   100  
   101      /// @notice Helper function to get the callData for an `externalCallWithMinGas
   102      function getCallData() public pure returns (bytes memory) {
   103          return abi.encodeWithSelector(ExternalRelay.externalCallWithMinGas.selector);
   104      }
   105  
   106      /// @notice Helper function to set the fuzzed sender
   107      function setFuzzedSender(address _fuzzedSender) public {
   108          fuzzedSender = _fuzzedSender;
   109      }
   110  }
   111  
   112  /// @title CrossDomainMessenger_RelayMessage_Test
   113  /// @notice Fuzz tests re-entrancy into the CrossDomainMessenger relayMessage function.
   114  contract CrossDomainMessenger_RelayMessage_Test is Bridge_Initializer {
   115      // Storage slot of the l2Sender
   116      uint256 constant senderSlotIndex = 50;
   117  
   118      ExternalRelay public er;
   119  
   120      function setUp() public override {
   121          super.setUp();
   122          er = new ExternalRelay(l1CrossDomainMessenger, address(optimismPortal));
   123      }
   124  
   125      /// @dev This test mocks an OptimismPortal call to the L1CrossDomainMessenger via
   126      ///      the relayMessage function. The relayMessage function will then use SafeCall's
   127      ///      callWithMinGas to call the target with call data packed in the callMessage.
   128      ///      For this test, the callWithMinGas will call the mock ExternalRelay test contract
   129      ///      defined above, executing the externalCallWithMinGas function which will try to
   130      ///      re-enter the CrossDomainMessenger's relayMessage function, resulting in that message
   131      ///      being recorded as failed.
   132      function testFuzz_relayMessageReenter_succeeds(address _sender, uint256 _gasLimit) external {
   133          vm.assume(_sender != Predeploys.L2_CROSS_DOMAIN_MESSENGER);
   134          address sender = Predeploys.L2_CROSS_DOMAIN_MESSENGER;
   135  
   136          er.setFuzzedSender(_sender);
   137          address target = address(er);
   138          bytes memory callMessage = er.getCallData();
   139  
   140          vm.expectCall(target, callMessage);
   141  
   142          uint64 gasLimit = uint64(bound(_gasLimit, 0, 30_000_000));
   143  
   144          bytes32 hash = Hashing.hashCrossDomainMessage({
   145              _nonce: Encoding.encodeVersionedNonce({ _nonce: 0, _version: 1 }),
   146              _sender: sender,
   147              _target: target,
   148              _value: 0,
   149              _gasLimit: gasLimit,
   150              _data: callMessage
   151          });
   152  
   153          // set the value of op.l2Sender() to be the L2 Cross Domain Messenger.
   154          vm.store(address(optimismPortal), bytes32(senderSlotIndex), bytes32(abi.encode(sender)));
   155          vm.prank(address(optimismPortal));
   156          l1CrossDomainMessenger.relayMessage({
   157              _nonce: Encoding.encodeVersionedNonce({ _nonce: 0, _version: 1 }),
   158              _sender: sender,
   159              _target: target,
   160              _value: 0,
   161              _minGasLimit: gasLimit,
   162              _message: callMessage
   163          });
   164  
   165          assertTrue(l1CrossDomainMessenger.successfulMessages(hash));
   166          assertEq(l1CrossDomainMessenger.failedMessages(hash), false);
   167  
   168          // Ensures that the `xDomainMsgSender` is set back to `Predeploys.L2_CROSS_DOMAIN_MESSENGER`
   169          vm.expectRevert("CrossDomainMessenger: xDomainMessageSender is not set");
   170          l1CrossDomainMessenger.xDomainMessageSender();
   171      }
   172  }