Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
N
nebula
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
exchain
nebula
Commits
37d0332d
Commit
37d0332d
authored
Jun 21, 2023
by
clabby
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Bug fixes, actor tests.
parent
01254782
Changes
3
Show whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
397 additions
and
77 deletions
+397
-77
.gas-snapshot
packages/contracts-bedrock/.gas-snapshot
+16
-13
FaultDisputeGame.sol
.../contracts-bedrock/contracts/dispute/FaultDisputeGame.sol
+22
-10
FaultDisputeGame.t.sol
...s/contracts-bedrock/contracts/test/FaultDisputeGame.t.sol
+359
-54
No files found.
packages/contracts-bedrock/.gas-snapshot
View file @
37d0332d
...
@@ -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: 176
91
)
FaultDisputeGame_Test:test_initialRootClaimData_succeeds() (gas: 176
69
)
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)
...
...
packages/contracts-bedrock/contracts/dispute/FaultDisputeGame.sol
View file @
37d0332d
...
@@ -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[_
state
Index].claim;
postStateClaim = claimData[_
parent
Index].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[_
parent
Index].claim;
preStateClaim = claimData[_
state
Index].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(
step
Pos) - 1
Position.unwrap(
parent
Pos) - 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;
...
...
packages/contracts-bedrock/contracts/test/FaultDisputeGame.t.sol
View file @
37d0332d
...
@@ -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;
}
}
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment