Commit 37d0332d authored by clabby's avatar clabby

Bug fixes, actor tests.

parent 01254782
...@@ -32,25 +32,28 @@ DisputeGameFactory_SetImplementation_Test:test_setImplementation_notOwner_revert ...@@ -32,25 +32,28 @@ DisputeGameFactory_SetImplementation_Test:test_setImplementation_notOwner_revert
DisputeGameFactory_SetImplementation_Test:test_setImplementation_succeeds() (gas: 44243) DisputeGameFactory_SetImplementation_Test:test_setImplementation_succeeds() (gas: 44243)
DisputeGameFactory_TransferOwnership_Test:test_transferOwnership_notOwner_reverts() (gas: 15950) DisputeGameFactory_TransferOwnership_Test:test_transferOwnership_notOwner_reverts() (gas: 15950)
DisputeGameFactory_TransferOwnership_Test:test_transferOwnership_succeeds() (gas: 18642) DisputeGameFactory_TransferOwnership_Test:test_transferOwnership_succeeds() (gas: 18642)
FaultDisputeGame_Test:test_clockTimeExceeded_reverts() (gas: 26385) FaultDisputeGame_ResolvesCorrectly_CorrectRoot2:test_resolvesCorrectly_succeeds() (gas: 491297)
FaultDisputeGame_Test:test_defendRoot_reverts() (gas: 13236) FaultDisputeGame_ResolvesCorrectly_CorrectRoot:test_resolvesCorrectly_succeeds() (gas: 484572)
FaultDisputeGame_Test:test_duplicateClaim_reverts() (gas: 103271) FaultDisputeGame_ResolvesCorrectly_IncorrectRoot2:test_resolvesCorrectly_succeeds() (gas: 488105)
FaultDisputeGame_ResolvesCorrectly_IncorrectRoot:test_resolvesCorrectly_succeeds() (gas: 481380)
FaultDisputeGame_Test:test_clockTimeExceeded_reverts() (gas: 26444)
FaultDisputeGame_Test:test_defendRoot_reverts() (gas: 13258)
FaultDisputeGame_Test:test_duplicateClaim_reverts() (gas: 103343)
FaultDisputeGame_Test:test_extraData_succeeds() (gas: 17478) FaultDisputeGame_Test:test_extraData_succeeds() (gas: 17478)
FaultDisputeGame_Test:test_gameData_succeeds() (gas: 17881) FaultDisputeGame_Test:test_gameData_succeeds() (gas: 17881)
FaultDisputeGame_Test:test_gameDepthExceeded_reverts() (gas: 5904481) FaultDisputeGame_Test:test_gameDepthExceeded_reverts() (gas: 407234)
FaultDisputeGame_Test:test_gameStart_succeeds() (gas: 10359) FaultDisputeGame_Test:test_gameStart_succeeds() (gas: 10359)
FaultDisputeGame_Test:test_gameType_succeeds() (gas: 8216) FaultDisputeGame_Test:test_gameType_succeeds() (gas: 8216)
FaultDisputeGame_Test:test_initialRootClaimData_succeeds() (gas: 17691) FaultDisputeGame_Test:test_initialRootClaimData_succeeds() (gas: 17669)
FaultDisputeGame_Test:test_moveAgainstNonexistentParent_reverts() (gas: 24610) FaultDisputeGame_Test:test_moveAgainstNonexistentParent_reverts() (gas: 24610)
FaultDisputeGame_Test:test_move_gameNotInProgress_reverts() (gas: 10901) FaultDisputeGame_Test:test_move_gameNotInProgress_reverts() (gas: 10901)
FaultDisputeGame_Test:test_resolve_challengeContested() (gas: 221762) FaultDisputeGame_Test:test_resolve_challengeContested() (gas: 221594)
FaultDisputeGame_Test:test_resolve_reverts() (gas: 26950) FaultDisputeGame_Test:test_resolve_notInProgress_reverts() (gas: 9657)
FaultDisputeGame_Test:test_resolve_rootContested() (gas: 106796) FaultDisputeGame_Test:test_resolve_rootContested() (gas: 106539)
FaultDisputeGame_Test:test_resolve_rootUncontested() (gas: 24266) FaultDisputeGame_Test:test_resolve_rootUncontested() (gas: 20173)
FaultDisputeGame_Test:test_resolve_teamDeathmatch() (gas: 392574) FaultDisputeGame_Test:test_resolve_teamDeathmatch() (gas: 392370)
FaultDisputeGame_Test:test_rootClaim_succeeds() (gas: 8203) FaultDisputeGame_Test:test_rootClaim_succeeds() (gas: 8225)
FaultDisputeGame_Test:test_simpleAttack_succeeds() (gas: 107435) FaultDisputeGame_Test:test_simpleAttack_succeeds() (gas: 107438)
FaultDisputeGame_Test:test_step_absolute_succeeds() (gas: 5943009)
FaultDisputeGame_Test:test_version_succeeds() (gas: 9802) FaultDisputeGame_Test:test_version_succeeds() (gas: 9802)
FeeVault_Test:test_constructor_succeeds() (gas: 18185) FeeVault_Test:test_constructor_succeeds() (gas: 18185)
GasBenchMark_L1CrossDomainMessenger:test_sendMessage_benchmark_0() (gas: 352135) GasBenchMark_L1CrossDomainMessenger:test_sendMessage_benchmark_0() (gas: 352135)
......
...@@ -27,12 +27,12 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone { ...@@ -27,12 +27,12 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone {
/** /**
* @notice The current Semver of the FaultDisputeGame implementation. * @notice The current Semver of the FaultDisputeGame implementation.
*/ */
string internal constant VERSION = "0.0.1"; string internal constant VERSION = "0.0.2";
/** /**
* @notice The max depth of the game. * @notice The max depth of the game.
*/ */
uint256 internal constant MAX_GAME_DEPTH = 63; uint256 internal constant MAX_GAME_DEPTH = 4;
/** /**
* @notice The duration of the game. * @notice The duration of the game.
...@@ -124,8 +124,8 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone { ...@@ -124,8 +124,8 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone {
// Determine the position of the step. // Determine the position of the step.
Position stepPos = _isAttack ? parentPos.attack() : parentPos.defend(); Position stepPos = _isAttack ? parentPos.attack() : parentPos.defend();
// Ensure that the step position is at the maximum game depth. // Ensure that the step position is 1 deeper than the maximum game depth.
if (stepPos.depth() != MAX_GAME_DEPTH) { if (stepPos.depth() != MAX_GAME_DEPTH + 1) {
revert InvalidParent(); revert InvalidParent();
} }
...@@ -136,14 +136,14 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone { ...@@ -136,14 +136,14 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone {
// If the step position's index at depth is 0, the prestate is the absolute prestate // If the step position's index at depth is 0, the prestate is the absolute prestate
// and the post state is the parent claim. // and the post state is the parent claim.
preStateClaim = ABSOLUTE_PRESTATE; preStateClaim = ABSOLUTE_PRESTATE;
postStateClaim = claimData[_stateIndex].claim; postStateClaim = claimData[_parentIndex].claim;
} else { } else {
Position preStatePos; Position preStatePos;
if (_isAttack) { if (_isAttack) {
// If the step is an attack, the prestate exists elsewhere in the game state, // If the step is an attack, the prestate exists elsewhere in the game state,
// and the parent claim is the expected post-state. // and the parent claim is the expected post-state.
preStatePos = claimData[_stateIndex].position; preStatePos = claimData[_stateIndex].position;
preStateClaim = claimData[_parentIndex].claim; preStateClaim = claimData[_stateIndex].claim;
postStateClaim = parent.claim; postStateClaim = parent.claim;
} else { } else {
// If the step is a defense, the poststate exists elsewhere in the game state, // If the step is a defense, the poststate exists elsewhere in the game state,
...@@ -156,7 +156,7 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone { ...@@ -156,7 +156,7 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone {
// Assert that the given prestate commits to the instruction at `gindex - 1`. // Assert that the given prestate commits to the instruction at `gindex - 1`.
if ( if (
Position.unwrap(preStatePos.rightIndex(MAX_GAME_DEPTH)) != Position.unwrap(preStatePos.rightIndex(MAX_GAME_DEPTH)) !=
Position.unwrap(stepPos) - 1 Position.unwrap(parentPos) - 1
) { ) {
revert InvalidPrestate(); revert InvalidPrestate();
} }
...@@ -215,7 +215,7 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone { ...@@ -215,7 +215,7 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone {
// At the leaf nodes of the game, the only option is to run a step to prove or disprove // At the leaf nodes of the game, the only option is to run a step to prove or disprove
// the above claim. At this depth, the parent claim commits to the state after a single // the above claim. At this depth, the parent claim commits to the state after a single
// instruction step. // instruction step.
if (nextPosition.depth() >= MAX_GAME_DEPTH) { if (nextPosition.depth() > MAX_GAME_DEPTH) {
revert GameDepthExceeded(); revert GameDepthExceeded();
} }
...@@ -300,14 +300,23 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone { ...@@ -300,14 +300,23 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone {
* @inheritdoc IDisputeGame * @inheritdoc IDisputeGame
*/ */
function resolve() external returns (GameStatus status_) { function resolve() external returns (GameStatus status_) {
// TODO: Do not allow resolution before clocks run out.
if (status != GameStatus.IN_PROGRESS) { if (status != GameStatus.IN_PROGRESS) {
// If the game is not in progress, it cannot be resolved.
revert GameNotInProgress(); revert GameNotInProgress();
} else if (!claimData[0].countered) {
// If the root claim has never been countered, it implicitly wins.
status_ = GameStatus.DEFENDER_WINS;
status = status_;
emit Resolved(status_);
return status_;
} }
// Search for the left-most dangling non-bottom node // Search for the left-most dangling non-bottom node
// The most recent claim is always a dangling, non-bottom node so we start with that // The most recent claim is always a dangling, non-bottom node so we start with that
uint256 leftMostIndex = claimData.length - 1; uint256 leftMostIndex = claimData.length - 1;
Position leftMostTraceIndex = claimData[leftMostIndex].position.rightIndex(MAX_GAME_DEPTH); Position leftMostTraceIndex = Position.wrap(type(uint128).max);
for (uint256 i = leftMostIndex; i < type(uint64).max; ) { for (uint256 i = leftMostIndex; i < type(uint64).max; ) {
// Fetch the claim at the current index. // Fetch the claim at the current index.
ClaimData storage claim = claimData[i]; ClaimData storage claim = claimData[i];
...@@ -338,7 +347,10 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone { ...@@ -338,7 +347,10 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone {
// If the left-most dangling node is at an even depth, the defender wins. // If the left-most dangling node is at an even depth, the defender wins.
// Otherwise, the challenger wins and the root claim is deemed invalid. // Otherwise, the challenger wins and the root claim is deemed invalid.
// slither-disable-next-line weak-prng // slither-disable-next-line weak-prng
if (claimData[leftMostIndex].position.depth() % 2 == 0) { if (
claimData[leftMostIndex].position.depth() % 2 == 0 &&
Position.unwrap(leftMostTraceIndex) != type(uint128).max
) {
status_ = GameStatus.DEFENDER_WINS; status_ = GameStatus.DEFENDER_WINS;
} else { } else {
status_ = GameStatus.CHALLENGER_WINS; status_ = GameStatus.CHALLENGER_WINS;
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
pragma solidity ^0.8.15; pragma solidity ^0.8.15;
import { Test } from "forge-std/Test.sol"; import { Test } from "forge-std/Test.sol";
import { Vm } from "forge-std/Vm.sol";
import { DisputeGameFactory_Init } from "./DisputeGameFactory.t.sol"; import { DisputeGameFactory_Init } from "./DisputeGameFactory.t.sol";
import { DisputeGameFactory } from "../dispute/DisputeGameFactory.sol"; import { DisputeGameFactory } from "../dispute/DisputeGameFactory.sol";
import { FaultDisputeGame } from "../dispute/FaultDisputeGame.sol"; import { FaultDisputeGame } from "../dispute/FaultDisputeGame.sol";
...@@ -11,11 +12,7 @@ import "../libraries/DisputeErrors.sol"; ...@@ -11,11 +12,7 @@ import "../libraries/DisputeErrors.sol";
import { LibClock } from "../dispute/lib/LibClock.sol"; import { LibClock } from "../dispute/lib/LibClock.sol";
import { LibPosition } from "../dispute/lib/LibPosition.sol"; import { LibPosition } from "../dispute/lib/LibPosition.sol";
contract FaultDisputeGame_Test is DisputeGameFactory_Init { contract FaultDisputeGame_Init is DisputeGameFactory_Init {
/**
* @dev The root claim of the game.
*/
Claim internal constant ROOT_CLAIM = Claim.wrap(bytes32(uint256(10)));
/** /**
* @dev The extra data passed to the game for initialization. * @dev The extra data passed to the game for initialization.
*/ */
...@@ -27,7 +24,7 @@ contract FaultDisputeGame_Test is DisputeGameFactory_Init { ...@@ -27,7 +24,7 @@ contract FaultDisputeGame_Test is DisputeGameFactory_Init {
/** /**
* @dev The current version of the `FaultDisputeGame` contract. * @dev The current version of the `FaultDisputeGame` contract.
*/ */
string internal constant VERSION = "0.0.1"; string internal constant VERSION = "0.0.2";
/** /**
* @dev The implementation of the game. * @dev The implementation of the game.
...@@ -40,18 +37,33 @@ contract FaultDisputeGame_Test is DisputeGameFactory_Init { ...@@ -40,18 +37,33 @@ contract FaultDisputeGame_Test is DisputeGameFactory_Init {
event Move(uint256 indexed parentIndex, Claim indexed pivot, address indexed claimant); event Move(uint256 indexed parentIndex, Claim indexed pivot, address indexed claimant);
function setUp() public override { function init(Claim rootClaim, Claim absolutePrestate) public {
super.setUp(); super.setUp();
// Deploy an implementation of the fault game // Deploy an implementation of the fault game
gameImpl = new FaultDisputeGame(Claim.wrap(bytes32(uint256(5)))); gameImpl = new FaultDisputeGame(absolutePrestate);
// Register the game implementation with the factory. // Register the game implementation with the factory.
factory.setImplementation(GAME_TYPE, gameImpl); factory.setImplementation(GAME_TYPE, gameImpl);
// Create a new game. // Create a new game.
gameProxy = FaultDisputeGame(address(factory.create(GAME_TYPE, ROOT_CLAIM, EXTRA_DATA))); gameProxy = FaultDisputeGame(address(factory.create(GAME_TYPE, rootClaim, EXTRA_DATA)));
// Label the proxy // Label the proxy
vm.label(address(gameProxy), "FaultDisputeGame_Clone"); vm.label(address(gameProxy), "FaultDisputeGame_Clone");
} }
}
contract FaultDisputeGame_Test is FaultDisputeGame_Init {
/**
* @dev The root claim of the game.
*/
Claim internal constant ROOT_CLAIM = Claim.wrap(bytes32(uint256(10)));
/**
* @dev The absolute prestate of the trace.
*/
Claim internal constant ABSOLUTE_PRESTATE = Claim.wrap(bytes32(uint256(0)));
function setUp() public override {
super.init(ROOT_CLAIM, ABSOLUTE_PRESTATE);
}
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
// `IDisputeGame` Implementation Tests // // `IDisputeGame` Implementation Tests //
...@@ -184,10 +196,10 @@ contract FaultDisputeGame_Test is DisputeGameFactory_Init { ...@@ -184,10 +196,10 @@ contract FaultDisputeGame_Test is DisputeGameFactory_Init {
function test_gameDepthExceeded_reverts() public { function test_gameDepthExceeded_reverts() public {
Claim claim = Claim.wrap(bytes32(uint256(5))); Claim claim = Claim.wrap(bytes32(uint256(5)));
for (uint256 i = 0; i < 63; i++) { for (uint256 i = 0; i < 5; i++) {
// At the max game depth, the `_move` function should revert with // At the max game depth, the `_move` function should revert with
// the `GameDepthExceeded` error. // the `GameDepthExceeded` error.
if (i == 62) { if (i == 4) {
vm.expectRevert(GameDepthExceeded.selector); vm.expectRevert(GameDepthExceeded.selector);
} }
gameProxy.attack(i, claim); gameProxy.attack(i, claim);
...@@ -279,10 +291,20 @@ contract FaultDisputeGame_Test is DisputeGameFactory_Init { ...@@ -279,10 +291,20 @@ contract FaultDisputeGame_Test is DisputeGameFactory_Init {
} }
/** /**
* @dev Static unit test asserting that resolve reverts when the game is not in progress. * @dev Static unit test asserting that resolve reverts when the game state is
* not in progress.
*/ */
function test_resolve_reverts() public { function test_resolve_notInProgress_reverts() public {
gameProxy.resolve(); uint256 chalWins = uint256(GameStatus.CHALLENGER_WINS);
// Replace the game status in storage. It exists in slot 0 at offset 8.
uint256 slot = uint256(vm.load(address(gameProxy), bytes32(0)));
uint256 offset = (8 << 3);
uint256 mask = 0xFF << offset;
// Replace the byte in the slot value with the challenger wins status.
slot = (slot & ~mask) | (chalWins << offset);
vm.store(address(gameProxy), bytes32(uint256(0)), bytes32(slot));
vm.expectRevert(GameNotInProgress.selector); vm.expectRevert(GameNotInProgress.selector);
gameProxy.resolve(); gameProxy.resolve();
} }
...@@ -323,57 +345,340 @@ contract FaultDisputeGame_Test is DisputeGameFactory_Init { ...@@ -323,57 +345,340 @@ contract FaultDisputeGame_Test is DisputeGameFactory_Init {
assertEq(uint8(status), uint8(GameStatus.CHALLENGER_WINS)); assertEq(uint8(status), uint8(GameStatus.CHALLENGER_WINS));
assertEq(uint8(gameProxy.status()), uint8(GameStatus.CHALLENGER_WINS)); assertEq(uint8(gameProxy.status()), uint8(GameStatus.CHALLENGER_WINS));
} }
}
/**
* @notice A generic game player actor with a configurable trace.
* @dev This actor always responds rationally with respect to their trace. The
* `play` function can be overridden to change this behavior.
*/
contract GamePlayer {
uint256 internal constant MAX_DEPTH = 4;
bool public failedToStep;
FaultDisputeGame public gameProxy;
GamePlayer internal counterParty;
Vm internal vm;
bytes internal trace;
/**
* @notice Initializes the player
*/
function init(
FaultDisputeGame _gameProxy,
GamePlayer _counterParty,
Vm _vm
) public virtual {
gameProxy = _gameProxy;
counterParty = _counterParty;
vm = _vm;
}
/** /**
* @notice Tests that the `step` function works from the absolute prestate -> instruction 1. * @notice Perform the next move in the game.
* @dev This is very janky atm.
*/ */
function test_step_absolute_succeeds() public { function play(uint256 _parentIndex) public virtual {
Claim claim = Claim.wrap(bytes32(uint256(5))); // Grab the claim data at the parent index.
(uint32 grandparentIndex, , Claim parentClaim, Position parentPos, ) = gameProxy.claimData(
_parentIndex
);
for (uint256 i = 0; i < 62; i++) { // The position to move to.
gameProxy.attack(i, claim); Position movePos;
// May or may not be used.
Position movePos2;
// Signifies whether the move is an attack or not.
bool isAttack;
if (grandparentIndex == type(uint32).max) {
// If the parent claim is the root claim, begin by attacking.
movePos = parentPos.attack();
// Flag the move as an attack.
isAttack = true;
} else {
// If the parent claim is not the root claim, check if we disagree with it and/or its grandparent
// to determine our next move(s).
// Fetch our claim at the parent's position.
Claim ourParentClaim = claimAt(parentPos);
// Fetch our claim at the grandparent's position.
(, , Claim grandparentClaim, Position grandparentPos, ) = gameProxy.claimData(
grandparentIndex
);
Claim ourGrandparentClaim = claimAt(grandparentPos);
if (Claim.unwrap(ourParentClaim) != Claim.unwrap(parentClaim)) {
// Attack parent.
movePos = parentPos.attack();
// If we also disagree with the grandparent, attack it as well.
if (Claim.unwrap(ourGrandparentClaim) != Claim.unwrap(grandparentClaim)) {
movePos2 = grandparentPos.attack();
} }
// This step should succeed, since it proves the above claim as invalid relative
// to the absolute prestate.
gameProxy.step(0, 62, true, hex"", hex"");
(, bool countered, , , ) = gameProxy.claimData(62); // Flag the move as an attack.
assertTrue(countered); isAttack = true;
} else if (
Claim.unwrap(ourParentClaim) == Claim.unwrap(parentClaim) &&
Claim.unwrap(ourGrandparentClaim) == Claim.unwrap(grandparentClaim)
) {
movePos = parentPos.defend();
}
}
GameStatus status = gameProxy.resolve(); // If we are past the maximum depth, break the recursion and step.
assertEq(uint8(status), uint8(GameStatus.DEFENDER_WINS)); if (movePos.depth() > MAX_DEPTH) {
// Perform a step.
uint256 stateIndex;
// First, we need to find the pre/post state index depending on whether we
// are making an attack step or a defense step. If the index at depth of the
// move position is 0, the prestate is the absolute prestate and we need to
// do nothing.
if (movePos.indexAtDepth() > 0) {
Position statePos;
if (isAttack) {
statePos = Position.wrap(Position.unwrap(parentPos) - 1);
// Walk up until the valid position that commits to the prestate's
// trace index is found.
while (
Position.unwrap(statePos.parent().rightIndex(MAX_DEPTH)) ==
Position.unwrap(statePos)
) {
statePos = statePos.parent().parent().parent();
}
} else {
// TODO - Defend step logic.
}
// Now, search for the index of the claim that commits to the prestate's trace
// index.
uint256 len = claimDataLen();
for (uint256 i = 0; i < len; i++) {
(, , , Position pos, ) = gameProxy.claimData(i);
if (Position.unwrap(pos) == Position.unwrap(statePos)) {
stateIndex = i;
break;
}
}
}
// Perform the step and hault recursion.
try gameProxy.step(stateIndex, _parentIndex, isAttack, hex"", hex"") {
// Do nothing, step succeeded.
} catch {
failedToStep = true;
}
} else {
// Find the trace index that our next claim must commit to.
uint256 traceIndex = movePos.rightIndex(MAX_DEPTH).indexAtDepth();
// Grab the claim that we need to make from the helper.
Claim ourClaim = claimAt(traceIndex);
if (isAttack) {
// Attack the parent claim.
gameProxy.attack(_parentIndex, ourClaim);
// Call out to our counter party to respond.
counterParty.play(claimDataLen() - 1);
// If we have a second move position, attack the grandparent.
if (Position.unwrap(movePos2) != 0) {
(, , , Position grandparentPos, ) = gameProxy.claimData(grandparentIndex);
Claim ourGrandparentClaim = claimAt(grandparentPos.attack());
gameProxy.attack(grandparentIndex, ourGrandparentClaim);
counterParty.play(claimDataLen() - 1);
}
} else {
// Defend the parent claim.
gameProxy.defend(_parentIndex, ourClaim);
// Call out to our counter party to respond.
counterParty.play(claimDataLen() - 1);
}
}
}
/**
* @notice Returns the length of the claim data array.
*/
function claimDataLen() internal view returns (uint256 len_) {
return uint256(vm.load(address(gameProxy), bytes32(uint256(1))));
}
/**
* @notice Returns the player's claim that commits to a given gindex.
*/
function claimAt(Position _position) internal view returns (Claim claim_) {
return claimAt(_position.rightIndex(MAX_DEPTH).indexAtDepth());
}
/**
* @notice Returns the player's claim that commits to a given trace index.
*/
function claimAt(uint256 _traceIndex) public view returns (Claim claim_) {
return Claim.wrap(bytes32(uint256(bytes32(trace[_traceIndex]) >> 248)));
} }
} }
/** contract OneVsOne_Arena is FaultDisputeGame_Init {
* @title BigStepper /**
* @notice A mock fault proof processor contract for testing purposes. * @dev The absolute prestate of the trace.
*⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ */
*⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣼⠶⢅⠒⢄⢔⣶⡦⣤⡤⠄⣀⠀⠀⠀⠀⠀⠀⠀ Claim internal constant ABSOLUTE_PRESTATE = Claim.wrap(bytes32(uint256(15)));
*⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠨⡏⠀⠀⠈⠢⣙⢯⣄⠀⢨⠯⡺⡘⢄⠀⠀⠀⠀⠀ /**
*⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣶⡆⠀⠀⠀⠀⠈⠓⠬⡒⠡⣀⢙⡜⡀⠓⠄⠀⠀⠀ * @dev The honest participant.
*⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⡷⠿⣧⣀⡀⠀⠀⠀⠀⠀⠀⠉⠣⣞⠩⠥⠀⠼⢄⠀⠀ */
*⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⡇⠀⠀⠀⠉⢹⣶⠒⠒⠂⠈⠉⠁⠘⡆⠀⣿⣿⠫⡄⠀ GamePlayer internal honest;
*⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⢶⣤⣀⡀⠀⠀⢸⡿⠀⠀⠀⠀⠀⢀⠞⠀⠀⢡⢨⢀⡄⠀ /**
*⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⡒⣿⢿⡤⠝⡣⠉⠁⠚⠛⠀⠤⠤⣄⡰⠁⠀⠀⠀⠉⠙⢸⠀⠀ * @dev The dishonest participant.
*⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⡤⢯⡌⡿⡇⠘⡷⠀⠁⠀⠀⢀⣰⠢⠲⠛⣈⣸⠦⠤⠶⠴⢬⣐⣊⡂⠀ */
*⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣤⡪⡗⢫⠞⠀⠆⣀⠻⠤⠴⠐⠚⣉⢀⠦⠂⠋⠁⠀⠁⠀⠀⠀⠀⢋⠉⠇⠀ GamePlayer internal dishonest;
*⠀⠀⠀⠀⣀⡤⠐⠒⠘⡹⠉⢸⠇⠸⠀⠀⠀⠀⣀⣤⠴⠚⠉⠈⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠼⠀⣾⠀
*⠀⠀⠀⡰⠀⠉⠉⠀⠁⠀⠀⠈⢇⠈⠒⠒⠘⠈⢀⢡⡂⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢰⠀⢸⡄ function init(
*⠀⠀⠸⣿⣆⠤⢀⡀⠀⠀⠀⠀⢘⡌⠀⠀⣀⣀⣀⡈⣤⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⠀⢸⡇ GamePlayer _honest,
*⠀⠀⢸⣀⠀⠉⠒⠐⠛⠋⠭⠭⠍⠉⠛⠒⠒⠒⠀⠒⠚⠛⠛⠛⠩⠭⠭⠭⠭⠤⠤⠤⠤⠤⠭⠭⠉⠓⡆ GamePlayer _dishonest,
*⠀⠀⠘⠿⣷⣶⣤⣤⣀⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣤⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇ Claim _rootClaim
*⠀⠀⠀⠀⠀⠉⠙⠛⠛⠻⠿⢿⣿⣿⣷⣶⣶⣶⣤⣤⣀⣁⣛⣃⣒⠿⠿⠿⠤⠠⠄⠤⠤⢤⣛⣓⣂⣻⡇ ) public {
*⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠉⠉⠉⠙⠛⠻⠿⠿⠿⢿⣿⣿⣿⣷⣶⣶⣾⣿⣿⣿⣿⠿⠟⠁ super.init(_rootClaim, ABSOLUTE_PRESTATE);
*⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠈⠉⠉⠉⠉⠁⠀⠀⠀⠀⠀ // Deploy a new honest player.
*/ honest = _honest;
contract BigStepper { // Deploy a new dishonest player.
/** dishonest = _dishonest;
* @notice Steps from the `preState` to the `postState` by adding 1 to the `preState`.
* @param preState The pre state to start from // Set the counterparties.
* @return postState The state stepped to honest.init(gameProxy, dishonest, vm);
*/ dishonest.init(gameProxy, honest, vm);
function step(Claim preState) external pure returns (Claim postState) {
postState = Claim.wrap(bytes32(uint256(Claim.unwrap(preState)) + 1)); // Label actors for trace.
vm.label(address(honest), "HonestPlayer");
vm.label(address(dishonest), "DishonestPlayer");
}
}
contract FaultDisputeGame_ResolvesCorrectly_IncorrectRoot is OneVsOne_Arena {
function setUp() public override {
GamePlayer honest = new HonestPlayer();
GamePlayer dishonest = new FullyDivergentPlayer();
super.init(honest, dishonest, Claim.wrap(bytes32(uint256(30))));
}
function test_resolvesCorrectly_succeeds() public {
// Play the game until a step is forced.
honest.play(0);
// Resolve the game and assert that the honest player challenged the root
// claim successfully.
assertEq(uint8(gameProxy.resolve()), uint8(GameStatus.CHALLENGER_WINS));
assertFalse(honest.failedToStep());
}
}
contract FaultDisputeGame_ResolvesCorrectly_CorrectRoot is OneVsOne_Arena {
function setUp() public override {
GamePlayer honest = new HonestPlayer();
GamePlayer dishonest = new FullyDivergentPlayer();
super.init(honest, dishonest, Claim.wrap(bytes32(uint256(31))));
}
function test_resolvesCorrectly_succeeds() public {
// Play the game until a step is forced.
dishonest.play(0);
// Resolve the game and assert that the honest player challenged the root
// claim successfully.
assertEq(uint8(gameProxy.resolve()), uint8(GameStatus.DEFENDER_WINS));
assertTrue(dishonest.failedToStep());
}
}
contract FaultDisputeGame_ResolvesCorrectly_IncorrectRoot2 is OneVsOne_Arena {
function setUp() public override {
GamePlayer honest = new HonestPlayer();
GamePlayer dishonest = new HalfDivergentPlayer();
super.init(honest, dishonest, Claim.wrap(bytes32(uint256(15))));
}
function test_resolvesCorrectly_succeeds() public {
// Play the game until a step is forced.
honest.play(0);
// Resolve the game and assert that the honest player challenged the root
// claim successfully.
assertEq(uint8(gameProxy.resolve()), uint8(GameStatus.CHALLENGER_WINS));
assertFalse(honest.failedToStep());
}
}
contract FaultDisputeGame_ResolvesCorrectly_CorrectRoot2 is OneVsOne_Arena {
function setUp() public override {
GamePlayer honest = new HonestPlayer();
GamePlayer dishonest = new HalfDivergentPlayer();
super.init(honest, dishonest, Claim.wrap(bytes32(uint256(31))));
}
function test_resolvesCorrectly_succeeds() public {
// Play the game until a step is forced.
dishonest.play(0);
// Resolve the game and assert that the honest player challenged the root
// claim successfully.
assertEq(uint8(gameProxy.resolve()), uint8(GameStatus.DEFENDER_WINS));
assertTrue(dishonest.failedToStep());
}
}
////////////////////////////////////////////////////////////////
// ACTORS //
////////////////////////////////////////////////////////////////
contract HonestPlayer is GamePlayer {
function init(
FaultDisputeGame _gameProxy,
GamePlayer _counterParty,
Vm _vm
) public virtual override {
super.init(_gameProxy, _counterParty, _vm);
uint8 absolutePrestate = uint8(uint256(Claim.unwrap(_gameProxy.ABSOLUTE_PRESTATE())));
bytes memory honestTrace = new bytes(16);
for (uint8 i = 0; i < honestTrace.length; i++) {
honestTrace[i] = bytes1(absolutePrestate + i + 1);
}
trace = honestTrace;
}
}
contract FullyDivergentPlayer is GamePlayer {
function init(
FaultDisputeGame _gameProxy,
GamePlayer _counterParty,
Vm _vm
) public virtual override {
super.init(_gameProxy, _counterParty, _vm);
uint8 absolutePrestate = uint8(uint256(Claim.unwrap(_gameProxy.ABSOLUTE_PRESTATE())));
bytes memory dishonestTrace = new bytes(16);
for (uint8 i = 0; i < dishonestTrace.length; i++) {
// Offset the honest trace by 1.
dishonestTrace[i] = bytes1(absolutePrestate + i);
}
trace = dishonestTrace;
}
}
contract HalfDivergentPlayer is GamePlayer {
function init(
FaultDisputeGame _gameProxy,
GamePlayer _counterParty,
Vm _vm
) public virtual override {
super.init(_gameProxy, _counterParty, _vm);
uint8 absolutePrestate = uint8(uint256(Claim.unwrap(_gameProxy.ABSOLUTE_PRESTATE())));
bytes memory dishonestTrace = new bytes(16);
for (uint8 i = 0; i < dishonestTrace.length; i++) {
// Offset the trace after the first half.
dishonestTrace[i] = i > 7 ? bytes1(i) : bytes1(absolutePrestate + i + 1);
}
trace = dishonestTrace;
} }
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment