Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion op-bindings/bindings/mips_more.go

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion op-bindings/bindings/outputbisectiongame.go

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion op-bindings/bindings/outputbisectiongame_more.go

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion op-bindings/bindings/preimageoracle_more.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions packages/contracts-bedrock/semver-lock.json
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,8 @@
"sourceCodeHash": "0xa995b54dce03ddf5c9c47451bd7181996b91398ad66b54ab0b8cbf582863a33e"
},
"src/dispute/OutputBisectionGame.sol": {
"initCodeHash": "0x400a99278755979b815712d1d26598463dd98ed193df8cd1736ae2ae5831d7c7",
"sourceCodeHash": "0x7e267ad18eb946a0242df41ba044c5ee6f0b456e74bef07605a7dd2eb5b3ed01"
"initCodeHash": "0x6efe83410be6fd58eb07a5297492c6a45598d1f0f84d4ec286d93beade28f40f",
"sourceCodeHash": "0x1cc70ebc403581213a4853e1ef1579abeb63496d0625424fa5b9ac8351b2eeca"
},
"src/legacy/DeployerWhitelist.sol": {
"initCodeHash": "0x8de80fb23b26dd9d849f6328e56ea7c173cd9e9ce1f05c9beea559d1720deb3d",
Expand Down
44 changes: 30 additions & 14 deletions packages/contracts-bedrock/src/dispute/OutputBisectionGame.sol
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,8 @@ contract OutputBisectionGame is IOutputBisectionGame, Clone, ISemver {
bool internal subgameAtRootResolved;

/// @notice Semantic version.
/// @custom:semver 0.0.13
string public constant version = "0.0.13";
/// @custom:semver 0.0.14
string public constant version = "0.0.14";

/// @param _gameType The type ID of the game.
/// @param _absolutePrestate The absolute prestate of the instruction trace.
Expand Down Expand Up @@ -147,16 +147,20 @@ contract OutputBisectionGame is IOutputBisectionGame, Clone, ISemver {
// prestate.
// If the step is an attack at a trace index > 0, the prestate exists elsewhere in
// the game state.
preStateClaim = stepPos.indexAtDepth() == 0
// 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() % (2 ** (MAX_GAME_DEPTH - SPLIT_DEPTH))) == 0
? ABSOLUTE_PRESTATE
: findTraceAncestor(Position.wrap(Position.unwrap(parentPos) - 1), parent.parentIndex).claim;
: findTraceAncestor(Position.wrap(Position.unwrap(parentPos) - 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(Position.unwrap(parentPos) + 1), parent.parentIndex);
postState = findTraceAncestor(Position.wrap(Position.unwrap(parentPos) + 1), parent.parentIndex, false);
}

// INVARIANT: The prestate is always invalid if the passed `_stateData` is not the
Expand Down Expand Up @@ -296,16 +300,16 @@ contract OutputBisectionGame is IOutputBisectionGame, Clone, ISemver {
Hash uuid = computeLocalContext(starting, startingPos, disputed, disputedPos);

IPreimageOracle oracle = VM.oracle();
if (_ident == 1) {
if (_ident == LocalPreimageKey.L1_HEAD_HASH) {
// Load the L1 head hash
oracle.loadLocalData(_ident, Hash.unwrap(uuid), Hash.unwrap(l1Head), 32, _partOffset);
} else if (_ident == 2) {
} else if (_ident == LocalPreimageKey.STARTING_OUTPUT_ROOT) {
// Load the starting proposal's output root.
oracle.loadLocalData(_ident, Hash.unwrap(uuid), Claim.unwrap(starting), 32, _partOffset);
} else if (_ident == 3) {
} else if (_ident == LocalPreimageKey.DISPUTED_OUTPUT_ROOT) {
// Load the disputed proposal's output root
oracle.loadLocalData(_ident, Hash.unwrap(uuid), Claim.unwrap(disputed), 32, _partOffset);
} else if (_ident == 4) {
} else if (_ident == LocalPreimageKey.STARTING_L2_BLOCK_NUMBER) {
// Load the starting proposal's L2 block number as a big-endian uint64 in the
// high order 8 bytes of the word.

Expand All @@ -316,7 +320,7 @@ contract OutputBisectionGame is IOutputBisectionGame, Clone, ISemver {
: GENESIS_BLOCK_NUMBER + startingPos.indexAtDepth() + 1;

oracle.loadLocalData(_ident, Hash.unwrap(uuid), bytes32(l2Number << 0xC0), 8, _partOffset);
} else if (_ident == 5) {
} else if (_ident == LocalPreimageKey.CHAIN_ID) {
// Load the chain ID as a big-endian uint64 in the high order 8 bytes of the word.
oracle.loadLocalData(_ident, Hash.unwrap(uuid), bytes32(block.chainid << 0xC0), 8, _partOffset);
} else {
Expand Down Expand Up @@ -479,10 +483,20 @@ contract OutputBisectionGame is IOutputBisectionGame, Clone, ISemver {
/// @notice Finds the trace ancestor of a given position within the DAG.
/// @param _pos The position to find the trace ancestor claim of.
/// @param _start The index to start searching from.
/// @param _global Whether or not to search the entire dag or just within an execution trace subgame. If set to
/// `true`, and `_pos` is at or above the split depth, this function will revert.
/// @return ancestor_ The ancestor claim that commits to the same trace index as `_pos`.
function findTraceAncestor(Position _pos, uint256 _start) internal view returns (ClaimData storage ancestor_) {
function findTraceAncestor(
Position _pos,
uint256 _start,
bool _global
)
internal
view
returns (ClaimData storage ancestor_)
{
// Grab the trace ancestor's expected position.
Position preStateTraceAncestor = _pos.traceAncestor();
Position preStateTraceAncestor = _global ? _pos.traceAncestor() : _pos.traceAncestorBounded(SPLIT_DEPTH);

// Walk up the DAG to find a claim that commits to the same trace index as `_pos`. It is
// guaranteed that such a claim exists.
Expand Down Expand Up @@ -547,14 +561,16 @@ contract OutputBisectionGame is IOutputBisectionGame, Clone, ISemver {
// starting claim nor position exists in the tree. We leave these as 0, which can be easily
// identified due to 0 being an invalid Gindex.
if (outputPos.indexAtDepth() > 0) {
ClaimData storage starting = findTraceAncestor(Position.wrap(Position.unwrap(outputPos) - 1), claimIdx);
ClaimData storage starting =
findTraceAncestor(Position.wrap(Position.unwrap(outputPos) - 1), claimIdx, true);
(startingClaim_, startingPos_) = (starting.claim, starting.position);
} else {
startingClaim_ = Claim.wrap(Hash.unwrap(GENESIS_OUTPUT_ROOT));
}
(disputedClaim_, disputedPos_) = (claim.claim, claim.position);
} else {
ClaimData storage disputed = findTraceAncestor(Position.wrap(Position.unwrap(outputPos) + 1), claimIdx);
ClaimData storage disputed =
findTraceAncestor(Position.wrap(Position.unwrap(outputPos) + 1), claimIdx, true);
(startingClaim_, startingPos_) = (claim.claim, claim.position);
(disputedClaim_, disputedPos_) = (disputed.claim, disputed.position);
}
Expand Down
39 changes: 39 additions & 0 deletions packages/contracts-bedrock/src/dispute/lib/LibPosition.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
pragma solidity ^0.8.15;

import "src/libraries/DisputeTypes.sol";
import "src/libraries/DisputeErrors.sol";

/// @title LibPosition
/// @notice This library contains helper functions for working with the `Position` type.
Expand Down Expand Up @@ -136,6 +137,44 @@ library LibPosition {
}
}

/// @notice Gets the position of the highest ancestor of `_position` that commits to the same
/// trace index, while still being below `_upperBoundExclusive`.
/// @param _position The position to get the highest ancestor of.
/// @param _upperBoundExclusive The exclusive upper depth bound, used to inform where to stop in order
/// to not escape a sub-tree.
/// @return ancestor_ The highest ancestor of `position` that commits to the same trace index.
function traceAncestorBounded(
Position _position,
uint256 _upperBoundExclusive
)
internal
pure
returns (Position ancestor_)
{
// This function only works for positions that are below the upper bound.
if (_position.depth() <= _upperBoundExclusive) revert ClaimAboveSplit();

// Create a field with only the lowest unset bit of `_position` set.
Position lsb;
assembly {
lsb := and(not(_position), add(_position, 1))
}
// Find the index of the lowest unset bit within the field.
uint256 msb = lsb.depth();
assembly {
let a := shr(msb, _position)
// Bound the ancestor to the minimum gindex, 1.
ancestor_ := or(a, iszero(a))
}

// If the ancestor is above or at the upper bound, shift it to be below the upper bound.
// This should be a special case that only covers positions that commit to the final leaf
// in a sub-tree.
if (ancestor_.depth() <= _upperBoundExclusive) {
ancestor_ = ancestor_.rightIndex(_upperBoundExclusive + 1);
}
}

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