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

     1  // SPDX-License-Identifier: MIT
     2  pragma solidity 0.8.15;
     3  
     4  import { Test } from "forge-std/Test.sol";
     5  import { DelayedVetoable } from "src/L1/DelayedVetoable.sol";
     6  
     7  contract DelayedVetoable_Init is Test {
     8      error Unauthorized(address expected, address actual);
     9      error ForwardingEarly();
    10  
    11      event Initiated(bytes32 indexed callHash, bytes data);
    12      event Forwarded(bytes32 indexed callHash, bytes data);
    13      event Vetoed(bytes32 indexed callHash, bytes data);
    14  
    15      address target;
    16      address initiator;
    17      address vetoer;
    18      uint256 operatingDelay = 14 days;
    19      DelayedVetoable delayedVetoable;
    20  
    21      function setUp() public {
    22          initiator = makeAddr("initiator");
    23          vetoer = makeAddr("vetoer");
    24          target = makeAddr("target");
    25          vm.deal(initiator, 10000 ether);
    26          vm.deal(vetoer, 10000 ether);
    27  
    28          delayedVetoable = new DelayedVetoable({
    29              initiator_: initiator,
    30              vetoer_: vetoer,
    31              target_: address(target),
    32              operatingDelay_: operatingDelay
    33          });
    34  
    35          // Most tests will use the operating delay, so we call as the initiator with null data
    36          // to set the delay. For tests that need to use the initial zero delay, we'll modify the
    37          // value in storage.
    38          vm.prank(initiator);
    39          (bool success,) = address(delayedVetoable).call(hex"");
    40          assertTrue(success);
    41      }
    42  
    43      /// @dev This function is used to prevent initiating the delay unintentionally.
    44      ///      It should only be used on tests prior to the delay being activated.
    45      /// @param data The data to be used in the call.
    46      function assumeNonzeroData(bytes memory data) internal pure {
    47          vm.assume(data.length > 0);
    48      }
    49  
    50      /// @dev This function is used to ensure that the data does not clash with the queuedAt function selector.
    51      /// @param data The data to be used in the call.
    52      function assumeNoClash(bytes calldata data) internal pure {
    53          if (data.length >= 4) {
    54              vm.assume(bytes4(data[0:4]) != bytes4(keccak256("queuedAt(bytes32)")));
    55          }
    56      }
    57  }
    58  
    59  contract DelayedVetoable_Getters_Test is DelayedVetoable_Init {
    60      /// @dev The getters return the expected values when called by the zero address.
    61      function test_getters() external {
    62          vm.startPrank(address(0));
    63          assertEq(delayedVetoable.initiator(), initiator);
    64          assertEq(delayedVetoable.vetoer(), vetoer);
    65          assertEq(delayedVetoable.target(), target);
    66          assertEq(delayedVetoable.delay(), operatingDelay);
    67          assertEq(delayedVetoable.queuedAt(keccak256(abi.encode(0))), 0);
    68      }
    69  }
    70  
    71  contract DelayedVetoable_Getters_TestFail is DelayedVetoable_Init {
    72      /// @dev Check that getter calls from unauthorized entities will revert.
    73      function test_getters_notZeroAddress_reverts() external {
    74          vm.expectRevert(abi.encodeWithSelector(Unauthorized.selector, initiator, address(this)));
    75          delayedVetoable.initiator();
    76          vm.expectRevert(abi.encodeWithSelector(Unauthorized.selector, initiator, address(this)));
    77          delayedVetoable.vetoer();
    78          vm.expectRevert(abi.encodeWithSelector(Unauthorized.selector, initiator, address(this)));
    79          delayedVetoable.target();
    80          vm.expectRevert(abi.encodeWithSelector(Unauthorized.selector, initiator, address(this)));
    81          delayedVetoable.delay();
    82          vm.expectRevert(abi.encodeWithSelector(Unauthorized.selector, initiator, address(this)));
    83          delayedVetoable.queuedAt(keccak256(abi.encode(0)));
    84      }
    85  }
    86  
    87  contract DelayedVetoable_HandleCall_Test is DelayedVetoable_Init {
    88      /// @dev A call can be initiated by the initiator.
    89      function testFuzz_handleCall_initiation_succeeds(bytes calldata data) external {
    90          assumeNoClash(data);
    91          vm.expectEmit(true, false, false, true, address(delayedVetoable));
    92          emit Initiated(keccak256(data), data);
    93  
    94          vm.prank(initiator);
    95          (bool success,) = address(delayedVetoable).call(data);
    96          assertTrue(success);
    97      }
    98  
    99      /// @dev The delay is inititially set to zero and the call is immediately forwarded.
   100      function testFuzz_handleCall_initialForwardingImmediately_succeeds(
   101          bytes calldata inData,
   102          bytes calldata outData
   103      )
   104          external
   105      {
   106          assumeNonzeroData(inData);
   107          assumeNoClash(inData);
   108  
   109          // Reset the delay to zero
   110          vm.store(address(delayedVetoable), bytes32(uint256(0)), bytes32(uint256(0)));
   111  
   112          vm.mockCall(target, inData, outData);
   113          vm.expectEmit(true, false, false, true, address(delayedVetoable));
   114          vm.expectCall({ callee: target, data: inData });
   115          emit Forwarded(keccak256(inData), inData);
   116          vm.prank(initiator);
   117          (bool success, bytes memory returnData) = address(delayedVetoable).call(inData);
   118          assertTrue(success);
   119          assertEq(returnData, outData);
   120  
   121          // Check that the callHash is not stored for future forwarding
   122          bytes32 callHash = keccak256(inData);
   123          vm.prank(address(0));
   124          assertEq(delayedVetoable.queuedAt(callHash), 0);
   125      }
   126  
   127      /// @dev Calls are not forwarded until the delay has passed.
   128      function testFuzz_handleCall_forwardingWithDelay_succeeds(bytes calldata data) external {
   129          assumeNonzeroData(data);
   130          assumeNoClash(data);
   131  
   132          vm.prank(initiator);
   133          (bool success,) = address(delayedVetoable).call(data);
   134  
   135          // Check that the call is in the _queuedAt mapping
   136          bytes32 callHash = keccak256(data);
   137          vm.prank(address(0));
   138          assertEq(delayedVetoable.queuedAt(callHash), block.timestamp);
   139  
   140          vm.warp(block.timestamp + operatingDelay);
   141          vm.expectEmit(true, false, false, true, address(delayedVetoable));
   142          emit Forwarded(keccak256(data), data);
   143  
   144          vm.expectCall({ callee: target, data: data });
   145          (success,) = address(delayedVetoable).call(data);
   146          assertTrue(success);
   147      }
   148  }
   149  
   150  contract DelayedVetoable_HandleCall_TestFail is DelayedVetoable_Init {
   151      /// @dev Only the initiator can initiate a call.
   152      function test_handleCall_unauthorizedInitiation_reverts() external {
   153          vm.expectRevert(abi.encodeWithSelector(DelayedVetoable.Unauthorized.selector, initiator, address(this)));
   154          (bool revertsAsExpected,) = address(delayedVetoable).call(hex"00001234");
   155          assertTrue(revertsAsExpected);
   156      }
   157  
   158      /// @dev The call cannot be forwarded until the delay has passed.
   159      function testFuzz_handleCall_forwardingTooSoon_reverts(bytes calldata data) external {
   160          assumeNoClash(data);
   161          vm.prank(initiator);
   162          (bool success,) = address(delayedVetoable).call(data);
   163          assertTrue(success);
   164  
   165          vm.expectRevert(DelayedVetoable.ForwardingEarly.selector);
   166          (bool revertsAsExpected,) = address(delayedVetoable).call(data);
   167          assertTrue(revertsAsExpected);
   168      }
   169  
   170      /// @dev The call cannot be forwarded a second time.
   171      function testFuzz_handleCall_forwardingTwice_reverts(bytes calldata data) external {
   172          assumeNoClash(data);
   173  
   174          // Initiate the call
   175          vm.prank(initiator);
   176          (bool success,) = address(delayedVetoable).call(data);
   177          assertTrue(success);
   178  
   179          vm.warp(block.timestamp + operatingDelay);
   180          vm.expectEmit(true, false, false, true, address(delayedVetoable));
   181          emit Forwarded(keccak256(data), data);
   182  
   183          // Forward the call
   184          vm.expectCall({ callee: target, data: data });
   185          (success,) = address(delayedVetoable).call(data);
   186          assertTrue(success);
   187  
   188          // Attempt to forward the same call again.
   189          vm.expectRevert(abi.encodeWithSelector(DelayedVetoable.Unauthorized.selector, initiator, address(this)));
   190          (bool revertsAsExpected,) = address(delayedVetoable).call(data);
   191          assertTrue(revertsAsExpected);
   192      }
   193  
   194      /// @dev If the target reverts, it is bubbled up.
   195      function testFuzz_handleCall_forwardingTargetReverts_reverts(
   196          bytes calldata inData,
   197          bytes calldata outData
   198      )
   199          external
   200      {
   201          assumeNoClash(inData);
   202  
   203          // Initiate the call
   204          vm.prank(initiator);
   205          (bool success,) = address(delayedVetoable).call(inData);
   206          assertTrue(success);
   207  
   208          vm.warp(block.timestamp + operatingDelay);
   209          vm.expectEmit(true, false, false, true, address(delayedVetoable));
   210          emit Forwarded(keccak256(inData), inData);
   211  
   212          vm.mockCallRevert(target, inData, outData);
   213  
   214          // Forward the call
   215          vm.expectRevert(outData);
   216          (bool revertsAsExpected,) = address(delayedVetoable).call(inData);
   217          assertTrue(revertsAsExpected);
   218      }
   219  
   220      function testFuzz_handleCall_forwardingTargetRetValue_succeeds(
   221          bytes calldata inData,
   222          bytes calldata outData
   223      )
   224          external
   225      {
   226          assumeNoClash(inData);
   227  
   228          // Initiate the call
   229          vm.prank(initiator);
   230          (bool success,) = address(delayedVetoable).call(inData);
   231          assertTrue(success);
   232  
   233          vm.warp(block.timestamp + operatingDelay);
   234          vm.expectEmit(true, false, false, true, address(delayedVetoable));
   235          emit Forwarded(keccak256(inData), inData);
   236  
   237          vm.mockCall(target, inData, outData);
   238  
   239          // Forward the call
   240          (bool success2, bytes memory retData) = address(delayedVetoable).call(inData);
   241          assertTrue(success2);
   242          assertEq(keccak256(retData), keccak256(outData));
   243      }
   244  
   245      /// @dev A test documenting the single instance in which the contract is not 'transparent' to the initiator.
   246      function testFuzz_handleCall_queuedAtClash_reverts() external {
   247          // This will get us calldata with the same function selector as the queuedAt function, but
   248          // with the incorrect input data length.
   249          bytes memory inData = abi.encodePacked(keccak256("queuedAt(bytes32)"));
   250  
   251          // Reset the delay to zero
   252          vm.store(address(delayedVetoable), bytes32(uint256(0)), bytes32(uint256(0)));
   253  
   254          vm.prank(initiator);
   255          vm.expectRevert(bytes(""));
   256          (bool revertsAsExpected,) = address(delayedVetoable).call(inData);
   257          assertTrue(revertsAsExpected);
   258      }
   259  }