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 }