Commit 6d07c4d6 authored by clabby's avatar clabby Committed by GitHub

Merge pull request #6080 from ethereum-optimism/clabby/dispute/alphabet-vm

feat(ctb): Alphabet `step` function in `FaultDisputeGame`
parents ee255c37 cac7a904
......@@ -32,25 +32,30 @@ DisputeGameFactory_SetImplementation_Test:test_setImplementation_notOwner_revert
DisputeGameFactory_SetImplementation_Test:test_setImplementation_succeeds() (gas: 44243)
DisputeGameFactory_TransferOwnership_Test:test_transferOwnership_notOwner_reverts() (gas: 15950)
DisputeGameFactory_TransferOwnership_Test:test_transferOwnership_succeeds() (gas: 18642)
FaultDisputeGame_Test:test_clockTimeExceeded_reverts() (gas: 26496)
FaultDisputeGame_Test:test_defendRoot_reverts() (gas: 13236)
FaultDisputeGame_Test:test_duplicateClaim_reverts() (gas: 103425)
FaultDisputeGame_Test:test_extraData_succeeds() (gas: 17478)
FaultDisputeGame_Test:test_gameData_succeeds() (gas: 17859)
FaultDisputeGame_Test:test_gameDepthExceeded_reverts() (gas: 5907231)
FaultDisputeGame_Test:test_gameStart_succeeds() (gas: 10337)
FaultDisputeGame_Test:test_gameType_succeeds() (gas: 8259)
FaultDisputeGame_Test:test_initialRootClaimData_succeeds() (gas: 17624)
FaultDisputeGame_Test:test_moveAgainstNonexistentParent_reverts() (gas: 24632)
FaultDisputeGame_Test:test_move_gameNotInProgress_reverts() (gas: 10945)
FaultDisputeGame_Test:test_resolve_challengeContested() (gas: 222383)
FaultDisputeGame_Test:test_resolve_reverts() (gas: 27121)
FaultDisputeGame_Test:test_resolve_rootContested() (gas: 107225)
FaultDisputeGame_Test:test_resolve_rootUncontested() (gas: 24459)
FaultDisputeGame_Test:test_resolve_teamDeathmatch() (gas: 393609)
FaultDisputeGame_Test:test_rootClaim_succeeds() (gas: 8169)
FaultDisputeGame_Test:test_simpleAttack_succeeds() (gas: 107389)
FaultDisputeGame_Test:test_version_succeeds() (gas: 9780)
FaultDisputeGame_ResolvesCorrectly_CorrectRoot2:test_resolvesCorrectly_succeeds() (gas: 498304)
FaultDisputeGame_ResolvesCorrectly_CorrectRoot4:test_resolvesCorrectly_succeeds() (gas: 500038)
FaultDisputeGame_ResolvesCorrectly_CorrectRoot:test_resolvesCorrectly_succeeds() (gas: 490057)
FaultDisputeGame_ResolvesCorrectly_IncorrectRoot2:test_resolvesCorrectly_succeeds() (gas: 495173)
FaultDisputeGame_ResolvesCorrectly_IncorrectRoot3:test_resolvesCorrectly_succeeds() (gas: 496907)
FaultDisputeGame_ResolvesCorrectly_IncorrectRoot:test_resolvesCorrectly_succeeds() (gas: 486926)
FaultDisputeGame_Test:test_clockTimeExceeded_reverts() (gas: 26444)
FaultDisputeGame_Test:test_defendRoot_reverts() (gas: 13281)
FaultDisputeGame_Test:test_duplicateClaim_reverts() (gas: 103343)
FaultDisputeGame_Test:test_extraData_succeeds() (gas: 17431)
FaultDisputeGame_Test:test_gameData_succeeds() (gas: 17834)
FaultDisputeGame_Test:test_gameDepthExceeded_reverts() (gas: 408128)
FaultDisputeGame_Test:test_gameStart_succeeds() (gas: 10359)
FaultDisputeGame_Test:test_gameType_succeeds() (gas: 8281)
FaultDisputeGame_Test:test_initialRootClaimData_succeeds() (gas: 17669)
FaultDisputeGame_Test:test_moveAgainstNonexistentParent_reverts() (gas: 24611)
FaultDisputeGame_Test:test_move_gameNotInProgress_reverts() (gas: 10901)
FaultDisputeGame_Test:test_resolve_challengeContested() (gas: 221222)
FaultDisputeGame_Test:test_resolve_notInProgress_reverts() (gas: 9657)
FaultDisputeGame_Test:test_resolve_rootContested() (gas: 106144)
FaultDisputeGame_Test:test_resolve_rootUncontested() (gas: 23697)
FaultDisputeGame_Test:test_resolve_teamDeathmatch() (gas: 391960)
FaultDisputeGame_Test:test_rootClaim_succeeds() (gas: 8225)
FaultDisputeGame_Test:test_simpleAttack_succeeds() (gas: 107438)
FeeVault_Test:test_constructor_succeeds() (gas: 18185)
GasBenchMark_L1CrossDomainMessenger:test_sendMessage_benchmark_0() (gas: 352135)
GasBenchMark_L1CrossDomainMessenger:test_sendMessage_benchmark_1() (gas: 2950342)
......
......@@ -27,12 +27,7 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone {
/**
* @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;
string internal constant VERSION = "0.0.2";
/**
* @notice The duration of the game.
......@@ -45,6 +40,17 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone {
*/
Position internal constant ROOT_POSITION = Position.wrap(1);
/**
* @notice The absolute prestate of the instruction trace. This is a constant that is defined
* by the program that is being used to execute the trace.
*/
Claim public immutable ABSOLUTE_PRESTATE;
/**
* @notice The max depth of the game.
*/
uint256 public immutable MAX_GAME_DEPTH;
/**
* @notice The starting timestamp of the game
*/
......@@ -70,6 +76,14 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone {
*/
mapping(ClaimHash => bool) internal claims;
/**
* @param _absolutePrestate The absolute prestate of the instruction trace.
*/
constructor(Claim _absolutePrestate, uint256 _maxGameDepth) {
ABSOLUTE_PRESTATE = _absolutePrestate;
MAX_GAME_DEPTH = _maxGameDepth;
}
////////////////////////////////////////////////////////////////
// External Logic //
////////////////////////////////////////////////////////////////
......@@ -92,12 +106,76 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone {
* @inheritdoc IFaultDisputeGame
*/
function step(
uint256 _prestateIndex,
uint256 _parentIndex,
bytes calldata _stateData,
bytes calldata _proof
uint256 _stateIndex,
uint256 _claimIndex,
bool _isAttack,
bytes calldata,
bytes calldata
) external {
// TODO - Call the VM to perform the execution step.
// Steps cannot be made unless the game is currently in progress.
if (status != GameStatus.IN_PROGRESS) {
revert GameNotInProgress();
}
// Get the parent. If it does not exist, the call will revert with OOB.
ClaimData storage parent = claimData[_claimIndex];
// Pull the parent position out of storage.
Position parentPos = parent.position;
// Determine the position of the step.
Position stepPos = _isAttack ? parentPos.attack() : parentPos.defend();
// Ensure that the step position is 1 deeper than the maximum game depth.
if (stepPos.depth() != MAX_GAME_DEPTH + 1) {
revert InvalidParent();
}
// Determine the expected pre & post states of the step.
Claim preStateClaim;
Claim postStateClaim;
if (stepPos.indexAtDepth() == 0) {
// If the step position's index at depth is 0, the prestate is the absolute prestate
// and the post state is the parent claim.
preStateClaim = ABSOLUTE_PRESTATE;
postStateClaim = claimData[_claimIndex].claim;
} else {
Position preStatePos;
Position postStatePos;
if (_isAttack) {
// If the step is an attack, the prestate exists elsewhere in the game state,
// and the parent claim is the expected post-state.
preStatePos = claimData[_stateIndex].position;
preStateClaim = claimData[_stateIndex].claim;
postStatePos = parentPos;
postStateClaim = parent.claim;
} else {
// If the step is a defense, the poststate exists elsewhere in the game state,
// and the parent claim is the expected pre-state.
preStatePos = parent.position;
preStateClaim = parent.claim;
postStatePos = claimData[_stateIndex].position;
postStateClaim = claimData[_stateIndex].claim;
}
// Assert that the given prestate commits to the instruction at `gindex - 1`.
if (
Position.unwrap(preStatePos.rightIndex(MAX_GAME_DEPTH)) !=
Position.unwrap(postStatePos.rightIndex(MAX_GAME_DEPTH)) - 1
) {
revert InvalidPrestate();
}
}
// TODO: Call `MIPS.sol#step` to verify the step.
// For now, we just use a simple state transition function that increments the prestate,
// `s_p`, by 1.
if (uint256(Claim.unwrap(preStateClaim)) + 1 == uint256(Claim.unwrap(postStateClaim))) {
revert ValidStep();
}
// Set the parent claim as countered. We do not need to append a new claim to the game;
// instead, we can just set the existing parent as countered.
parent.countered = true;
}
////////////////////////////////////////////////////////////////
......@@ -141,7 +219,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
// the above claim. At this depth, the parent claim commits to the state after a single
// instruction step.
if (nextPosition.depth() >= MAX_GAME_DEPTH) {
if (nextPosition.depth() > MAX_GAME_DEPTH) {
revert GameDepthExceeded();
}
......@@ -197,6 +275,13 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone {
emit Move(_challengeIndex, _pivot, msg.sender);
}
/**
* @inheritdoc IFaultDisputeGame
*/
function l2BlockNumber() public pure returns (uint256 l2BlockNumber_) {
l2BlockNumber_ = _getArgUint256(0x20);
}
////////////////////////////////////////////////////////////////
// `IDisputeGame` impl //
////////////////////////////////////////////////////////////////
......@@ -219,17 +304,17 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone {
* @inheritdoc IDisputeGame
*/
function resolve() external returns (GameStatus status_) {
// TODO: Do not allow resolution before clocks run out.
if (status != GameStatus.IN_PROGRESS) {
// If the game is not in progress, it cannot be resolved.
revert GameNotInProgress();
}
// 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
uint256 leftMostIndex = claimData.length - 1;
uint256 leftMostTraceIndex = LibPosition.rightIndex(
claimData[leftMostIndex].position,
MAX_GAME_DEPTH
);
Position leftMostTraceIndex = Position.wrap(type(uint128).max);
for (uint256 i = leftMostIndex; i < type(uint64).max; ) {
// Fetch the claim at the current index.
ClaimData storage claim = claimData[i];
......@@ -243,24 +328,29 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone {
// If the claim is not a dangling node above the bottom of the tree,
// we can skip over it. These nodes are not relevant to the game resolution.
Position claimPos = claim.position;
if (LibPosition.depth(claimPos) == MAX_GAME_DEPTH || claim.countered) {
if (claim.countered) {
continue;
}
// If the claim is a dangling node, we can check if it is the left-most
// dangling node we've come across so far. If it is, we can update the
// left-most trace index.
uint256 traceIndex = LibPosition.rightIndex(claimPos, MAX_GAME_DEPTH);
if (traceIndex < leftMostTraceIndex) {
Position traceIndex = claimPos.rightIndex(MAX_GAME_DEPTH);
if (Position.unwrap(traceIndex) < Position.unwrap(leftMostTraceIndex)) {
leftMostTraceIndex = traceIndex;
leftMostIndex = i + 1;
unchecked {
leftMostIndex = i + 1;
}
}
}
// 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.
// slither-disable-next-line weak-prng
if (LibPosition.depth(claimData[leftMostIndex].position) % 2 == 0) {
if (
// slither-disable-next-line weak-prng
claimData[leftMostIndex].position.depth() % 2 == 0 &&
Position.unwrap(leftMostTraceIndex) != type(uint128).max
) {
status_ = GameStatus.DEFENDER_WINS;
} else {
status_ = GameStatus.CHALLENGER_WINS;
......
......@@ -51,15 +51,24 @@ interface IFaultDisputeGame is IDisputeGame {
* 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
* fault proof VMs (MIPS, RiscV5, etc.)
* @param _prestateIndex The index of the prestate of the step within `claimData`.
* @param _parentIndex The index of the parent claim within `claimData`.
* @param _stateIndex The index of the pre/post state of the step within `claimData`.
* @param _claimIndex The index of the challenged claim within `claimData`.
* @param _isAttack Whether or not the step is an attack or a defense.
* @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(
uint256 _prestateIndex,
uint256 _parentIndex,
uint256 _stateIndex,
uint256 _claimIndex,
bool _isAttack,
bytes calldata _stateData,
bytes calldata _proof
) external;
/**
* @notice The l2BlockNumber that the `rootClaim` commits to. The trace being bisected within
* the game is from `l2BlockNumber - 1` -> `l2BlockNumber`.
* @return l2BlockNumber_ The l2BlockNumber that the `rootClaim` commits to.
*/
function l2BlockNumber() external view returns (uint256 l2BlockNumber_);
}
......@@ -99,7 +99,7 @@ library LibPosition {
function rightIndex(
Position _position,
uint256 _maxDepth
) internal pure returns (uint64 rightIndex_) {
) internal pure returns (Position rightIndex_) {
uint256 msb = depth(_position);
assembly {
switch eq(msb, _maxDepth)
......@@ -110,7 +110,6 @@ library LibPosition {
let remaining := sub(_maxDepth, msb)
rightIndex_ := or(shl(remaining, _position), sub(shl(remaining, 1), 1))
}
rightIndex_ := sub(rightIndex_, shl(_maxDepth, 1))
}
}
......
......@@ -60,6 +60,21 @@ error ClockTimeExceeded();
*/
error GameDepthExceeded();
/**
* @notice Thrown when a step is attempted above the maximum game depth.
*/
error InvalidParent();
/**
* @notice Thrown when an invalid prestate is supplied to `step`.
*/
error InvalidPrestate();
/**
* @notice Thrown when a step is made that computes the expected post state correctly.
*/
error ValidStep();
////////////////////////////////////////////////////////////////
// `AttestationDisputeGame` Errors //
////////////////////////////////////////////////////////////////
......
......@@ -105,15 +105,14 @@ contract LibPosition_Test is Test {
_indexAtDepth = boundIndexAtDepth(_depth, _indexAtDepth);
Position position = LibPosition.wrap(_depth, _indexAtDepth);
uint64 rightIndex = position.rightIndex(_maxDepth);
Position rightIndex = position.rightIndex(_maxDepth);
// Find the deepest, rightmost index in Solidity rather than Yul
for (uint256 i = _depth; i < _maxDepth; ++i) {
position = position.right();
}
uint64 _rightIndex = position.indexAtDepth();
assertEq(rightIndex, _rightIndex);
assertEq(Position.unwrap(rightIndex), Position.unwrap(position));
}
/**
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment