github.com/ethereum-optimism/optimism@v1.7.2/packages/contracts-bedrock/test/L2/CrossDomainOwnable3.t.sol (about) 1 // SPDX-License-Identifier: MIT 2 pragma solidity 0.8.15; 3 4 // Testing utilities 5 import { Bridge_Initializer } from "test/setup/Bridge_Initializer.sol"; 6 7 // Libraries 8 import { Hashing } from "src/libraries/Hashing.sol"; 9 import { Encoding } from "src/libraries/Encoding.sol"; 10 import { Bytes32AddressLib } from "@rari-capital/solmate/src/utils/Bytes32AddressLib.sol"; 11 12 // Target contract dependencies 13 import { AddressAliasHelper } from "src/vendor/AddressAliasHelper.sol"; 14 15 // Target contract 16 import { CrossDomainOwnable3 } from "src/L2/CrossDomainOwnable3.sol"; 17 18 contract XDomainSetter3 is CrossDomainOwnable3 { 19 uint256 public value; 20 21 function set(uint256 _value) external onlyOwner { 22 value = _value; 23 } 24 } 25 26 contract CrossDomainOwnable3_Test is Bridge_Initializer { 27 XDomainSetter3 setter; 28 29 /// @dev CrossDomainOwnable3.sol transferOwnership event 30 event OwnershipTransferred(address indexed previousOwner, address indexed newOwner, bool isLocal); 31 32 /// @dev Sets up the test suite. 33 function setUp() public override { 34 super.setUp(); 35 vm.prank(alice); 36 setter = new XDomainSetter3(); 37 } 38 39 /// @dev Tests that the constructor sets the correct variables. 40 function test_constructor_succeeds() public { 41 assertEq(setter.owner(), alice); 42 assertEq(setter.isLocal(), true); 43 } 44 45 /// @dev Tests that `set` reverts when the caller is not the owner. 46 function test_localOnlyOwner_notOwner_reverts() public { 47 vm.prank(bob); 48 vm.expectRevert("CrossDomainOwnable3: caller is not the owner"); 49 setter.set(1); 50 } 51 52 /// @dev Tests that `transferOwnership` reverts when the caller is not the owner. 53 function test_transferOwnership_notOwner_reverts() public { 54 vm.prank(bob); 55 vm.expectRevert("CrossDomainOwnable3: caller is not the owner"); 56 setter.transferOwnership({ _owner: bob, _isLocal: true }); 57 } 58 59 /// @dev Tests that the `XDomainSetter3` contract reverts after the ownership 60 /// has been transferred to a new owner. 61 function test_crossDomainOnlyOwner_notOwner_reverts() public { 62 vm.expectEmit(true, true, true, true); 63 64 // OpenZeppelin Ownable.sol transferOwnership event 65 emit OwnershipTransferred(alice, alice); 66 67 // CrossDomainOwnable3.sol transferOwnership event 68 emit OwnershipTransferred(alice, alice, false); 69 70 vm.prank(setter.owner()); 71 setter.transferOwnership({ _owner: alice, _isLocal: false }); 72 73 // set the xDomainMsgSender storage slot 74 bytes32 key = bytes32(uint256(204)); 75 bytes32 value = Bytes32AddressLib.fillLast12Bytes(bob); 76 vm.store(address(l2CrossDomainMessenger), key, value); 77 78 vm.prank(address(l2CrossDomainMessenger)); 79 vm.expectRevert("CrossDomainOwnable3: caller is not the owner"); 80 setter.set(1); 81 } 82 83 /// @dev Tests that a relayed message to the `XDomainSetter3` contract reverts 84 /// after its ownership has been transferred to a new owner. 85 function test_crossDomainOnlyOwner_notOwner2_reverts() public { 86 vm.expectEmit(true, true, true, true); 87 88 // OpenZeppelin Ownable.sol transferOwnership event 89 emit OwnershipTransferred(alice, alice); 90 91 // CrossDomainOwnable3.sol transferOwnership event 92 emit OwnershipTransferred(alice, alice, false); 93 94 vm.prank(setter.owner()); 95 setter.transferOwnership({ _owner: alice, _isLocal: false }); 96 97 assertEq(setter.isLocal(), false); 98 99 uint240 nonce = 0; 100 address sender = bob; 101 address target = address(setter); 102 uint256 value = 0; 103 uint256 minGasLimit = 0; 104 bytes memory message = abi.encodeWithSelector(XDomainSetter3.set.selector, 1); 105 106 bytes32 hash = Hashing.hashCrossDomainMessage( 107 Encoding.encodeVersionedNonce(nonce, 1), sender, target, value, minGasLimit, message 108 ); 109 110 // It should be a failed message. The revert is caught, 111 // so we cannot expectRevert here. 112 vm.expectEmit(true, true, true, true, address(l2CrossDomainMessenger)); 113 emit FailedRelayedMessage(hash); 114 115 vm.prank(AddressAliasHelper.applyL1ToL2Alias(address(l1CrossDomainMessenger))); 116 l2CrossDomainMessenger.relayMessage( 117 Encoding.encodeVersionedNonce(nonce, 1), sender, target, value, minGasLimit, message 118 ); 119 120 assertEq(setter.value(), 0); 121 } 122 123 /// @dev Tests that the `XDomainSetter3` contract reverts for a non-messenger 124 /// caller after the ownership has been transferred to a new owner. 125 function test_crossDomainOnlyOwner_notMessenger_reverts() public { 126 vm.expectEmit(true, true, true, true); 127 128 // OpenZeppelin Ownable.sol transferOwnership event 129 emit OwnershipTransferred(alice, alice); 130 131 // CrossDomainOwnable3.sol transferOwnership event 132 emit OwnershipTransferred(alice, alice, false); 133 134 vm.prank(setter.owner()); 135 setter.transferOwnership({ _owner: alice, _isLocal: false }); 136 137 vm.prank(bob); 138 vm.expectRevert("CrossDomainOwnable3: caller is not the messenger"); 139 setter.set(1); 140 } 141 142 /// @dev Tests that `transferOwnership` reverts for ownership transfers 143 /// to the zero address when set locally. 144 function test_transferOwnership_zeroAddress_reverts() public { 145 vm.prank(setter.owner()); 146 vm.expectRevert("CrossDomainOwnable3: new owner is the zero address"); 147 setter.transferOwnership({ _owner: address(0), _isLocal: true }); 148 } 149 150 /// @dev Tests that `transferOwnership` reverts for ownership transfers 151 /// to the zero address. 152 function test_transferOwnership_noLocalZeroAddress_reverts() public { 153 vm.prank(setter.owner()); 154 vm.expectRevert("Ownable: new owner is the zero address"); 155 setter.transferOwnership(address(0)); 156 } 157 158 /// @dev Tests that `onlyOwner` allows the owner to call a protected 159 /// function locally. 160 function test_localOnlyOwner_succeeds() public { 161 assertEq(setter.isLocal(), true); 162 vm.prank(setter.owner()); 163 setter.set(1); 164 assertEq(setter.value(), 1); 165 } 166 167 /// @dev Tests that `transferOwnership` succeeds when the caller is the 168 /// owner and the ownership is transferred locally. 169 function test_localTransferOwnership_succeeds() public { 170 vm.expectEmit(true, true, true, true, address(setter)); 171 emit OwnershipTransferred(alice, bob); 172 emit OwnershipTransferred(alice, bob, true); 173 174 vm.prank(setter.owner()); 175 setter.transferOwnership({ _owner: bob, _isLocal: true }); 176 177 assertEq(setter.isLocal(), true); 178 179 vm.prank(bob); 180 setter.set(2); 181 assertEq(setter.value(), 2); 182 } 183 184 /// @dev The existing transferOwnership(address) method still 185 /// exists on the contract. 186 function test_transferOwnershipNoLocal_succeeds() public { 187 bool isLocal = setter.isLocal(); 188 189 vm.expectEmit(true, true, true, true, address(setter)); 190 emit OwnershipTransferred(alice, bob); 191 192 vm.prank(setter.owner()); 193 setter.transferOwnership(bob); 194 195 // isLocal has not changed 196 assertEq(setter.isLocal(), isLocal); 197 198 vm.prank(bob); 199 setter.set(2); 200 assertEq(setter.value(), 2); 201 } 202 203 /// @dev Tests that `transferOwnership` succeeds when the caller is the 204 /// owner and the ownership is transferred non-locally. 205 function test_crossDomainTransferOwnership_succeeds() public { 206 vm.expectEmit(true, true, true, true, address(setter)); 207 emit OwnershipTransferred(alice, bob); 208 emit OwnershipTransferred(alice, bob, false); 209 210 vm.prank(setter.owner()); 211 setter.transferOwnership({ _owner: bob, _isLocal: false }); 212 213 assertEq(setter.isLocal(), false); 214 215 // Simulate the L2 execution where the call is coming from 216 // the L1CrossDomainMessenger 217 vm.prank(AddressAliasHelper.applyL1ToL2Alias(address(l1CrossDomainMessenger))); 218 l2CrossDomainMessenger.relayMessage( 219 Encoding.encodeVersionedNonce(1, 1), 220 bob, 221 address(setter), 222 0, 223 0, 224 abi.encodeWithSelector(XDomainSetter3.set.selector, 2) 225 ); 226 227 assertEq(setter.value(), 2); 228 } 229 }