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
8a879c41
Unverified
Commit
8a879c41
authored
Jun 15, 2023
by
OptimismBot
Committed by
GitHub
Jun 15, 2023
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #5984 from ethereum-optimism/clabby/ctb/start-bisection-game
feat(ctb): Start bisection game
parents
c7949cb3
84e8b55b
Changes
12
Hide whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
976 additions
and
93 deletions
+976
-93
.gas-snapshot
packages/contracts-bedrock/.gas-snapshot
+8
-0
FaultDisputeGame.sol
.../contracts-bedrock/contracts/dispute/FaultDisputeGame.sol
+300
-0
IDisputeGame.sol
...cts-bedrock/contracts/dispute/interfaces/IDisputeGame.sol
+20
-17
IFaultDisputeGame.sol
...edrock/contracts/dispute/interfaces/IFaultDisputeGame.sol
+32
-70
LibClock.sol
...ages/contracts-bedrock/contracts/dispute/lib/LibClock.sol
+47
-0
LibHashing.sol
...es/contracts-bedrock/contracts/dispute/lib/LibHashing.sol
+24
-0
LibPosition.sol
...s/contracts-bedrock/contracts/dispute/lib/LibPosition.sol
+127
-0
DisputeErrors.sol
...s/contracts-bedrock/contracts/libraries/DisputeErrors.sol
+26
-0
DisputeTypes.sol
...es/contracts-bedrock/contracts/libraries/DisputeTypes.sol
+6
-6
FaultDisputeGame.t.sol
...s/contracts-bedrock/contracts/test/FaultDisputeGame.t.sol
+211
-0
LibClock.t.sol
packages/contracts-bedrock/contracts/test/LibClock.t.sol
+27
-0
LibPosition.t.sol
packages/contracts-bedrock/contracts/test/LibPosition.t.sol
+148
-0
No files found.
packages/contracts-bedrock/.gas-snapshot
View file @
8a879c41
...
@@ -32,6 +32,14 @@ DisputeGameFactory_Test:test_setImplementation_notOwner_reverts() (gas: 16099)
...
@@ -32,6 +32,14 @@ DisputeGameFactory_Test:test_setImplementation_notOwner_reverts() (gas: 16099)
DisputeGameFactory_Test:test_setImplementation_succeeds() (gas: 44302)
DisputeGameFactory_Test:test_setImplementation_succeeds() (gas: 44302)
DisputeGameFactory_Test:test_transferOwnership_notOwner_reverts() (gas: 15974)
DisputeGameFactory_Test:test_transferOwnership_notOwner_reverts() (gas: 15974)
DisputeGameFactory_Test:test_transferOwnership_succeeds() (gas: 18694)
DisputeGameFactory_Test:test_transferOwnership_succeeds() (gas: 18694)
FaultDisputeGame_Test:test_extraData_succeeds() (gas: 17456)
FaultDisputeGame_Test:test_gameData_succeeds() (gas: 17882)
FaultDisputeGame_Test:test_gameStart_succeeds() (gas: 10315)
FaultDisputeGame_Test:test_gameType_succeeds() (gas: 8237)
FaultDisputeGame_Test:test_initialRootClaimData_succeeds() (gas: 17602)
FaultDisputeGame_Test:test_rootClaim_succeeds() (gas: 8213)
FaultDisputeGame_Test:test_simpleAttack_succeeds() (gas: 105058)
FaultDisputeGame_Test:test_version_succeeds() (gas: 9758)
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)
GasBenchMark_L1CrossDomainMessenger:test_sendMessage_benchmark_1() (gas: 2950342)
GasBenchMark_L1CrossDomainMessenger:test_sendMessage_benchmark_1() (gas: 2950342)
...
...
packages/contracts-bedrock/contracts/dispute/FaultDisputeGame.sol
0 → 100644
View file @
8a879c41
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;
import { IDisputeGame } from "./interfaces/IDisputeGame.sol";
import { IVersioned } from "./interfaces/IVersioned.sol";
import { IFaultDisputeGame } from "./interfaces/IFaultDisputeGame.sol";
import { IInitializable } from "./interfaces/IInitializable.sol";
import { IBondManager } from "./interfaces/IBondManager.sol";
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";
/**
* @title FaultDisputeGame
* @notice An implementation of the `IFaultDisputeGame` interface.
*/
contract FaultDisputeGame is IFaultDisputeGame, Clone {
////////////////////////////////////////////////////////////////
// State Vars //
////////////////////////////////////////////////////////////////
/**
* @notice The current Semver of the FaultDisputeGame implementation.
*/
string internal constant VERSION = "0.0.1";
/**
* @notice The max depth of the game.
*/
uint256 internal constant MAX_GAME_DEPTH = 63;
/**
* @notice The duration of the game.
* @dev TODO: Account for resolution buffer. (?)
*/
Duration internal constant GAME_DURATION = Duration.wrap(7 days);
/**
* @notice The root claim's position is always at depth 0; index 0.
*/
Position internal constant ROOT_POSITION = Position.wrap(0);
/**
* @notice The starting timestamp of the game
*/
Timestamp public gameStart;
/**
* @inheritdoc IDisputeGame
*/
GameStatus public status;
/**
* @inheritdoc IDisputeGame
*/
IBondManager public bondManager;
/**
* @notice An append-only array of all claims made during the dispute game.
*/
ClaimData[] public claimData;
/**
* @notice An internal mapping to allow for constant-time lookups of existing claims.
*/
mapping(ClaimHash => bool) internal claims;
////////////////////////////////////////////////////////////////
// External Logic //
////////////////////////////////////////////////////////////////
/**
* @inheritdoc IFaultDisputeGame
*/
function attack(uint256 _parentIndex, Claim _pivot) external payable {
_move(_parentIndex, _pivot, true);
}
/**
* @inheritdoc IFaultDisputeGame
*/
function defend(uint256 _parentIndex, Claim _pivot) external payable {
_move(_parentIndex, _pivot, false);
}
/**
* @inheritdoc IFaultDisputeGame
*/
function step(
uint256 _prestateIndex,
uint256 _parentIndex,
bytes calldata _stateData,
bytes calldata _proof
) external {
// TODO - Call the VM to perform the execution step.
}
////////////////////////////////////////////////////////////////
// Internal Logic //
////////////////////////////////////////////////////////////////
/**
* @notice Internal move function, used by both `attack` and `defend`.
* @param _challengeIndex 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(
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();
}
// The zero hash is not a valid claim.
if (Claim.unwrap(_pivot) == bytes32(0)) {
revert InvalidClaim();
}
// 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 parent
ClaimData memory parent = claimData[_challengeIndex];
// The parent must exist.
if (Claim.unwrap(parent.claim) == bytes32(0)) {
revert ParentDoesNotExist();
}
// 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);
// 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
// instruction step.
if (LibPosition.depth(nextPosition) >= MAX_GAME_DEPTH) {
revert GameDepthExceeded();
}
// Fetch the grandparent clock, if it exists.
// The grandparent clock should always exist unless the parent is the root claim.
Clock grandparentClock;
if (parent.parentIndex != type(uint32).max) {
grandparentClock = claimData[parent.parentIndex].clock;
}
// Compute the duration of the next clock. This is done by adding the duration of the
// grandparent claim to the difference between the current block timestamp and the
// parent's clock timestamp.
Duration nextDuration = Duration.wrap(
uint64(
// First, fetch the duration of the grandparent claim.
Duration.unwrap(LibClock.duration(grandparentClock)) +
// Second, add the difference between the current block timestamp and the
// parent's clock timestamp.
block.timestamp -
Timestamp.unwrap(LibClock.timestamp(parent.clock))
)
);
// Enforce the clock time rules. 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(nextDuration) > Duration.unwrap(GAME_DURATION) >> 1) {
revert ClockTimeExceeded();
}
// Construct the next clock with the new duration and the current block timestamp.
Clock nextClock = LibClock.wrap(nextDuration, Timestamp.wrap(uint64(block.timestamp)));
// Do not allow for a duplicate claim to be made.
ClaimHash claimHash = LibHashing.hashClaimPos(_pivot, nextPosition);
if (claims[claimHash]) {
revert ClaimAlreadyExists();
}
claims[claimHash] = true;
// Create the new claim.
claimData.push(
ClaimData({
parentIndex: uint32(_challengeIndex),
claim: _pivot,
position: nextPosition,
clock: nextClock,
countered: false
})
);
// Emit the appropriate event for the attack or defense.
emit Move(_challengeIndex, _pivot, msg.sender);
}
////////////////////////////////////////////////////////////////
// `IDisputeGame` impl //
////////////////////////////////////////////////////////////////
/**
* @inheritdoc IDisputeGame
*/
function gameType() public pure override returns (GameType gameType_) {
gameType_ = GameTypes.FAULT;
}
/**
* @inheritdoc IDisputeGame
*/
function createdAt() external view returns (Timestamp createdAt_) {
createdAt_ = gameStart;
}
/**
* @inheritdoc IDisputeGame
*/
function resolve() external returns (GameStatus status_) {
// TODO - Resolve the game
status = GameStatus.IN_PROGRESS;
status_ = status;
}
/**
* @inheritdoc IDisputeGame
*/
function rootClaim() public pure returns (Claim rootClaim_) {
rootClaim_ = Claim.wrap(_getArgFixedBytes(0x00));
}
/**
* @inheritdoc IDisputeGame
*/
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.?
extraData_ = _getArgDynBytes(0x20, 0x20);
}
/**
* @inheritdoc IDisputeGame
*/
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 game status
status = GameStatus.IN_PROGRESS;
// 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))),
countered: false
})
);
}
/**
* @inheritdoc IVersioned
*/
function version() external pure override returns (string memory version_) {
version_ = VERSION;
}
}
packages/contracts-bedrock/contracts/dispute/interfaces/IDisputeGame.sol
View file @
8a879c41
...
@@ -20,44 +20,44 @@ interface IDisputeGame is IInitializable, IVersioned {
...
@@ -20,44 +20,44 @@ interface IDisputeGame is IInitializable, IVersioned {
/**
/**
* @notice Returns the timestamp that the DisputeGame contract was created at.
* @notice Returns the timestamp that the DisputeGame contract was created at.
* @return
_createdAt
The timestamp that the DisputeGame contract was created at.
* @return
createdAt_
The timestamp that the DisputeGame contract was created at.
*/
*/
function createdAt() external view returns (Timestamp
_createdAt
);
function createdAt() external view returns (Timestamp
createdAt_
);
/**
/**
* @notice Returns the current status of the game.
* @notice Returns the current status of the game.
* @return
_status
The current status of the game.
* @return
status_
The current status of the game.
*/
*/
function status() external view returns (GameStatus
_status
);
function status() external view returns (GameStatus
status_
);
/**
/**
* @notice Getter for the game type.
* @notice Getter for the game type.
* @dev `clones-with-immutable-args` argument #1
* @dev `clones-with-immutable-args` argument #1
* @dev The reference impl should be entirely different depending on the type (fault, validity)
* @dev The reference impl should be entirely different depending on the type (fault, validity)
* i.e. The game type should indicate the security model.
* i.e. The game type should indicate the security model.
* @return
_gameType
The type of proof system being used.
* @return
gameType_
The type of proof system being used.
*/
*/
function gameType() external pure returns (GameType
_gameType
);
function gameType() external pure returns (GameType
gameType_
);
/**
/**
* @notice Getter for the root claim.
* @notice Getter for the root claim.
* @dev `clones-with-immutable-args` argument #2
* @dev `clones-with-immutable-args` argument #2
* @return
_rootClaim
The root claim of the DisputeGame.
* @return
rootClaim_
The root claim of the DisputeGame.
*/
*/
function rootClaim() external pure returns (Claim
_rootClaim
);
function rootClaim() external pure returns (Claim
rootClaim_
);
/**
/**
* @notice Getter for the extra data.
* @notice Getter for the extra data.
* @dev `clones-with-immutable-args` argument #3
* @dev `clones-with-immutable-args` argument #3
* @return
_extraData
Any extra data supplied to the dispute game contract by the creator.
* @return
extraData_
Any extra data supplied to the dispute game contract by the creator.
*/
*/
function extraData() external pure returns (bytes memory
_extraData
);
function extraData() external pure returns (bytes memory
extraData_
);
/**
/**
* @notice Returns the address of the `BondManager` used.
* @notice Returns the address of the `BondManager` used.
* @return
_bondManager
The address of the `BondManager` used.
* @return
bondManager_
The address of the `BondManager` used.
*/
*/
function bondManager() external view returns (IBondManager
_bondManager
);
function bondManager() external view returns (IBondManager
bondManager_
);
/**
/**
* @notice If all necessary information has been gathered, this function should mark the game
* @notice If all necessary information has been gathered, this function should mark the game
...
@@ -65,22 +65,25 @@ interface IDisputeGame is IInitializable, IVersioned {
...
@@ -65,22 +65,25 @@ interface IDisputeGame is IInitializable, IVersioned {
* the resolved game. It is at this stage that the bonds should be awarded to the
* the resolved game. It is at this stage that the bonds should be awarded to the
* necessary parties.
* necessary parties.
* @dev May only be called if the `status` is `IN_PROGRESS`.
* @dev May only be called if the `status` is `IN_PROGRESS`.
* @return
_status
The status of the game after resolution.
* @return
status_
The status of the game after resolution.
*/
*/
function resolve() external returns (GameStatus
_status
);
function resolve() external returns (GameStatus
status_
);
/**
/**
* @notice A compliant implementation of this interface should return the components of the
* @notice A compliant implementation of this interface should return the components of the
* game UUID's preimage provided in the cwia payload. The preimage of the UUID is
* game UUID's preimage provided in the cwia payload. The preimage of the UUID is
* constructed as `keccak256(gameType . rootClaim . extraData)` where `.` denotes
* constructed as `keccak256(gameType . rootClaim . extraData)` where `.` denotes
* concatenation.
* concatenation.
* @return gameType_ The type of proof system being used.
* @return rootClaim_ The root claim of the DisputeGame.
* @return extraData_ Any extra data supplied to the dispute game contract by the creator.
*/
*/
function gameData()
function gameData()
external
external
pure
pure
returns (
returns (
GameType
_gameType
,
GameType
gameType_
,
Claim
_rootClaim
,
Claim
rootClaim_
,
bytes memory
_extraData
bytes memory
extraData_
);
);
}
}
packages/contracts-bedrock/contracts/dispute/interfaces/IFaultDisputeGame.sol
View file @
8a879c41
...
@@ -11,85 +11,39 @@ import { IDisputeGame } from "./IDisputeGame.sol";
...
@@ -11,85 +11,39 @@ import { IDisputeGame } from "./IDisputeGame.sol";
*/
*/
interface IFaultDisputeGame is IDisputeGame {
interface IFaultDisputeGame is IDisputeGame {
/**
/**
* @notice Emitted when a subclaim is disagreed upon by `claimant`
* @notice The `ClaimData` struct represents the data associated with a Claim.
* @dev Disagreeing with a subclaim is akin to attacking it.
* @dev TODO: Pack `Clock` and `Position` into the same slot. Should require 4 64 bit arms.
* @param claimHash The unique ClaimHash that is being disagreed upon
* @dev TODO: Add bond ID information.
* @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);
struct ClaimData {
uint32 parentIndex;
bool countered;
Claim claim;
Position position;
Clock clock;
}
/**
/**
* @notice Emitted when a subclaim is agreed upon by `claimant`
* @notice Emitted when a new claim is added to the DAG by `claimant`
* @dev Agreeing with a subclaim is akin to defending it.
* @param parentIndex The index within the `claimData` array of the parent claim
* @param claimHash The unique ClaimHash that is being agreed upon
* @param pivot The claim being added
* @param pivot The claim for the following pivot (agreement = go right)
* @param claimant The address of the claimant
* @param claimant The address of the claimant
*/
*/
event Defend(ClaimHash indexed claimHash, Claim indexed pivot, address indexed claimant);
event Move(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
* @notice
Attack a disagreed upon `Claim`.
* @param
disagreement The ClaimHash of the disagreement
* @param
_parentIndex Index of the `Claim` to attack in `claimData`.
* @param
pivot The claimed pivot
* @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
* @notice
Defend an agreed upon `Claim`.
* @param
agreement The ClaimHash of the agreement
* @param
_parentIndex Index of the claim to defend in `claimData`.
* @param
pivot The claimed pivot
* @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
* @notice Perform the final step via an on-chain fault proof processor
...
@@ -97,7 +51,15 @@ interface IFaultDisputeGame is IDisputeGame {
...
@@ -97,7 +51,15 @@ interface IFaultDisputeGame is IDisputeGame {
* a step in the fault proof program on-chain. The interface of the fault proof
* a step in the fault proof program on-chain. The interface of the fault proof
* processor contract should be generic enough such that we can use different
* processor contract should be generic enough such that we can use different
* fault proof VMs (MIPS, RiscV5, etc.)
* fault proof VMs (MIPS, RiscV5, etc.)
* @param disagreement The ClaimHash of the disagreement
* @param _prestateIndex The index of the prestate of the step within `claimData`.
* @param _parentIndex The index of the parent claim within `claimData`.
* @param _stateData The stateData of the step is the preimage of the claim @ `prestateIndex`
* @param _proof Proof to access memory leaf nodes in the VM.
*/
*/
function step(ClaimHash disagreement) external;
function step(
uint256 _prestateIndex,
uint256 _parentIndex,
bytes calldata _stateData,
bytes calldata _proof
) external;
}
}
packages/contracts-bedrock/contracts/dispute/lib/LibClock.sol
0 → 100644
View file @
8a879c41
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;
import "../../libraries/DisputeTypes.sol";
/**
* @title LibClock
* @notice This library contains helper functions for working with the `Clock` type.
*/
library LibClock {
/**
* @notice Packs a `Duration` and `Timestamp` into a `Clock` type.
* @param _duration The `Duration` to pack into the `Clock` type.
* @param _timestamp The `Timestamp` to pack into the `Clock` type.
* @return clock_ The `Clock` containing the `_duration` and `_timestamp`.
*/
function wrap(Duration _duration, Timestamp _timestamp) internal pure returns (Clock clock_) {
assembly {
clock_ := or(shl(0x40, _duration), _timestamp)
}
}
/**
* @notice Pull the `Duration` out of a `Clock` type.
* @param _clock The `Clock` type to pull the `Duration` out of.
* @return duration_ The `Duration` pulled out of `_clock`.
*/
function duration(Clock _clock) internal pure returns (Duration duration_) {
// Shift the high-order 64 bits into the low-order 64 bits, leaving only the `duration`.
assembly {
duration_ := shr(0x40, _clock)
}
}
/**
* @notice Pull the `Timestamp` out of a `Clock` type.
* @param _clock The `Clock` type to pull the `Timestamp` out of.
* @return timestamp_ The `Timestamp` pulled out of `_clock`.
*/
function timestamp(Clock _clock) internal pure returns (Timestamp timestamp_) {
// Clean the high-order 192 bits by shifting the clock left and then right again, leaving
// only the `timestamp`.
assembly {
timestamp_ := shr(0xC0, shl(0xC0, _clock))
}
}
}
packages/contracts-bedrock/contracts/dispute/lib/LibHashing.sol
0 → 100644
View file @
8a879c41
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;
import "../../libraries/DisputeTypes.sol";
/**
* @title Hashing
* @notice This library contains all of the hashing utilities used in the Cannon contracts.
*/
library LibHashing {
/**
* @notice Hashes a claim and a position together.
* @param _claim A Claim type.
* @param _position The position of `claim`.
* @return claimHash_ A hash of abi.encodePacked(claim, position);
*/
function hashClaimPos(Claim _claim, Position _position) internal pure returns (ClaimHash claimHash_) {
assembly {
mstore(0x00, _claim)
mstore(0x20, _position)
claimHash_ := keccak256(0x00, 0x40)
}
}
}
packages/contracts-bedrock/contracts/dispute/lib/LibPosition.sol
0 → 100644
View file @
8a879c41
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;
import "../../libraries/DisputeTypes.sol";
/**
* @title LibPosition
* @notice This library contains helper functions for working with the `Position` type.
*/
library LibPosition {
function wrap(uint64 _depth, uint64 _indexAtDepth) internal pure returns (Position position_) {
assembly {
position_ := or(shl(0x40, _depth), _indexAtDepth)
}
}
/**
* @notice Pulls the `depth` out of a packed `Position` type.
* @param _position The position to get the `depth` of.
* @return depth_ The `depth` of the `position`.
*/
function depth(Position _position) internal pure returns (uint64 depth_) {
// Shift the high-order 64 bits into the low-order 64 bits, leaving only the `depth`.
assembly {
depth_ := shr(0x40, _position)
}
}
/**
* @notice Pulls the `indexAtDepth` out of a packed `Position` type.
* @param _position The position to get the `indexAtDepth` of.
* @return indexAtDepth_ The `indexAtDepth` of the `position`.
*/
function indexAtDepth(Position _position) internal pure returns (uint64 indexAtDepth_) {
// Clean the high-order 192 bits by shifting the position left and then right again, leaving
// only the `indexAtDepth`.
assembly {
indexAtDepth_ := shr(0xC0, shl(0xC0, _position))
}
}
/**
* @notice Get the position to the left of `position`.
* @param _position The position to get the left position of.
* @return left_ The position to the left of `position`.
*/
function left(Position _position) internal pure returns (Position left_) {
uint64 _depth = depth(_position);
uint64 _indexAtDepth = indexAtDepth(_position);
// Left = { depth: position.depth + 1, indexAtDepth: position.indexAtDepth * 2 }
assembly {
left_ := or(shl(0x40, add(_depth, 0x01)), shl(0x01, _indexAtDepth))
}
}
/**
* @notice Get the position to the right of `position`.
* @param _position The position to get the right position of.
* @return right_ The position to the right of `position`.
*/
function right(Position _position) internal pure returns (Position right_) {
uint64 _depth = depth(_position);
uint64 _indexAtDepth = indexAtDepth(_position);
// Right = { depth: position.depth + 1, indexAtDepth: position.indexAtDepth * 2 + 1 }
assembly {
right_ := or(shl(0x40, add(_depth, 0x01)), add(shl(0x01, _indexAtDepth), 0x01))
}
}
/**
* @notice Get the parent position of `position`.
* @param _position The position to get the parent position of.
* @return parent_ The parent position of `position`.
*/
function parent(Position _position) internal pure returns (Position parent_) {
uint64 _depth = depth(_position);
uint64 _indexAtDepth = indexAtDepth(_position);
// Parent = { depth: position.depth - 1, indexAtDepth: position.indexAtDepth / 2 }
assembly {
parent_ := or(shl(0x40, sub(_depth, 0x01)), shr(0x01, _indexAtDepth))
}
}
/**
* @notice Get the deepest, right most index relative to the `position`.
* @param _position The position to get the relative deepest, right most index of.
* @param _maxDepth The maximum depth of the game.
* @return rightIndex_ The deepest, right most index relative to the `position`.
*/
function rightIndex(Position _position, uint256 _maxDepth) internal pure returns (uint64 rightIndex_) {
assembly {
rightIndex_ := shr(0xC0, shl(0xC0, _position))
// Walk down to the max depth by moving right
for { let i := shr(0x40, _position) } lt(i, sub(_maxDepth, 0x01)) { i := add(i, 0x01) } {
rightIndex_ := add(0x01, shl(0x01, rightIndex_))
}
}
}
/**
* @notice Get the attack position relative to `position`.
* @param _position The position to get the relative attack position of.
* @return attack_ The attack position relative to `position`.
*/
function attack(Position _position) internal pure returns (Position attack_) {
return left(_position);
}
/**
* @notice Get the defense position relative to `position`.
* @param _position The position to get the relative defense position of.
* @return defense_ The defense position relative to `position`.
*/
function defend(Position _position) internal pure returns (Position defense_) {
uint64 _depth = depth(_position);
uint64 _indexAtDepth = indexAtDepth(_position);
// Defend = { depth: position.depth + 1, indexAtDepth: ((position.indexAtDepth / 2) * 2 + 1) * 2 }
assembly {
defense_ := or(shl(0x40, add(_depth, 0x01)), shl(0x01, add(0x01, shl(0x01, shr(0x01, _indexAtDepth)))))
}
}
}
packages/contracts-bedrock/contracts/libraries/DisputeErrors.sol
View file @
8a879c41
...
@@ -39,6 +39,32 @@ error CannotDefendRootClaim();
...
@@ -39,6 +39,32 @@ error CannotDefendRootClaim();
*/
*/
error ClaimAlreadyExists();
error ClaimAlreadyExists();
/**
* @notice Thrown when a given claim is invalid (0).
*/
error InvalidClaim();
/**
* @notice Thrown when an action that requires the game to be `IN_PROGRESS` is invoked when
* the game is not in progress.
*/
error GameNotInProgress();
/**
* @notice Thrown when a move is attempted to be made against a claim that does not exist.
*/
error ParentDoesNotExist();
/**
* @notice Thrown when a move is attempted to be made after the clock has timed out.
*/
error ClockTimeExceeded();
/**
* @notice Thrown when a move is attempted to be made at or greater than the max depth of the game.
*/
error GameDepthExceeded();
////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////
// `AttestationDisputeGame` Errors //
// `AttestationDisputeGame` Errors //
////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////
...
...
packages/contracts-bedrock/contracts/libraries/DisputeTypes.sol
View file @
8a879c41
...
@@ -39,11 +39,11 @@ type Duration is uint64;
...
@@ -39,11 +39,11 @@ type Duration is uint64;
* ┌────────────┬────────────────┐
* ┌────────────┬────────────────┐
* │ Bits │ Value │
* │ Bits │ Value │
* ├────────────┼────────────────┤
* ├────────────┼────────────────┤
* │ [0,
128)
│ Duration │
* │ [0,
64)
│ Duration │
* │ [
128, 256)
│ Timestamp │
* │ [
64, 128)
│ Timestamp │
* └────────────┴────────────────┘
* └────────────┴────────────────┘
*/
*/
type Clock is uint
256
;
type Clock is uint
128
;
/**
/**
* @notice A `Position` represents a position of a claim within the game tree.
* @notice A `Position` represents a position of a claim within the game tree.
...
@@ -51,11 +51,11 @@ type Clock is uint256;
...
@@ -51,11 +51,11 @@ type Clock is uint256;
* ┌────────────┬────────────────┐
* ┌────────────┬────────────────┐
* │ Bits │ Value │
* │ Bits │ Value │
* ├────────────┼────────────────┤
* ├────────────┼────────────────┤
* │ [0,
128)
│ Depth │
* │ [0,
64)
│ Depth │
* │ [
128, 256)
│ Index at depth │
* │ [
64, 128)
│ Index at depth │
* └────────────┴────────────────┘
* └────────────┴────────────────┘
*/
*/
type Position is uint
256
;
type Position is uint
128
;
/**
/**
* @notice A `GameType` represents the type of game being played.
* @notice A `GameType` represents the type of game being played.
...
...
packages/contracts-bedrock/contracts/test/FaultDisputeGame.t.sol
0 → 100644
View file @
8a879c41
// 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.wrap(0);
/**
* @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();
factory.initialize(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(GameType.unwrap(gameProxy.gameType()), GameType.unwrap(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(GameType.unwrap(gameType), GameType.unwrap(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,
bool countered,
Claim claim,
Position position,
Clock clock
) = gameProxy.claimData(0);
assertEq(parentIndex, type(uint32).max);
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(uint256(5))));
// Grab the claim data of the attack.
(
uint32 parentIndex,
bool countered,
Claim claim,
Position position,
Clock clock
) = gameProxy.claimData(1);
// Assert correctness of the attack claim's data.
assertEq(parentIndex, 0);
assertEq(countered, false);
assertEq(Claim.unwrap(claim), Claim.unwrap(Claim.wrap(bytes32(uint256(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, countered, claim, position, clock) = gameProxy.claimData(0);
// Assert correctness of the parent claim's data.
assertEq(parentIndex, type(uint32).max);
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)))
)
);
}
}
/**
* @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));
}
}
packages/contracts-bedrock/contracts/test/LibClock.t.sol
0 → 100644
View file @
8a879c41
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;
import { Test } from "forge-std/Test.sol";
import { LibClock } from "../dispute/lib/LibClock.sol";
import "../libraries/DisputeTypes.sol";
/**
* @notice Tests for `LibClock`
*/
contract LibClock_Test is Test {
/**
* @notice Tests that the `duration` function correctly shifts out the `Duration` from a packed `Clock` type.
*/
function testFuzz_duration_succeeds(Duration _duration, Timestamp _timestamp) public {
Clock clock = LibClock.wrap(_duration, _timestamp);
assertEq(Duration.unwrap(LibClock.duration(clock)), Duration.unwrap(_duration));
}
/**
* @notice Tests that the `timestamp` function correctly shifts out the `Timestamp` from a packed `Clock` type.
*/
function testFuzz_timestamp_succeeds(Duration _duration, Timestamp _timestamp) public {
Clock clock = LibClock.wrap(_duration, _timestamp);
assertEq(Timestamp.unwrap(LibClock.timestamp(clock)), Timestamp.unwrap(_timestamp));
}
}
packages/contracts-bedrock/contracts/test/LibPosition.t.sol
0 → 100644
View file @
8a879c41
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;
import { Test } from "forge-std/Test.sol";
import { LibPosition } from "../dispute/lib/LibPosition.sol";
import "../libraries/DisputeTypes.sol";
/**
* @notice Tests for `LibPosition`
*/
contract LibPosition_Test is Test {
/**
* @dev Assumes a MAX depth of 63 for the Position type. Any greater depth can cause overflows.
* @dev At the lowest level of the tree, this allows for 2 ** 63 leaves. In reality, the max game depth
* will likely be much lower.
*/
uint8 internal constant MAX_DEPTH = 63;
/**
* @notice Tests that the `depth` function correctly shifts out the `depth` from a packed `Position` type.
*/
function testFuzz_depth_correctness(uint64 _depth, uint64 _indexAtDepth) public {
Position position = LibPosition.wrap(_depth, _indexAtDepth);
assertEq(LibPosition.depth(position), _depth);
}
/**
* @notice Tests that the `indexAtDepth` function correctly shifts out the `indexAtDepth` from a packed `Position` type.
*/
function testFuzz_indexAtDepth_correctness(uint64 _depth, uint64 _indexAtDepth) public {
Position position = LibPosition.wrap(_depth, _indexAtDepth);
assertEq(LibPosition.indexAtDepth(position), _indexAtDepth);
}
/**
* @notice Tests that the `left` function correctly computes the position of the left child.
*/
function testFuzz_left_correctness(uint8 _depth, uint64 _indexAtDepth) public {
// Depth bound: [0, 63]
_depth = uint8(bound(_depth, 0, MAX_DEPTH));
// Index at depth bound: [0, 2 ** _depth]
_indexAtDepth = uint64(bound(_indexAtDepth, 0, 2**_depth));
Position position = LibPosition.wrap(_depth, _indexAtDepth);
Position left = LibPosition.left(position);
assertEq(LibPosition.depth(left), uint64(_depth) + 1);
assertEq(LibPosition.indexAtDepth(left), _indexAtDepth * 2);
}
/**
* @notice Tests that the `right` function correctly computes the position of the right child.
*/
function testFuzz_right_correctness(uint8 _depth, uint64 _indexAtDepth) public {
// Depth bound: [0, 63]
_depth = uint8(bound(_depth, 0, MAX_DEPTH));
// Index at depth bound: [0, 2 ** _depth]
_indexAtDepth = uint64(bound(_indexAtDepth, 0, 2**_depth));
Position position = LibPosition.wrap(_depth, _indexAtDepth);
Position right = LibPosition.right(position);
assertEq(LibPosition.depth(right), _depth + 1);
assertEq(LibPosition.indexAtDepth(right), _indexAtDepth * 2 + 1);
}
/**
* @notice Tests that the `parent` function correctly computes the position of the parent.
*/
function testFuzz_parent_correctness(uint8 _depth, uint64 _indexAtDepth) public {
// Depth bound: [1, 63]
_depth = uint8(bound(_depth, 1, MAX_DEPTH));
// Index at depth bound: [0, 2 ** _depth]
_indexAtDepth = uint64(bound(_indexAtDepth, 0, 2**_depth));
Position position = LibPosition.wrap(_depth, _indexAtDepth);
Position parent = LibPosition.parent(position);
assertEq(LibPosition.depth(parent), _depth - 1);
assertEq(LibPosition.indexAtDepth(parent), _indexAtDepth / 2);
}
/**
* @notice Tests that the `rightIndex` function correctly computes the deepest, right most index relative
* to a given position.
*/
function testFuzz_rightIndex_correctness(
uint8 _maxDepth,
uint8 _depth,
uint64 _indexAtDepth
) public {
// Max depth bound: [1, 63]
// The max game depth MUST be at least 1.
_maxDepth = uint8(bound(_maxDepth, 1, MAX_DEPTH));
// Depth bound: [0, _maxDepth]
_depth = uint8(bound(_depth, 0, _maxDepth));
// Index at depth bound: [0, 2 ** _depth]
_indexAtDepth = uint64(bound(_indexAtDepth, 0, 2**_depth));
Position position = LibPosition.wrap(_depth, _indexAtDepth);
uint64 rightIndex = LibPosition.rightIndex(position, _maxDepth);
// Find the deepest, rightmost index in Solidity rather than Yul
for (uint256 i = _depth; i < _maxDepth - 1; ++i) {
position = LibPosition.right(position);
}
uint64 _rightIndex = LibPosition.indexAtDepth(position);
assertEq(rightIndex, _rightIndex);
}
/**
* @notice Tests that the `attack` function correctly computes the position of the attack relative to
* a given position.
* @dev `attack` is an alias for `left`, but we test it separately for completeness.
*/
function testFuzz_attack_correctness(uint8 _depth, uint64 _indexAtDepth) public {
// Depth bound: [0, 63]
_depth = uint8(bound(_depth, 0, MAX_DEPTH));
// Index at depth bound: [0, 2 ** _depth]
_indexAtDepth = uint64(bound(_indexAtDepth, 0, 2**_depth));
Position position = LibPosition.wrap(_depth, _indexAtDepth);
Position attack = LibPosition.attack(position);
assertEq(LibPosition.depth(attack), _depth + 1);
assertEq(LibPosition.indexAtDepth(attack), _indexAtDepth * 2);
}
/**
* @notice Tests that the `defend` function correctly computes the position of the defense relative to
* a given position.
* @dev A defense can only be given if the position does not belong to the root claim, hence the bound of [1, 127]
* on the depth.
*/
function testFuzz_defend_correctness(uint8 _depth, uint64 _indexAtDepth) public {
// Depth bound: [1, 63]
_depth = uint8(bound(_depth, 1, MAX_DEPTH));
// Index at depth bound: [0, 2 ** _depth]
_indexAtDepth = uint64(bound(_indexAtDepth, 0, 2**_depth));
Position position = LibPosition.wrap(_depth, _indexAtDepth);
Position defend = LibPosition.defend(position);
assertEq(LibPosition.depth(defend), _depth + 1);
assertEq(LibPosition.indexAtDepth(defend), ((_indexAtDepth / 2) * 2 + 1) * 2);
}
}
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