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
507ddd2c
Commit
507ddd2c
authored
Jun 10, 2023
by
clabby
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Start simple bisection
parent
93a02de0
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
426 additions
and
228 deletions
+426
-228
FaultDisputeGame.sol
.../contracts-bedrock/contracts/dispute/FaultDisputeGame.sol
+185
-167
IFaultDisputeGame.sol
...edrock/contracts/dispute/interfaces/IFaultDisputeGame.sol
+26
-61
FaultDisputeGame.t.sol
...s/contracts-bedrock/contracts/test/FaultDisputeGame.t.sol
+215
-0
No files found.
packages/contracts-bedrock/contracts/dispute/FaultDisputeGame.sol
View file @
507ddd2c
...
...
@@ -11,7 +11,7 @@ import { Clone } from "../libraries/Clone.sol";
import { LibHashing } from "./lib/LibHashing.sol";
import { LibPosition } from "./lib/LibPosition.sol";
import { LibClock } from "./lib/LibClock.sol";
import "../libraries/DisputeTypes.sol";
import "../libraries/DisputeErrors.sol";
...
...
@@ -33,13 +33,14 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone {
/**
* @notice The max depth of the game.
* @dev TODO: Update this to the value that we will use in prod.
* @dev TODO: Update this to the value that we will use in prod. Do we want to have the factory
* set this value? Should it be a constant?
*/
uint256 internal constant MAX_GAME_DEPTH = 4;
/**
* @notice The duration of the game.
* @dev TODO: Account for resolution buffer.
* @dev TODO: Account for resolution buffer.
(?)
*/
Duration internal constant GAME_DURATION = Duration.wrap(7 days);
...
...
@@ -67,55 +68,31 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone {
* @notice The left most, deepest position found during the resolution phase.
* @dev Defaults to the position of the root claim, but will be set during the resolution
* phase to the left most, deepest position found (if any qualify.)
* @dev TODO: Consider removing this if games can be resolved within a single block reliably.
*/
Position public leftMostPosition;
/**
* @notice Maps a unique ClaimHash to a Claim.
*/
mapping(ClaimHash => Claim) public claims;
/**
* @notice Maps a unique ClaimHash to its parent.
*/
mapping(ClaimHash => ClaimHash) public parents;
/**
* @notice Maps a unique ClaimHash to its position in the game tree.
* @notice An append-only array of all claims made during the dispute game.
*/
mapping(ClaimHash => Position) public positions;
/**
* @notice Maps a unique ClaimHash to a Bond.
*/
mapping(ClaimHash => BondAmount) public bonds;
/**
* @notice Maps a unique ClaimHash its chess clock.
*/
mapping(ClaimHash => Clock) public clocks;
/**
* @notice Maps a unique ClaimHash to its reference counter.
*/
mapping(ClaimHash => uint64) public rc;
/**
* @notice Tracks whether or not a unique ClaimHash has been countered.
*/
mapping(ClaimHash => bool) public countered;
ClaimData[] public claimData;
////////////////////////////////////////////////////////////////
// External Logic //
////////////////////////////////////////////////////////////////
/**
* Attack a disagreed upon ClaimHash.
* @param disagreement Disagreed upon ClaimHash
* @param pivot The supplied pivot to the disagreement.
* @inheritdoc IFaultDisputeGame
*/
function attack(
ClaimHash disagreement, Claim pivot) external
{
_move(
disagreement
, pivot, true);
function attack(
uint256 parentIndex, Claim pivot) external payable
{
_move(
parentIndex
, pivot, true);
}
// TODO: Go right instead of left
// The pivot goes into the right subtree rather than the left subtree
function defend(ClaimHash agreement, Claim pivot) external {
_move(agreement, pivot, false);
/**
* @inheritdoc IFaultDisputeGame
*/
function defend(uint256 parentIndex, Claim pivot) external payable {
_move(parentIndex, pivot, false);
}
/**
...
...
@@ -135,100 +112,82 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone {
/**
* @notice Internal move function, used by both `attack` and `defend`.
* @param c
laimHash The claim hash that the move is being made
against.
* @param pivot The
pivot point claim provided in response to `claimHash`
.
* @param c
hallengeIndex The index of the claim being moved
against.
* @param pivot The
claim at the next logical position in the game
.
* @param isAttack Whether or not the move is an attack or defense.
*/
function _move(ClaimHash claimHash, Claim pivot, bool isAttack) internal {
// TODO: Require & store bond for the pivot point claim
// Get the position of the claimHash
Position claimHashPos = positions[claimHash];
// If the current depth of the claimHash is 0, revert. The root claim cannot be defended, only
// attacked.
if (LibPosition.depth(claimHashPos) == 0 && !isAttack) {
revert CannotDefendRootClaim();
function _move(
uint256 challengeIndex,
Claim pivot,
bool isAttack
) internal {
// Moves cannot be made unless the game is currently in progress.
if (status != GameStatus.IN_PROGRESS) {
revert GameNotInProgress();
}
// If the `claimHash` is at max depth - 1, we can perform a step.
if (LibPosition.depth(claimHashPos) == MAX_GAME_DEPTH - 1) {
// TODO: Step
revert("todo: unimplemented");
// The only move that can be made against a root claim is an attack. This is because the
// root claim commits to the entire state; Therefore, the only valid defense is to do
// nothing if it is agreed with.
if (challengeIndex == 0 && !isAttack) {
revert CannotDefendRootClaim();
}
// Get the p
osition of the move.
Position pivotPos = isAttack ? LibPosition.attack(claimHashPos) : LibPosition.defend(claimHashPos)
;
// Get the p
arent
ClaimData memory parent = claimData[challengeIndex]
;
// Compute the claim hash for the pivot point claim
ClaimHash pivotClaimHash = LibHashing.hashClaimPos(pivot, pivotPos);
// Revert if the same claim has already been made.
// Note: We assume no one will ever claim the zero hash here.
if (Claim.unwrap(claims[pivotClaimHash]) != bytes32(0)) {
revert ClaimAlreadyExists();
// The parent must exist.
if (Claim.unwrap(parent.claim) == bytes32(0)) {
revert ParentDoesNotExist();
}
// Store information about the counterclaim
// TODO: Good lord, this is a lot of storage usage. Devs do something
// Map `pivotClaimHash` to `pivot` in the `claims` mapping.
claims[pivotClaimHash] = pivot;
// Map the `pivotClaimHash` to `pivotPos` in the `positions` mapping.
positions[pivotClaimHash] = pivotPos;
// Map the `pivotClaimHash` to `claimHash` in the `parents` mapping.
parents[pivotClaimHash] = claimHash;
// Increment the reference counter for the `claimHash` claim.
rc[claimHash] += 1;
// Mark `claimHash` as countered.
countered[claimHash] = true;
// Attempt to inherit grandparent's clock.
ClaimHash claimHashParent = parents[claimHash];
// If the grandparent claim doesn't exist, the disagreed upon claim is the root claim.
// In this case, the mover's clock starts at half the game duration minus the time elapsed since the game started.
if (ClaimHash.unwrap(claimHashParent) == bytes32(0)) {
// Calculate the time since the game started
Duration timeSinceGameStart = Duration.wrap(uint64(block.timestamp - Timestamp.unwrap(gameStart)));
// Set the clock for the pivot point claim.
clocks[pivotClaimHash] = LibClock.wrap(
Duration.wrap((Duration.unwrap(GAME_DURATION) >> 1) - Duration.unwrap(timeSinceGameStart)),
Timestamp.wrap(uint64(block.timestamp))
);
} else {
Clock grandparentClock = clocks[claimHashParent];
Clock parentClock = clocks[claimHash];
// Calculate the remaining clock time for the pivot point claim.
// Grandparent clock time - (block timestamp - parent clock timestamp)
// TODO: Correct this
Clock newClock = LibClock.wrap(
Duration.wrap(
uint64(
Duration.unwrap(LibClock.duration(grandparentClock))
- (block.timestamp - Timestamp.unwrap(LibClock.timestamp(parentClock)))
)
),
Timestamp.wrap(uint64(block.timestamp))
);
// Store the remaining clock time for the pivot point claim.
clocks[pivotClaimHash] = newClock;
// Bump the parent's reference counter.
claimData[challengeIndex].rc += 1;
// Set the parent claim as countered.
claimData[challengeIndex].countered = true;
// Compute the position that the claim commits to. Because the parent's position is already
// known, we can compute the next position by moving left or right depending on whether
// or not the move is an attack or defense.
Position nextPosition = isAttack
? LibPosition.attack(parent.position)
: LibPosition.defend(parent.position);
// Compute the clock for the next claim. The clock's duration is computed by taking the
// difference between the current block timestamp and the parent's clock timestamp.
Clock nextClock = LibClock.wrap(
Duration.wrap(
uint64(block.timestamp - Timestamp.unwrap(LibClock.timestamp(parent.clock)))
),
Timestamp.wrap(uint64(block.timestamp))
);
// Enforce the clock time. If the new clock duration is greater than half of the game
// duration, then the move is invalid and cannot be made.
if (Duration.unwrap(LibClock.duration(nextClock)) > Duration.unwrap(GAME_DURATION) >> 1) {
revert ClockTimeExceeded();
}
// Emit the proper event for other challenge agents to pick up on.
// Do not allow for a duplicate claim to be made.
// TODO.
// Maybe map the claimHash? There's no efficient way to check for this with the flat DAG.
// Create the new claim.
claimData.push(
ClaimData({
parentIndex: uint32(challengeIndex),
claim: pivot,
position: nextPosition,
clock: nextClock,
rc: 0,
countered: false
})
);
if (isAttack) {
// Emit the `Attack` event for other challenge agents.
emit Attack(pivotClaimHash, pivot, msg.sender);
emit Attack(challengeIndex, pivot, msg.sender);
} else {
// Emit the `Defend` event for other challenge agents.
emit Defend(pivotClaimHash, pivot, msg.sender);
emit Defend(challengeIndex, pivot, msg.sender);
}
}
...
...
@@ -237,65 +196,88 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone {
////////////////////////////////////////////////////////////////
/**
* @notice Initializes the `DisputeGame_Fault` contract.
*/
function initialize() external {
// Grab the root claim from the CWIA calldata.
Claim _rootClaim = rootClaim();
// The root claim is hashed with the root gindex to create the root ClaimHash.
ClaimHash rootClaimHash = LibHashing.hashClaimPos(_rootClaim, ROOT_POSITION);
// The root claim is the first claim in the game.
claims[rootClaimHash] = _rootClaim;
// We do not need to set the position slot for the root claim; It is already zero.
// The root claim's chess clock begins with half of the game duration.
clocks[rootClaimHash] =
LibClock.wrap(Duration.wrap(Duration.unwrap(GAME_DURATION) >> 1), Timestamp.wrap(uint64(block.timestamp)));
// The game starts when the `init()` function is called.
gameStart = Timestamp.wrap(uint64(block.timestamp));
// TODO: Init bond (possibly should be done in the factory.)
}
/**
* @inheritdoc IVersioned
*/
function version() external pure override returns (string memory) {
// TODO: Alias to constant is unnecessary.
return VERSION;
}
/**
* @notice Fetches the game type for the implementation of `IDisputeGame`.
* @dev The reference impl should be entirely different depending on the type (fault, validity)
* i.e. The game type should indicate the security model.
* @return _gameType The type of proof system being used.
* @inheritdoc IDisputeGame
*/
function gameType() public pure override returns (GameType _gameType) {
_gameType = GameType.FAULT;
}
/**
* @
notice Returns the timestamp that the DisputeGame contract was created at.
* @
inheritdoc IDisputeGame
*/
function createdAt() external view returns (Timestamp _createdAt) {
return gameStart;
}
/**
* @notice If all necessary information has been gathered, this function should mark the game
* status as either `CHALLENGER_WINS` or `DEFENDER_WINS` and return the status of
* the resolved game. It is at this stage that the bonds should be awarded to the
* necessary parties.
* @dev May only be called if the `status` is `IN_PROGRESS`.
* @inheritdoc IDisputeGame
*/
function resolve() external view returns (GameStatus _status) {
// TODO
return status;
function resolve() external returns (GameStatus _status) {
// The game may only be resolved if it is currently in progress.
if (status != GameStatus.IN_PROGRESS) {
revert GameNotInProgress();
}
// TODO: Block the game from being resolved if the preconditions for resolution have not
// been met.
// Fetch the final index of the claim data DAG.
uint256 i = claimData.length - 1;
// Store a variable on the stack to keep track of the left most, deepest claim found during
// the search.
Position leftMost;
// Run an exhaustive search (`O(n)`) over the DAG to find the left most, deepest
// uncontested claim.
for (; i > 0; i--) {
ClaimData memory claim = claimData[i];
// If the claim has no refereces, we can virtually prune it.
if (claim.rc == 0) {
Position position = claim.position;
uint128 depth = LibPosition.depth(position);
// 1. Do not count nodes at the max game depth. These can be truthy, but they do not
// give us any intuition about the final outcome of the game.
// 2. Any node that has been countered is not a dangling claim, which is all that
// we're concerned about.
// All claims that pass this check qualify for pruning.
if (depth != MAX_GAME_DEPTH && !claim.countered) {
// If the claim here is deeper than the current left most, deepest claim,
// update `leftMost`.
// If the claim here is at the same depth, but further left, update `leftMost`.
if (
depth > LibPosition.depth(leftMost) ||
(depth == LibPosition.depth(leftMost) &&
LibPosition.indexAtDepth(position) <=
LibPosition.indexAtDepth(leftMost))
) {
leftMost = position;
}
}
// If the claim has a parent, decrement the reference count of the parent. This
// effectively "prunes" the claim from the DAG without spending extra gas on
// deleting it from storage.
if (claim.parentIndex != type(uint32).max) {
claimData[claim.parentIndex].rc -= 1;
}
}
}
// If the depth of the left most, deepest dangling claim is odd, the root was attacked
// successfully and the defender wins. Otherwise, the challenger wins.
if (LibPosition.depth(leftMost) % 2 == 0) {
_status = GameStatus.DEFENDER_WINS;
} else {
_status = GameStatus.CHALLENGER_WINS;
}
// Emit the `Resolved` event.
emit Resolved(_status);
// Store the resolved status of the game.
status = _status;
}
/**
...
...
@@ -310,16 +292,52 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone {
*/
function extraData() public pure returns (bytes memory _extraData) {
// The extra data starts at the second word within the cwia calldata.
// TODO: What data do we need to pass along to this contract from the factory? Block hash, preimage data, etc.?
// TODO: What data do we need to pass along to this contract from the factory?
// Block hash, preimage data, etc.?
_extraData = _getArgDynBytes(0x20, 0x20);
}
/**
* @inheritdoc IDisputeGame
*/
function gameData() external pure returns (GameType _gameType, Claim _rootClaim, bytes memory _extraData) {
function gameData()
external
pure
returns (
GameType _gameType,
Claim _rootClaim,
bytes memory _extraData
)
{
_gameType = gameType();
_rootClaim = rootClaim();
_extraData = extraData();
}
/**
* @inheritdoc IInitializable
*/
function initialize() external {
// Set the game start
gameStart = Timestamp.wrap(uint64(block.timestamp));
// Set the root claim
claimData.push(
ClaimData({
parentIndex: type(uint32).max,
claim: rootClaim(),
position: ROOT_POSITION,
clock: LibClock.wrap(Duration.wrap(0), Timestamp.wrap(uint64(block.timestamp))),
rc: 0,
countered: false
})
);
}
/**
* @inheritdoc IVersioned
*/
function version() external pure override returns (string memory) {
return VERSION;
}
}
packages/contracts-bedrock/contracts/dispute/interfaces/IFaultDisputeGame.sol
View file @
507ddd2c
...
...
@@ -10,86 +10,51 @@ import { IDisputeGame } from "./IDisputeGame.sol";
* @notice The interface for a fault proof backed dispute game.
*/
interface IFaultDisputeGame is IDisputeGame {
/**
* @notice The `ClaimData` struct represents the data associated with a Claim.
* @dev TODO: Pack `Clock` and `Position` into the same slot. Should require 4 64 bit arms.
* @dev TODO: Add bond ID information.
*/
struct ClaimData {
uint32 parentIndex;
uint32 rc;
bool countered;
Claim claim;
Position position;
Clock clock;
}
/**
* @notice Emitted when a subclaim is disagreed upon by `claimant`
* @dev Disagreeing with a subclaim is akin to attacking it.
* @param
claimHash The unique ClaimHash that is being disagreed upon
* @param
parentIndex The index within the `claimData` array of the parent claim
* @param pivot The claim for the following pivot (disagreement = go left)
* @param claimant The address of the claimant
*/
event Attack(
ClaimHash indexed claimHash
, Claim indexed pivot, address indexed claimant);
event Attack(
uint256 indexed parentIndex
, Claim indexed pivot, address indexed claimant);
/**
* @notice Emitted when a subclaim is agreed upon by `claimant`
* @dev Agreeing with a subclaim is akin to defending it.
* @param
claimHash The unique ClaimHash that is being agreed upon
* @param
parentIndex The index within the `claimData` array of the parent claim
* @param pivot The claim for the following pivot (agreement = go right)
* @param claimant The address of the claimant
*/
event Defend(
ClaimHash indexed claimHash
, Claim indexed pivot, address indexed claimant);
event Defend(
uint256 indexed parentIndex
, Claim indexed pivot, address indexed claimant);
/**
* @notice Maps a unique ClaimHash to a Claim.
* @param claimHash The unique ClaimHash
* @return claim The Claim associated with the ClaimHash
*/
function claims(ClaimHash claimHash) external view returns (Claim claim);
/**
* @notice Maps a unique ClaimHash to its parent.
* @param claimHash The unique ClaimHash
* @return parent The parent ClaimHash of the passed ClaimHash
*/
function parents(ClaimHash claimHash) external view returns (ClaimHash parent);
/**
* @notice Maps a unique ClaimHash to its Position.
* @param claimHash The unique ClaimHash
* @return position The Position associated with the ClaimHash
*/
function positions(ClaimHash claimHash) external view returns (Position position);
/**
* @notice Maps a unique ClaimHash to a Bond.
* @param claimHash The unique ClaimHash
* @return bond The Bond associated with the ClaimHash
*/
function bonds(ClaimHash claimHash) external view returns (BondAmount bond);
/**
* @notice Maps a unique ClaimHash its chess clock.
* @param claimHash The unique ClaimHash
* @return clock The chess clock associated with the ClaimHash
*/
function clocks(ClaimHash claimHash) external view returns (Clock clock);
/**
* @notice Maps a unique ClaimHash to its reference counter.
* @param claimHash The unique ClaimHash
* @return _rc The reference counter associated with the ClaimHash
*/
function rc(ClaimHash claimHash) external view returns (uint64 _rc);
/**
* @notice Maps a unique ClaimHash to a boolean indicating whether or not it has been countered.
* @param claimHash The unique claimHash
* @return _countered Whether or not `claimHash` has been countered
*/
function countered(ClaimHash claimHash) external view returns (bool _countered);
/**
* @notice Disagree with a subclaim
* @param disagreement The ClaimHash of the disagreement
* @param pivot The claimed pivot
* Attack a disagreed upon `Claim`.
* @param parentIndex Index of the `Claim` to attack in `claimData`.
* @param pivot The `Claim` at the relative attack position.
*/
function attack(
ClaimHash disagreement, Claim pivot) external
;
function attack(
uint256 parentIndex, Claim pivot) external payable
;
/**
*
@notice Agree with a subclaim
* @param
agreement The ClaimHash of the agreement
* @param pivot The
claimed pivot
*
Defend an agreed upon `Claim`.
* @param
parentIndex Index of the claim to defend in `claimData`.
* @param pivot The
`Claim` at the relative defense position.
*/
function defend(
ClaimHash agreement, Claim pivot) external
;
function defend(
uint256 parentIndex, Claim pivot) external payable
;
/**
* @notice Perform the final step via an on-chain fault proof processor
...
...
packages/contracts-bedrock/contracts/test/FaultDisputeGame.t.sol
0 → 100644
View file @
507ddd2c
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;
import { Test } from "forge-std/Test.sol";
import { DisputeGameFactory } from "../dispute/DisputeGameFactory.sol";
import { FaultDisputeGame } from "../dispute/FaultDisputeGame.sol";
import "../libraries/DisputeTypes.sol";
import { LibClock } from "../dispute/lib/LibClock.sol";
import { LibPosition } from "../dispute/lib/LibPosition.sol";
contract FaultDisputeGame_Test is Test {
/**
* @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.
*/
bytes internal constant EXTRA_DATA = abi.encode(1);
/**
* @dev The type of the game being tested.
*/
GameType internal constant GAME_TYPE = GameType.FAULT;
/**
* @dev The current version of the `FaultDisputeGame` contract.
*/
string internal constant VERSION = "0.0.1";
/**
* @dev The factory that will be used to create the game.
*/
DisputeGameFactory internal factory;
/**
* @dev The implementation of the game.
*/
FaultDisputeGame internal gameImpl;
/**
* @dev The `Clone` proxy of the game.
*/
FaultDisputeGame internal gameProxy;
function setUp() public {
// Deploy a new dispute game factory.
factory = new DisputeGameFactory(address(this));
// Deploy an implementation of the fault game
gameImpl = new FaultDisputeGame();
// Register the game implementation with the factory.
factory.setImplementation(GAME_TYPE, gameImpl);
// Create a new game.
gameProxy = FaultDisputeGame(address(factory.create(GAME_TYPE, ROOT_CLAIM, EXTRA_DATA)));
// Label the proxy
vm.label(address(gameProxy), "FaultDisputeGame_Clone");
}
////////////////////////////////////////////////////////////////
// `IDisputeGame` Implementation Tests //
////////////////////////////////////////////////////////////////
/**
* @dev Tests that the game's root claim is set correctly.
*/
function test_rootClaim_succeeds() public {
assertEq(Claim.unwrap(gameProxy.rootClaim()), Claim.unwrap(ROOT_CLAIM));
}
/**
* @dev Tests that the game's extra data is set correctly.
*/
function test_extraData_succeeds() public {
assertEq(gameProxy.extraData(), EXTRA_DATA);
}
/**
* @dev Tests that the game's version is set correctly.
*/
function test_version_succeeds() public {
assertEq(gameProxy.version(), VERSION);
}
/**
* @dev Tests that the game's status is set correctly.
*/
function test_gameStart_succeeds() public {
assertEq(Timestamp.unwrap(gameProxy.gameStart()), block.timestamp);
}
/**
* @dev Tests that the game's type is set correctly.
*/
function test_gameType_succeeds() public {
assertEq(uint256(gameProxy.gameType()), uint256(GAME_TYPE));
}
/**
* @dev Tests that the game's data is set correctly.
*/
function test_gameData_succeeds() public {
(GameType gameType, Claim rootClaim, bytes memory extraData) =
gameProxy.gameData();
assertEq(uint256(gameType), uint256(GAME_TYPE));
assertEq(Claim.unwrap(rootClaim), Claim.unwrap(ROOT_CLAIM));
assertEq(extraData, EXTRA_DATA);
}
////////////////////////////////////////////////////////////////
// `IFaultDisputeGame` Implementation Tests //
////////////////////////////////////////////////////////////////
/**
* @dev Tests that the root claim's data is set correctly when the game is initialized.
*/
function test_initialRootClaimData_succeeds() public {
(
uint32 parentIndex,
uint32 rc,
bool countered,
Claim claim,
Position position,
Clock clock
) = gameProxy.claimData(0);
assertEq(parentIndex, type(uint32).max);
assertEq(rc, 0);
assertEq(countered, false);
assertEq(Claim.unwrap(claim), Claim.unwrap(ROOT_CLAIM));
assertEq(Position.unwrap(position), 0);
assertEq(Clock.unwrap(clock), Clock.unwrap(LibClock.wrap(Duration.wrap(0), Timestamp.wrap(uint64(block.timestamp)))));
}
/**
* @dev Static unit test for the correctness of an opening attack.
*/
function test_simpleAttack_succeeds() public {
// Warp ahead 5 seconds.
vm.warp(block.timestamp + 5);
// Perform the attack.
gameProxy.attack(0, Claim.wrap(bytes32(uint(5))));
// Grab the claim data of the attack.
(
uint32 parentIndex,
uint32 rc,
bool countered,
Claim claim,
Position position,
Clock clock
) = gameProxy.claimData(1);
// Assert correctness of the attack claim's data.
assertEq(parentIndex, 0);
assertEq(rc, 0);
assertEq(countered, false);
assertEq(Claim.unwrap(claim), Claim.unwrap(Claim.wrap(bytes32(uint(5)))));
assertEq(Position.unwrap(position), Position.unwrap(LibPosition.attack(Position.wrap(0))));
assertEq(Clock.unwrap(clock), Clock.unwrap(LibClock.wrap(Duration.wrap(5), Timestamp.wrap(uint64(block.timestamp)))));
// Grab the claim data of the parent.
(
parentIndex,
rc,
countered,
claim,
position,
clock
) = gameProxy.claimData(0);
// Assert correctness of the parent claim's data.
assertEq(parentIndex, type(uint32).max);
assertEq(rc, 1);
assertEq(countered, true);
assertEq(Claim.unwrap(claim), Claim.unwrap(ROOT_CLAIM));
assertEq(Position.unwrap(position), 0);
assertEq(Clock.unwrap(clock), Clock.unwrap(LibClock.wrap(Duration.wrap(0), Timestamp.wrap(uint64(block.timestamp - 5)))));
// Resolve the game.
assertEq(uint256(gameProxy.resolve()), uint256(GameStatus.CHALLENGER_WINS));
}
}
/**
* @title BigStepper
* @notice A mock fault proof processor contract for testing purposes.
*⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
*⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣼⠶⢅⠒⢄⢔⣶⡦⣤⡤⠄⣀⠀⠀⠀⠀⠀⠀⠀
*⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠨⡏⠀⠀⠈⠢⣙⢯⣄⠀⢨⠯⡺⡘⢄⠀⠀⠀⠀⠀
*⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣶⡆⠀⠀⠀⠀⠈⠓⠬⡒⠡⣀⢙⡜⡀⠓⠄⠀⠀⠀
*⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⡷⠿⣧⣀⡀⠀⠀⠀⠀⠀⠀⠉⠣⣞⠩⠥⠀⠼⢄⠀⠀
*⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⡇⠀⠀⠀⠉⢹⣶⠒⠒⠂⠈⠉⠁⠘⡆⠀⣿⣿⠫⡄⠀
*⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⢶⣤⣀⡀⠀⠀⢸⡿⠀⠀⠀⠀⠀⢀⠞⠀⠀⢡⢨⢀⡄⠀
*⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⡒⣿⢿⡤⠝⡣⠉⠁⠚⠛⠀⠤⠤⣄⡰⠁⠀⠀⠀⠉⠙⢸⠀⠀
*⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⡤⢯⡌⡿⡇⠘⡷⠀⠁⠀⠀⢀⣰⠢⠲⠛⣈⣸⠦⠤⠶⠴⢬⣐⣊⡂⠀
*⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣤⡪⡗⢫⠞⠀⠆⣀⠻⠤⠴⠐⠚⣉⢀⠦⠂⠋⠁⠀⠁⠀⠀⠀⠀⢋⠉⠇⠀
*⠀⠀⠀⠀⣀⡤⠐⠒⠘⡹⠉⢸⠇⠸⠀⠀⠀⠀⣀⣤⠴⠚⠉⠈⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠼⠀⣾⠀
*⠀⠀⠀⡰⠀⠉⠉⠀⠁⠀⠀⠈⢇⠈⠒⠒⠘⠈⢀⢡⡂⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢰⠀⢸⡄
*⠀⠀⠸⣿⣆⠤⢀⡀⠀⠀⠀⠀⢘⡌⠀⠀⣀⣀⣀⡈⣤⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⠀⢸⡇
*⠀⠀⢸⣀⠀⠉⠒⠐⠛⠋⠭⠭⠍⠉⠛⠒⠒⠒⠀⠒⠚⠛⠛⠛⠩⠭⠭⠭⠭⠤⠤⠤⠤⠤⠭⠭⠉⠓⡆
*⠀⠀⠘⠿⣷⣶⣤⣤⣀⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣤⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇
*⠀⠀⠀⠀⠀⠉⠙⠛⠛⠻⠿⢿⣿⣿⣷⣶⣶⣶⣤⣤⣀⣁⣛⣃⣒⠿⠿⠿⠤⠠⠄⠤⠤⢤⣛⣓⣂⣻⡇
*⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠉⠉⠉⠙⠛⠻⠿⠿⠿⢿⣿⣿⣿⣷⣶⣶⣾⣿⣿⣿⣿⠿⠟⠁
*⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠈⠉⠉⠉⠉⠁⠀⠀⠀⠀⠀⠀
*/
contract BigStepper {
/**
* @notice Steps from the `preState` to the `postState` by adding 1 to the `preState`.
* @param preState The pre state to start from
* @return postState The state stepped to
*/
function step(Claim preState) external pure returns (Claim postState) {
postState = Claim.wrap(bytes32(uint256(Claim.unwrap(preState)) + 1));
}
}
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