Skip to content

Commit

Permalink
test multi-sec on 2-ary
Browse files Browse the repository at this point in the history
  • Loading branch information
dajuguan committed Mar 19, 2024
1 parent b36eb55 commit d7fbb4b
Show file tree
Hide file tree
Showing 2 changed files with 116 additions and 39 deletions.
142 changes: 103 additions & 39 deletions packages/contracts-bedrock/src/dispute/FaultDisputeGame.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;


import "forge-std/console.sol";

import { IDisputeGame } from "src/dispute/interfaces/IDisputeGame.sol";
import { IFaultDisputeGame } from "src/dispute/interfaces/IFaultDisputeGame.sol";
import { IInitializable } from "src/dispute/interfaces/IInitializable.sol";
Expand Down Expand Up @@ -80,6 +83,9 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, ISemver {
/// @notice Flag for the `initialize` function to prevent re-initialization.
bool internal initialized;

/// @notice Bits of N-ary search
uint256 internal constant nBits = 1;

/// @notice Semantic version.
/// @custom:semver 0.7.0
string public constant version = "0.7.0";
Expand Down Expand Up @@ -121,9 +127,16 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, ISemver {
////////////////////////////////////////////////////////////////

/// @inheritdoc IFaultDisputeGame
function step(
function step(uint256 _claimIndex, bool _isAttack, bytes calldata _stateData, bytes calldata _proof) public virtual {
uint256 _attackBranch = _isAttack ? 0 : 1;
stepV2(_claimIndex, _attackBranch, _stateData, _proof);
}

function stepV2(
uint256 _claimIndex,
bool _isAttack,
//bool _isAttack,
// 0 for attack, 1 for defense
uint256 _attackBranch,
bytes calldata _stateData,
bytes calldata _proof
)
Expand All @@ -139,34 +152,26 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, ISemver {
// Pull the parent position out of storage.
Position parentPos = parent.position;
// Determine the position of the step.
Position stepPos = parentPos.move(_isAttack);
Position stepPos = parentPos.moveN(nBits, _attackBranch);
// Position stepPos = parentPos.moveN(parentPos, nBits, _attackBranch);

// INVARIANT: A step cannot be made unless the move position is 1 below the `MAX_GAME_DEPTH`
if (stepPos.depth() != MAX_GAME_DEPTH + 1) revert InvalidParent();

// Determine the expected pre & post states of the step.
Claim preStateClaim;
ClaimData storage postState;
if (_isAttack) {
// If the step position's index at depth is 0, the prestate is the absolute
// prestate.
// If the step is an attack at a trace index > 0, the prestate exists elsewhere in
// the game state.
// NOTE: We localize the `indexAtDepth` for the current execution trace subgame by finding
// the remainder of the index at depth divided by 2 ** (MAX_GAME_DEPTH - SPLIT_DEPTH),
// which is the number of leaves in each execution trace subgame. This is so that we can
// determine whether or not the step position is represents the `ABSOLUTE_PRESTATE`.
preStateClaim = (stepPos.indexAtDepth() % (1 << (MAX_GAME_DEPTH - SPLIT_DEPTH))) == 0
? ABSOLUTE_PRESTATE
: _findTraceAncestor(Position.wrap(parentPos.raw() - 1), parent.parentIndex, false).claim;
// For all attacks, the poststate is the parent claim.
postState = parent;
} else {
// If the step is a defense, the poststate exists elsewhere in the game state,
// and the parent claim is the expected pre-state.
preStateClaim = parent.claim;
postState = _findTraceAncestor(Position.wrap(parentPos.raw() + 1), parent.parentIndex, false);
}
Position preStatePosition;
Claim postStateClaim;
Position postStatePosition;
//ClaimData storage postState;

// TODO: deal with SPLIT_DEPTH
//(preStatePosition, preStateClaim) = findPreStateClaim(1 << nBits, stepPos, _claimIndex);
(preStatePosition, preStateClaim) = findPreStateClaim(1 << nBits, parentPos, _claimIndex);
console.log("step pre pos:%d", preStatePosition.raw());
//(postStatePosition, postStateClaim) = findPostStateClaim(1 << nBits, stepPos, _claimIndex);
(postStatePosition, postStateClaim) = findPostStateClaim(1 << nBits, parentPos, _claimIndex);
console.log("step post pos:%d", postStatePosition.raw());

// INVARIANT: The prestate is always invalid if the passed `_stateData` is not the
// preimage of the prestate claim hash.
Expand All @@ -189,8 +194,8 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, ISemver {
// SAFETY: While the `attack` path does not need an extra check for the post
// state's depth in relation to the parent, we don't need another
// branch because (n - n) % 2 == 0.
bool validStep = VM.step(_stateData, _proof, uuid.raw()) == postState.claim.raw();
bool parentPostAgree = (parentPos.depth() - postState.position.depth()) % 2 == 0;
bool validStep = VM.step(_stateData, _proof, uuid.raw()) == postStateClaim.raw();
bool parentPostAgree = (parentPos.depth() - postStatePosition.depth()) % 2 == 0;
if (parentPostAgree == validStep) revert ValidStep();

// INVARIANT: A step cannot be made against a claim for a second time.
Expand All @@ -201,11 +206,21 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, ISemver {
parent.counteredBy = msg.sender;
}

function move(uint256 _challengeIndex, Claim _claim, bool _isAttack) public payable virtual {
uint256 _attackBranch = _isAttack ? 0 : 1;
moveV2(_challengeIndex, _claim, _attackBranch);
}

/// @notice Generic move function, used for both `attack` and `defend` moves.
/// @param _challengeIndex The index of the claim being moved against.
/// @param _claim 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 _claim, bool _isAttack) public payable virtual {
/// @param _attackBranch Which branch claim to attack.
function moveV2(uint256 _challengeIndex, Claim _claim, uint256 _attackBranch) public payable {
// For N = 2 (bisec),
// 1. _attackBranch == 0 (attack)
// 2. _attackBranch == 1 (defense)
require(_attackBranch < (1 << nBits));

// INVARIANT: Moves cannot be made unless the game is currently in progress.
if (status != GameStatus.IN_PROGRESS) revert GameNotInProgress();

Expand All @@ -216,13 +231,13 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, ISemver {
// 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 parentPos = parent.position;
Position nextPosition = parentPos.move(_isAttack);
Position nextPosition = parentPos.moveN(nBits, _attackBranch);
uint256 nextPositionDepth = nextPosition.depth();

// INVARIANT: A defense can never be made against the root claim of either the output root game or any
// of the execution trace bisection subgames. 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 || nextPositionDepth == SPLIT_DEPTH + 2) && !_isAttack) {
if ((_challengeIndex == 0 || nextPositionDepth == SPLIT_DEPTH + 2) && _attackBranch != 0) {
revert CannotDefendRootClaim();
}

Expand All @@ -234,9 +249,10 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, ISemver {

// When the next position surpasses the split depth (i.e., it is the root claim of an execution
// trace bisection sub-game), we need to perform some extra verification steps.
if (nextPositionDepth == SPLIT_DEPTH + 1) {
_verifyExecBisectionRoot(_claim, _challengeIndex, parentPos, _isAttack);
}
// TODO
// if (nextPositionDepth == SPLIT_DEPTH + 1) {
// _verifyExecBisectionRoot(_claim, _challengeIndex, parentPos, _isAttack);
// }

// INVARIANT: The `msg.value` must be sufficient to cover the required bond.
if (getRequiredBond(nextPosition) > msg.value) revert InsufficientBond();
Expand Down Expand Up @@ -296,14 +312,10 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, ISemver {
emit Move(_challengeIndex, _claim, msg.sender);
}

/// @inheritdoc IFaultDisputeGame
function attack(uint256 _parentIndex, Claim _claim) external payable {
move(_parentIndex, _claim, true);
}

/// @inheritdoc IFaultDisputeGame
function defend(uint256 _parentIndex, Claim _claim) external payable {
move(_parentIndex, _claim, false);
// Compatible with bi-sect
attackAt(_parentIndex, _claim, 1);
}

/// @inheritdoc IFaultDisputeGame
Expand Down Expand Up @@ -779,4 +791,56 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, ISemver {
uuid_ = Hash.wrap(keccak256(abi.encode(_starting, _startingPos, _disputed, _disputedPos)));
}
}

function attackAt(uint256 _parentIndex, Claim _claim, uint256 _attackBranch) public payable {
moveV2(_parentIndex, _claim, _attackBranch);
}

/// @inheritdoc IFaultDisputeGame
function attack(uint256 _parentIndex, Claim _claim) external payable {
// Compatible with bi-sect
attackAt(_parentIndex, _claim, 0);
}

function getClaimFromClaimHash(
Claim claimsHash,
uint256 claimIndex
) internal returns (Claim) {
// TODO: retrieve the claim from the claimsHash
// Either: from EIP-4844 BLOB with point-evaluation proof or calldata with Merkle proof
}

function findPreStateClaim(
uint256 _nary,
Position _pos,
uint256 _start
) public returns (Position pos_, Claim claim_) {
ClaimData storage ancestor_ = claimData[_start];
uint256 pos = _pos.raw();
while (pos % _nary == 0 && pos != 1) {
pos = pos / _nary;
ancestor_ = claimData[ancestor_.parentIndex];
}
if (pos == 1) {
// S_0
claim_ = ABSOLUTE_PRESTATE;
} else {
claim_ = getClaimFromClaimHash(ancestor_.claim, (pos - 1) % _nary);
}
return (Position.wrap(uint128(pos)), claim_);
}

function findPostStateClaim(
uint256 _nary,
Position _pos,
uint256 _start
) public returns (Position pos_, Claim claim_) {
ClaimData storage ancestor_ = claimData[_start];
uint256 pos = _pos.raw();
while ((pos + 1) % _nary == 0 && pos != 1) {
pos = pos / _nary;
ancestor_ = claimData[ancestor_.parentIndex];
}
return (Position.wrap(uint128(pos)), getClaimFromClaimHash(ancestor_.claim, pos % _nary));
}
}
13 changes: 13 additions & 0 deletions packages/contracts-bedrock/src/dispute/lib/LibPosition.sol
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,19 @@ library LibPosition {
}
}

/// @notice Get the move position of `_position`, which is the left child of:
/// 1. `_position` if `_isAttack` is true.
/// 2. `_position | 1` if `_isAttack` is false.
/// @param _position The position to get the relative attack/defense position of.
/// @param _bits Bits of N-ary search.
/// @param _branch The branch to be attacked at.
/// @return move_ The move position relative to `position`.
function moveN(Position _position, uint256 _bits, uint256 _branch) internal pure returns (Position move_) {
assembly {
move_ := shl(_bits, or(_branch, _position))
}
}

/// @notice Get the move position of `_position`, which is the left child of:
/// 1. `_position` if `_isAttack` is true.
/// 2. `_position | 1` if `_isAttack` is false.
Expand Down

0 comments on commit d7fbb4b

Please sign in to comment.