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

     1  // SPDX-License-Identifier: MIT
     2  pragma solidity 0.8.15;
     3  
     4  // Testing utilities
     5  import { stdError } from "forge-std/Test.sol";
     6  
     7  import { CommonTest } from "test/setup/CommonTest.sol";
     8  import { NextImpl } from "test/mocks/NextImpl.sol";
     9  import { EIP1967Helper } from "test/mocks/EIP1967Helper.sol";
    10  
    11  // Libraries
    12  import { Types } from "src/libraries/Types.sol";
    13  import { Hashing } from "src/libraries/Hashing.sol";
    14  import { Constants } from "src/libraries/Constants.sol";
    15  
    16  // Target contract dependencies
    17  import { Proxy } from "src/universal/Proxy.sol";
    18  import { ResourceMetering } from "src/L1/ResourceMetering.sol";
    19  import { AddressAliasHelper } from "src/vendor/AddressAliasHelper.sol";
    20  import { SystemConfig } from "src/L1/SystemConfig.sol";
    21  import { SuperchainConfig } from "src/L1/SuperchainConfig.sol";
    22  import { OptimismPortal2 } from "src/L1/OptimismPortal2.sol";
    23  
    24  import { FaultDisputeGame, IDisputeGame } from "src/dispute/FaultDisputeGame.sol";
    25  import "src/libraries/DisputeTypes.sol";
    26  
    27  contract OptimismPortal2_Test is CommonTest {
    28      address depositor;
    29  
    30      function setUp() public override {
    31          super.enableFaultProofs();
    32          super.setUp();
    33  
    34          depositor = makeAddr("depositor");
    35      }
    36  
    37      /// @dev Tests that the constructor sets the correct values.
    38      /// @notice Marked virtual to be overridden in
    39      ///         test/kontrol/deployment/DeploymentSummary.t.sol
    40      function test_constructor_succeeds() external virtual {
    41          OptimismPortal2 opImpl = OptimismPortal2(payable(deploy.mustGetAddress("OptimismPortal2")));
    42          assertEq(address(opImpl.disputeGameFactory()), address(0));
    43          assertEq(address(opImpl.SYSTEM_CONFIG()), address(0));
    44          assertEq(address(opImpl.systemConfig()), address(0));
    45          assertEq(address(opImpl.superchainConfig()), address(0));
    46          assertEq(opImpl.l2Sender(), Constants.DEFAULT_L2_SENDER);
    47          assertEq(opImpl.respectedGameType().raw(), deploy.cfg().respectedGameType());
    48      }
    49  
    50      /// @dev Tests that the initializer sets the correct values.
    51      /// @notice Marked virtual to be overridden in
    52      ///         test/kontrol/deployment/DeploymentSummary.t.sol
    53      function test_initialize_succeeds() external virtual {
    54          address guardian = deploy.cfg().superchainConfigGuardian();
    55          assertEq(address(optimismPortal2.disputeGameFactory()), address(disputeGameFactory));
    56          assertEq(address(optimismPortal2.SYSTEM_CONFIG()), address(systemConfig));
    57          assertEq(address(optimismPortal2.systemConfig()), address(systemConfig));
    58          assertEq(optimismPortal2.GUARDIAN(), guardian);
    59          assertEq(optimismPortal2.guardian(), guardian);
    60          assertEq(address(optimismPortal2.superchainConfig()), address(superchainConfig));
    61          assertEq(optimismPortal2.l2Sender(), Constants.DEFAULT_L2_SENDER);
    62          assertEq(optimismPortal2.paused(), false);
    63          assertEq(optimismPortal2.respectedGameType().raw(), deploy.cfg().respectedGameType());
    64      }
    65  
    66      /// @dev Tests that `pause` successfully pauses
    67      ///      when called by the GUARDIAN.
    68      function test_pause_succeeds() external {
    69          address guardian = optimismPortal2.GUARDIAN();
    70  
    71          assertEq(optimismPortal2.paused(), false);
    72  
    73          vm.expectEmit(address(superchainConfig));
    74          emit Paused("identifier");
    75  
    76          vm.prank(guardian);
    77          superchainConfig.pause("identifier");
    78  
    79          assertEq(optimismPortal2.paused(), true);
    80      }
    81  
    82      /// @dev Tests that `pause` reverts when called by a non-GUARDIAN.
    83      function test_pause_onlyGuardian_reverts() external {
    84          assertEq(optimismPortal2.paused(), false);
    85  
    86          assertTrue(optimismPortal2.GUARDIAN() != alice);
    87          vm.expectRevert("SuperchainConfig: only guardian can pause");
    88          vm.prank(alice);
    89          superchainConfig.pause("identifier");
    90  
    91          assertEq(optimismPortal2.paused(), false);
    92      }
    93  
    94      /// @dev Tests that `unpause` successfully unpauses
    95      ///      when called by the GUARDIAN.
    96      function test_unpause_succeeds() external {
    97          address guardian = optimismPortal2.GUARDIAN();
    98  
    99          vm.prank(guardian);
   100          superchainConfig.pause("identifier");
   101          assertEq(optimismPortal2.paused(), true);
   102  
   103          vm.expectEmit(address(superchainConfig));
   104          emit Unpaused();
   105          vm.prank(guardian);
   106          superchainConfig.unpause();
   107  
   108          assertEq(optimismPortal2.paused(), false);
   109      }
   110  
   111      /// @dev Tests that `unpause` reverts when called by a non-GUARDIAN.
   112      function test_unpause_onlyGuardian_reverts() external {
   113          address guardian = optimismPortal2.GUARDIAN();
   114  
   115          vm.prank(guardian);
   116          superchainConfig.pause("identifier");
   117          assertEq(optimismPortal2.paused(), true);
   118  
   119          assertTrue(optimismPortal2.GUARDIAN() != alice);
   120          vm.expectRevert("SuperchainConfig: only guardian can unpause");
   121          vm.prank(alice);
   122          superchainConfig.unpause();
   123  
   124          assertEq(optimismPortal2.paused(), true);
   125      }
   126  
   127      /// @dev Tests that `receive` successdully deposits ETH.
   128      function testFuzz_receive_succeeds(uint256 _value) external {
   129          vm.expectEmit(address(optimismPortal2));
   130          emitTransactionDeposited({
   131              _from: alice,
   132              _to: alice,
   133              _value: _value,
   134              _mint: _value,
   135              _gasLimit: 100_000,
   136              _isCreation: false,
   137              _data: hex""
   138          });
   139  
   140          // give alice money and send as an eoa
   141          vm.deal(alice, _value);
   142          vm.prank(alice, alice);
   143          (bool s,) = address(optimismPortal2).call{ value: _value }(hex"");
   144  
   145          assertTrue(s);
   146          assertEq(address(optimismPortal2).balance, _value);
   147      }
   148  
   149      /// @dev Tests that `depositTransaction` reverts when the destination address is non-zero
   150      ///      for a contract creation deposit.
   151      function test_depositTransaction_contractCreation_reverts() external {
   152          // contract creation must have a target of address(0)
   153          vm.expectRevert("OptimismPortal: must send to address(0) when creating a contract");
   154          optimismPortal2.depositTransaction(address(1), 1, 0, true, hex"");
   155      }
   156  
   157      /// @dev Tests that `depositTransaction` reverts when the data is too large.
   158      ///      This places an upper bound on unsafe blocks sent over p2p.
   159      function test_depositTransaction_largeData_reverts() external {
   160          uint256 size = 120_001;
   161          uint64 gasLimit = optimismPortal2.minimumGasLimit(uint64(size));
   162          vm.expectRevert("OptimismPortal: data too large");
   163          optimismPortal2.depositTransaction({
   164              _to: address(0),
   165              _value: 0,
   166              _gasLimit: gasLimit,
   167              _isCreation: false,
   168              _data: new bytes(size)
   169          });
   170      }
   171  
   172      /// @dev Tests that `depositTransaction` reverts when the gas limit is too small.
   173      function test_depositTransaction_smallGasLimit_reverts() external {
   174          vm.expectRevert("OptimismPortal: gas limit too small");
   175          optimismPortal2.depositTransaction({ _to: address(1), _value: 0, _gasLimit: 0, _isCreation: false, _data: hex"" });
   176      }
   177  
   178      /// @dev Tests that `depositTransaction` succeeds for small,
   179      ///      but sufficient, gas limits.
   180      function testFuzz_depositTransaction_smallGasLimit_succeeds(bytes memory _data, bool _shouldFail) external {
   181          uint64 gasLimit = optimismPortal2.minimumGasLimit(uint64(_data.length));
   182          if (_shouldFail) {
   183              gasLimit = uint64(bound(gasLimit, 0, gasLimit - 1));
   184              vm.expectRevert("OptimismPortal: gas limit too small");
   185          }
   186  
   187          optimismPortal2.depositTransaction({
   188              _to: address(0x40),
   189              _value: 0,
   190              _gasLimit: gasLimit,
   191              _isCreation: false,
   192              _data: _data
   193          });
   194      }
   195  
   196      /// @dev Tests that `minimumGasLimit` succeeds for small calldata sizes.
   197      ///      The gas limit should be 21k for 0 calldata and increase linearly
   198      ///      for larger calldata sizes.
   199      function test_minimumGasLimit_succeeds() external {
   200          assertEq(optimismPortal2.minimumGasLimit(0), 21_000);
   201          assertTrue(optimismPortal2.minimumGasLimit(2) > optimismPortal2.minimumGasLimit(1));
   202          assertTrue(optimismPortal2.minimumGasLimit(3) > optimismPortal2.minimumGasLimit(2));
   203      }
   204  
   205      /// @dev Tests that `depositTransaction` succeeds for an EOA.
   206      function testFuzz_depositTransaction_eoa_succeeds(
   207          address _to,
   208          uint64 _gasLimit,
   209          uint256 _value,
   210          uint256 _mint,
   211          bool _isCreation,
   212          bytes memory _data
   213      )
   214          external
   215      {
   216          _gasLimit = uint64(
   217              bound(
   218                  _gasLimit,
   219                  optimismPortal2.minimumGasLimit(uint64(_data.length)),
   220                  systemConfig.resourceConfig().maxResourceLimit
   221              )
   222          );
   223          if (_isCreation) _to = address(0);
   224  
   225          // EOA emulation
   226          vm.expectEmit(address(optimismPortal2));
   227          emitTransactionDeposited({
   228              _from: depositor,
   229              _to: _to,
   230              _value: _value,
   231              _mint: _mint,
   232              _gasLimit: _gasLimit,
   233              _isCreation: _isCreation,
   234              _data: _data
   235          });
   236  
   237          vm.deal(depositor, _mint);
   238          vm.prank(depositor, depositor);
   239          optimismPortal2.depositTransaction{ value: _mint }({
   240              _to: _to,
   241              _value: _value,
   242              _gasLimit: _gasLimit,
   243              _isCreation: _isCreation,
   244              _data: _data
   245          });
   246          assertEq(address(optimismPortal2).balance, _mint);
   247      }
   248  
   249      /// @dev Tests that `depositTransaction` succeeds for a contract.
   250      function testFuzz_depositTransaction_contract_succeeds(
   251          address _to,
   252          uint64 _gasLimit,
   253          uint256 _value,
   254          uint256 _mint,
   255          bool _isCreation,
   256          bytes memory _data
   257      )
   258          external
   259      {
   260          _gasLimit = uint64(
   261              bound(
   262                  _gasLimit,
   263                  optimismPortal2.minimumGasLimit(uint64(_data.length)),
   264                  systemConfig.resourceConfig().maxResourceLimit
   265              )
   266          );
   267          if (_isCreation) _to = address(0);
   268  
   269          vm.expectEmit(address(optimismPortal2));
   270          emitTransactionDeposited({
   271              _from: AddressAliasHelper.applyL1ToL2Alias(address(this)),
   272              _to: _to,
   273              _value: _value,
   274              _mint: _mint,
   275              _gasLimit: _gasLimit,
   276              _isCreation: _isCreation,
   277              _data: _data
   278          });
   279  
   280          vm.deal(address(this), _mint);
   281          vm.prank(address(this));
   282          optimismPortal2.depositTransaction{ value: _mint }({
   283              _to: _to,
   284              _value: _value,
   285              _gasLimit: _gasLimit,
   286              _isCreation: _isCreation,
   287              _data: _data
   288          });
   289          assertEq(address(optimismPortal2).balance, _mint);
   290      }
   291  }
   292  
   293  contract OptimismPortal2_FinalizeWithdrawal_Test is CommonTest {
   294      // Reusable default values for a test withdrawal
   295      Types.WithdrawalTransaction _defaultTx;
   296  
   297      FaultDisputeGame game;
   298      uint256 _proposedGameIndex;
   299      uint256 _proposedBlockNumber;
   300      bytes32 _stateRoot;
   301      bytes32 _storageRoot;
   302      bytes32 _outputRoot;
   303      bytes32 _withdrawalHash;
   304      bytes[] _withdrawalProof;
   305      Types.OutputRootProof internal _outputRootProof;
   306  
   307      // Use a constructor to set the storage vars above, so as to minimize the number of ffi calls.
   308      constructor() {
   309          super.enableFaultProofs();
   310          super.setUp();
   311  
   312          _defaultTx = Types.WithdrawalTransaction({
   313              nonce: 0,
   314              sender: alice,
   315              target: bob,
   316              value: 100,
   317              gasLimit: 100_000,
   318              data: hex""
   319          });
   320          // Get withdrawal proof data we can use for testing.
   321          (_stateRoot, _storageRoot, _outputRoot, _withdrawalHash, _withdrawalProof) =
   322              ffi.getProveWithdrawalTransactionInputs(_defaultTx);
   323  
   324          // Setup a dummy output root proof for reuse.
   325          _outputRootProof = Types.OutputRootProof({
   326              version: bytes32(uint256(0)),
   327              stateRoot: _stateRoot,
   328              messagePasserStorageRoot: _storageRoot,
   329              latestBlockhash: bytes32(uint256(0))
   330          });
   331      }
   332  
   333      /// @dev Setup the system for a ready-to-use state.
   334      function setUp() public override {
   335          _proposedBlockNumber = 0xFF;
   336          game = FaultDisputeGame(
   337              payable(
   338                  address(
   339                      disputeGameFactory.create(
   340                          optimismPortal2.respectedGameType(), Claim.wrap(_outputRoot), abi.encode(_proposedBlockNumber)
   341                      )
   342                  )
   343              )
   344          );
   345          _proposedGameIndex = disputeGameFactory.gameCount() - 1;
   346  
   347          // Warp beyond the chess clocks and finalize the game.
   348          vm.warp(block.timestamp + game.gameDuration().raw() / 2 + 1 seconds);
   349  
   350          // Fund the portal so that we can withdraw ETH.
   351          vm.deal(address(optimismPortal2), 0xFFFFFFFF);
   352      }
   353  
   354      /// @dev Asserts that the reentrant call will revert.
   355      function callPortalAndExpectRevert() external payable {
   356          vm.expectRevert("OptimismPortal: can only trigger one withdrawal per transaction");
   357          // Arguments here don't matter, as the require check is the first thing that happens.
   358          // We assume that this has already been proven.
   359          optimismPortal2.finalizeWithdrawalTransaction(_defaultTx);
   360          // Assert that the withdrawal was not finalized.
   361          assertFalse(optimismPortal2.finalizedWithdrawals(Hashing.hashWithdrawal(_defaultTx)));
   362      }
   363  
   364      /// @dev Tests that `blacklistDisputeGame` reverts when called by a non-guardian.
   365      function testFuzz_blacklist_onlyGuardian_reverts(address _act) external {
   366          vm.assume(_act != address(optimismPortal2.guardian()));
   367  
   368          vm.expectRevert("OptimismPortal: only the guardian can blacklist dispute games");
   369          optimismPortal2.blacklistDisputeGame(IDisputeGame(address(0xdead)));
   370      }
   371  
   372      /// @dev Tests that the guardian role can blacklist any dispute game.
   373      function testFuzz_blacklist_guardian_succeeds(address _addr) external {
   374          vm.prank(optimismPortal2.guardian());
   375          optimismPortal2.blacklistDisputeGame(IDisputeGame(_addr));
   376  
   377          assertTrue(optimismPortal2.disputeGameBlacklist(IDisputeGame(_addr)));
   378      }
   379  
   380      /// @dev Tests that `setRespectedGameType` reverts when called by a non-guardian.
   381      function testFuzz_setRespectedGameType_onlyGuardian_reverts(address _act, GameType _ty) external {
   382          vm.assume(_act != address(optimismPortal2.guardian()));
   383  
   384          vm.prank(_act);
   385          vm.expectRevert("OptimismPortal: only the guardian can set the respected game type");
   386          optimismPortal2.setRespectedGameType(_ty);
   387      }
   388  
   389      /// @dev Tests that the guardian role can set the respected game type to anything they want.
   390      function testFuzz_setRespectedGameType_guardian_succeeds(GameType _ty) external {
   391          vm.prank(optimismPortal2.guardian());
   392          optimismPortal2.setRespectedGameType(_ty);
   393  
   394          assertEq(optimismPortal2.respectedGameType().raw(), _ty.raw());
   395      }
   396  
   397      /// @dev Tests that `proveWithdrawalTransaction` reverts when paused.
   398      function test_proveWithdrawalTransaction_paused_reverts() external {
   399          vm.prank(optimismPortal2.GUARDIAN());
   400          superchainConfig.pause("identifier");
   401  
   402          vm.expectRevert("OptimismPortal: paused");
   403          optimismPortal2.proveWithdrawalTransaction({
   404              _tx: _defaultTx,
   405              _disputeGameIndex: _proposedGameIndex,
   406              _outputRootProof: _outputRootProof,
   407              _withdrawalProof: _withdrawalProof
   408          });
   409      }
   410  
   411      /// @dev Tests that `proveWithdrawalTransaction` reverts when the target is the portal contract.
   412      function test_proveWithdrawalTransaction_onSelfCall_reverts() external {
   413          _defaultTx.target = address(optimismPortal2);
   414          vm.expectRevert("OptimismPortal: you cannot send messages to the portal contract");
   415          optimismPortal2.proveWithdrawalTransaction({
   416              _tx: _defaultTx,
   417              _disputeGameIndex: _proposedGameIndex,
   418              _outputRootProof: _outputRootProof,
   419              _withdrawalProof: _withdrawalProof
   420          });
   421      }
   422  
   423      /// @dev Tests that `proveWithdrawalTransaction` reverts when the outputRootProof does not match the output root
   424      function test_proveWithdrawalTransaction_onInvalidOutputRootProof_reverts() external {
   425          // Modify the version to invalidate the withdrawal proof.
   426          _outputRootProof.version = bytes32(uint256(1));
   427          vm.expectRevert("OptimismPortal: invalid output root proof");
   428          optimismPortal2.proveWithdrawalTransaction({
   429              _tx: _defaultTx,
   430              _disputeGameIndex: _proposedGameIndex,
   431              _outputRootProof: _outputRootProof,
   432              _withdrawalProof: _withdrawalProof
   433          });
   434      }
   435  
   436      /// @dev Tests that `proveWithdrawalTransaction` reverts when the withdrawal is missing.
   437      function test_proveWithdrawalTransaction_onInvalidWithdrawalProof_reverts() external {
   438          // modify the default test values to invalidate the proof.
   439          _defaultTx.data = hex"abcd";
   440          vm.expectRevert("MerkleTrie: path remainder must share all nibbles with key");
   441          optimismPortal2.proveWithdrawalTransaction({
   442              _tx: _defaultTx,
   443              _disputeGameIndex: _proposedGameIndex,
   444              _outputRootProof: _outputRootProof,
   445              _withdrawalProof: _withdrawalProof
   446          });
   447      }
   448  
   449      /// @dev Tests that `proveWithdrawalTransaction` reverts when the withdrawal has already
   450      ///      been proven.
   451      function test_proveWithdrawalTransaction_replayProve_reverts() external {
   452          vm.expectEmit(true, true, true, true);
   453          emit WithdrawalProven(_withdrawalHash, alice, bob);
   454          optimismPortal2.proveWithdrawalTransaction({
   455              _tx: _defaultTx,
   456              _disputeGameIndex: _proposedGameIndex,
   457              _outputRootProof: _outputRootProof,
   458              _withdrawalProof: _withdrawalProof
   459          });
   460  
   461          vm.expectRevert(
   462              "OptimismPortal: withdrawal hash has already been proven, and the old dispute game is not invalid"
   463          );
   464          optimismPortal2.proveWithdrawalTransaction({
   465              _tx: _defaultTx,
   466              _disputeGameIndex: _proposedGameIndex,
   467              _outputRootProof: _outputRootProof,
   468              _withdrawalProof: _withdrawalProof
   469          });
   470      }
   471  
   472      /// @dev Tests that `proveWithdrawalTransaction` reverts if the dispute game being proven against is not of the
   473      ///      respected game type.
   474      function test_proveWithdrawalTransaction_badGameType_reverts() external {
   475          vm.mockCall(
   476              address(disputeGameFactory),
   477              abi.encodeCall(disputeGameFactory.gameAtIndex, (_proposedGameIndex)),
   478              abi.encode(GameType.wrap(0xFF), Timestamp.wrap(uint64(block.timestamp)), IDisputeGame(address(game)))
   479          );
   480  
   481          vm.expectRevert("OptimismPortal: invalid game type");
   482          optimismPortal2.proveWithdrawalTransaction({
   483              _tx: _defaultTx,
   484              _disputeGameIndex: _proposedGameIndex,
   485              _outputRootProof: _outputRootProof,
   486              _withdrawalProof: _withdrawalProof
   487          });
   488      }
   489  
   490      /// @dev Tests that `proveWithdrawalTransaction` can be re-executed if the dispute game proven against has been
   491      ///      blacklisted.
   492      function test_proveWithdrawalTransaction_replayProveBlacklisted_suceeds() external {
   493          vm.expectEmit(true, true, true, true);
   494          emit WithdrawalProven(_withdrawalHash, alice, bob);
   495          optimismPortal2.proveWithdrawalTransaction({
   496              _tx: _defaultTx,
   497              _disputeGameIndex: _proposedGameIndex,
   498              _outputRootProof: _outputRootProof,
   499              _withdrawalProof: _withdrawalProof
   500          });
   501  
   502          // Blacklist the dispute dispute game.
   503          vm.prank(optimismPortal2.guardian());
   504          optimismPortal2.blacklistDisputeGame(IDisputeGame(address(game)));
   505  
   506          vm.expectEmit(true, true, true, true);
   507          emit WithdrawalProven(_withdrawalHash, alice, bob);
   508          optimismPortal2.proveWithdrawalTransaction({
   509              _tx: _defaultTx,
   510              _disputeGameIndex: _proposedGameIndex,
   511              _outputRootProof: _outputRootProof,
   512              _withdrawalProof: _withdrawalProof
   513          });
   514      }
   515  
   516      /// @dev Tests that `proveWithdrawalTransaction` can be re-executed if the dispute game proven against has resolved
   517      ///      against the favor of the root claim.
   518      function test_proveWithdrawalTransaction_replayProveBadProposal_suceeds() external {
   519          vm.expectEmit(true, true, true, true);
   520          emit WithdrawalProven(_withdrawalHash, alice, bob);
   521          optimismPortal2.proveWithdrawalTransaction({
   522              _tx: _defaultTx,
   523              _disputeGameIndex: _proposedGameIndex,
   524              _outputRootProof: _outputRootProof,
   525              _withdrawalProof: _withdrawalProof
   526          });
   527  
   528          vm.mockCall(address(game), abi.encodeCall(game.status, ()), abi.encode(GameStatus.CHALLENGER_WINS));
   529  
   530          vm.expectEmit(true, true, true, true);
   531          emit WithdrawalProven(_withdrawalHash, alice, bob);
   532          optimismPortal2.proveWithdrawalTransaction({
   533              _tx: _defaultTx,
   534              _disputeGameIndex: _proposedGameIndex,
   535              _outputRootProof: _outputRootProof,
   536              _withdrawalProof: _withdrawalProof
   537          });
   538      }
   539  
   540      /// @dev Tests that `proveWithdrawalTransaction` can be re-executed if the dispute game proven against is no longer
   541      ///      of the respected game type.
   542      function test_proveWithdrawalTransaction_replayRespectedGameTypeChanged_suceeds() external {
   543          // Prove the withdrawal against a game with the current respected game type.
   544          vm.expectEmit(true, true, true, true);
   545          emit WithdrawalProven(_withdrawalHash, alice, bob);
   546          optimismPortal2.proveWithdrawalTransaction({
   547              _tx: _defaultTx,
   548              _disputeGameIndex: _proposedGameIndex,
   549              _outputRootProof: _outputRootProof,
   550              _withdrawalProof: _withdrawalProof
   551          });
   552  
   553          // Update the respected game type to 0xbeef.
   554          vm.prank(optimismPortal2.guardian());
   555          optimismPortal2.setRespectedGameType(GameType.wrap(0xbeef));
   556  
   557          // Create a new game and mock the game type as 0xbeef in the factory.
   558          IDisputeGame newGame =
   559              disputeGameFactory.create(GameType.wrap(0), Claim.wrap(_outputRoot), abi.encode(_proposedBlockNumber + 1));
   560          vm.mockCall(
   561              address(disputeGameFactory),
   562              abi.encodeCall(disputeGameFactory.gameAtIndex, (_proposedGameIndex + 1)),
   563              abi.encode(GameType.wrap(0xbeef), Timestamp.wrap(uint64(block.timestamp)), IDisputeGame(address(newGame)))
   564          );
   565  
   566          // Re-proving should be successful against the new game.
   567          vm.expectEmit(true, true, true, true);
   568          emit WithdrawalProven(_withdrawalHash, alice, bob);
   569          optimismPortal2.proveWithdrawalTransaction({
   570              _tx: _defaultTx,
   571              _disputeGameIndex: _proposedGameIndex + 1,
   572              _outputRootProof: _outputRootProof,
   573              _withdrawalProof: _withdrawalProof
   574          });
   575      }
   576  
   577      /// @dev Tests that `proveWithdrawalTransaction` succeeds.
   578      function test_proveWithdrawalTransaction_validWithdrawalProof_succeeds() external {
   579          vm.expectEmit(true, true, true, true);
   580          emit WithdrawalProven(_withdrawalHash, alice, bob);
   581          optimismPortal2.proveWithdrawalTransaction({
   582              _tx: _defaultTx,
   583              _disputeGameIndex: _proposedGameIndex,
   584              _outputRootProof: _outputRootProof,
   585              _withdrawalProof: _withdrawalProof
   586          });
   587      }
   588  
   589      /// @dev Tests that `finalizeWithdrawalTransaction` succeeds.
   590      function test_finalizeWithdrawalTransaction_provenWithdrawalHash_succeeds() external {
   591          uint256 bobBalanceBefore = address(bob).balance;
   592  
   593          vm.expectEmit(true, true, true, true);
   594          emit WithdrawalProven(_withdrawalHash, alice, bob);
   595          optimismPortal2.proveWithdrawalTransaction({
   596              _tx: _defaultTx,
   597              _disputeGameIndex: _proposedGameIndex,
   598              _outputRootProof: _outputRootProof,
   599              _withdrawalProof: _withdrawalProof
   600          });
   601  
   602          // Warp and resolve the dispute game.
   603          game.resolveClaim(0);
   604          game.resolve();
   605          vm.warp(block.timestamp + optimismPortal2.proofMaturityDelaySeconds() + 1 seconds);
   606  
   607          vm.expectEmit(true, true, false, true);
   608          emit WithdrawalFinalized(_withdrawalHash, true);
   609          optimismPortal2.finalizeWithdrawalTransaction(_defaultTx);
   610  
   611          assert(address(bob).balance == bobBalanceBefore + 100);
   612      }
   613  
   614      /// @dev Tests that `finalizeWithdrawalTransaction` reverts if the contract is paused.
   615      function test_finalizeWithdrawalTransaction_paused_reverts() external {
   616          vm.prank(optimismPortal2.GUARDIAN());
   617          superchainConfig.pause("identifier");
   618  
   619          vm.expectRevert("OptimismPortal: paused");
   620          optimismPortal2.finalizeWithdrawalTransaction(_defaultTx);
   621      }
   622  
   623      /// @dev Tests that `finalizeWithdrawalTransaction` reverts if the withdrawal has not been
   624      function test_finalizeWithdrawalTransaction_ifWithdrawalNotProven_reverts() external {
   625          uint256 bobBalanceBefore = address(bob).balance;
   626  
   627          vm.expectRevert("OptimismPortal: withdrawal has not been proven yet");
   628          optimismPortal2.finalizeWithdrawalTransaction(_defaultTx);
   629  
   630          assert(address(bob).balance == bobBalanceBefore);
   631      }
   632  
   633      /// @dev Tests that `finalizeWithdrawalTransaction` reverts if the withdrawal has not been
   634      ///      proven long enough ago.
   635      function test_finalizeWithdrawalTransaction_ifWithdrawalProofNotOldEnough_reverts() external {
   636          uint256 bobBalanceBefore = address(bob).balance;
   637  
   638          vm.expectEmit(true, true, true, true);
   639          emit WithdrawalProven(_withdrawalHash, alice, bob);
   640          optimismPortal2.proveWithdrawalTransaction({
   641              _tx: _defaultTx,
   642              _disputeGameIndex: _proposedGameIndex,
   643              _outputRootProof: _outputRootProof,
   644              _withdrawalProof: _withdrawalProof
   645          });
   646  
   647          vm.expectRevert("OptimismPortal: proven withdrawal has not matured yet");
   648          optimismPortal2.finalizeWithdrawalTransaction(_defaultTx);
   649  
   650          assert(address(bob).balance == bobBalanceBefore);
   651      }
   652  
   653      /// @dev Tests that `finalizeWithdrawalTransaction` reverts if the provenWithdrawal's timestamp
   654      ///      is less than the dispute game's creation timestamp.
   655      function test_finalizeWithdrawalTransaction_timestampLessThanGameCreation_reverts() external {
   656          uint256 bobBalanceBefore = address(bob).balance;
   657  
   658          // Prove our withdrawal
   659          vm.expectEmit(true, true, true, true);
   660          emit WithdrawalProven(_withdrawalHash, alice, bob);
   661          optimismPortal2.proveWithdrawalTransaction({
   662              _tx: _defaultTx,
   663              _disputeGameIndex: _proposedGameIndex,
   664              _outputRootProof: _outputRootProof,
   665              _withdrawalProof: _withdrawalProof
   666          });
   667  
   668          // Warp to after the finalization period
   669          vm.warp(block.timestamp + optimismPortal2.proofMaturityDelaySeconds() + 1);
   670  
   671          // Mock a createdAt change in the dispute game.
   672          vm.mockCall(address(game), abi.encodeWithSignature("createdAt()"), abi.encode(block.timestamp + 1));
   673  
   674          // Attempt to finalize the withdrawal
   675          vm.expectRevert("OptimismPortal: withdrawal timestamp less than dispute game creation timestamp");
   676          optimismPortal2.finalizeWithdrawalTransaction(_defaultTx);
   677  
   678          // Ensure that bob's balance has remained the same
   679          assertEq(bobBalanceBefore, address(bob).balance);
   680      }
   681  
   682      /// @dev Tests that `finalizeWithdrawalTransaction` reverts if the dispute game has not resolved in favor of the
   683      ///      root claim.
   684      function test_finalizeWithdrawalTransaction_ifDisputeGameNotResolved_reverts() external {
   685          uint256 bobBalanceBefore = address(bob).balance;
   686  
   687          // Prove our withdrawal
   688          vm.expectEmit(true, true, true, true);
   689          emit WithdrawalProven(_withdrawalHash, alice, bob);
   690          optimismPortal2.proveWithdrawalTransaction({
   691              _tx: _defaultTx,
   692              _disputeGameIndex: _proposedGameIndex,
   693              _outputRootProof: _outputRootProof,
   694              _withdrawalProof: _withdrawalProof
   695          });
   696  
   697          // Warp to after the finalization period
   698          vm.warp(block.timestamp + optimismPortal2.proofMaturityDelaySeconds() + 1);
   699  
   700          // Attempt to finalize the withdrawal
   701          vm.expectRevert("OptimismPortal: output proposal has not been finalized yet");
   702          optimismPortal2.finalizeWithdrawalTransaction(_defaultTx);
   703  
   704          // Ensure that bob's balance has remained the same
   705          assertEq(bobBalanceBefore, address(bob).balance);
   706      }
   707  
   708      /// @dev Tests that `finalizeWithdrawalTransaction` reverts if the target reverts.
   709      function test_finalizeWithdrawalTransaction_targetFails_fails() external {
   710          uint256 bobBalanceBefore = address(bob).balance;
   711          vm.etch(bob, hex"fe"); // Contract with just the invalid opcode.
   712  
   713          vm.expectEmit(true, true, true, true);
   714          emit WithdrawalProven(_withdrawalHash, alice, bob);
   715          optimismPortal2.proveWithdrawalTransaction({
   716              _tx: _defaultTx,
   717              _disputeGameIndex: _proposedGameIndex,
   718              _outputRootProof: _outputRootProof,
   719              _withdrawalProof: _withdrawalProof
   720          });
   721  
   722          // Resolve the dispute game.
   723          game.resolveClaim(0);
   724          game.resolve();
   725  
   726          vm.warp(block.timestamp + optimismPortal2.proofMaturityDelaySeconds() + 1);
   727          vm.expectEmit(true, true, true, true);
   728          emit WithdrawalFinalized(_withdrawalHash, false);
   729          optimismPortal2.finalizeWithdrawalTransaction(_defaultTx);
   730  
   731          assert(address(bob).balance == bobBalanceBefore);
   732      }
   733  
   734      /// @dev Tests that `finalizeWithdrawalTransaction` reverts if the withdrawal has already been
   735      ///      finalized.
   736      function test_finalizeWithdrawalTransaction_onReplay_reverts() external {
   737          vm.expectEmit(true, true, true, true);
   738          emit WithdrawalProven(_withdrawalHash, alice, bob);
   739          optimismPortal2.proveWithdrawalTransaction({
   740              _tx: _defaultTx,
   741              _disputeGameIndex: _proposedGameIndex,
   742              _outputRootProof: _outputRootProof,
   743              _withdrawalProof: _withdrawalProof
   744          });
   745  
   746          // Resolve the dispute game.
   747          game.resolveClaim(0);
   748          game.resolve();
   749  
   750          vm.warp(block.timestamp + optimismPortal2.proofMaturityDelaySeconds() + 1);
   751          vm.expectEmit(true, true, true, true);
   752          emit WithdrawalFinalized(_withdrawalHash, true);
   753          optimismPortal2.finalizeWithdrawalTransaction(_defaultTx);
   754  
   755          vm.expectRevert("OptimismPortal: withdrawal has already been finalized");
   756          optimismPortal2.finalizeWithdrawalTransaction(_defaultTx);
   757      }
   758  
   759      /// @dev Tests that `finalizeWithdrawalTransaction` reverts if the withdrawal transaction
   760      ///      does not have enough gas to execute.
   761      function test_finalizeWithdrawalTransaction_onInsufficientGas_reverts() external {
   762          // This number was identified through trial and error.
   763          uint256 gasLimit = 150_000;
   764          Types.WithdrawalTransaction memory insufficientGasTx = Types.WithdrawalTransaction({
   765              nonce: 0,
   766              sender: alice,
   767              target: bob,
   768              value: 100,
   769              gasLimit: gasLimit,
   770              data: hex""
   771          });
   772  
   773          // Get updated proof inputs.
   774          (bytes32 stateRoot, bytes32 storageRoot,,, bytes[] memory withdrawalProof) =
   775              ffi.getProveWithdrawalTransactionInputs(insufficientGasTx);
   776          Types.OutputRootProof memory outputRootProof = Types.OutputRootProof({
   777              version: bytes32(0),
   778              stateRoot: stateRoot,
   779              messagePasserStorageRoot: storageRoot,
   780              latestBlockhash: bytes32(0)
   781          });
   782  
   783          vm.mockCall(
   784              address(game), abi.encodeCall(game.rootClaim, ()), abi.encode(Hashing.hashOutputRootProof(outputRootProof))
   785          );
   786  
   787          optimismPortal2.proveWithdrawalTransaction({
   788              _tx: insufficientGasTx,
   789              _disputeGameIndex: _proposedGameIndex,
   790              _outputRootProof: outputRootProof,
   791              _withdrawalProof: withdrawalProof
   792          });
   793  
   794          // Resolve the dispute game.
   795          game.resolveClaim(0);
   796          game.resolve();
   797  
   798          vm.warp(block.timestamp + optimismPortal2.proofMaturityDelaySeconds() + 1);
   799          vm.expectRevert("SafeCall: Not enough gas");
   800          optimismPortal2.finalizeWithdrawalTransaction{ gas: gasLimit }(insufficientGasTx);
   801      }
   802  
   803      /// @dev Tests that `finalizeWithdrawalTransaction` reverts if a sub-call attempts to finalize
   804      ///      another withdrawal.
   805      function test_finalizeWithdrawalTransaction_onReentrancy_reverts() external {
   806          uint256 bobBalanceBefore = address(bob).balance;
   807  
   808          // Copy and modify the default test values to attempt a reentrant call by first calling to
   809          // this contract's callPortalAndExpectRevert() function above.
   810          Types.WithdrawalTransaction memory _testTx = _defaultTx;
   811          _testTx.target = address(this);
   812          _testTx.data = abi.encodeWithSelector(this.callPortalAndExpectRevert.selector);
   813  
   814          // Get modified proof inputs.
   815          (
   816              bytes32 stateRoot,
   817              bytes32 storageRoot,
   818              bytes32 outputRoot,
   819              bytes32 withdrawalHash,
   820              bytes[] memory withdrawalProof
   821          ) = ffi.getProveWithdrawalTransactionInputs(_testTx);
   822          Types.OutputRootProof memory outputRootProof = Types.OutputRootProof({
   823              version: bytes32(0),
   824              stateRoot: stateRoot,
   825              messagePasserStorageRoot: storageRoot,
   826              latestBlockhash: bytes32(0)
   827          });
   828  
   829          // Return a mock output root from the game.
   830          vm.mockCall(address(game), abi.encodeCall(game.rootClaim, ()), abi.encode(outputRoot));
   831  
   832          vm.expectEmit(true, true, true, true);
   833          emit WithdrawalProven(withdrawalHash, alice, address(this));
   834          optimismPortal2.proveWithdrawalTransaction(_testTx, _proposedGameIndex, outputRootProof, withdrawalProof);
   835  
   836          // Resolve the dispute game.
   837          game.resolveClaim(0);
   838          game.resolve();
   839  
   840          vm.warp(block.timestamp + optimismPortal2.proofMaturityDelaySeconds() + 1);
   841          vm.expectCall(address(this), _testTx.data);
   842          vm.expectEmit(true, true, true, true);
   843          emit WithdrawalFinalized(withdrawalHash, true);
   844          optimismPortal2.finalizeWithdrawalTransaction(_testTx);
   845  
   846          // Ensure that bob's balance was not changed by the reentrant call.
   847          assert(address(bob).balance == bobBalanceBefore);
   848      }
   849  
   850      /// @dev Tests that `finalizeWithdrawalTransaction` succeeds.
   851      function testDiff_finalizeWithdrawalTransaction_succeeds(
   852          address _sender,
   853          address _target,
   854          uint256 _value,
   855          uint256 _gasLimit,
   856          bytes memory _data
   857      )
   858          external
   859      {
   860          vm.assume(
   861              _target != address(optimismPortal2) // Cannot call the optimism portal or a contract
   862                  && _target.code.length == 0 // No accounts with code
   863                  && _target != CONSOLE // The console has no code but behaves like a contract
   864                  && uint160(_target) > 9 // No precompiles (or zero address)
   865          );
   866  
   867          // Total ETH supply is currently about 120M ETH.
   868          uint256 value = bound(_value, 0, 200_000_000 ether);
   869          vm.deal(address(optimismPortal2), value);
   870  
   871          uint256 gasLimit = bound(_gasLimit, 0, 50_000_000);
   872          uint256 nonce = l2ToL1MessagePasser.messageNonce();
   873  
   874          // Get a withdrawal transaction and mock proof from the differential testing script.
   875          Types.WithdrawalTransaction memory _tx = Types.WithdrawalTransaction({
   876              nonce: nonce,
   877              sender: _sender,
   878              target: _target,
   879              value: value,
   880              gasLimit: gasLimit,
   881              data: _data
   882          });
   883          (
   884              bytes32 stateRoot,
   885              bytes32 storageRoot,
   886              bytes32 outputRoot,
   887              bytes32 withdrawalHash,
   888              bytes[] memory withdrawalProof
   889          ) = ffi.getProveWithdrawalTransactionInputs(_tx);
   890  
   891          // Create the output root proof
   892          Types.OutputRootProof memory proof = Types.OutputRootProof({
   893              version: bytes32(uint256(0)),
   894              stateRoot: stateRoot,
   895              messagePasserStorageRoot: storageRoot,
   896              latestBlockhash: bytes32(uint256(0))
   897          });
   898  
   899          // Ensure the values returned from ffi are correct
   900          assertEq(outputRoot, Hashing.hashOutputRootProof(proof));
   901          assertEq(withdrawalHash, Hashing.hashWithdrawal(_tx));
   902  
   903          // Setup the dispute game to return the output root
   904          vm.mockCall(address(game), abi.encodeCall(game.rootClaim, ()), abi.encode(outputRoot));
   905  
   906          // Prove the withdrawal transaction
   907          optimismPortal2.proveWithdrawalTransaction(_tx, _proposedGameIndex, proof, withdrawalProof);
   908          (IDisputeGame _game,) = optimismPortal2.provenWithdrawals(withdrawalHash);
   909          assertTrue(_game.rootClaim().raw() != bytes32(0));
   910  
   911          // Resolve the dispute game
   912          game.resolveClaim(0);
   913          game.resolve();
   914  
   915          // Warp past the finalization period
   916          vm.warp(block.timestamp + optimismPortal2.proofMaturityDelaySeconds() + 1);
   917  
   918          // Finalize the withdrawal transaction
   919          vm.expectCallMinGas(_tx.target, _tx.value, uint64(_tx.gasLimit), _tx.data);
   920          optimismPortal2.finalizeWithdrawalTransaction(_tx);
   921          assertTrue(optimismPortal2.finalizedWithdrawals(withdrawalHash));
   922      }
   923  
   924      /// @dev Tests that `finalizeWithdrawalTransaction` reverts if the withdrawal's dispute game has been blacklisted.
   925      function test_finalizeWithdrawalTransaction_blacklisted_reverts() external {
   926          vm.expectEmit(true, true, true, true);
   927          emit WithdrawalProven(_withdrawalHash, alice, bob);
   928          optimismPortal2.proveWithdrawalTransaction({
   929              _tx: _defaultTx,
   930              _disputeGameIndex: _proposedGameIndex,
   931              _outputRootProof: _outputRootProof,
   932              _withdrawalProof: _withdrawalProof
   933          });
   934  
   935          // Resolve the dispute game.
   936          game.resolveClaim(0);
   937          game.resolve();
   938  
   939          vm.prank(optimismPortal2.guardian());
   940          optimismPortal2.blacklistDisputeGame(IDisputeGame(address(game)));
   941  
   942          vm.warp(block.timestamp + optimismPortal2.proofMaturityDelaySeconds() + 1);
   943  
   944          vm.expectRevert("OptimismPortal: dispute game has been blacklisted");
   945          optimismPortal2.finalizeWithdrawalTransaction(_defaultTx);
   946      }
   947  
   948      /// @dev Tests that `finalizeWithdrawalTransaction` reverts if the withdrawal's dispute game is still in the air
   949      ///      gap.
   950      function test_finalizeWithdrawalTransaction_gameInAirGap_reverts() external {
   951          vm.expectEmit(true, true, true, true);
   952          emit WithdrawalProven(_withdrawalHash, alice, bob);
   953          optimismPortal2.proveWithdrawalTransaction({
   954              _tx: _defaultTx,
   955              _disputeGameIndex: _proposedGameIndex,
   956              _outputRootProof: _outputRootProof,
   957              _withdrawalProof: _withdrawalProof
   958          });
   959  
   960          // Warp past the finalization period.
   961          vm.warp(block.timestamp + optimismPortal2.proofMaturityDelaySeconds() + 1);
   962  
   963          // Resolve the dispute game.
   964          game.resolveClaim(0);
   965          game.resolve();
   966  
   967          // Attempt to finalize the withdrawal directly after the game resolves. This should fail.
   968          vm.expectRevert("OptimismPortal: output proposal in air-gap");
   969          optimismPortal2.finalizeWithdrawalTransaction(_defaultTx);
   970  
   971          // Finalize the withdrawal transaction. This should succeed.
   972          vm.warp(block.timestamp + optimismPortal2.disputeGameFinalityDelaySeconds() + 1);
   973          optimismPortal2.finalizeWithdrawalTransaction(_defaultTx);
   974          assertTrue(optimismPortal2.finalizedWithdrawals(_withdrawalHash));
   975      }
   976  
   977      /// @dev Tests that `finalizeWithdrawalTransaction` reverts if the respected game type has changed since the
   978      ///      withdrawal was proven.
   979      function test_finalizeWithdrawalTransaction_respectedTypeChangedSinceProving_reverts() external {
   980          vm.expectEmit(true, true, true, true);
   981          emit WithdrawalProven(_withdrawalHash, alice, bob);
   982          optimismPortal2.proveWithdrawalTransaction({
   983              _tx: _defaultTx,
   984              _disputeGameIndex: _proposedGameIndex,
   985              _outputRootProof: _outputRootProof,
   986              _withdrawalProof: _withdrawalProof
   987          });
   988  
   989          // Warp past the finalization period.
   990          vm.warp(block.timestamp + optimismPortal2.proofMaturityDelaySeconds() + 1);
   991  
   992          // Resolve the dispute game.
   993          game.resolveClaim(0);
   994          game.resolve();
   995  
   996          // Change the respected game type in the portal.
   997          vm.prank(optimismPortal2.guardian());
   998          optimismPortal2.setRespectedGameType(GameType.wrap(0xFF));
   999  
  1000          vm.expectRevert("OptimismPortal: invalid game type");
  1001          optimismPortal2.finalizeWithdrawalTransaction(_defaultTx);
  1002      }
  1003  
  1004      /// @dev Tests that `finalizeWithdrawalTransaction` reverts if the respected game type was updated after the
  1005      ///      dispute game was created.
  1006      function test_finalizeWithdrawalTransaction_gameOlderThanRespectedGameTypeUpdate_reverts() external {
  1007          vm.expectEmit(true, true, true, true);
  1008          emit WithdrawalProven(_withdrawalHash, alice, bob);
  1009          optimismPortal2.proveWithdrawalTransaction({
  1010              _tx: _defaultTx,
  1011              _disputeGameIndex: _proposedGameIndex,
  1012              _outputRootProof: _outputRootProof,
  1013              _withdrawalProof: _withdrawalProof
  1014          });
  1015  
  1016          // Warp past the finalization period.
  1017          vm.warp(block.timestamp + optimismPortal2.proofMaturityDelaySeconds() + 1);
  1018  
  1019          // Resolve the dispute game.
  1020          game.resolveClaim(0);
  1021          game.resolve();
  1022  
  1023          // Change the respected game type in the portal.
  1024          vm.prank(optimismPortal2.guardian());
  1025          optimismPortal2.setRespectedGameType(GameType.wrap(0xFF));
  1026  
  1027          // Mock the game's type so that we pass the correct game type check.
  1028          vm.mockCall(address(game), abi.encodeCall(game.gameType, ()), abi.encode(GameType.wrap(0xFF)));
  1029  
  1030          vm.expectRevert("OptimismPortal: dispute game created before respected game type was updated");
  1031          optimismPortal2.finalizeWithdrawalTransaction(_defaultTx);
  1032      }
  1033  
  1034      /// @dev Tests an e2e prove -> finalize path, checking the edges of each delay for correctness.
  1035      function test_finalizeWithdrawalTransaction_delayEdges_succeeds() external {
  1036          // Prove the withdrawal transaction.
  1037          vm.expectEmit(true, true, true, true);
  1038          emit WithdrawalProven(_withdrawalHash, alice, bob);
  1039          optimismPortal2.proveWithdrawalTransaction({
  1040              _tx: _defaultTx,
  1041              _disputeGameIndex: _proposedGameIndex,
  1042              _outputRootProof: _outputRootProof,
  1043              _withdrawalProof: _withdrawalProof
  1044          });
  1045  
  1046          // Attempt to finalize the withdrawal transaction 1 second before the proof has matured. This should fail.
  1047          vm.warp(block.timestamp + optimismPortal2.proofMaturityDelaySeconds());
  1048          vm.expectRevert("OptimismPortal: proven withdrawal has not matured yet");
  1049          optimismPortal2.finalizeWithdrawalTransaction(_defaultTx);
  1050  
  1051          // Warp 1 second in the future, past the proof maturity delay, and attempt to finalize the withdrawal.
  1052          // This should also fail, since the dispute game has not resolved yet.
  1053          vm.warp(block.timestamp + 1 seconds);
  1054          vm.expectRevert("OptimismPortal: output proposal has not been finalized yet");
  1055          optimismPortal2.finalizeWithdrawalTransaction(_defaultTx);
  1056  
  1057          // Finalize the dispute game and attempt to finalize the withdrawal again. This should also fail, since the
  1058          // air gap dispute game delay has not elapsed.
  1059          game.resolveClaim(0);
  1060          game.resolve();
  1061          vm.warp(block.timestamp + optimismPortal2.disputeGameFinalityDelaySeconds());
  1062          vm.expectRevert("OptimismPortal: output proposal in air-gap");
  1063          optimismPortal2.finalizeWithdrawalTransaction(_defaultTx);
  1064  
  1065          // Warp 1 second in the future, past the air gap dispute game delay, and attempt to finalize the withdrawal.
  1066          // This should succeed.
  1067          vm.warp(block.timestamp + 1 seconds);
  1068          optimismPortal2.finalizeWithdrawalTransaction(_defaultTx);
  1069          assertTrue(optimismPortal2.finalizedWithdrawals(_withdrawalHash));
  1070      }
  1071  }
  1072  
  1073  contract OptimismPortal2_Upgradeable_Test is CommonTest {
  1074      function setUp() public override {
  1075          super.enableFaultProofs();
  1076          super.setUp();
  1077      }
  1078  
  1079      /// @dev Tests that the proxy is initialized correctly.
  1080      function test_params_initValuesOnProxy_succeeds() external {
  1081          (uint128 prevBaseFee, uint64 prevBoughtGas, uint64 prevBlockNum) = optimismPortal2.params();
  1082          ResourceMetering.ResourceConfig memory rcfg = systemConfig.resourceConfig();
  1083  
  1084          assertEq(prevBaseFee, rcfg.minimumBaseFee);
  1085          assertEq(prevBoughtGas, 0);
  1086          assertEq(prevBlockNum, block.number);
  1087      }
  1088  
  1089      /// @dev Tests that the proxy can be upgraded.
  1090      function test_upgradeToAndCall_upgrading_succeeds() external {
  1091          // Check an unused slot before upgrading.
  1092          bytes32 slot21Before = vm.load(address(optimismPortal2), bytes32(uint256(21)));
  1093          assertEq(bytes32(0), slot21Before);
  1094  
  1095          NextImpl nextImpl = new NextImpl();
  1096  
  1097          vm.startPrank(EIP1967Helper.getAdmin(address(optimismPortal2)));
  1098          // The value passed to the initialize must be larger than the last value
  1099          // that initialize was called with.
  1100          Proxy(payable(address(optimismPortal2))).upgradeToAndCall(
  1101              address(nextImpl), abi.encodeWithSelector(NextImpl.initialize.selector, 2)
  1102          );
  1103          assertEq(Proxy(payable(address(optimismPortal2))).implementation(), address(nextImpl));
  1104  
  1105          // Verify that the NextImpl contract initialized its values according as expected
  1106          bytes32 slot21After = vm.load(address(optimismPortal2), bytes32(uint256(21)));
  1107          bytes32 slot21Expected = NextImpl(address(optimismPortal2)).slot21Init();
  1108          assertEq(slot21Expected, slot21After);
  1109      }
  1110  }
  1111  
  1112  /// @title OptimismPortal2_ResourceFuzz_Test
  1113  /// @dev Test various values of the resource metering config to ensure that deposits cannot be
  1114  ///      broken by changing the config.
  1115  contract OptimismPortal2_ResourceFuzz_Test is CommonTest {
  1116      /// @dev The max gas limit observed throughout this test. Setting this too high can cause
  1117      ///      the test to take too long to run.
  1118      uint256 constant MAX_GAS_LIMIT = 30_000_000;
  1119  
  1120      function setUp() public override {
  1121          super.enableFaultProofs();
  1122          super.setUp();
  1123      }
  1124  
  1125      /// @dev Test that various values of the resource metering config will not break deposits.
  1126      function testFuzz_systemConfigDeposit_succeeds(
  1127          uint32 _maxResourceLimit,
  1128          uint8 _elasticityMultiplier,
  1129          uint8 _baseFeeMaxChangeDenominator,
  1130          uint32 _minimumBaseFee,
  1131          uint32 _systemTxMaxGas,
  1132          uint128 _maximumBaseFee,
  1133          uint64 _gasLimit,
  1134          uint64 _prevBoughtGas,
  1135          uint128 _prevBaseFee,
  1136          uint8 _blockDiff
  1137      )
  1138          external
  1139      {
  1140          // Get the set system gas limit
  1141          uint64 gasLimit = systemConfig.gasLimit();
  1142          // Bound resource config
  1143          _maxResourceLimit = uint32(bound(_maxResourceLimit, 21000, MAX_GAS_LIMIT / 8));
  1144          _gasLimit = uint64(bound(_gasLimit, 21000, _maxResourceLimit));
  1145          _prevBaseFee = uint128(bound(_prevBaseFee, 0, 3 gwei));
  1146          // Prevent values that would cause reverts
  1147          vm.assume(gasLimit >= _gasLimit);
  1148          vm.assume(_minimumBaseFee < _maximumBaseFee);
  1149          vm.assume(_baseFeeMaxChangeDenominator > 1);
  1150          vm.assume(uint256(_maxResourceLimit) + uint256(_systemTxMaxGas) <= gasLimit);
  1151          vm.assume(_elasticityMultiplier > 0);
  1152          vm.assume(((_maxResourceLimit / _elasticityMultiplier) * _elasticityMultiplier) == _maxResourceLimit);
  1153          _prevBoughtGas = uint64(bound(_prevBoughtGas, 0, _maxResourceLimit - _gasLimit));
  1154          _blockDiff = uint8(bound(_blockDiff, 0, 3));
  1155          // Pick a pseudorandom block number
  1156          vm.roll(uint256(keccak256(abi.encode(_blockDiff))) % uint256(type(uint16).max) + uint256(_blockDiff));
  1157  
  1158          // Create a resource config to mock the call to the system config with
  1159          ResourceMetering.ResourceConfig memory rcfg = ResourceMetering.ResourceConfig({
  1160              maxResourceLimit: _maxResourceLimit,
  1161              elasticityMultiplier: _elasticityMultiplier,
  1162              baseFeeMaxChangeDenominator: _baseFeeMaxChangeDenominator,
  1163              minimumBaseFee: _minimumBaseFee,
  1164              systemTxMaxGas: _systemTxMaxGas,
  1165              maximumBaseFee: _maximumBaseFee
  1166          });
  1167          vm.mockCall(
  1168              address(systemConfig), abi.encodeWithSelector(systemConfig.resourceConfig.selector), abi.encode(rcfg)
  1169          );
  1170  
  1171          // Set the resource params
  1172          uint256 _prevBlockNum = block.number - _blockDiff;
  1173          vm.store(
  1174              address(optimismPortal2),
  1175              bytes32(uint256(1)),
  1176              bytes32((_prevBlockNum << 192) | (uint256(_prevBoughtGas) << 128) | _prevBaseFee)
  1177          );
  1178          // Ensure that the storage setting is correct
  1179          (uint128 prevBaseFee, uint64 prevBoughtGas, uint64 prevBlockNum) = optimismPortal2.params();
  1180          assertEq(prevBaseFee, _prevBaseFee);
  1181          assertEq(prevBoughtGas, _prevBoughtGas);
  1182          assertEq(prevBlockNum, _prevBlockNum);
  1183  
  1184          // Do a deposit, should not revert
  1185          optimismPortal2.depositTransaction{ gas: MAX_GAS_LIMIT }({
  1186              _to: address(0x20),
  1187              _value: 0x40,
  1188              _gasLimit: _gasLimit,
  1189              _isCreation: false,
  1190              _data: hex""
  1191          });
  1192      }
  1193  }