github.com/ethereum-optimism/optimism@v1.7.2/packages/contracts-bedrock/test/dispute/FaultDisputeGame.t.sol (about) 1 // SPDX-License-Identifier: MIT 2 pragma solidity ^0.8.15; 3 4 import { Test } from "forge-std/Test.sol"; 5 import { Vm } from "forge-std/Vm.sol"; 6 import { DisputeGameFactory_Init } from "test/dispute/DisputeGameFactory.t.sol"; 7 import { DisputeGameFactory } from "src/dispute/DisputeGameFactory.sol"; 8 import { FaultDisputeGame } from "src/dispute/FaultDisputeGame.sol"; 9 import { DelayedWETH } from "src/dispute/weth/DelayedWETH.sol"; 10 import { PreimageOracle } from "src/cannon/PreimageOracle.sol"; 11 12 import "src/libraries/DisputeTypes.sol"; 13 import "src/libraries/DisputeErrors.sol"; 14 import { LibClock } from "src/dispute/lib/LibUDT.sol"; 15 import { LibPosition } from "src/dispute/lib/LibPosition.sol"; 16 import { IPreimageOracle } from "src/dispute/interfaces/IBigStepper.sol"; 17 import { IAnchorStateRegistry } from "src/dispute/interfaces/IAnchorStateRegistry.sol"; 18 import { AlphabetVM } from "test/mocks/AlphabetVM.sol"; 19 20 import { DisputeActor, HonestDisputeActor } from "test/actors/FaultDisputeActors.sol"; 21 22 contract FaultDisputeGame_Init is DisputeGameFactory_Init { 23 /// @dev The type of the game being tested. 24 GameType internal constant GAME_TYPE = GameType.wrap(0); 25 26 /// @dev The implementation of the game. 27 FaultDisputeGame internal gameImpl; 28 /// @dev The `Clone` proxy of the game. 29 FaultDisputeGame internal gameProxy; 30 31 /// @dev The extra data passed to the game for initialization. 32 bytes internal extraData; 33 34 event Move(uint256 indexed parentIndex, Claim indexed pivot, address indexed claimant); 35 36 function init(Claim rootClaim, Claim absolutePrestate, uint256 l2BlockNumber) public { 37 // Set the time to a realistic date. 38 vm.warp(1690906994); 39 40 // Set the extra data for the game creation 41 extraData = abi.encode(l2BlockNumber); 42 43 AlphabetVM _vm = new AlphabetVM(absolutePrestate, new PreimageOracle(0, 0)); 44 45 // Deploy an implementation of the fault game 46 gameImpl = new FaultDisputeGame({ 47 _gameType: GAME_TYPE, 48 _absolutePrestate: absolutePrestate, 49 _maxGameDepth: 2 ** 3, 50 _splitDepth: 2 ** 2, 51 _gameDuration: Duration.wrap(7 days), 52 _vm: _vm, 53 _weth: delayedWeth, 54 _anchorStateRegistry: anchorStateRegistry, 55 _l2ChainId: 10 56 }); 57 58 // Register the game implementation with the factory. 59 disputeGameFactory.setImplementation(GAME_TYPE, gameImpl); 60 // Create a new game. 61 gameProxy = FaultDisputeGame(payable(address(disputeGameFactory.create(GAME_TYPE, rootClaim, extraData)))); 62 63 // Check immutables 64 assertEq(gameProxy.gameType().raw(), GAME_TYPE.raw()); 65 assertEq(gameProxy.absolutePrestate().raw(), absolutePrestate.raw()); 66 assertEq(gameProxy.maxGameDepth(), 2 ** 3); 67 assertEq(gameProxy.splitDepth(), 2 ** 2); 68 assertEq(gameProxy.gameDuration().raw(), 7 days); 69 assertEq(address(gameProxy.vm()), address(_vm)); 70 71 // Label the proxy 72 vm.label(address(gameProxy), "FaultDisputeGame_Clone"); 73 } 74 75 fallback() external payable { } 76 77 receive() external payable { } 78 } 79 80 contract FaultDisputeGame_Test is FaultDisputeGame_Init { 81 /// @dev The root claim of the game. 82 Claim internal constant ROOT_CLAIM = Claim.wrap(bytes32((uint256(1) << 248) | uint256(10))); 83 84 /// @dev The preimage of the absolute prestate claim 85 bytes internal absolutePrestateData; 86 /// @dev The absolute prestate of the trace. 87 Claim internal absolutePrestate; 88 89 /// @dev Minimum bond value that covers all possible moves. 90 uint256 internal constant MIN_BOND = 50 ether; 91 92 function setUp() public override { 93 absolutePrestateData = abi.encode(0); 94 absolutePrestate = _changeClaimStatus(Claim.wrap(keccak256(absolutePrestateData)), VMStatuses.UNFINISHED); 95 96 super.setUp(); 97 super.init({ rootClaim: ROOT_CLAIM, absolutePrestate: absolutePrestate, l2BlockNumber: 0x10 }); 98 } 99 100 //////////////////////////////////////////////////////////////// 101 // `IDisputeGame` Implementation Tests // 102 //////////////////////////////////////////////////////////////// 103 104 /// @dev Tests that the constructor of the `FaultDisputeGame` reverts when the `_splitDepth` 105 /// parameter is greater than or equal to the `MAX_GAME_DEPTH` 106 function test_constructor_wrongArgs_reverts(uint256 _splitDepth) public { 107 AlphabetVM alphabetVM = new AlphabetVM(absolutePrestate, new PreimageOracle(0, 0)); 108 109 // Test that the constructor reverts when the `_splitDepth` parameter is greater than or equal 110 // to the `MAX_GAME_DEPTH` parameter. 111 _splitDepth = bound(_splitDepth, 2 ** 3, type(uint256).max); 112 vm.expectRevert(InvalidSplitDepth.selector); 113 new FaultDisputeGame({ 114 _gameType: GAME_TYPE, 115 _absolutePrestate: absolutePrestate, 116 _maxGameDepth: 2 ** 3, 117 _splitDepth: _splitDepth, 118 _gameDuration: Duration.wrap(7 days), 119 _vm: alphabetVM, 120 _weth: DelayedWETH(payable(address(0))), 121 _anchorStateRegistry: IAnchorStateRegistry(address(0)), 122 _l2ChainId: 10 123 }); 124 } 125 126 /// @dev Tests that the game's root claim is set correctly. 127 function test_rootClaim_succeeds() public { 128 assertEq(gameProxy.rootClaim().raw(), ROOT_CLAIM.raw()); 129 } 130 131 /// @dev Tests that the game's extra data is set correctly. 132 function test_extraData_succeeds() public { 133 assertEq(gameProxy.extraData(), extraData); 134 } 135 136 /// @dev Tests that the game's starting timestamp is set correctly. 137 function test_createdAt_succeeds() public { 138 assertEq(gameProxy.createdAt().raw(), block.timestamp); 139 } 140 141 /// @dev Tests that the game's type is set correctly. 142 function test_gameType_succeeds() public { 143 assertEq(gameProxy.gameType().raw(), GAME_TYPE.raw()); 144 } 145 146 /// @dev Tests that the game's data is set correctly. 147 function test_gameData_succeeds() public { 148 (GameType gameType, Claim rootClaim, bytes memory _extraData) = gameProxy.gameData(); 149 150 assertEq(gameType.raw(), GAME_TYPE.raw()); 151 assertEq(rootClaim.raw(), ROOT_CLAIM.raw()); 152 assertEq(_extraData, extraData); 153 } 154 155 //////////////////////////////////////////////////////////////// 156 // `IFaultDisputeGame` Implementation Tests // 157 //////////////////////////////////////////////////////////////// 158 159 /// @dev Tests that the game cannot be initialized with an output root that commits to <= the configured starting 160 /// block number 161 function testFuzz_initialize_cannotProposeGenesis_reverts(uint256 _blockNumber) public { 162 (, uint256 startingL2Block) = gameProxy.startingOutputRoot(); 163 _blockNumber = bound(_blockNumber, 0, startingL2Block); 164 165 Claim claim = _dummyClaim(); 166 vm.expectRevert(abi.encodeWithSelector(UnexpectedRootClaim.selector, claim)); 167 gameProxy = 168 FaultDisputeGame(payable(address(disputeGameFactory.create(GAME_TYPE, claim, abi.encode(_blockNumber))))); 169 } 170 171 /// @dev Tests that the proxy receives ETH from the dispute game factory. 172 function test_initialize_receivesETH_succeeds(uint128 _value) public { 173 _value = uint128(bound(_value, gameProxy.getRequiredBond(Position.wrap(1)), type(uint128).max)); 174 vm.deal(address(this), _value); 175 176 assertEq(address(gameProxy).balance, 0); 177 gameProxy = FaultDisputeGame( 178 payable(address(disputeGameFactory.create{ value: _value }(GAME_TYPE, ROOT_CLAIM, abi.encode(1)))) 179 ); 180 assertEq(address(gameProxy).balance, 0); 181 assertEq(delayedWeth.balanceOf(address(gameProxy)), _value); 182 } 183 184 /// @dev Tests that the game cannot be initialized with extra data > 64 bytes long (root claim + l2 block number 185 /// concatenated) 186 function testFuzz_initialize_extraDataTooLong_reverts(uint256 _extraDataLen) public { 187 // The `DisputeGameFactory` will pack the root claim and the extra data into a single array, which is enforced 188 // to be at least 64 bytes long. 189 // We bound the upper end to 23.5KB to ensure that the minimal proxy never surpasses the contract size limit 190 // in this test, as CWIA proxies store the immutable args in their bytecode. 191 // [33 bytes, 23.5 KB] 192 _extraDataLen = bound(_extraDataLen, 33, 23_500); 193 bytes memory _extraData = new bytes(_extraDataLen); 194 195 // Assign the first 32 bytes in `extraData` to a valid L2 block number passed the starting block. 196 (, uint256 startingL2Block) = gameProxy.startingOutputRoot(); 197 assembly { 198 mstore(add(_extraData, 0x20), add(startingL2Block, 1)) 199 } 200 201 Claim claim = _dummyClaim(); 202 vm.expectRevert(abi.encodeWithSelector(ExtraDataTooLong.selector)); 203 gameProxy = FaultDisputeGame(payable(address(disputeGameFactory.create(GAME_TYPE, claim, _extraData)))); 204 } 205 206 /// @dev Tests that the game is initialized with the correct data. 207 function test_initialize_correctData_succeeds() public { 208 // Assert that the root claim is initialized correctly. 209 ( 210 uint32 parentIndex, 211 address counteredBy, 212 address claimant, 213 uint128 bond, 214 Claim claim, 215 Position position, 216 Clock clock 217 ) = gameProxy.claimData(0); 218 assertEq(parentIndex, type(uint32).max); 219 assertEq(counteredBy, address(0)); 220 assertEq(claimant, DEFAULT_SENDER); 221 assertEq(bond, 0); 222 assertEq(claim.raw(), ROOT_CLAIM.raw()); 223 assertEq(position.raw(), 1); 224 assertEq(clock.raw(), LibClock.wrap(Duration.wrap(0), Timestamp.wrap(uint64(block.timestamp))).raw()); 225 226 // Assert that the `createdAt` timestamp is correct. 227 assertEq(gameProxy.createdAt().raw(), block.timestamp); 228 229 // Assert that the blockhash provided is correct. 230 assertEq(gameProxy.l1Head().raw(), blockhash(block.number - 1)); 231 } 232 233 /// @dev Tests that the game cannot be initialized twice. 234 function test_initialize_onlyOnce_succeeds() public { 235 vm.expectRevert(AlreadyInitialized.selector); 236 gameProxy.initialize(); 237 } 238 239 /// @dev Tests that the bond during the bisection game depths is correct. 240 function test_getRequiredBond_succeeds() public { 241 for (uint64 i = 0; i < uint64(gameProxy.splitDepth()); i++) { 242 Position pos = LibPosition.wrap(i, 0); 243 uint256 bond = gameProxy.getRequiredBond(pos); 244 245 // Reasonable approximation for a max depth of 8. 246 uint256 expected = 0.08 ether; 247 for (uint64 j = 0; j < i; j++) { 248 expected = expected * 217456; 249 expected = expected / 100000; 250 } 251 252 assertApproxEqAbs(bond, expected, 0.01 ether); 253 } 254 } 255 256 /// @dev Tests that the bond at a depth greater than the maximum game depth reverts. 257 function test_getRequiredBond_outOfBounds_reverts() public { 258 Position pos = LibPosition.wrap(uint64(gameProxy.maxGameDepth() + 1), 0); 259 vm.expectRevert(GameDepthExceeded.selector); 260 gameProxy.getRequiredBond(pos); 261 } 262 263 /// @dev Tests that a move while the game status is not `IN_PROGRESS` causes the call to revert 264 /// with the `GameNotInProgress` error 265 function test_move_gameNotInProgress_reverts() public { 266 uint256 chalWins = uint256(GameStatus.CHALLENGER_WINS); 267 268 // Replace the game status in storage. It exists in slot 0 at offset 16. 269 uint256 slot = uint256(vm.load(address(gameProxy), bytes32(0))); 270 uint256 offset = 16 << 3; 271 uint256 mask = 0xFF << offset; 272 // Replace the byte in the slot value with the challenger wins status. 273 slot = (slot & ~mask) | (chalWins << offset); 274 vm.store(address(gameProxy), bytes32(0), bytes32(slot)); 275 276 // Ensure that the game status was properly updated. 277 GameStatus status = gameProxy.status(); 278 assertEq(uint256(status), chalWins); 279 280 // Attempt to make a move. Should revert. 281 vm.expectRevert(GameNotInProgress.selector); 282 gameProxy.attack(0, Claim.wrap(0)); 283 } 284 285 /// @dev Tests that an attempt to defend the root claim reverts with the `CannotDefendRootClaim` error. 286 function test_move_defendRoot_reverts() public { 287 vm.expectRevert(CannotDefendRootClaim.selector); 288 gameProxy.defend(0, _dummyClaim()); 289 } 290 291 /// @dev Tests that an attempt to move against a claim that does not exist reverts with the 292 /// `ParentDoesNotExist` error. 293 function test_move_nonExistentParent_reverts() public { 294 Claim claim = _dummyClaim(); 295 296 // Expect an out of bounds revert for an attack 297 vm.expectRevert(abi.encodeWithSignature("Panic(uint256)", 0x32)); 298 gameProxy.attack(1, claim); 299 300 // Expect an out of bounds revert for a defense 301 vm.expectRevert(abi.encodeWithSignature("Panic(uint256)", 0x32)); 302 gameProxy.defend(1, claim); 303 } 304 305 /// @dev Tests that an attempt to move at the maximum game depth reverts with the 306 /// `GameDepthExceeded` error. 307 function test_move_gameDepthExceeded_reverts() public { 308 Claim claim = _changeClaimStatus(_dummyClaim(), VMStatuses.PANIC); 309 310 uint256 maxDepth = gameProxy.maxGameDepth(); 311 312 for (uint256 i = 0; i <= maxDepth; i++) { 313 // At the max game depth, the `_move` function should revert with 314 // the `GameDepthExceeded` error. 315 if (i == maxDepth) { 316 vm.expectRevert(GameDepthExceeded.selector); 317 } 318 gameProxy.attack{ value: MIN_BOND }(i, claim); 319 } 320 } 321 322 /// @dev Tests that a move made after the clock time has exceeded reverts with the 323 /// `ClockTimeExceeded` error. 324 function test_move_clockTimeExceeded_reverts() public { 325 // Warp ahead past the clock time for the first move (3 1/2 days) 326 vm.warp(block.timestamp + 3 days + 12 hours + 1); 327 vm.expectRevert(ClockTimeExceeded.selector); 328 gameProxy.attack{ value: MIN_BOND }(0, _dummyClaim()); 329 } 330 331 /// @notice Static unit test for the correctness of the chess clock incrementation. 332 function test_move_clockCorrectness_succeeds() public { 333 (,,,,,, Clock clock) = gameProxy.claimData(0); 334 assertEq(clock.raw(), LibClock.wrap(Duration.wrap(0), Timestamp.wrap(uint64(block.timestamp))).raw()); 335 336 Claim claim = _dummyClaim(); 337 338 vm.warp(block.timestamp + 15); 339 gameProxy.attack{ value: MIN_BOND }(0, claim); 340 (,,,,,, clock) = gameProxy.claimData(1); 341 assertEq(clock.raw(), LibClock.wrap(Duration.wrap(15), Timestamp.wrap(uint64(block.timestamp))).raw()); 342 343 vm.warp(block.timestamp + 10); 344 gameProxy.attack{ value: MIN_BOND }(1, claim); 345 (,,,,,, clock) = gameProxy.claimData(2); 346 assertEq(clock.raw(), LibClock.wrap(Duration.wrap(10), Timestamp.wrap(uint64(block.timestamp))).raw()); 347 348 // We are at the split depth, so we need to set the status byte of the claim 349 // for the next move. 350 claim = _changeClaimStatus(claim, VMStatuses.PANIC); 351 352 vm.warp(block.timestamp + 10); 353 gameProxy.attack{ value: MIN_BOND }(2, claim); 354 (,,,,,, clock) = gameProxy.claimData(3); 355 assertEq(clock.raw(), LibClock.wrap(Duration.wrap(25), Timestamp.wrap(uint64(block.timestamp))).raw()); 356 357 vm.warp(block.timestamp + 10); 358 gameProxy.attack{ value: MIN_BOND }(3, claim); 359 (,,,,,, clock) = gameProxy.claimData(4); 360 assertEq(clock.raw(), LibClock.wrap(Duration.wrap(20), Timestamp.wrap(uint64(block.timestamp))).raw()); 361 } 362 363 /// @dev Tests that an identical claim cannot be made twice. The duplicate claim attempt should 364 /// revert with the `ClaimAlreadyExists` error. 365 function test_move_duplicateClaim_reverts() public { 366 Claim claim = _dummyClaim(); 367 368 // Make the first move. This should succeed. 369 gameProxy.attack{ value: MIN_BOND }(0, claim); 370 371 // Attempt to make the same move again. 372 vm.expectRevert(ClaimAlreadyExists.selector); 373 gameProxy.attack{ value: MIN_BOND }(0, claim); 374 } 375 376 /// @dev Static unit test asserting that identical claims at the same position can be made in different subgames. 377 function test_move_duplicateClaimsDifferentSubgames_succeeds() public { 378 Claim claimA = _dummyClaim(); 379 Claim claimB = _dummyClaim(); 380 381 // Make the first moves. This should succeed. 382 gameProxy.attack{ value: MIN_BOND }(0, claimA); 383 gameProxy.attack{ value: MIN_BOND }(0, claimB); 384 385 // Perform an attack at the same position with the same claim value in both subgames. 386 // These both should succeed. 387 gameProxy.attack{ value: MIN_BOND }(1, claimA); 388 gameProxy.attack{ value: MIN_BOND }(2, claimA); 389 } 390 391 /// @dev Static unit test for the correctness of an opening attack. 392 function test_move_simpleAttack_succeeds() public { 393 // Warp ahead 5 seconds. 394 vm.warp(block.timestamp + 5); 395 396 Claim counter = _dummyClaim(); 397 398 // Perform the attack. 399 vm.expectEmit(true, true, true, false); 400 emit Move(0, counter, address(this)); 401 gameProxy.attack{ value: MIN_BOND }(0, counter); 402 403 // Grab the claim data of the attack. 404 ( 405 uint32 parentIndex, 406 address counteredBy, 407 address claimant, 408 uint128 bond, 409 Claim claim, 410 Position position, 411 Clock clock 412 ) = gameProxy.claimData(1); 413 414 // Assert correctness of the attack claim's data. 415 assertEq(parentIndex, 0); 416 assertEq(counteredBy, address(0)); 417 assertEq(claimant, address(this)); 418 assertEq(bond, MIN_BOND); 419 assertEq(claim.raw(), counter.raw()); 420 assertEq(position.raw(), Position.wrap(1).move(true).raw()); 421 assertEq(clock.raw(), LibClock.wrap(Duration.wrap(5), Timestamp.wrap(uint64(block.timestamp))).raw()); 422 423 // Grab the claim data of the parent. 424 (parentIndex, counteredBy, claimant, bond, claim, position, clock) = gameProxy.claimData(0); 425 426 // Assert correctness of the parent claim's data. 427 assertEq(parentIndex, type(uint32).max); 428 assertEq(counteredBy, address(0)); 429 assertEq(claimant, DEFAULT_SENDER); 430 assertEq(bond, 0); 431 assertEq(claim.raw(), ROOT_CLAIM.raw()); 432 assertEq(position.raw(), 1); 433 assertEq(clock.raw(), LibClock.wrap(Duration.wrap(0), Timestamp.wrap(uint64(block.timestamp - 5))).raw()); 434 } 435 436 /// @dev Tests that making a claim at the execution trace bisection root level with an invalid status 437 /// byte reverts with the `UnexpectedRootClaim` error. 438 function test_move_incorrectStatusExecRoot_reverts() public { 439 for (uint256 i; i < 4; i++) { 440 gameProxy.attack{ value: MIN_BOND }(i, _dummyClaim()); 441 } 442 443 vm.expectRevert(abi.encodeWithSelector(UnexpectedRootClaim.selector, bytes32(0))); 444 gameProxy.attack{ value: MIN_BOND }(4, Claim.wrap(bytes32(0))); 445 } 446 447 /// @dev Tests that making a claim at the execution trace bisection root level with a valid status 448 /// byte succeeds. 449 function test_move_correctStatusExecRoot_succeeds() public { 450 for (uint256 i; i < 4; i++) { 451 gameProxy.attack{ value: MIN_BOND }(i, _dummyClaim()); 452 } 453 gameProxy.attack{ value: MIN_BOND }(4, _changeClaimStatus(_dummyClaim(), VMStatuses.PANIC)); 454 } 455 456 /// @dev Static unit test asserting that a move reverts when the bond is insufficient. 457 function test_move_insufficientBond_reverts() public { 458 vm.expectRevert(InsufficientBond.selector); 459 gameProxy.attack{ value: 0 }(0, _dummyClaim()); 460 } 461 462 /// @dev Tests that a claim cannot be stepped against twice. 463 function test_step_duplicateStep_reverts() public { 464 // Give the test contract some ether 465 vm.deal(address(this), 1000 ether); 466 467 // Make claims all the way down the tree. 468 gameProxy.attack{ value: MIN_BOND }(0, _dummyClaim()); 469 gameProxy.attack{ value: MIN_BOND }(1, _dummyClaim()); 470 gameProxy.attack{ value: MIN_BOND }(2, _dummyClaim()); 471 gameProxy.attack{ value: MIN_BOND }(3, _dummyClaim()); 472 gameProxy.attack{ value: MIN_BOND }(4, _changeClaimStatus(_dummyClaim(), VMStatuses.PANIC)); 473 gameProxy.attack{ value: MIN_BOND }(5, _dummyClaim()); 474 gameProxy.attack{ value: MIN_BOND }(6, _dummyClaim()); 475 gameProxy.attack{ value: MIN_BOND }(7, _dummyClaim()); 476 gameProxy.addLocalData(LocalPreimageKey.DISPUTED_L2_BLOCK_NUMBER, 8, 0); 477 gameProxy.step(8, true, absolutePrestateData, hex""); 478 479 vm.expectRevert(DuplicateStep.selector); 480 gameProxy.step(8, true, absolutePrestateData, hex""); 481 } 482 483 /// @dev Static unit test for the correctness an uncontested root resolution. 484 function test_resolve_rootUncontested_succeeds() public { 485 vm.warp(block.timestamp + 3 days + 12 hours + 1 seconds); 486 gameProxy.resolveClaim(0); 487 assertEq(uint8(gameProxy.resolve()), uint8(GameStatus.DEFENDER_WINS)); 488 } 489 490 /// @dev Static unit test for the correctness an uncontested root resolution. 491 function test_resolve_rootUncontestedClockNotExpired_succeeds() public { 492 vm.warp(block.timestamp + 3 days + 12 hours); 493 vm.expectRevert(ClockNotExpired.selector); 494 gameProxy.resolveClaim(0); 495 } 496 497 /// @dev Static unit test asserting that resolve reverts when the absolute root 498 /// subgame has not been resolved. 499 function test_resolve_rootUncontestedButUnresolved_reverts() public { 500 vm.warp(block.timestamp + 3 days + 12 hours + 1 seconds); 501 vm.expectRevert(OutOfOrderResolution.selector); 502 gameProxy.resolve(); 503 } 504 505 /// @dev Static unit test asserting that resolve reverts when the game state is 506 /// not in progress. 507 function test_resolve_notInProgress_reverts() public { 508 uint256 chalWins = uint256(GameStatus.CHALLENGER_WINS); 509 510 // Replace the game status in storage. It exists in slot 0 at offset 16. 511 uint256 slot = uint256(vm.load(address(gameProxy), bytes32(0))); 512 uint256 offset = 16 << 3; 513 uint256 mask = 0xFF << offset; 514 // Replace the byte in the slot value with the challenger wins status. 515 slot = (slot & ~mask) | (chalWins << offset); 516 517 vm.store(address(gameProxy), bytes32(uint256(0)), bytes32(slot)); 518 vm.expectRevert(GameNotInProgress.selector); 519 gameProxy.resolveClaim(0); 520 } 521 522 /// @dev Static unit test for the correctness of resolving a single attack game state. 523 function test_resolve_rootContested_succeeds() public { 524 gameProxy.attack{ value: MIN_BOND }(0, _dummyClaim()); 525 526 vm.warp(block.timestamp + 3 days + 12 hours + 1 seconds); 527 528 gameProxy.resolveClaim(0); 529 assertEq(uint8(gameProxy.resolve()), uint8(GameStatus.CHALLENGER_WINS)); 530 } 531 532 /// @dev Static unit test for the correctness of resolving a game with a contested challenge claim. 533 function test_resolve_challengeContested_succeeds() public { 534 gameProxy.attack{ value: MIN_BOND }(0, _dummyClaim()); 535 gameProxy.defend{ value: MIN_BOND }(1, _dummyClaim()); 536 537 vm.warp(block.timestamp + 3 days + 12 hours + 1 seconds); 538 539 gameProxy.resolveClaim(1); 540 gameProxy.resolveClaim(0); 541 assertEq(uint8(gameProxy.resolve()), uint8(GameStatus.DEFENDER_WINS)); 542 } 543 544 /// @dev Static unit test for the correctness of resolving a game with multiplayer moves. 545 function test_resolve_teamDeathmatch_succeeds() public { 546 gameProxy.attack{ value: MIN_BOND }(0, _dummyClaim()); 547 gameProxy.attack{ value: MIN_BOND }(0, _dummyClaim()); 548 gameProxy.defend{ value: MIN_BOND }(1, _dummyClaim()); 549 gameProxy.defend{ value: MIN_BOND }(1, _dummyClaim()); 550 551 vm.warp(block.timestamp + 3 days + 12 hours + 1 seconds); 552 553 gameProxy.resolveClaim(1); 554 gameProxy.resolveClaim(0); 555 assertEq(uint8(gameProxy.resolve()), uint8(GameStatus.CHALLENGER_WINS)); 556 } 557 558 /// @dev Static unit test for the correctness of resolving a game that reaches max game depth. 559 function test_resolve_stepReached_succeeds() public { 560 Claim claim = _dummyClaim(); 561 for (uint256 i; i < gameProxy.splitDepth(); i++) { 562 gameProxy.attack{ value: MIN_BOND }(i, claim); 563 } 564 565 claim = _changeClaimStatus(claim, VMStatuses.PANIC); 566 for (uint256 i = gameProxy.claimDataLen() - 1; i < gameProxy.maxGameDepth(); i++) { 567 gameProxy.attack{ value: MIN_BOND }(i, claim); 568 } 569 570 vm.warp(block.timestamp + 3 days + 12 hours + 1 seconds); 571 572 // resolving claim at 8 isn't necessary 573 for (uint256 i = 8; i > 0; i--) { 574 gameProxy.resolveClaim(i - 1); 575 } 576 assertEq(uint8(gameProxy.resolve()), uint8(GameStatus.DEFENDER_WINS)); 577 } 578 579 /// @dev Static unit test asserting that resolve reverts when attempting to resolve a subgame multiple times 580 function test_resolve_claimAlreadyResolved_reverts() public { 581 vm.deal(address(this), 2 * MIN_BOND); 582 583 Claim claim = _dummyClaim(); 584 gameProxy.attack{ value: MIN_BOND }(0, claim); 585 gameProxy.attack{ value: MIN_BOND }(1, claim); 586 587 vm.warp(block.timestamp + 3 days + 12 hours + 1 seconds); 588 589 assertEq(address(this).balance, 0); 590 gameProxy.resolveClaim(1); 591 592 // Wait for the withdrawal delay. 593 vm.warp(block.timestamp + delayedWeth.delay() + 1 seconds); 594 595 gameProxy.claimCredit(address(this)); 596 assertEq(address(this).balance, MIN_BOND); 597 598 vm.expectRevert(ClaimAlreadyResolved.selector); 599 gameProxy.resolveClaim(1); 600 assertEq(address(this).balance, MIN_BOND); 601 } 602 603 /// @dev Static unit test asserting that resolve reverts when attempting to resolve a subgame at max depth 604 function test_resolve_claimAtMaxDepthAlreadyResolved_reverts() public { 605 Claim claim = _dummyClaim(); 606 for (uint256 i; i < gameProxy.splitDepth(); i++) { 607 gameProxy.attack{ value: MIN_BOND }(i, claim); 608 } 609 610 vm.deal(address(this), 10000 ether); 611 claim = _changeClaimStatus(claim, VMStatuses.PANIC); 612 for (uint256 i = gameProxy.claimDataLen() - 1; i < gameProxy.maxGameDepth(); i++) { 613 gameProxy.attack{ value: MIN_BOND }(i, claim); 614 } 615 616 vm.warp(block.timestamp + 3 days + 12 hours + 1 seconds); 617 618 // Resolve to claim bond 619 uint256 balanceBefore = address(this).balance; 620 gameProxy.resolveClaim(8); 621 622 // Wait for the withdrawal delay. 623 vm.warp(block.timestamp + delayedWeth.delay() + 1 seconds); 624 625 gameProxy.claimCredit(address(this)); 626 assertEq(address(this).balance, balanceBefore + MIN_BOND); 627 628 vm.expectRevert(ClaimAlreadyResolved.selector); 629 gameProxy.resolveClaim(8); 630 } 631 632 /// @dev Static unit test asserting that resolve reverts when attempting to resolve subgames out of order 633 function test_resolve_outOfOrderResolution_reverts() public { 634 gameProxy.attack{ value: MIN_BOND }(0, _dummyClaim()); 635 gameProxy.attack{ value: MIN_BOND }(1, _dummyClaim()); 636 637 vm.warp(block.timestamp + 3 days + 12 hours + 1 seconds); 638 639 vm.expectRevert(OutOfOrderResolution.selector); 640 gameProxy.resolveClaim(0); 641 } 642 643 /// @dev Static unit test asserting that resolve pays out bonds on step, output bisection, and execution trace 644 /// moves. 645 function test_resolve_bondPayouts_succeeds() public { 646 // Give the test contract some ether 647 uint256 bal = 1000 ether; 648 vm.deal(address(this), bal); 649 650 // Make claims all the way down the tree. 651 gameProxy.attack{ value: MIN_BOND }(0, _dummyClaim()); 652 gameProxy.attack{ value: MIN_BOND }(1, _dummyClaim()); 653 gameProxy.attack{ value: MIN_BOND }(2, _dummyClaim()); 654 gameProxy.attack{ value: MIN_BOND }(3, _dummyClaim()); 655 gameProxy.attack{ value: MIN_BOND }(4, _changeClaimStatus(_dummyClaim(), VMStatuses.PANIC)); 656 gameProxy.attack{ value: MIN_BOND }(5, _dummyClaim()); 657 gameProxy.attack{ value: MIN_BOND }(6, _dummyClaim()); 658 gameProxy.attack{ value: MIN_BOND }(7, _dummyClaim()); 659 gameProxy.addLocalData(LocalPreimageKey.DISPUTED_L2_BLOCK_NUMBER, 8, 0); 660 gameProxy.step(8, true, absolutePrestateData, hex""); 661 662 // Ensure that the step successfully countered the leaf claim. 663 (, address counteredBy,,,,,) = gameProxy.claimData(8); 664 assertEq(counteredBy, address(this)); 665 666 // Ensure we bonded the correct amounts 667 uint256 bonded = (gameProxy.claimDataLen() - 1) * MIN_BOND; 668 assertEq(address(this).balance, bal - bonded); 669 assertEq(address(gameProxy).balance, 0); 670 assertEq(delayedWeth.balanceOf(address(gameProxy)), bonded); 671 672 // Resolve all claims 673 vm.warp(block.timestamp + 3 days + 12 hours + 1 seconds); 674 for (uint256 i = gameProxy.claimDataLen(); i > 0; i--) { 675 (bool success,) = address(gameProxy).call(abi.encodeCall(gameProxy.resolveClaim, (i - 1))); 676 assertTrue(success); 677 } 678 gameProxy.resolve(); 679 680 // Wait for the withdrawal delay. 681 vm.warp(block.timestamp + delayedWeth.delay() + 1 seconds); 682 683 gameProxy.claimCredit(address(this)); 684 685 // Ensure that bonds were paid out correctly. 686 assertEq(address(this).balance, bal); 687 assertEq(address(gameProxy).balance, 0); 688 assertEq(delayedWeth.balanceOf(address(gameProxy)), 0); 689 690 // Ensure that the init bond for the game is 0, in case we change it in the test suite in the future. 691 assertEq(disputeGameFactory.initBonds(GAME_TYPE), 0); 692 } 693 694 /// @dev Static unit test asserting that resolve pays out bonds on step, output bisection, and execution trace 695 /// moves with 2 actors and a dishonest root claim. 696 function test_resolve_bondPayoutsSeveralActors_succeeds() public { 697 // Give the test contract and bob some ether 698 uint256 bal = 1000 ether; 699 address bob = address(0xb0b); 700 vm.deal(address(this), bal); 701 vm.deal(bob, bal); 702 703 // Make claims all the way down the tree, trading off between bob and the test contract. 704 gameProxy.attack{ value: MIN_BOND }(0, _dummyClaim()); 705 706 vm.prank(bob); 707 gameProxy.attack{ value: MIN_BOND }(1, _dummyClaim()); 708 709 gameProxy.attack{ value: MIN_BOND }(2, _dummyClaim()); 710 711 vm.prank(bob); 712 gameProxy.attack{ value: MIN_BOND }(3, _dummyClaim()); 713 714 gameProxy.attack{ value: MIN_BOND }(4, _changeClaimStatus(_dummyClaim(), VMStatuses.PANIC)); 715 716 vm.prank(bob); 717 gameProxy.attack{ value: MIN_BOND }(5, _dummyClaim()); 718 719 gameProxy.attack{ value: MIN_BOND }(6, _dummyClaim()); 720 721 vm.prank(bob); 722 gameProxy.attack{ value: MIN_BOND }(7, _dummyClaim()); 723 724 gameProxy.addLocalData(LocalPreimageKey.DISPUTED_L2_BLOCK_NUMBER, 8, 0); 725 gameProxy.step(8, true, absolutePrestateData, hex""); 726 727 // Ensure that the step successfully countered the leaf claim. 728 (, address counteredBy,,,,,) = gameProxy.claimData(8); 729 assertEq(counteredBy, address(this)); 730 731 // Ensure we bonded the correct amounts 732 uint256 bonded = ((gameProxy.claimDataLen() - 1) / 2) * MIN_BOND; 733 assertEq(address(this).balance, bal - bonded); 734 assertEq(bob.balance, bal - bonded); 735 assertEq(address(gameProxy).balance, 0); 736 assertEq(delayedWeth.balanceOf(address(gameProxy)), bonded * 2); 737 738 // Resolve all claims 739 vm.warp(block.timestamp + 3 days + 12 hours + 1 seconds); 740 for (uint256 i = gameProxy.claimDataLen(); i > 0; i--) { 741 (bool success,) = address(gameProxy).call(abi.encodeCall(gameProxy.resolveClaim, (i - 1))); 742 assertTrue(success); 743 } 744 gameProxy.resolve(); 745 746 // Wait for the withdrawal delay. 747 vm.warp(block.timestamp + delayedWeth.delay() + 1 seconds); 748 749 gameProxy.claimCredit(address(this)); 750 751 // Bob's claim should revert since it's value is 0 752 vm.expectRevert(NoCreditToClaim.selector); 753 gameProxy.claimCredit(bob); 754 755 // Ensure that bonds were paid out correctly. 756 assertEq(address(this).balance, bal + bonded); 757 assertEq(bob.balance, bal - bonded); 758 assertEq(address(gameProxy).balance, 0); 759 assertEq(delayedWeth.balanceOf(address(gameProxy)), 0); 760 761 // Ensure that the init bond for the game is 0, in case we change it in the test suite in the future. 762 assertEq(disputeGameFactory.initBonds(GAME_TYPE), 0); 763 } 764 765 /// @dev Static unit test asserting that resolve pays out bonds on moves to the leftmost actor 766 /// in subgames containing successful counters. 767 function test_resolve_leftmostBondPayout_succeeds() public { 768 uint256 bal = 1000 ether; 769 address alice = address(0xa11ce); 770 address bob = address(0xb0b); 771 address charlie = address(0xc0c); 772 vm.deal(address(this), bal); 773 vm.deal(alice, bal); 774 vm.deal(bob, bal); 775 vm.deal(charlie, bal); 776 777 // Make claims with bob, charlie and the test contract on defense, and alice as the challenger 778 // charlie is successfully countered by alice 779 // alice is successfully countered by both bob and the test contract 780 vm.prank(alice); 781 gameProxy.attack{ value: MIN_BOND }(0, _dummyClaim()); 782 783 vm.prank(bob); 784 gameProxy.defend{ value: MIN_BOND }(1, _dummyClaim()); 785 vm.prank(charlie); 786 gameProxy.attack{ value: MIN_BOND }(1, _dummyClaim()); 787 gameProxy.attack{ value: MIN_BOND }(1, _dummyClaim()); 788 789 vm.prank(alice); 790 gameProxy.attack{ value: MIN_BOND }(3, _dummyClaim()); 791 792 // Resolve all claims 793 vm.warp(block.timestamp + 3 days + 12 hours + 1 seconds); 794 for (uint256 i = gameProxy.claimDataLen(); i > 0; i--) { 795 (bool success,) = address(gameProxy).call(abi.encodeCall(gameProxy.resolveClaim, (i - 1))); 796 assertTrue(success); 797 } 798 gameProxy.resolve(); 799 800 // Wait for the withdrawal delay. 801 vm.warp(block.timestamp + delayedWeth.delay() + 1 seconds); 802 803 gameProxy.claimCredit(address(this)); 804 gameProxy.claimCredit(alice); 805 gameProxy.claimCredit(bob); 806 807 // Charlie's claim should revert since it's value is 0 808 vm.expectRevert(NoCreditToClaim.selector); 809 gameProxy.claimCredit(charlie); 810 811 // Ensure that bonds were paid out correctly. 812 uint256 aliceLosses = MIN_BOND; 813 uint256 charlieLosses = MIN_BOND; 814 assertEq(address(this).balance, bal + aliceLosses, "incorrect this balance"); 815 assertEq(alice.balance, bal - aliceLosses + charlieLosses, "incorrect alice balance"); 816 assertEq(bob.balance, bal, "incorrect bob balance"); 817 assertEq(charlie.balance, bal - charlieLosses, "incorrect charlie balance"); 818 assertEq(address(gameProxy).balance, 0); 819 820 // Ensure that the init bond for the game is 0, in case we change it in the test suite in the future. 821 assertEq(disputeGameFactory.initBonds(GAME_TYPE), 0); 822 } 823 824 /// @dev Static unit test asserting that the anchor state updates when the game resolves in 825 /// favor of the defender and the anchor state is older than the game state. 826 function test_resolve_validNewerStateUpdatesAnchor_succeeds() public { 827 // Confirm that the anchor state is older than the game state. 828 (Hash root, uint256 l2BlockNumber) = anchorStateRegistry.anchors(gameProxy.gameType()); 829 assert(l2BlockNumber < gameProxy.l2BlockNumber()); 830 831 // Resolve the game. 832 vm.warp(block.timestamp + 3 days + 12 hours + 1 seconds); 833 gameProxy.resolveClaim(0); 834 assertEq(uint8(gameProxy.resolve()), uint8(GameStatus.DEFENDER_WINS)); 835 836 // Confirm that the anchor state is now the same as the game state. 837 (root, l2BlockNumber) = anchorStateRegistry.anchors(gameProxy.gameType()); 838 assertEq(l2BlockNumber, gameProxy.l2BlockNumber()); 839 assertEq(root.raw(), gameProxy.rootClaim().raw()); 840 } 841 842 /// @dev Static unit test asserting that the anchor state does not change when the game 843 /// resolves in favor of the defender but the game state is not newer than the anchor state. 844 function test_resolve_validOlderStateSameAnchor_succeeds() public { 845 // Mock the game block to be older than the game state. 846 vm.mockCall(address(gameProxy), abi.encodeWithSelector(gameProxy.l2BlockNumber.selector), abi.encode(0)); 847 848 // Confirm that the anchor state is newer than the game state. 849 (Hash root, uint256 l2BlockNumber) = anchorStateRegistry.anchors(gameProxy.gameType()); 850 assert(l2BlockNumber >= gameProxy.l2BlockNumber()); 851 852 // Resolve the game. 853 vm.mockCall(address(gameProxy), abi.encodeWithSelector(gameProxy.l2BlockNumber.selector), abi.encode(0)); 854 vm.warp(block.timestamp + 3 days + 12 hours + 1 seconds); 855 gameProxy.resolveClaim(0); 856 assertEq(uint8(gameProxy.resolve()), uint8(GameStatus.DEFENDER_WINS)); 857 858 // Confirm that the anchor state is the same as the initial anchor state. 859 (Hash updatedRoot, uint256 updatedL2BlockNumber) = anchorStateRegistry.anchors(gameProxy.gameType()); 860 assertEq(updatedL2BlockNumber, l2BlockNumber); 861 assertEq(updatedRoot.raw(), root.raw()); 862 } 863 864 /// @dev Static unit test asserting that the anchor state does not change when the game 865 /// resolves in favor of the challenger, even if the game state is newer than the anchor. 866 function test_resolve_invalidStateSameAnchor_succeeds() public { 867 // Confirm that the anchor state is older than the game state. 868 (Hash root, uint256 l2BlockNumber) = anchorStateRegistry.anchors(gameProxy.gameType()); 869 assert(l2BlockNumber < gameProxy.l2BlockNumber()); 870 871 // Challenge the claim and resolve it. 872 gameProxy.attack{ value: MIN_BOND }(0, _dummyClaim()); 873 vm.warp(block.timestamp + 3 days + 12 hours + 1 seconds); 874 gameProxy.resolveClaim(0); 875 assertEq(uint8(gameProxy.resolve()), uint8(GameStatus.CHALLENGER_WINS)); 876 877 // Confirm that the anchor state is the same as the initial anchor state. 878 (Hash updatedRoot, uint256 updatedL2BlockNumber) = anchorStateRegistry.anchors(gameProxy.gameType()); 879 assertEq(updatedL2BlockNumber, l2BlockNumber); 880 assertEq(updatedRoot.raw(), root.raw()); 881 } 882 883 /// @dev Static unit test asserting that credit may not be drained past allowance through reentrancy. 884 function test_claimCredit_claimAlreadyResolved_reverts() public { 885 ClaimCreditReenter reenter = new ClaimCreditReenter(gameProxy, vm); 886 vm.startPrank(address(reenter)); 887 888 // Give the test contract some ether to bond. 889 vm.deal(address(reenter), MIN_BOND * 2); 890 // Give the game proxy 1 extra ether, unregistered. 891 vm.deal(address(gameProxy), 1 ether); 892 893 // Perform a bonded move. 894 Claim claim = _dummyClaim(); 895 gameProxy.attack{ value: MIN_BOND }(0, claim); 896 gameProxy.attack{ value: MIN_BOND }(1, claim); 897 898 // Warp past the finalization period 899 vm.warp(block.timestamp + 3 days + 12 hours + 1 seconds); 900 901 // Ensure that we bonded all the test contract's ETH 902 assertEq(address(reenter).balance, 0); 903 // Ensure the game proxy has 1 ether in it. 904 assertEq(address(gameProxy).balance, 1 ether); 905 // Ensure the game has a balance of 2 * MIN_BOND in the delayedWeth contract. 906 assertEq(delayedWeth.balanceOf(address(gameProxy)), MIN_BOND * 2); 907 908 // Resolve the claim at gindex 1 and claim the reenter contract's credit. 909 gameProxy.resolveClaim(1); 910 911 // Ensure that the game registered the `reenter` contract's credit. 912 assertEq(gameProxy.credit(address(reenter)), MIN_BOND); 913 914 // Wait for the withdrawal delay. 915 vm.warp(block.timestamp + delayedWeth.delay() + 1 seconds); 916 917 // Initiate the reentrant credit claim. 918 reenter.claimCredit(address(reenter)); 919 920 // The reenter contract should have performed 2 calls to `claimCredit`. 921 // Once all the credit is claimed, all subsequent calls will revert since there is 0 credit left to claim. 922 // The claimant must only have received the amount bonded for the gindex 1 subgame. 923 // The root claim bond and the unregistered ETH should still exist in the game proxy. 924 assertEq(reenter.numCalls(), 2); 925 assertEq(address(reenter).balance, MIN_BOND); 926 assertEq(address(gameProxy).balance, 1 ether); 927 assertEq(delayedWeth.balanceOf(address(gameProxy)), MIN_BOND); 928 929 vm.stopPrank(); 930 } 931 932 /// @dev Tests that adding local data with an out of bounds identifier reverts. 933 function testFuzz_addLocalData_oob_reverts(uint256 _ident) public { 934 // Get a claim below the split depth so that we can add local data for an execution trace subgame. 935 for (uint256 i; i < 4; i++) { 936 gameProxy.attack{ value: MIN_BOND }(i, _dummyClaim()); 937 } 938 gameProxy.attack{ value: MIN_BOND }(4, _changeClaimStatus(_dummyClaim(), VMStatuses.PANIC)); 939 940 // [1, 5] are valid local data identifiers. 941 if (_ident <= 5) _ident = 0; 942 943 vm.expectRevert(InvalidLocalIdent.selector); 944 gameProxy.addLocalData(_ident, 5, 0); 945 } 946 947 /// @dev Tests that local data is loaded into the preimage oracle correctly in the subgame 948 /// that is disputing the transition from `GENESIS -> GENESIS + 1` 949 function test_addLocalDataGenesisTransition_static_succeeds() public { 950 IPreimageOracle oracle = IPreimageOracle(address(gameProxy.vm().oracle())); 951 952 // Get a claim below the split depth so that we can add local data for an execution trace subgame. 953 for (uint256 i; i < 4; i++) { 954 gameProxy.attack{ value: MIN_BOND }(i, Claim.wrap(bytes32(i))); 955 } 956 gameProxy.attack{ value: MIN_BOND }(4, _changeClaimStatus(_dummyClaim(), VMStatuses.PANIC)); 957 958 // Expected start/disputed claims 959 (Hash root,) = gameProxy.startingOutputRoot(); 960 bytes32 startingClaim = root.raw(); 961 bytes32 disputedClaim = bytes32(uint256(3)); 962 Position disputedPos = LibPosition.wrap(4, 0); 963 964 // Expected local data 965 bytes32[5] memory data = [ 966 gameProxy.l1Head().raw(), 967 startingClaim, 968 disputedClaim, 969 bytes32(uint256(1) << 0xC0), 970 bytes32(gameProxy.l2ChainId() << 0xC0) 971 ]; 972 973 for (uint256 i = 1; i <= 5; i++) { 974 uint256 expectedLen = i > 3 ? 8 : 32; 975 bytes32 key = _getKey(i, keccak256(abi.encode(disputedClaim, disputedPos))); 976 977 gameProxy.addLocalData(i, 5, 0); 978 (bytes32 dat, uint256 datLen) = oracle.readPreimage(key, 0); 979 assertEq(dat >> 0xC0, bytes32(expectedLen)); 980 // Account for the length prefix if i > 3 (the data stored 981 // at identifiers i <= 3 are 32 bytes long, so the expected 982 // length is already correct. If i > 3, the data is only 8 983 // bytes long, so the length prefix + the data is 16 bytes 984 // total.) 985 assertEq(datLen, expectedLen + (i > 3 ? 8 : 0)); 986 987 gameProxy.addLocalData(i, 5, 8); 988 (dat, datLen) = oracle.readPreimage(key, 8); 989 assertEq(dat, data[i - 1]); 990 assertEq(datLen, expectedLen); 991 } 992 } 993 994 /// @dev Tests that local data is loaded into the preimage oracle correctly. 995 function test_addLocalDataMiddle_static_succeeds() public { 996 IPreimageOracle oracle = IPreimageOracle(address(gameProxy.vm().oracle())); 997 998 // Get a claim below the split depth so that we can add local data for an execution trace subgame. 999 for (uint256 i; i < 4; i++) { 1000 gameProxy.attack{ value: MIN_BOND }(i, Claim.wrap(bytes32(i))); 1001 } 1002 gameProxy.defend{ value: MIN_BOND }(4, _changeClaimStatus(ROOT_CLAIM, VMStatuses.VALID)); 1003 1004 // Expected start/disputed claims 1005 bytes32 startingClaim = bytes32(uint256(3)); 1006 Position startingPos = LibPosition.wrap(4, 0); 1007 bytes32 disputedClaim = bytes32(uint256(2)); 1008 Position disputedPos = LibPosition.wrap(3, 0); 1009 1010 // Expected local data 1011 bytes32[5] memory data = [ 1012 gameProxy.l1Head().raw(), 1013 startingClaim, 1014 disputedClaim, 1015 bytes32(uint256(2) << 0xC0), 1016 bytes32(gameProxy.l2ChainId() << 0xC0) 1017 ]; 1018 1019 for (uint256 i = 1; i <= 5; i++) { 1020 uint256 expectedLen = i > 3 ? 8 : 32; 1021 bytes32 key = _getKey(i, keccak256(abi.encode(startingClaim, startingPos, disputedClaim, disputedPos))); 1022 1023 gameProxy.addLocalData(i, 5, 0); 1024 (bytes32 dat, uint256 datLen) = oracle.readPreimage(key, 0); 1025 assertEq(dat >> 0xC0, bytes32(expectedLen)); 1026 // Account for the length prefix if i > 3 (the data stored 1027 // at identifiers i <= 3 are 32 bytes long, so the expected 1028 // length is already correct. If i > 3, the data is only 8 1029 // bytes long, so the length prefix + the data is 16 bytes 1030 // total.) 1031 assertEq(datLen, expectedLen + (i > 3 ? 8 : 0)); 1032 1033 gameProxy.addLocalData(i, 5, 8); 1034 (dat, datLen) = oracle.readPreimage(key, 8); 1035 assertEq(dat, data[i - 1]); 1036 assertEq(datLen, expectedLen); 1037 } 1038 } 1039 1040 /// @dev Helper to return a pseudo-random claim 1041 function _dummyClaim() internal view returns (Claim) { 1042 return Claim.wrap(keccak256(abi.encode(gasleft()))); 1043 } 1044 1045 /// @dev Helper to get the localized key for an identifier in the context of the game proxy. 1046 function _getKey(uint256 _ident, bytes32 _localContext) internal view returns (bytes32) { 1047 bytes32 h = keccak256(abi.encode(_ident | (1 << 248), address(gameProxy), _localContext)); 1048 return bytes32((uint256(h) & ~uint256(0xFF << 248)) | (1 << 248)); 1049 } 1050 } 1051 1052 contract FaultDispute_1v1_Actors_Test is FaultDisputeGame_Init { 1053 /// @dev The honest actor 1054 DisputeActor internal honest; 1055 /// @dev The dishonest actor 1056 DisputeActor internal dishonest; 1057 1058 function setUp() public override { 1059 // Setup the `FaultDisputeGame` 1060 super.setUp(); 1061 } 1062 1063 /// @notice Fuzz test for a 1v1 output bisection dispute. 1064 /// @dev The alphabet game has a constant status byte, and is not safe from someone being dishonest in 1065 /// output bisection and then posting a correct execution trace bisection root claim. This test 1066 /// does not cover this case (i.e. root claim of output bisection is dishonest, root claim of 1067 /// execution trace bisection is made by the dishonest actor but is honest, honest actor cannot 1068 /// attack it without risk of losing). 1069 function testFuzz_outputBisection1v1honestRoot_succeeds(uint8 _divergeOutput, uint8 _divergeStep) public { 1070 uint256[] memory honestL2Outputs = new uint256[](16); 1071 for (uint256 i; i < honestL2Outputs.length; i++) { 1072 honestL2Outputs[i] = i + 1; 1073 } 1074 bytes memory honestTrace = new bytes(256); 1075 for (uint256 i; i < honestTrace.length; i++) { 1076 honestTrace[i] = bytes1(uint8(i)); 1077 } 1078 1079 uint256 divergeAtOutput = bound(_divergeOutput, 0, 15); 1080 uint256 divergeAtStep = bound(_divergeStep, 0, 7); 1081 uint256 divergeStepOffset = (divergeAtOutput << 4) + divergeAtStep; 1082 1083 uint256[] memory dishonestL2Outputs = new uint256[](16); 1084 for (uint256 i; i < dishonestL2Outputs.length; i++) { 1085 dishonestL2Outputs[i] = i >= divergeAtOutput ? 0xFF : i + 1; 1086 } 1087 bytes memory dishonestTrace = new bytes(256); 1088 for (uint256 i; i < dishonestTrace.length; i++) { 1089 dishonestTrace[i] = i >= divergeStepOffset ? bytes1(uint8(0xFF)) : bytes1(uint8(i)); 1090 } 1091 1092 // Run the actor test 1093 _actorTest({ 1094 _rootClaim: 16, 1095 _absolutePrestateData: 0, 1096 _honestTrace: honestTrace, 1097 _honestL2Outputs: honestL2Outputs, 1098 _dishonestTrace: dishonestTrace, 1099 _dishonestL2Outputs: dishonestL2Outputs, 1100 _expectedStatus: GameStatus.DEFENDER_WINS 1101 }); 1102 } 1103 1104 /// @notice Static unit test for a 1v1 output bisection dispute. 1105 function test_static_1v1honestRootGenesisAbsolutePrestate_succeeds() public { 1106 // The honest l2 outputs are from [1, 16] in this game. 1107 uint256[] memory honestL2Outputs = new uint256[](16); 1108 for (uint256 i; i < honestL2Outputs.length; i++) { 1109 honestL2Outputs[i] = i + 1; 1110 } 1111 // The honest trace covers all block -> block + 1 transitions, and is 256 bytes long, consisting 1112 // of bytes [0, 255]. 1113 bytes memory honestTrace = new bytes(256); 1114 for (uint256 i; i < honestTrace.length; i++) { 1115 honestTrace[i] = bytes1(uint8(i)); 1116 } 1117 1118 // The dishonest l2 outputs are from [2, 17] in this game. 1119 uint256[] memory dishonestL2Outputs = new uint256[](16); 1120 for (uint256 i; i < dishonestL2Outputs.length; i++) { 1121 dishonestL2Outputs[i] = i + 2; 1122 } 1123 // The dishonest trace covers all block -> block + 1 transitions, and is 256 bytes long, consisting 1124 // of all set bits. 1125 bytes memory dishonestTrace = new bytes(256); 1126 for (uint256 i; i < dishonestTrace.length; i++) { 1127 dishonestTrace[i] = bytes1(0xFF); 1128 } 1129 1130 // Run the actor test 1131 _actorTest({ 1132 _rootClaim: 16, 1133 _absolutePrestateData: 0, 1134 _honestTrace: honestTrace, 1135 _honestL2Outputs: honestL2Outputs, 1136 _dishonestTrace: dishonestTrace, 1137 _dishonestL2Outputs: dishonestL2Outputs, 1138 _expectedStatus: GameStatus.DEFENDER_WINS 1139 }); 1140 } 1141 1142 /// @notice Static unit test for a 1v1 output bisection dispute. 1143 function test_static_1v1dishonestRootGenesisAbsolutePrestate_succeeds() public { 1144 // The honest l2 outputs are from [1, 16] in this game. 1145 uint256[] memory honestL2Outputs = new uint256[](16); 1146 for (uint256 i; i < honestL2Outputs.length; i++) { 1147 honestL2Outputs[i] = i + 1; 1148 } 1149 // The honest trace covers all block -> block + 1 transitions, and is 256 bytes long, consisting 1150 // of bytes [0, 255]. 1151 bytes memory honestTrace = new bytes(256); 1152 for (uint256 i; i < honestTrace.length; i++) { 1153 honestTrace[i] = bytes1(uint8(i)); 1154 } 1155 1156 // The dishonest l2 outputs are from [2, 17] in this game. 1157 uint256[] memory dishonestL2Outputs = new uint256[](16); 1158 for (uint256 i; i < dishonestL2Outputs.length; i++) { 1159 dishonestL2Outputs[i] = i + 2; 1160 } 1161 // The dishonest trace covers all block -> block + 1 transitions, and is 256 bytes long, consisting 1162 // of all set bits. 1163 bytes memory dishonestTrace = new bytes(256); 1164 for (uint256 i; i < dishonestTrace.length; i++) { 1165 dishonestTrace[i] = bytes1(0xFF); 1166 } 1167 1168 // Run the actor test 1169 _actorTest({ 1170 _rootClaim: 17, 1171 _absolutePrestateData: 0, 1172 _honestTrace: honestTrace, 1173 _honestL2Outputs: honestL2Outputs, 1174 _dishonestTrace: dishonestTrace, 1175 _dishonestL2Outputs: dishonestL2Outputs, 1176 _expectedStatus: GameStatus.CHALLENGER_WINS 1177 }); 1178 } 1179 1180 /// @notice Static unit test for a 1v1 output bisection dispute. 1181 function test_static_1v1honestRoot_succeeds() public { 1182 // The honest l2 outputs are from [1, 16] in this game. 1183 uint256[] memory honestL2Outputs = new uint256[](16); 1184 for (uint256 i; i < honestL2Outputs.length; i++) { 1185 honestL2Outputs[i] = i + 1; 1186 } 1187 // The honest trace covers all block -> block + 1 transitions, and is 256 bytes long, consisting 1188 // of bytes [0, 255]. 1189 bytes memory honestTrace = new bytes(256); 1190 for (uint256 i; i < honestTrace.length; i++) { 1191 honestTrace[i] = bytes1(uint8(i)); 1192 } 1193 1194 // The dishonest l2 outputs are from [2, 17] in this game. 1195 uint256[] memory dishonestL2Outputs = new uint256[](16); 1196 for (uint256 i; i < dishonestL2Outputs.length; i++) { 1197 dishonestL2Outputs[i] = i + 2; 1198 } 1199 // The dishonest trace covers all block -> block + 1 transitions, and is 256 bytes long, consisting 1200 // of all zeros. 1201 bytes memory dishonestTrace = new bytes(256); 1202 1203 // Run the actor test 1204 _actorTest({ 1205 _rootClaim: 16, 1206 _absolutePrestateData: 0, 1207 _honestTrace: honestTrace, 1208 _honestL2Outputs: honestL2Outputs, 1209 _dishonestTrace: dishonestTrace, 1210 _dishonestL2Outputs: dishonestL2Outputs, 1211 _expectedStatus: GameStatus.DEFENDER_WINS 1212 }); 1213 } 1214 1215 /// @notice Static unit test for a 1v1 output bisection dispute. 1216 function test_static_1v1dishonestRoot_succeeds() public { 1217 // The honest l2 outputs are from [1, 16] in this game. 1218 uint256[] memory honestL2Outputs = new uint256[](16); 1219 for (uint256 i; i < honestL2Outputs.length; i++) { 1220 honestL2Outputs[i] = i + 1; 1221 } 1222 // The honest trace covers all block -> block + 1 transitions, and is 256 bytes long, consisting 1223 // of bytes [0, 255]. 1224 bytes memory honestTrace = new bytes(256); 1225 for (uint256 i; i < honestTrace.length; i++) { 1226 honestTrace[i] = bytes1(uint8(i)); 1227 } 1228 1229 // The dishonest l2 outputs are from [2, 17] in this game. 1230 uint256[] memory dishonestL2Outputs = new uint256[](16); 1231 for (uint256 i; i < dishonestL2Outputs.length; i++) { 1232 dishonestL2Outputs[i] = i + 2; 1233 } 1234 // The dishonest trace covers all block -> block + 1 transitions, and is 256 bytes long, consisting 1235 // of all zeros. 1236 bytes memory dishonestTrace = new bytes(256); 1237 1238 // Run the actor test 1239 _actorTest({ 1240 _rootClaim: 17, 1241 _absolutePrestateData: 0, 1242 _honestTrace: honestTrace, 1243 _honestL2Outputs: honestL2Outputs, 1244 _dishonestTrace: dishonestTrace, 1245 _dishonestL2Outputs: dishonestL2Outputs, 1246 _expectedStatus: GameStatus.CHALLENGER_WINS 1247 }); 1248 } 1249 1250 /// @notice Static unit test for a 1v1 output bisection dispute. 1251 function test_static_1v1correctRootHalfWay_succeeds() public { 1252 // The honest l2 outputs are from [1, 16] in this game. 1253 uint256[] memory honestL2Outputs = new uint256[](16); 1254 for (uint256 i; i < honestL2Outputs.length; i++) { 1255 honestL2Outputs[i] = i + 1; 1256 } 1257 // The honest trace covers all block -> block + 1 transitions, and is 256 bytes long, consisting 1258 // of bytes [0, 255]. 1259 bytes memory honestTrace = new bytes(256); 1260 for (uint256 i; i < honestTrace.length; i++) { 1261 honestTrace[i] = bytes1(uint8(i)); 1262 } 1263 1264 // The dishonest l2 outputs are half correct, half incorrect. 1265 uint256[] memory dishonestL2Outputs = new uint256[](16); 1266 for (uint256 i; i < dishonestL2Outputs.length; i++) { 1267 dishonestL2Outputs[i] = i > 7 ? 0xFF : i + 1; 1268 } 1269 // The dishonest trace is half correct, half incorrect. 1270 bytes memory dishonestTrace = new bytes(256); 1271 for (uint256 i; i < dishonestTrace.length; i++) { 1272 dishonestTrace[i] = i > (127 + 4) ? bytes1(0xFF) : bytes1(uint8(i)); 1273 } 1274 1275 // Run the actor test 1276 _actorTest({ 1277 _rootClaim: 16, 1278 _absolutePrestateData: 0, 1279 _honestTrace: honestTrace, 1280 _honestL2Outputs: honestL2Outputs, 1281 _dishonestTrace: dishonestTrace, 1282 _dishonestL2Outputs: dishonestL2Outputs, 1283 _expectedStatus: GameStatus.DEFENDER_WINS 1284 }); 1285 } 1286 1287 /// @notice Static unit test for a 1v1 output bisection dispute. 1288 function test_static_1v1dishonestRootHalfWay_succeeds() public { 1289 // The honest l2 outputs are from [1, 16] in this game. 1290 uint256[] memory honestL2Outputs = new uint256[](16); 1291 for (uint256 i; i < honestL2Outputs.length; i++) { 1292 honestL2Outputs[i] = i + 1; 1293 } 1294 // The honest trace covers all block -> block + 1 transitions, and is 256 bytes long, consisting 1295 // of bytes [0, 255]. 1296 bytes memory honestTrace = new bytes(256); 1297 for (uint256 i; i < honestTrace.length; i++) { 1298 honestTrace[i] = bytes1(uint8(i)); 1299 } 1300 1301 // The dishonest l2 outputs are half correct, half incorrect. 1302 uint256[] memory dishonestL2Outputs = new uint256[](16); 1303 for (uint256 i; i < dishonestL2Outputs.length; i++) { 1304 dishonestL2Outputs[i] = i > 7 ? 0xFF : i + 1; 1305 } 1306 // The dishonest trace is half correct, half incorrect. 1307 bytes memory dishonestTrace = new bytes(256); 1308 for (uint256 i; i < dishonestTrace.length; i++) { 1309 dishonestTrace[i] = i > (127 + 4) ? bytes1(0xFF) : bytes1(uint8(i)); 1310 } 1311 1312 // Run the actor test 1313 _actorTest({ 1314 _rootClaim: 0xFF, 1315 _absolutePrestateData: 0, 1316 _honestTrace: honestTrace, 1317 _honestL2Outputs: honestL2Outputs, 1318 _dishonestTrace: dishonestTrace, 1319 _dishonestL2Outputs: dishonestL2Outputs, 1320 _expectedStatus: GameStatus.CHALLENGER_WINS 1321 }); 1322 } 1323 1324 /// @notice Static unit test for a 1v1 output bisection dispute. 1325 function test_static_1v1correctAbsolutePrestate_succeeds() public { 1326 // The honest l2 outputs are from [1, 16] in this game. 1327 uint256[] memory honestL2Outputs = new uint256[](16); 1328 for (uint256 i; i < honestL2Outputs.length; i++) { 1329 honestL2Outputs[i] = i + 1; 1330 } 1331 // The honest trace covers all block -> block + 1 transitions, and is 256 bytes long, consisting 1332 // of bytes [0, 255]. 1333 bytes memory honestTrace = new bytes(256); 1334 for (uint256 i; i < honestTrace.length; i++) { 1335 honestTrace[i] = bytes1(uint8(i)); 1336 } 1337 1338 // The dishonest l2 outputs are half correct, half incorrect. 1339 uint256[] memory dishonestL2Outputs = new uint256[](16); 1340 for (uint256 i; i < dishonestL2Outputs.length; i++) { 1341 dishonestL2Outputs[i] = i > 7 ? 0xFF : i + 1; 1342 } 1343 // The dishonest trace correct is half correct, half incorrect. 1344 bytes memory dishonestTrace = new bytes(256); 1345 for (uint256 i; i < dishonestTrace.length; i++) { 1346 dishonestTrace[i] = i > 127 ? bytes1(0xFF) : bytes1(uint8(i)); 1347 } 1348 1349 // Run the actor test 1350 _actorTest({ 1351 _rootClaim: 16, 1352 _absolutePrestateData: 0, 1353 _honestTrace: honestTrace, 1354 _honestL2Outputs: honestL2Outputs, 1355 _dishonestTrace: dishonestTrace, 1356 _dishonestL2Outputs: dishonestL2Outputs, 1357 _expectedStatus: GameStatus.DEFENDER_WINS 1358 }); 1359 } 1360 1361 /// @notice Static unit test for a 1v1 output bisection dispute. 1362 function test_static_1v1dishonestAbsolutePrestate_succeeds() public { 1363 // The honest l2 outputs are from [1, 16] in this game. 1364 uint256[] memory honestL2Outputs = new uint256[](16); 1365 for (uint256 i; i < honestL2Outputs.length; i++) { 1366 honestL2Outputs[i] = i + 1; 1367 } 1368 // The honest trace covers all block -> block + 1 transitions, and is 256 bytes long, consisting 1369 // of bytes [0, 255]. 1370 bytes memory honestTrace = new bytes(256); 1371 for (uint256 i; i < honestTrace.length; i++) { 1372 honestTrace[i] = bytes1(uint8(i)); 1373 } 1374 1375 // The dishonest l2 outputs are half correct, half incorrect. 1376 uint256[] memory dishonestL2Outputs = new uint256[](16); 1377 for (uint256 i; i < dishonestL2Outputs.length; i++) { 1378 dishonestL2Outputs[i] = i > 7 ? 0xFF : i + 1; 1379 } 1380 // The dishonest trace correct is half correct, half incorrect. 1381 bytes memory dishonestTrace = new bytes(256); 1382 for (uint256 i; i < dishonestTrace.length; i++) { 1383 dishonestTrace[i] = i > 127 ? bytes1(0xFF) : bytes1(uint8(i)); 1384 } 1385 1386 // Run the actor test 1387 _actorTest({ 1388 _rootClaim: 0xFF, 1389 _absolutePrestateData: 0, 1390 _honestTrace: honestTrace, 1391 _honestL2Outputs: honestL2Outputs, 1392 _dishonestTrace: dishonestTrace, 1393 _dishonestL2Outputs: dishonestL2Outputs, 1394 _expectedStatus: GameStatus.CHALLENGER_WINS 1395 }); 1396 } 1397 1398 /// @notice Static unit test for a 1v1 output bisection dispute. 1399 function test_static_1v1honestRootFinalInstruction_succeeds() public { 1400 // The honest l2 outputs are from [1, 16] in this game. 1401 uint256[] memory honestL2Outputs = new uint256[](16); 1402 for (uint256 i; i < honestL2Outputs.length; i++) { 1403 honestL2Outputs[i] = i + 1; 1404 } 1405 // The honest trace covers all block -> block + 1 transitions, and is 256 bytes long, consisting 1406 // of bytes [0, 255]. 1407 bytes memory honestTrace = new bytes(256); 1408 for (uint256 i; i < honestTrace.length; i++) { 1409 honestTrace[i] = bytes1(uint8(i)); 1410 } 1411 1412 // The dishonest l2 outputs are half correct, half incorrect. 1413 uint256[] memory dishonestL2Outputs = new uint256[](16); 1414 for (uint256 i; i < dishonestL2Outputs.length; i++) { 1415 dishonestL2Outputs[i] = i > 7 ? 0xFF : i + 1; 1416 } 1417 // The dishonest trace is half correct, and correct all the way up to the final instruction of the exec 1418 // subgame. 1419 bytes memory dishonestTrace = new bytes(256); 1420 for (uint256 i; i < dishonestTrace.length; i++) { 1421 dishonestTrace[i] = i > (127 + 7) ? bytes1(0xFF) : bytes1(uint8(i)); 1422 } 1423 1424 // Run the actor test 1425 _actorTest({ 1426 _rootClaim: 16, 1427 _absolutePrestateData: 0, 1428 _honestTrace: honestTrace, 1429 _honestL2Outputs: honestL2Outputs, 1430 _dishonestTrace: dishonestTrace, 1431 _dishonestL2Outputs: dishonestL2Outputs, 1432 _expectedStatus: GameStatus.DEFENDER_WINS 1433 }); 1434 } 1435 1436 /// @notice Static unit test for a 1v1 output bisection dispute. 1437 function test_static_1v1dishonestRootFinalInstruction_succeeds() public { 1438 // The honest l2 outputs are from [1, 16] in this game. 1439 uint256[] memory honestL2Outputs = new uint256[](16); 1440 for (uint256 i; i < honestL2Outputs.length; i++) { 1441 honestL2Outputs[i] = i + 1; 1442 } 1443 // The honest trace covers all block -> block + 1 transitions, and is 256 bytes long, consisting 1444 // of bytes [0, 255]. 1445 bytes memory honestTrace = new bytes(256); 1446 for (uint256 i; i < honestTrace.length; i++) { 1447 honestTrace[i] = bytes1(uint8(i)); 1448 } 1449 1450 // The dishonest l2 outputs are half correct, half incorrect. 1451 uint256[] memory dishonestL2Outputs = new uint256[](16); 1452 for (uint256 i; i < dishonestL2Outputs.length; i++) { 1453 dishonestL2Outputs[i] = i > 7 ? 0xFF : i + 1; 1454 } 1455 // The dishonest trace is half correct, and correct all the way up to the final instruction of the exec 1456 // subgame. 1457 bytes memory dishonestTrace = new bytes(256); 1458 for (uint256 i; i < dishonestTrace.length; i++) { 1459 dishonestTrace[i] = i > (127 + 7) ? bytes1(0xFF) : bytes1(uint8(i)); 1460 } 1461 1462 // Run the actor test 1463 _actorTest({ 1464 _rootClaim: 0xFF, 1465 _absolutePrestateData: 0, 1466 _honestTrace: honestTrace, 1467 _honestL2Outputs: honestL2Outputs, 1468 _dishonestTrace: dishonestTrace, 1469 _dishonestL2Outputs: dishonestL2Outputs, 1470 _expectedStatus: GameStatus.CHALLENGER_WINS 1471 }); 1472 } 1473 1474 //////////////////////////////////////////////////////////////// 1475 // HELPERS // 1476 //////////////////////////////////////////////////////////////// 1477 1478 /// @dev Helper to run a 1v1 actor test 1479 function _actorTest( 1480 uint256 _rootClaim, 1481 uint256 _absolutePrestateData, 1482 bytes memory _honestTrace, 1483 uint256[] memory _honestL2Outputs, 1484 bytes memory _dishonestTrace, 1485 uint256[] memory _dishonestL2Outputs, 1486 GameStatus _expectedStatus 1487 ) 1488 internal 1489 { 1490 // Setup the environment 1491 bytes memory absolutePrestateData = 1492 _setup({ _absolutePrestateData: _absolutePrestateData, _rootClaim: _rootClaim }); 1493 1494 // Create actors 1495 _createActors({ 1496 _honestTrace: _honestTrace, 1497 _honestPreStateData: absolutePrestateData, 1498 _honestL2Outputs: _honestL2Outputs, 1499 _dishonestTrace: _dishonestTrace, 1500 _dishonestPreStateData: absolutePrestateData, 1501 _dishonestL2Outputs: _dishonestL2Outputs 1502 }); 1503 1504 // Exhaust all moves from both actors 1505 _exhaustMoves(); 1506 1507 // Resolve the game and assert that the defender won 1508 _warpAndResolve(); 1509 assertEq(uint8(gameProxy.status()), uint8(_expectedStatus)); 1510 } 1511 1512 /// @dev Helper to setup the 1v1 test 1513 function _setup( 1514 uint256 _absolutePrestateData, 1515 uint256 _rootClaim 1516 ) 1517 internal 1518 returns (bytes memory absolutePrestateData_) 1519 { 1520 absolutePrestateData_ = abi.encode(_absolutePrestateData); 1521 Claim absolutePrestateExec = 1522 _changeClaimStatus(Claim.wrap(keccak256(absolutePrestateData_)), VMStatuses.UNFINISHED); 1523 Claim rootClaim = Claim.wrap(bytes32(uint256(_rootClaim))); 1524 super.init({ rootClaim: rootClaim, absolutePrestate: absolutePrestateExec, l2BlockNumber: _rootClaim }); 1525 } 1526 1527 /// @dev Helper to create actors for the 1v1 dispute. 1528 function _createActors( 1529 bytes memory _honestTrace, 1530 bytes memory _honestPreStateData, 1531 uint256[] memory _honestL2Outputs, 1532 bytes memory _dishonestTrace, 1533 bytes memory _dishonestPreStateData, 1534 uint256[] memory _dishonestL2Outputs 1535 ) 1536 internal 1537 { 1538 honest = new HonestDisputeActor({ 1539 _gameProxy: gameProxy, 1540 _l2Outputs: _honestL2Outputs, 1541 _trace: _honestTrace, 1542 _preStateData: _honestPreStateData 1543 }); 1544 dishonest = new HonestDisputeActor({ 1545 _gameProxy: gameProxy, 1546 _l2Outputs: _dishonestL2Outputs, 1547 _trace: _dishonestTrace, 1548 _preStateData: _dishonestPreStateData 1549 }); 1550 1551 vm.deal(address(honest), 100 ether); 1552 vm.deal(address(dishonest), 100 ether); 1553 vm.label(address(honest), "HonestActor"); 1554 vm.label(address(dishonest), "DishonestActor"); 1555 } 1556 1557 /// @dev Helper to exhaust all moves from both actors. 1558 function _exhaustMoves() internal { 1559 while (true) { 1560 // Allow the dishonest actor to make their moves, and then the honest actor. 1561 (uint256 numMovesA,) = dishonest.move(); 1562 (uint256 numMovesB, bool success) = honest.move(); 1563 1564 require(success, "Honest actor's moves should always be successful"); 1565 1566 // If both actors have run out of moves, we're done. 1567 if (numMovesA == 0 && numMovesB == 0) break; 1568 } 1569 } 1570 1571 /// @dev Helper to warp past the chess clock and resolve all claims within the dispute game. 1572 function _warpAndResolve() internal { 1573 // Warp past the chess clock 1574 vm.warp(block.timestamp + 3 days + 12 hours + 1 seconds); 1575 1576 // Resolve all claims in reverse order. We allow `resolveClaim` calls to fail due to 1577 // the check that prevents claims with no subgames attached from being passed to 1578 // `resolveClaim`. There's also a check in `resolve` to ensure all children have been 1579 // resolved before global resolution, which catches any unresolved subgames here. 1580 for (uint256 i = gameProxy.claimDataLen(); i > 0; i--) { 1581 (bool success,) = address(gameProxy).call(abi.encodeCall(gameProxy.resolveClaim, (i - 1))); 1582 assertTrue(success); 1583 } 1584 gameProxy.resolve(); 1585 } 1586 } 1587 1588 contract ClaimCreditReenter { 1589 Vm internal immutable vm; 1590 FaultDisputeGame internal immutable GAME; 1591 uint256 public numCalls; 1592 1593 constructor(FaultDisputeGame _gameProxy, Vm _vm) { 1594 GAME = _gameProxy; 1595 vm = _vm; 1596 } 1597 1598 function claimCredit(address _recipient) public { 1599 numCalls += 1; 1600 if (numCalls > 1) { 1601 vm.expectRevert(NoCreditToClaim.selector); 1602 } 1603 GAME.claimCredit(_recipient); 1604 } 1605 1606 receive() external payable { 1607 if (numCalls == 5) { 1608 return; 1609 } 1610 claimCredit(address(this)); 1611 } 1612 } 1613 1614 /// @dev Helper to change the VM status byte of a claim. 1615 function _changeClaimStatus(Claim _claim, VMStatus _status) pure returns (Claim out_) { 1616 assembly { 1617 out_ := or(and(not(shl(248, 0xFF)), _claim), shl(248, _status)) 1618 } 1619 }