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

     1  // SPDX-License-Identifier: MIT
     2  pragma solidity 0.8.15;
     3  
     4  import { ISemver } from "src/universal/ISemver.sol";
     5  
     6  /// @title DelayedVetoable
     7  /// @notice This contract enables a delay before a call is forwarded to a target contract, and during the delay period
     8  ///         the call can be vetoed by the authorized vetoer.
     9  ///         This contract does not support value transfers, only data is forwarded.
    10  ///         Additionally, this contract cannot be used to forward calls with data beginning with the function selector
    11  ///         of the queuedAt(bytes32) function. This is because of input validation checks which solidity performs at
    12  ///         runtime on functions which take an argument.
    13  contract DelayedVetoable is ISemver {
    14      /// @notice Error for when attempting to forward too early.
    15      error ForwardingEarly();
    16  
    17      /// @notice Error for unauthorized calls.
    18      error Unauthorized(address expected, address actual);
    19  
    20      /// @notice An event that is emitted when the delay is activated.
    21      /// @param delay The delay that was activated.
    22      event DelayActivated(uint256 delay);
    23  
    24      /// @notice An event that is emitted when a call is initiated.
    25      /// @param callHash The hash of the call data.
    26      /// @param data The data of the initiated call.
    27      event Initiated(bytes32 indexed callHash, bytes data);
    28  
    29      /// @notice An event that is emitted each time a call is forwarded.
    30      /// @param callHash The hash of the call data.
    31      /// @param data The data forwarded to the target.
    32      event Forwarded(bytes32 indexed callHash, bytes data);
    33  
    34      /// @notice An event that is emitted each time a call is vetoed.
    35      /// @param callHash The hash of the call data.
    36      /// @param data The data forwarded to the target.
    37      event Vetoed(bytes32 indexed callHash, bytes data);
    38  
    39      /// @notice The address that all calls are forwarded to after the delay.
    40      address internal immutable TARGET;
    41  
    42      /// @notice The address that can veto a call.
    43      address internal immutable VETOER;
    44  
    45      /// @notice The address that can initiate a call.
    46      address internal immutable INITIATOR;
    47  
    48      /// @notice The delay which will be set after the initial system deployment is completed.
    49      uint256 internal immutable OPERATING_DELAY;
    50  
    51      /// @notice The current amount of time to wait before forwarding a call.
    52      uint256 internal _delay;
    53  
    54      /// @notice The time that a call was initiated.
    55      mapping(bytes32 => uint256) internal _queuedAt;
    56  
    57      /// @notice A modifier that reverts if not called by the vetoer or by address(0) to allow
    58      ///         eth_call to interact with this proxy without needing to use low-level storage
    59      ///         inspection. We assume that nobody is able to trigger calls from address(0) during
    60      ///         normal EVM execution.
    61      modifier readOrHandle() {
    62          if (msg.sender == address(0)) {
    63              _;
    64          } else {
    65              // This WILL halt the call frame on completion.
    66              _handleCall();
    67          }
    68      }
    69  
    70      /// @notice Semantic version.
    71      /// @custom:semver 1.0.0
    72      string public constant version = "1.0.0";
    73  
    74      /// @notice Sets the target admin during contract deployment.
    75      /// @param vetoer_ Address of the vetoer.
    76      /// @param initiator_ Address of the initiator.
    77      /// @param target_ Address of the target.
    78      /// @param operatingDelay_ Time to delay when the system is operational.
    79      constructor(address vetoer_, address initiator_, address target_, uint256 operatingDelay_) {
    80          // Note that the _delay value is not set here. Having an initial delay of 0 is helpful
    81          // during the deployment of a new system.
    82          VETOER = vetoer_;
    83          INITIATOR = initiator_;
    84          TARGET = target_;
    85          OPERATING_DELAY = operatingDelay_;
    86      }
    87  
    88      /// @notice Gets the initiator
    89      /// @return initiator_ Initiator address.
    90      function initiator() external virtual readOrHandle returns (address initiator_) {
    91          initiator_ = INITIATOR;
    92      }
    93  
    94      //// @notice Queries the vetoer address.
    95      /// @return vetoer_ Vetoer address.
    96      function vetoer() external virtual readOrHandle returns (address vetoer_) {
    97          vetoer_ = VETOER;
    98      }
    99  
   100      //// @notice Queries the target address.
   101      /// @return target_ Target address.
   102      function target() external readOrHandle returns (address target_) {
   103          target_ = TARGET;
   104      }
   105  
   106      /// @notice Gets the delay
   107      /// @return delay_ Delay address.
   108      function delay() external readOrHandle returns (uint256 delay_) {
   109          delay_ = _delay;
   110      }
   111  
   112      /// @notice Gets entries in the _queuedAt mapping.
   113      /// @param callHash The hash of the call data.
   114      /// @return queuedAt_ The time the callHash was recorded.
   115      function queuedAt(bytes32 callHash) external readOrHandle returns (uint256 queuedAt_) {
   116          queuedAt_ = _queuedAt[callHash];
   117      }
   118  
   119      /// @notice Used for all calls that pass data to the contract.
   120      fallback() external {
   121          _handleCall();
   122      }
   123  
   124      /// @notice Receives all calls other than those made by the vetoer.
   125      ///         This enables transparent initiation and forwarding of calls to the target and avoids
   126      ///         the need for additional layers of abi encoding.
   127      function _handleCall() internal {
   128          // The initiator and vetoer activate the delay by passing in null data.
   129          if (msg.data.length == 0 && _delay == 0) {
   130              if (msg.sender != INITIATOR && msg.sender != VETOER) {
   131                  revert Unauthorized(INITIATOR, msg.sender);
   132              }
   133              _delay = OPERATING_DELAY;
   134              emit DelayActivated(_delay);
   135              return;
   136          }
   137  
   138          bytes32 callHash = keccak256(msg.data);
   139  
   140          // Case 1: The initiator is calling the contract to initiate a call.
   141          if (msg.sender == INITIATOR && _queuedAt[callHash] == 0) {
   142              if (_delay == 0) {
   143                  // This forward function will halt the call frame on completion.
   144                  _forwardAndHalt(callHash);
   145              }
   146              _queuedAt[callHash] = block.timestamp;
   147              emit Initiated(callHash, msg.data);
   148              return;
   149          }
   150  
   151          // Case 2: The vetoer is calling the contract to veto a call.
   152          // Note: The vetoer retains the ability to veto even after the delay has passed. This makes censoring the vetoer
   153          //       more costly, as there is no time limit after which their transaction can be included.
   154          if (msg.sender == VETOER && _queuedAt[callHash] != 0) {
   155              delete _queuedAt[callHash];
   156              emit Vetoed(callHash, msg.data);
   157              return;
   158          }
   159  
   160          // Case 3: The call is from an unpermissioned actor. We'll forward the call if the delay has
   161          // passed.
   162          if (_queuedAt[callHash] == 0) {
   163              // The call has not been initiated, so we'll treat this is an unauthorized initiation attempt.
   164              revert Unauthorized(INITIATOR, msg.sender);
   165          }
   166  
   167          if (_queuedAt[callHash] + _delay > block.timestamp) {
   168              // Not enough time has passed, so we'll revert.
   169              revert ForwardingEarly();
   170          }
   171  
   172          // Delete the call to prevent replays
   173          delete _queuedAt[callHash];
   174          _forwardAndHalt(callHash);
   175      }
   176  
   177      /// @notice Forwards the call to the target and halts the call frame.
   178      function _forwardAndHalt(bytes32 callHash) internal {
   179          // Forward the call
   180          emit Forwarded(callHash, msg.data);
   181          (bool success, bytes memory returndata) = TARGET.call(msg.data);
   182          if (success == true) {
   183              assembly {
   184                  return(add(returndata, 0x20), mload(returndata))
   185              }
   186          } else {
   187              assembly {
   188                  revert(add(returndata, 0x20), mload(returndata))
   189              }
   190          }
   191      }
   192  }