Skip to content

Commit

Permalink
Merge pull request #35 from ethstorage/develop-exec-mu-root
Browse files Browse the repository at this point in the history
rename _verifyExecBisectionRoot to _verifyExecMultisectionRoot and add a test for last attack branch…
  • Loading branch information
qizhou authored Jul 16, 2024
2 parents b37e73d + 85f1b3a commit 1100b04
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 19 deletions.
20 changes: 12 additions & 8 deletions packages/contracts-bedrock/src/dispute/FaultDisputeGameN.sol
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,9 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, ISemver {
/// @notice Bits of N-ary search
uint256 internal immutable N_BITS;

/// @notice Bits of N-ary search
uint256 internal immutable MAX_ATTACK_BRANCH;

/// @notice Flag for whether or not the L2 block number claim has been invalidated via `challengeRootL2Block`.
bool public l2BlockNumberChallenged;

Expand Down Expand Up @@ -162,6 +165,7 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, ISemver {
L2_CHAIN_ID = _l2ChainId;
// N_BITS ** 2 = N-ary
N_BITS = 2;
MAX_ATTACK_BRANCH = (1 << N_BITS) - 1;
}

/// @inheritdoc IInitializable
Expand Down Expand Up @@ -255,7 +259,7 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, ISemver {
public
virtual
{
require(_attackBranch < (1 << N_BITS));
require(_attackBranch <= MAX_ATTACK_BRANCH);
// INVARIANT: Steps cannot be made unless the game is currently in progress.
if (status != GameStatus.IN_PROGRESS) revert GameNotInProgress();

Expand All @@ -275,7 +279,7 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, ISemver {
Position preStatePos;
Claim postStateClaim;
Position postStatePos;
if ((1 << N_BITS) - 1 != _attackBranch) {
if (MAX_ATTACK_BRANCH != _attackBranch) {
// 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
Expand Down Expand Up @@ -348,7 +352,7 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, ISemver {
// 2. _attackBranch == 1 (attack)
// 3. _attackBranch == 2 (attack)
// 4. _attackBranch == 3 (defend)
require(_attackBranch < (1 << N_BITS));
require(_attackBranch <= MAX_ATTACK_BRANCH);
// INVARIANT: Moves cannot be made unless the game is currently in progress.
if (status != GameStatus.IN_PROGRESS) revert GameNotInProgress();

Expand Down Expand Up @@ -385,7 +389,7 @@ 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 + N_BITS) {
_verifyExecBisectionRoot(_claim, _challengeIndex, parentPos, _attackBranch);
_verifyExecMultisectionRoot(_claim, _challengeIndex, parentPos, _attackBranch);
}

// INVARIANT: The `msg.value` must exactly equal the required bond.
Expand Down Expand Up @@ -894,7 +898,7 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, ISemver {
/// @notice Verifies the integrity of an execution bisection subgame's root claim. Reverts if the claim
/// is invalid.
/// @param _rootClaim The root claim of the execution bisection subgame.
function _verifyExecBisectionRoot(
function _verifyExecMultisectionRoot(
Claim _rootClaim,
uint256 _parentIdx,
Position _parentPos,
Expand All @@ -910,12 +914,12 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, ISemver {

// If the move is a defense, the disputed output could have been made by either party. In this case, we
// need to search for the parent output to determine what the expected status byte should be.
Position disputedLeafPos = Position.wrap(_parentPos.raw() + 1);
Position disputedLeafPos = Position.wrap(_parentPos.raw() + _attackBranch);
(Claim disputedClaim, Position disputedPos) =
_findTraceAncestorV2({ _pos: disputedLeafPos, _start: _parentIdx, _global: true });
uint8 vmStatus = uint8(_rootClaim.raw()[0]);

if ((0 != _attackBranch) || (disputedPos.depth() / N_BITS) % 2 == (SPLIT_DEPTH / N_BITS) % 2) {
if ((MAX_ATTACK_BRANCH != _attackBranch) || (disputedPos.depth() / N_BITS) % 2 == (SPLIT_DEPTH / N_BITS) % 2) {
// If the move is an attack, the parent output is always deemed to be disputed. In this case, we only need
// to check that the root claim signals that the VM panicked or resulted in an invalid transition.
// If the move is a defense, and the disputed output and creator of the execution trace subgame disagree,
Expand Down Expand Up @@ -1070,7 +1074,7 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, ISemver {
: _firstValidRightIndex(_pos.traceAncestorBounded(SPLIT_DEPTH), N_BITS);

uint256 offset = ancestorPos_.raw() % (1 << N_BITS);
if (1 << N_BITS - 1 == offset) {
if (MAX_ATTACK_BRANCH == offset) {
offset = 0;
}
uint256 traceAncestorPosValue = ancestorPos_.raw() - offset;
Expand Down
36 changes: 25 additions & 11 deletions packages/contracts-bedrock/test/dispute/FaultDisputeGameN.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -584,29 +584,43 @@ contract FaultDisputeGameN_Test is FaultDisputeGame_Init {
/// byte reverts with the `UnexpectedRootClaim` error.
function test_move_incorrectStatusExecRoot_reverts() public {
Claim disputed;
for (uint256 i; i < 4; i++) {
for (uint256 i; i < 2; i++) {
(,,,, disputed,,) = gameProxy.claimData(i);
gameProxy.attack{ value: _getRequiredBond(i) }(disputed, i, _dummyClaim());
gameProxy.attackV2{ value: _getRequiredBondV2(i, 0) }(disputed, i, _dummyClaim(), 0);
}

uint256 bond = _getRequiredBond(4);
(,,,, disputed,,) = gameProxy.claimData(4);
uint256 bond = _getRequiredBondV2(2, 2);
(,,,, disputed,,) = gameProxy.claimData(2);
vm.expectRevert(abi.encodeWithSelector(UnexpectedRootClaim.selector, bytes32(0)));
gameProxy.attack{ value: bond }(disputed, 4, Claim.wrap(bytes32(0)));
gameProxy.attackV2{ value: bond }(disputed, 2, Claim.wrap(bytes32(0)), 2);
}

/// @dev Tests that making a claim at the execution trace bisection root level with a valid status
/// byte succeeds.
function test_move_correctStatusExecRoot_succeeds() public {
Claim disputed;
for (uint256 i; i < 4; i++) {
uint256 bond = _getRequiredBond(i);
for (uint256 i; i < 2; i++) {
uint256 bond = _getRequiredBondV2(i, 0);
(,,,, disputed,,) = gameProxy.claimData(i);
gameProxy.attack{ value: bond }(disputed, i, _dummyClaim());
gameProxy.attackV2{ value: bond }(disputed, i, _dummyClaim(), 0);
}
uint256 lastBond = _getRequiredBond(4);
(,,,, disputed,,) = gameProxy.claimData(4);
gameProxy.attack{ value: lastBond }(disputed, 4, _changeClaimStatus(_dummyClaim(), VMStatuses.PANIC));
uint256 lastBond = _getRequiredBondV2(2, 0);
(,,,, disputed,,) = gameProxy.claimData(2);
gameProxy.attackV2{ value: lastBond }(disputed, 2, _changeClaimStatus(_dummyClaim(), VMStatuses.PANIC), 2);
}

/// @dev Tests that making a claim at the execution trace quadsection root level with a valid status
/// byte succeeds when the attack branch is the last branch.
function test_move_correctStatusExecRootForLastAttackBranch_succeeds() public {
Claim disputed;
for (uint256 i; i < 2; i++) {
uint256 bond = _getRequiredBondV2(i, 0);
(,,,, disputed,,) = gameProxy.claimData(i);
gameProxy.attackV2{ value: bond }(disputed, i, _dummyClaim(), 0);
}
uint256 lastBond = _getRequiredBondV2(2, 0);
(,,,, disputed,,) = gameProxy.claimData(2);
gameProxy.attackV2{ value: lastBond }(disputed, 2, Claim.wrap(bytes32(0)), 3);
}

/// @dev Static unit test asserting that a move reverts when the bonded amount is incorrect.
Expand Down

0 comments on commit 1100b04

Please sign in to comment.