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

     1  // SPDX-License-Identifier: MIT
     2  pragma solidity 0.8.15;
     3  
     4  import { FixedPointMathLib } from "solady/utils/FixedPointMathLib.sol";
     5  
     6  import { IDelayedWETH } from "src/dispute/interfaces/IDelayedWETH.sol";
     7  import { IDisputeGame } from "src/dispute/interfaces/IDisputeGame.sol";
     8  import { IFaultDisputeGame } from "src/dispute/interfaces/IFaultDisputeGame.sol";
     9  import { IInitializable } from "src/dispute/interfaces/IInitializable.sol";
    10  import { IBigStepper, IPreimageOracle } from "src/dispute/interfaces/IBigStepper.sol";
    11  import { IAnchorStateRegistry } from "src/dispute/interfaces/IAnchorStateRegistry.sol";
    12  
    13  import { Clone } from "src/libraries/Clone.sol";
    14  import { Types } from "src/libraries/Types.sol";
    15  import { ISemver } from "src/universal/ISemver.sol";
    16  import { LibClock } from "src/dispute/lib/LibUDT.sol";
    17  
    18  import "src/libraries/DisputeTypes.sol";
    19  import "src/libraries/DisputeErrors.sol";
    20  
    21  /// @title FaultDisputeGame
    22  /// @notice An implementation of the `IFaultDisputeGame` interface.
    23  contract FaultDisputeGame is IFaultDisputeGame, Clone, ISemver {
    24      ////////////////////////////////////////////////////////////////
    25      //                         State Vars                         //
    26      ////////////////////////////////////////////////////////////////
    27  
    28      /// @notice The absolute prestate of the instruction trace. This is a constant that is defined
    29      ///         by the program that is being used to execute the trace.
    30      Claim internal immutable ABSOLUTE_PRESTATE;
    31  
    32      /// @notice The max depth of the game.
    33      uint256 internal immutable MAX_GAME_DEPTH;
    34  
    35      /// @notice The max depth of the output bisection portion of the position tree. Immediately beneath
    36      ///         this depth, execution trace bisection begins.
    37      uint256 internal immutable SPLIT_DEPTH;
    38  
    39      /// @notice The duration of the game.
    40      Duration internal immutable GAME_DURATION;
    41  
    42      /// @notice An onchain VM that performs single instruction steps on a fault proof program trace.
    43      IBigStepper internal immutable VM;
    44  
    45      /// @notice The game type ID.
    46      GameType internal immutable GAME_TYPE;
    47  
    48      /// @notice WETH contract for holding ETH.
    49      IDelayedWETH internal immutable WETH;
    50  
    51      /// @notice The anchor state registry.
    52      IAnchorStateRegistry internal immutable ANCHOR_STATE_REGISTRY;
    53  
    54      /// @notice The chain ID of the L2 network this contract argues about.
    55      uint256 internal immutable L2_CHAIN_ID;
    56  
    57      /// @notice The global root claim's position is always at gindex 1.
    58      Position internal constant ROOT_POSITION = Position.wrap(1);
    59  
    60      /// @notice The flag set in the `bond` field of a `ClaimData` struct to indicate that the bond has been claimed.
    61      uint128 internal constant CLAIMED_BOND_FLAG = type(uint128).max;
    62  
    63      /// @notice The starting timestamp of the game
    64      Timestamp public createdAt;
    65  
    66      /// @notice The timestamp of the game's global resolution.
    67      Timestamp public resolvedAt;
    68  
    69      /// @inheritdoc IDisputeGame
    70      GameStatus public status;
    71  
    72      /// @notice An append-only array of all claims made during the dispute game.
    73      ClaimData[] public claimData;
    74  
    75      /// @notice Credited balances for winning participants.
    76      mapping(address => uint256) public credit;
    77  
    78      /// @notice An internal mapping to allow for constant-time lookups of existing claims.
    79      mapping(ClaimHash => bool) internal claims;
    80  
    81      /// @notice An internal mapping of subgames rooted at a claim index to other claim indices in the subgame.
    82      mapping(uint256 => uint256[]) internal subgames;
    83  
    84      /// @notice Indicates whether the subgame rooted at the root claim has been resolved.
    85      bool internal subgameAtRootResolved;
    86  
    87      /// @notice Flag for the `initialize` function to prevent re-initialization.
    88      bool internal initialized;
    89  
    90      /// @notice The latest finalized output root, serving as the anchor for output bisection.
    91      OutputRoot public startingOutputRoot;
    92  
    93      /// @notice Semantic version.
    94      /// @custom:semver 0.8.1
    95      string public constant version = "0.8.1";
    96  
    97      /// @param _gameType The type ID of the game.
    98      /// @param _absolutePrestate The absolute prestate of the instruction trace.
    99      /// @param _maxGameDepth The maximum depth of bisection.
   100      /// @param _splitDepth The final depth of the output bisection portion of the game.
   101      /// @param _gameDuration The duration of the game.
   102      /// @param _vm An onchain VM that performs single instruction steps on an FPP trace.
   103      /// @param _weth WETH contract for holding ETH.
   104      /// @param _anchorStateRegistry The contract that stores the anchor state for each game type.
   105      /// @param _l2ChainId Chain ID of the L2 network this contract argues about.
   106      constructor(
   107          GameType _gameType,
   108          Claim _absolutePrestate,
   109          uint256 _maxGameDepth,
   110          uint256 _splitDepth,
   111          Duration _gameDuration,
   112          IBigStepper _vm,
   113          IDelayedWETH _weth,
   114          IAnchorStateRegistry _anchorStateRegistry,
   115          uint256 _l2ChainId
   116      ) {
   117          // The split depth cannot be greater than or equal to the max game depth.
   118          if (_splitDepth >= _maxGameDepth) revert InvalidSplitDepth();
   119  
   120          GAME_TYPE = _gameType;
   121          ABSOLUTE_PRESTATE = _absolutePrestate;
   122          MAX_GAME_DEPTH = _maxGameDepth;
   123          SPLIT_DEPTH = _splitDepth;
   124          GAME_DURATION = _gameDuration;
   125          VM = _vm;
   126          WETH = _weth;
   127          ANCHOR_STATE_REGISTRY = _anchorStateRegistry;
   128          L2_CHAIN_ID = _l2ChainId;
   129      }
   130  
   131      /// @notice Receive function to allow the contract to receive ETH.
   132      receive() external payable { }
   133  
   134      /// @notice Fallback function to allow the contract to receive ETH.
   135      fallback() external payable { }
   136  
   137      ////////////////////////////////////////////////////////////////
   138      //                  `IFaultDisputeGame` impl                  //
   139      ////////////////////////////////////////////////////////////////
   140  
   141      /// @inheritdoc IFaultDisputeGame
   142      function step(
   143          uint256 _claimIndex,
   144          bool _isAttack,
   145          bytes calldata _stateData,
   146          bytes calldata _proof
   147      )
   148          public
   149          virtual
   150      {
   151          // INVARIANT: Steps cannot be made unless the game is currently in progress.
   152          if (status != GameStatus.IN_PROGRESS) revert GameNotInProgress();
   153  
   154          // Get the parent. If it does not exist, the call will revert with OOB.
   155          ClaimData storage parent = claimData[_claimIndex];
   156  
   157          // Pull the parent position out of storage.
   158          Position parentPos = parent.position;
   159          // Determine the position of the step.
   160          Position stepPos = parentPos.move(_isAttack);
   161  
   162          // INVARIANT: A step cannot be made unless the move position is 1 below the `MAX_GAME_DEPTH`
   163          if (stepPos.depth() != MAX_GAME_DEPTH + 1) revert InvalidParent();
   164  
   165          // Determine the expected pre & post states of the step.
   166          Claim preStateClaim;
   167          ClaimData storage postState;
   168          if (_isAttack) {
   169              // If the step position's index at depth is 0, the prestate is the absolute
   170              // prestate.
   171              // If the step is an attack at a trace index > 0, the prestate exists elsewhere in
   172              // the game state.
   173              // NOTE: We localize the `indexAtDepth` for the current execution trace subgame by finding
   174              //       the remainder of the index at depth divided by 2 ** (MAX_GAME_DEPTH - SPLIT_DEPTH),
   175              //       which is the number of leaves in each execution trace subgame. This is so that we can
   176              //       determine whether or not the step position is represents the `ABSOLUTE_PRESTATE`.
   177              preStateClaim = (stepPos.indexAtDepth() % (1 << (MAX_GAME_DEPTH - SPLIT_DEPTH))) == 0
   178                  ? ABSOLUTE_PRESTATE
   179                  : _findTraceAncestor(Position.wrap(parentPos.raw() - 1), parent.parentIndex, false).claim;
   180              // For all attacks, the poststate is the parent claim.
   181              postState = parent;
   182          } else {
   183              // If the step is a defense, the poststate exists elsewhere in the game state,
   184              // and the parent claim is the expected pre-state.
   185              preStateClaim = parent.claim;
   186              postState = _findTraceAncestor(Position.wrap(parentPos.raw() + 1), parent.parentIndex, false);
   187          }
   188  
   189          // INVARIANT: The prestate is always invalid if the passed `_stateData` is not the
   190          //            preimage of the prestate claim hash.
   191          //            We ignore the highest order byte of the digest because it is used to
   192          //            indicate the VM Status and is added after the digest is computed.
   193          if (keccak256(_stateData) << 8 != preStateClaim.raw() << 8) revert InvalidPrestate();
   194  
   195          // Compute the local preimage context for the step.
   196          Hash uuid = _findLocalContext(_claimIndex);
   197  
   198          // INVARIANT: If a step is an attack, the poststate is valid if the step produces
   199          //            the same poststate hash as the parent claim's value.
   200          //            If a step is a defense:
   201          //              1. If the parent claim and the found post state agree with each other
   202          //                 (depth diff % 2 == 0), the step is valid if it produces the same
   203          //                 state hash as the post state's claim.
   204          //              2. If the parent claim and the found post state disagree with each other
   205          //                 (depth diff % 2 != 0), the parent cannot be countered unless the step
   206          //                 produces the same state hash as `postState.claim`.
   207          // SAFETY:    While the `attack` path does not need an extra check for the post
   208          //            state's depth in relation to the parent, we don't need another
   209          //            branch because (n - n) % 2 == 0.
   210          bool validStep = VM.step(_stateData, _proof, uuid.raw()) == postState.claim.raw();
   211          bool parentPostAgree = (parentPos.depth() - postState.position.depth()) % 2 == 0;
   212          if (parentPostAgree == validStep) revert ValidStep();
   213  
   214          // INVARIANT: A step cannot be made against a claim for a second time.
   215          if (parent.counteredBy != address(0)) revert DuplicateStep();
   216  
   217          // Set the parent claim as countered. We do not need to append a new claim to the game;
   218          // instead, we can just set the existing parent as countered.
   219          parent.counteredBy = msg.sender;
   220      }
   221  
   222      /// @notice Generic move function, used for both `attack` and `defend` moves.
   223      /// @param _challengeIndex The index of the claim being moved against.
   224      /// @param _claim The claim at the next logical position in the game.
   225      /// @param _isAttack Whether or not the move is an attack or defense.
   226      function move(uint256 _challengeIndex, Claim _claim, bool _isAttack) public payable virtual {
   227          // INVARIANT: Moves cannot be made unless the game is currently in progress.
   228          if (status != GameStatus.IN_PROGRESS) revert GameNotInProgress();
   229  
   230          // Get the parent. If it does not exist, the call will revert with OOB.
   231          ClaimData memory parent = claimData[_challengeIndex];
   232  
   233          // Compute the position that the claim commits to. Because the parent's position is already
   234          // known, we can compute the next position by moving left or right depending on whether
   235          // or not the move is an attack or defense.
   236          Position parentPos = parent.position;
   237          Position nextPosition = parentPos.move(_isAttack);
   238          uint256 nextPositionDepth = nextPosition.depth();
   239  
   240          // INVARIANT: A defense can never be made against the root claim of either the output root game or any
   241          //            of the execution trace bisection subgames. This is because the root claim commits to the
   242          //            entire state. Therefore, the only valid defense is to do nothing if it is agreed with.
   243          if ((_challengeIndex == 0 || nextPositionDepth == SPLIT_DEPTH + 2) && !_isAttack) {
   244              revert CannotDefendRootClaim();
   245          }
   246  
   247          // INVARIANT: A move can never surpass the `MAX_GAME_DEPTH`. The only option to counter a
   248          //            claim at this depth is to perform a single instruction step on-chain via
   249          //            the `step` function to prove that the state transition produces an unexpected
   250          //            post-state.
   251          if (nextPositionDepth > MAX_GAME_DEPTH) revert GameDepthExceeded();
   252  
   253          // When the next position surpasses the split depth (i.e., it is the root claim of an execution
   254          // trace bisection sub-game), we need to perform some extra verification steps.
   255          if (nextPositionDepth == SPLIT_DEPTH + 1) {
   256              _verifyExecBisectionRoot(_claim, _challengeIndex, parentPos, _isAttack);
   257          }
   258  
   259          // INVARIANT: The `msg.value` must be sufficient to cover the required bond.
   260          if (getRequiredBond(nextPosition) > msg.value) revert InsufficientBond();
   261  
   262          // Fetch the grandparent clock, if it exists.
   263          // The grandparent clock should always exist unless the parent is the root claim.
   264          Clock grandparentClock;
   265          if (parent.parentIndex != type(uint32).max) {
   266              grandparentClock = claimData[parent.parentIndex].clock;
   267          }
   268  
   269          // Compute the duration of the next clock. This is done by adding the duration of the
   270          // grandparent claim to the difference between the current block timestamp and the
   271          // parent's clock timestamp.
   272          Duration nextDuration = Duration.wrap(
   273              uint64(
   274                  // First, fetch the duration of the grandparent claim.
   275                  grandparentClock.duration().raw()
   276                  // Second, add the difference between the current block timestamp and the
   277                  // parent's clock timestamp.
   278                  + block.timestamp - parent.clock.timestamp().raw()
   279              )
   280          );
   281  
   282          // INVARIANT: A move can never be made once its clock has exceeded `GAME_DURATION / 2`
   283          //            seconds of time.
   284          if (nextDuration.raw() > GAME_DURATION.raw() >> 1) revert ClockTimeExceeded();
   285  
   286          // Construct the next clock with the new duration and the current block timestamp.
   287          Clock nextClock = LibClock.wrap(nextDuration, Timestamp.wrap(uint64(block.timestamp)));
   288  
   289          // INVARIANT: There cannot be multiple identical claims with identical moves on the same challengeIndex. Multiple
   290          //            claims at the same position may dispute the same challengeIndex. However, they must have different
   291          //            values.
   292          ClaimHash claimHash = _claim.hashClaimPos(nextPosition, _challengeIndex);
   293          if (claims[claimHash]) revert ClaimAlreadyExists();
   294          claims[claimHash] = true;
   295  
   296          // Create the new claim.
   297          claimData.push(
   298              ClaimData({
   299                  parentIndex: uint32(_challengeIndex),
   300                  // This is updated during subgame resolution
   301                  counteredBy: address(0),
   302                  claimant: msg.sender,
   303                  bond: uint128(msg.value),
   304                  claim: _claim,
   305                  position: nextPosition,
   306                  clock: nextClock
   307              })
   308          );
   309  
   310          // Update the subgame rooted at the parent claim.
   311          subgames[_challengeIndex].push(claimData.length - 1);
   312  
   313          // Deposit the bond.
   314          WETH.deposit{ value: msg.value }();
   315  
   316          // Emit the appropriate event for the attack or defense.
   317          emit Move(_challengeIndex, _claim, msg.sender);
   318      }
   319  
   320      /// @inheritdoc IFaultDisputeGame
   321      function attack(uint256 _parentIndex, Claim _claim) external payable {
   322          move(_parentIndex, _claim, true);
   323      }
   324  
   325      /// @inheritdoc IFaultDisputeGame
   326      function defend(uint256 _parentIndex, Claim _claim) external payable {
   327          move(_parentIndex, _claim, false);
   328      }
   329  
   330      /// @inheritdoc IFaultDisputeGame
   331      function addLocalData(uint256 _ident, uint256 _execLeafIdx, uint256 _partOffset) external {
   332          // INVARIANT: Local data can only be added if the game is currently in progress.
   333          if (status != GameStatus.IN_PROGRESS) revert GameNotInProgress();
   334  
   335          (Claim starting, Position startingPos, Claim disputed, Position disputedPos) =
   336              _findStartingAndDisputedOutputs(_execLeafIdx);
   337          Hash uuid = _computeLocalContext(starting, startingPos, disputed, disputedPos);
   338  
   339          IPreimageOracle oracle = VM.oracle();
   340          if (_ident == LocalPreimageKey.L1_HEAD_HASH) {
   341              // Load the L1 head hash
   342              oracle.loadLocalData(_ident, uuid.raw(), l1Head().raw(), 32, _partOffset);
   343          } else if (_ident == LocalPreimageKey.STARTING_OUTPUT_ROOT) {
   344              // Load the starting proposal's output root.
   345              oracle.loadLocalData(_ident, uuid.raw(), starting.raw(), 32, _partOffset);
   346          } else if (_ident == LocalPreimageKey.DISPUTED_OUTPUT_ROOT) {
   347              // Load the disputed proposal's output root
   348              oracle.loadLocalData(_ident, uuid.raw(), disputed.raw(), 32, _partOffset);
   349          } else if (_ident == LocalPreimageKey.DISPUTED_L2_BLOCK_NUMBER) {
   350              // Load the disputed proposal's L2 block number as a big-endian uint64 in the
   351              // high order 8 bytes of the word.
   352  
   353              // We add the index at depth + 1 to the starting block number to get the disputed L2
   354              // block number.
   355              uint256 l2Number = startingOutputRoot.l2BlockNumber + disputedPos.traceIndex(SPLIT_DEPTH) + 1;
   356  
   357              oracle.loadLocalData(_ident, uuid.raw(), bytes32(l2Number << 0xC0), 8, _partOffset);
   358          } else if (_ident == LocalPreimageKey.CHAIN_ID) {
   359              // Load the chain ID as a big-endian uint64 in the high order 8 bytes of the word.
   360              oracle.loadLocalData(_ident, uuid.raw(), bytes32(L2_CHAIN_ID << 0xC0), 8, _partOffset);
   361          } else {
   362              revert InvalidLocalIdent();
   363          }
   364      }
   365  
   366      /// @inheritdoc IFaultDisputeGame
   367      function l1Head() public pure returns (Hash l1Head_) {
   368          l1Head_ = Hash.wrap(_getArgFixedBytes(0x20));
   369      }
   370  
   371      /// @inheritdoc IFaultDisputeGame
   372      function l2BlockNumber() public pure returns (uint256 l2BlockNumber_) {
   373          l2BlockNumber_ = _getArgUint256(0x40);
   374      }
   375  
   376      ////////////////////////////////////////////////////////////////
   377      //                    `IDisputeGame` impl                     //
   378      ////////////////////////////////////////////////////////////////
   379  
   380      /// @inheritdoc IDisputeGame
   381      function gameType() public view override returns (GameType gameType_) {
   382          gameType_ = GAME_TYPE;
   383      }
   384  
   385      /// @inheritdoc IDisputeGame
   386      function resolve() external returns (GameStatus status_) {
   387          // INVARIANT: Resolution cannot occur unless the game is currently in progress.
   388          if (status != GameStatus.IN_PROGRESS) revert GameNotInProgress();
   389  
   390          // INVARIANT: Resolution cannot occur unless the absolute root subgame has been resolved.
   391          if (!subgameAtRootResolved) revert OutOfOrderResolution();
   392  
   393          // Update the global game status; The dispute has concluded.
   394          status_ = claimData[0].counteredBy == address(0) ? GameStatus.DEFENDER_WINS : GameStatus.CHALLENGER_WINS;
   395          resolvedAt = Timestamp.wrap(uint64(block.timestamp));
   396  
   397          // Update the status and emit the resolved event, note that we're performing an assignment here.
   398          emit Resolved(status = status_);
   399  
   400          // Try to update the anchor state, this should not revert.
   401          ANCHOR_STATE_REGISTRY.tryUpdateAnchorState();
   402      }
   403  
   404      /// @inheritdoc IFaultDisputeGame
   405      function resolveClaim(uint256 _claimIndex) external payable {
   406          // INVARIANT: Resolution cannot occur unless the game is currently in progress.
   407          if (status != GameStatus.IN_PROGRESS) revert GameNotInProgress();
   408  
   409          ClaimData storage parent = claimData[_claimIndex];
   410  
   411          // INVARIANT: Cannot resolve a subgame unless the clock of its root has expired
   412          uint64 parentClockDuration = parent.clock.duration().raw();
   413          uint64 timeSinceParentMove = uint64(block.timestamp) - parent.clock.timestamp().raw();
   414          if (parentClockDuration + timeSinceParentMove <= GAME_DURATION.raw() >> 1) {
   415              revert ClockNotExpired();
   416          }
   417  
   418          uint256[] storage challengeIndices = subgames[_claimIndex];
   419          uint256 challengeIndicesLen = challengeIndices.length;
   420  
   421          // INVARIANT: Cannot resolve subgames twice
   422          if (_claimIndex == 0 && subgameAtRootResolved) {
   423              revert ClaimAlreadyResolved();
   424          }
   425  
   426          // Uncontested claims are resolved implicitly unless they are the root claim. Pay out the bond to the claimant
   427          // and return early.
   428          if (challengeIndicesLen == 0 && _claimIndex != 0) {
   429              // In the event that the parent claim is at the max depth, there will always be 0 subgames. If the
   430              // `counteredBy` field is set and there are no subgames, this implies that the parent claim was successfully
   431              // stepped against. In this case, we pay out the bond to the party that stepped against the parent claim.
   432              // Otherwise, the parent claim is uncontested, and the bond is returned to the claimant.
   433              address counteredBy = parent.counteredBy;
   434              address recipient = counteredBy == address(0) ? parent.claimant : counteredBy;
   435              _distributeBond(recipient, parent);
   436              return;
   437          }
   438  
   439          // Assume parent is honest until proven otherwise
   440          address countered = address(0);
   441          Position leftmostCounter = Position.wrap(type(uint128).max);
   442          for (uint256 i = 0; i < challengeIndicesLen; ++i) {
   443              uint256 challengeIndex = challengeIndices[i];
   444  
   445              // INVARIANT: Cannot resolve a subgame containing an unresolved claim
   446              if (subgames[challengeIndex].length != 0) revert OutOfOrderResolution();
   447  
   448              ClaimData storage claim = claimData[challengeIndex];
   449  
   450              // If the child subgame is uncountered and further left than the current left-most counter,
   451              // update the parent subgame's `countered` address and the current `leftmostCounter`.
   452              // The left-most correct counter is preferred in bond payouts in order to discourage attackers
   453              // from countering invalid subgame roots via an invalid defense position. As such positions
   454              // cannot be correctly countered.
   455              // Note that correctly positioned defense, but invalid claimes can still be successfully countered.
   456              if (claim.counteredBy == address(0) && leftmostCounter.raw() > claim.position.raw()) {
   457                  countered = claim.claimant;
   458                  leftmostCounter = claim.position;
   459              }
   460          }
   461  
   462          // If the parent was not successfully countered, pay out the parent's bond to the claimant.
   463          // If the parent was successfully countered, pay out the parent's bond to the challenger.
   464          _distributeBond(countered == address(0) ? parent.claimant : countered, parent);
   465  
   466          // Once a subgame is resolved, we percolate the result up the DAG so subsequent calls to
   467          // resolveClaim will not need to traverse this subgame.
   468          parent.counteredBy = countered;
   469  
   470          // Resolved subgames have no entries
   471          delete subgames[_claimIndex];
   472  
   473          // Indicate the game is ready to be resolved globally.
   474          if (_claimIndex == 0) {
   475              subgameAtRootResolved = true;
   476          }
   477      }
   478  
   479      /// @inheritdoc IDisputeGame
   480      function rootClaim() public pure returns (Claim rootClaim_) {
   481          rootClaim_ = Claim.wrap(_getArgFixedBytes(0x00));
   482      }
   483  
   484      /// @inheritdoc IDisputeGame
   485      function extraData() public pure returns (bytes memory extraData_) {
   486          // The extra data starts at the second word within the cwia calldata and
   487          // is 32 bytes long.
   488          extraData_ = _getArgDynBytes(0x40, 0x20);
   489      }
   490  
   491      /// @inheritdoc IDisputeGame
   492      function gameData() external view returns (GameType gameType_, Claim rootClaim_, bytes memory extraData_) {
   493          gameType_ = gameType();
   494          rootClaim_ = rootClaim();
   495          extraData_ = extraData();
   496      }
   497  
   498      /// @inheritdoc IFaultDisputeGame
   499      function startingBlockNumber() external view returns (uint256 startingBlockNumber_) {
   500          startingBlockNumber_ = startingOutputRoot.l2BlockNumber;
   501      }
   502  
   503      /// @inheritdoc IFaultDisputeGame
   504      function startingRootHash() external view returns (Hash startingRootHash_) {
   505          startingRootHash_ = startingOutputRoot.root;
   506      }
   507  
   508      ////////////////////////////////////////////////////////////////
   509      //                       MISC EXTERNAL                        //
   510      ////////////////////////////////////////////////////////////////
   511  
   512      /// @inheritdoc IInitializable
   513      function initialize() public payable virtual {
   514          // SAFETY: Any revert in this function will bubble up to the DisputeGameFactory and
   515          // prevent the game from being created.
   516          //
   517          // Implicit assumptions:
   518          // - The `gameStatus` state variable defaults to 0, which is `GameStatus.IN_PROGRESS`
   519          // - The dispute game factory will enforce the required bond to initialize the game.
   520          //
   521          // Explicit checks:
   522          // - The game must not have already been initialized.
   523          // - An output root cannot be proposed at or before the starting block number.
   524  
   525          // INVARIANT: The game must not have already been initialized.
   526          if (initialized) revert AlreadyInitialized();
   527  
   528          // Grab the latest anchor root.
   529          (Hash root, uint256 rootBlockNumber) = ANCHOR_STATE_REGISTRY.anchors(GAME_TYPE);
   530  
   531          // Should only happen if this is a new game type that hasn't been set up yet.
   532          if (root.raw() == bytes32(0)) revert AnchorRootNotFound();
   533  
   534          // Set the starting output root.
   535          startingOutputRoot = OutputRoot({ l2BlockNumber: rootBlockNumber, root: root });
   536  
   537          // Do not allow the game to be initialized if the root claim corresponds to a block at or before the
   538          // configured starting block number.
   539          if (l2BlockNumber() <= rootBlockNumber) revert UnexpectedRootClaim(rootClaim());
   540  
   541          // Revert if the calldata size is too large, which signals that the `extraData` contains more than expected.
   542          // This is to prevent adding extra bytes to the `extraData` that result in a different game UUID in the factory,
   543          // but are not used by the game, which would allow for multiple dispute games for the same output proposal to
   544          // be created.
   545          // Expected length: 0x66 (0x04 selector + 0x20 root claim + 0x20 l1 head + 0x20 extraData + 0x02 CWIA bytes)
   546          assembly {
   547              if gt(calldatasize(), 0x66) {
   548                  // Store the selector for `ExtraDataTooLong()` & revert
   549                  mstore(0x00, 0xc407e025)
   550                  revert(0x1C, 0x04)
   551              }
   552          }
   553  
   554          // Set the root claim
   555          claimData.push(
   556              ClaimData({
   557                  parentIndex: type(uint32).max,
   558                  counteredBy: address(0),
   559                  claimant: tx.origin,
   560                  bond: uint128(msg.value),
   561                  claim: rootClaim(),
   562                  position: ROOT_POSITION,
   563                  clock: LibClock.wrap(Duration.wrap(0), Timestamp.wrap(uint64(block.timestamp)))
   564              })
   565          );
   566  
   567          // Deposit the bond.
   568          WETH.deposit{ value: msg.value }();
   569  
   570          // Set the game's starting timestamp
   571          createdAt = Timestamp.wrap(uint64(block.timestamp));
   572  
   573          // Set the game as initialized.
   574          initialized = true;
   575      }
   576  
   577      /// @notice Returns the length of the `claimData` array.
   578      function claimDataLen() external view returns (uint256 len_) {
   579          len_ = claimData.length;
   580      }
   581  
   582      /// @notice Returns the required bond for a given move kind.
   583      /// @param _position The position of the bonded interaction.
   584      /// @return requiredBond_ The required ETH bond for the given move, in wei.
   585      function getRequiredBond(Position _position) public view returns (uint256 requiredBond_) {
   586          uint256 depth = uint256(_position.depth());
   587          if (depth > MAX_GAME_DEPTH) revert GameDepthExceeded();
   588  
   589          // Values taken from Big Bonds v1.5 (TM) spec.
   590          uint256 assumedBaseFee = 200 gwei;
   591          uint256 baseGasCharged = 400_000;
   592          uint256 highGasCharged = 200_000_000;
   593  
   594          // Goal here is to compute the fixed multiplier that will be applied to the base gas
   595          // charged to get the required gas amount for the given depth. We apply this multiplier
   596          // some `n` times where `n` is the depth of the position. We are looking for some number
   597          // that, when multiplied by itself `MAX_GAME_DEPTH` times and then multiplied by the base
   598          // gas charged, will give us the maximum gas that we want to charge.
   599          // We want to solve for (highGasCharged/baseGasCharged) ** (1/MAX_GAME_DEPTH).
   600          // We know that a ** (b/c) is equal to e ** (ln(a) * (b/c)).
   601          // We can compute e ** (ln(a) * (b/c)) quite easily with FixedPointMathLib.
   602  
   603          // Set up a, b, and c.
   604          uint256 a = highGasCharged / baseGasCharged;
   605          uint256 b = FixedPointMathLib.WAD;
   606          uint256 c = MAX_GAME_DEPTH * FixedPointMathLib.WAD;
   607  
   608          // Compute ln(a).
   609          // slither-disable-next-line divide-before-multiply
   610          uint256 lnA = uint256(FixedPointMathLib.lnWad(int256(a * FixedPointMathLib.WAD)));
   611  
   612          // Computes (b / c) with full precision using WAD = 1e18.
   613          uint256 bOverC = FixedPointMathLib.divWad(b, c);
   614  
   615          // Compute e ** (ln(a) * (b/c))
   616          // sMulWad can be used here since WAD = 1e18 maintains the same precision.
   617          uint256 numerator = FixedPointMathLib.mulWad(lnA, bOverC);
   618          int256 base = FixedPointMathLib.expWad(int256(numerator));
   619  
   620          // Compute the required gas amount.
   621          int256 rawGas = FixedPointMathLib.powWad(base, int256(depth * FixedPointMathLib.WAD));
   622          uint256 requiredGas = FixedPointMathLib.mulWad(baseGasCharged, uint256(rawGas));
   623  
   624          // Compute the required bond.
   625          requiredBond_ = assumedBaseFee * requiredGas;
   626      }
   627  
   628      /// @notice Claim the credit belonging to the recipient address.
   629      /// @param _recipient The owner and recipient of the credit.
   630      function claimCredit(address _recipient) external {
   631          // Remove the credit from the recipient prior to performing the external call.
   632          uint256 recipientCredit = credit[_recipient];
   633          credit[_recipient] = 0;
   634  
   635          // Revert if the recipient has no credit to claim.
   636          if (recipientCredit == 0) {
   637              revert NoCreditToClaim();
   638          }
   639  
   640          // Try to withdraw the WETH amount so it can be used here.
   641          WETH.withdraw(_recipient, recipientCredit);
   642  
   643          // Transfer the credit to the recipient.
   644          (bool success,) = _recipient.call{ value: recipientCredit }(hex"");
   645          if (!success) revert BondTransferFailed();
   646      }
   647  
   648      /// @notice Returns the flag set in the `bond` field of a `ClaimData` struct to indicate that the bond has been
   649      ///         claimed.
   650      function claimedBondFlag() external pure returns (uint128 claimedBondFlag_) {
   651          claimedBondFlag_ = CLAIMED_BOND_FLAG;
   652      }
   653  
   654      ////////////////////////////////////////////////////////////////
   655      //                     IMMUTABLE GETTERS                      //
   656      ////////////////////////////////////////////////////////////////
   657  
   658      /// @notice Returns the absolute prestate of the instruction trace.
   659      function absolutePrestate() external view returns (Claim absolutePrestate_) {
   660          absolutePrestate_ = ABSOLUTE_PRESTATE;
   661      }
   662  
   663      /// @notice Returns the max game depth.
   664      function maxGameDepth() external view returns (uint256 maxGameDepth_) {
   665          maxGameDepth_ = MAX_GAME_DEPTH;
   666      }
   667  
   668      /// @notice Returns the split depth.
   669      function splitDepth() external view returns (uint256 splitDepth_) {
   670          splitDepth_ = SPLIT_DEPTH;
   671      }
   672  
   673      /// @notice Returns the game duration.
   674      function gameDuration() external view returns (Duration gameDuration_) {
   675          gameDuration_ = GAME_DURATION;
   676      }
   677  
   678      /// @notice Returns the address of the VM.
   679      function vm() external view returns (IBigStepper vm_) {
   680          vm_ = VM;
   681      }
   682  
   683      /// @notice Returns the WETH contract for holding ETH.
   684      function weth() external view returns (IDelayedWETH weth_) {
   685          weth_ = WETH;
   686      }
   687  
   688      /// @notice Returns the chain ID of the L2 network this contract argues about.
   689      function l2ChainId() external view returns (uint256 l2ChainId_) {
   690          l2ChainId_ = L2_CHAIN_ID;
   691      }
   692  
   693      ////////////////////////////////////////////////////////////////
   694      //                          HELPERS                           //
   695      ////////////////////////////////////////////////////////////////
   696  
   697      /// @notice Pays out the bond of a claim to a given recipient.
   698      /// @param _recipient The recipient of the bond.
   699      /// @param _bonded The claim to pay out the bond of.
   700      function _distributeBond(address _recipient, ClaimData storage _bonded) internal {
   701          // Set all bits in the bond value to indicate that the bond has been paid out.
   702          uint256 bond = _bonded.bond;
   703          if (bond == CLAIMED_BOND_FLAG) revert ClaimAlreadyResolved();
   704          _bonded.bond = CLAIMED_BOND_FLAG;
   705  
   706          // Increase the recipient's credit.
   707          credit[_recipient] += bond;
   708  
   709          // Unlock the bond.
   710          WETH.unlock(_recipient, bond);
   711      }
   712  
   713      /// @notice Verifies the integrity of an execution bisection subgame's root claim. Reverts if the claim
   714      ///         is invalid.
   715      /// @param _rootClaim The root claim of the execution bisection subgame.
   716      function _verifyExecBisectionRoot(
   717          Claim _rootClaim,
   718          uint256 _parentIdx,
   719          Position _parentPos,
   720          bool _isAttack
   721      )
   722          internal
   723          view
   724      {
   725          // The root claim of an execution trace bisection sub-game must:
   726          // 1. Signal that the VM panicked or resulted in an invalid transition if the disputed output root
   727          //    was made by the opposing party.
   728          // 2. Signal that the VM resulted in a valid transition if the disputed output root was made by the same party.
   729  
   730          // If the move is a defense, the disputed output could have been made by either party. In this case, we
   731          // need to search for the parent output to determine what the expected status byte should be.
   732          Position disputedLeafPos = Position.wrap(_parentPos.raw() + 1);
   733          ClaimData storage disputed = _findTraceAncestor({ _pos: disputedLeafPos, _start: _parentIdx, _global: true });
   734          uint8 vmStatus = uint8(_rootClaim.raw()[0]);
   735  
   736          if (_isAttack || disputed.position.depth() % 2 == SPLIT_DEPTH % 2) {
   737              // If the move is an attack, the parent output is always deemed to be disputed. In this case, we only need
   738              // to check that the root claim signals that the VM panicked or resulted in an invalid transition.
   739              // If the move is a defense, and the disputed output and creator of the execution trace subgame disagree,
   740              // the root claim should also signal that the VM panicked or resulted in an invalid transition.
   741              if (!(vmStatus == VMStatuses.INVALID.raw() || vmStatus == VMStatuses.PANIC.raw())) {
   742                  revert UnexpectedRootClaim(_rootClaim);
   743              }
   744          } else if (vmStatus != VMStatuses.VALID.raw()) {
   745              // The disputed output and the creator of the execution trace subgame agree. The status byte should
   746              // have signaled that the VM succeeded.
   747              revert UnexpectedRootClaim(_rootClaim);
   748          }
   749      }
   750  
   751      /// @notice Finds the trace ancestor of a given position within the DAG.
   752      /// @param _pos The position to find the trace ancestor claim of.
   753      /// @param _start The index to start searching from.
   754      /// @param _global Whether or not to search the entire dag or just within an execution trace subgame. If set to
   755      ///                `true`, and `_pos` is at or above the split depth, this function will revert.
   756      /// @return ancestor_ The ancestor claim that commits to the same trace index as `_pos`.
   757      function _findTraceAncestor(
   758          Position _pos,
   759          uint256 _start,
   760          bool _global
   761      )
   762          internal
   763          view
   764          returns (ClaimData storage ancestor_)
   765      {
   766          // Grab the trace ancestor's expected position.
   767          Position traceAncestorPos = _global ? _pos.traceAncestor() : _pos.traceAncestorBounded(SPLIT_DEPTH);
   768  
   769          // Walk up the DAG to find a claim that commits to the same trace index as `_pos`. It is
   770          // guaranteed that such a claim exists.
   771          ancestor_ = claimData[_start];
   772          while (ancestor_.position.raw() != traceAncestorPos.raw()) {
   773              ancestor_ = claimData[ancestor_.parentIndex];
   774          }
   775      }
   776  
   777      /// @notice Finds the starting and disputed output root for a given `ClaimData` within the DAG. This
   778      ///         `ClaimData` must be below the `SPLIT_DEPTH`.
   779      /// @param _start The index within `claimData` of the claim to start searching from.
   780      /// @return startingClaim_ The starting output root claim.
   781      /// @return startingPos_ The starting output root position.
   782      /// @return disputedClaim_ The disputed output root claim.
   783      /// @return disputedPos_ The disputed output root position.
   784      function _findStartingAndDisputedOutputs(uint256 _start)
   785          internal
   786          view
   787          returns (Claim startingClaim_, Position startingPos_, Claim disputedClaim_, Position disputedPos_)
   788      {
   789          // Fatch the starting claim.
   790          uint256 claimIdx = _start;
   791          ClaimData storage claim = claimData[claimIdx];
   792  
   793          // If the starting claim's depth is less than or equal to the split depth, we revert as this is UB.
   794          if (claim.position.depth() <= SPLIT_DEPTH) revert ClaimAboveSplit();
   795  
   796          // We want to:
   797          // 1. Find the first claim at the split depth.
   798          // 2. Determine whether it was the starting or disputed output for the exec game.
   799          // 3. Find the complimentary claim depending on the info from #2 (pre or post).
   800  
   801          // Walk up the DAG until the ancestor's depth is equal to the split depth.
   802          uint256 currentDepth;
   803          ClaimData storage execRootClaim = claim;
   804          while ((currentDepth = claim.position.depth()) > SPLIT_DEPTH) {
   805              uint256 parentIndex = claim.parentIndex;
   806  
   807              // If we're currently at the split depth + 1, we're at the root of the execution sub-game.
   808              // We need to keep track of the root claim here to determine whether the execution sub-game was
   809              // started with an attack or defense against the output leaf claim.
   810              if (currentDepth == SPLIT_DEPTH + 1) execRootClaim = claim;
   811  
   812              claim = claimData[parentIndex];
   813              claimIdx = parentIndex;
   814          }
   815  
   816          // Determine whether the start of the execution sub-game was an attack or defense to the output root
   817          // above. This is important because it determines which claim is the starting output root and which
   818          // is the disputed output root.
   819          (Position execRootPos, Position outputPos) = (execRootClaim.position, claim.position);
   820          bool wasAttack = execRootPos.parent().raw() == outputPos.raw();
   821  
   822          // Determine the starting and disputed output root indices.
   823          // 1. If it was an attack, the disputed output root is `claim`, and the starting output root is
   824          //    elsewhere in the DAG (it must commit to the block # index at depth of `outputPos - 1`).
   825          // 2. If it was a defense, the starting output root is `claim`, and the disputed output root is
   826          //    elsewhere in the DAG (it must commit to the block # index at depth of `outputPos + 1`).
   827          if (wasAttack) {
   828              // If this is an attack on the first output root (the block directly after the starting
   829              // block number), the starting claim nor position exists in the tree. We leave these as
   830              // 0, which can be easily identified due to 0 being an invalid Gindex.
   831              if (outputPos.indexAtDepth() > 0) {
   832                  ClaimData storage starting = _findTraceAncestor(Position.wrap(outputPos.raw() - 1), claimIdx, true);
   833                  (startingClaim_, startingPos_) = (starting.claim, starting.position);
   834              } else {
   835                  startingClaim_ = Claim.wrap(startingOutputRoot.root.raw());
   836              }
   837              (disputedClaim_, disputedPos_) = (claim.claim, claim.position);
   838          } else {
   839              ClaimData storage disputed = _findTraceAncestor(Position.wrap(outputPos.raw() + 1), claimIdx, true);
   840              (startingClaim_, startingPos_) = (claim.claim, claim.position);
   841              (disputedClaim_, disputedPos_) = (disputed.claim, disputed.position);
   842          }
   843      }
   844  
   845      /// @notice Finds the local context hash for a given claim index that is present in an execution trace subgame.
   846      /// @param _claimIndex The index of the claim to find the local context hash for.
   847      /// @return uuid_ The local context hash.
   848      function _findLocalContext(uint256 _claimIndex) internal view returns (Hash uuid_) {
   849          (Claim starting, Position startingPos, Claim disputed, Position disputedPos) =
   850              _findStartingAndDisputedOutputs(_claimIndex);
   851          uuid_ = _computeLocalContext(starting, startingPos, disputed, disputedPos);
   852      }
   853  
   854      /// @notice Computes the local context hash for a set of starting/disputed claim values and positions.
   855      /// @param _starting The starting claim.
   856      /// @param _startingPos The starting claim's position.
   857      /// @param _disputed The disputed claim.
   858      /// @param _disputedPos The disputed claim's position.
   859      /// @return uuid_ The local context hash.
   860      function _computeLocalContext(
   861          Claim _starting,
   862          Position _startingPos,
   863          Claim _disputed,
   864          Position _disputedPos
   865      )
   866          internal
   867          pure
   868          returns (Hash uuid_)
   869      {
   870          // A position of 0 indicates that the starting claim is the absolute prestate. In this special case,
   871          // we do not include the starting claim within the local context hash.
   872          if (_startingPos.raw() == 0) {
   873              uuid_ = Hash.wrap(keccak256(abi.encode(_disputed, _disputedPos)));
   874          } else {
   875              uuid_ = Hash.wrap(keccak256(abi.encode(_starting, _startingPos, _disputed, _disputedPos)));
   876          }
   877      }
   878  }