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
8 changes: 4 additions & 4 deletions packages/contracts-bedrock/snapshots/semver-lock.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@
"sourceCodeHash": "0x0ef8461e8bc55314e61e60924465574db17bdf30bbbfe9739b52cac623bba2db"
},
"src/L1/OPContractsManagerStandardValidator.sol:OPContractsManagerStandardValidator": {
"initCodeHash": "0x9a704de7a4137faa533a63249c02268512af5ed4e188d2e1057e3d75f3a550de",
"sourceCodeHash": "0xeceae49028d9f20fd3606cfe4fecfd076e24773c2c455df00292608f25f4a934"
"initCodeHash": "0x6c04d1e2333f52dd27b42bbd889bf3ba6d5edc0670af4514fb1a646566dcc5fb",
"sourceCodeHash": "0x8a2409bb3f08ead8a9619a7f54a995aca224f17fc96121cac0e0f2f8531d3cf6"
},
"src/L1/OptimismPortal2.sol:OptimismPortal2": {
"initCodeHash": "0x02f8cfb0213d614c34c12904f0d06b4f8fad14f7d127b3b780f9d5cc5ebba72a",
Expand Down Expand Up @@ -180,8 +180,8 @@
"sourceCodeHash": "0x2cc9c53d544e5715e38a50ad266551fe7b0f05488e7a66f3f70bca618fbd7fc7"
},
"src/dispute/zk/ZKDisputeGame.sol:ZKDisputeGame": {
"initCodeHash": "0x66e573b7b42ba3b9addc46340bb3ca3ac21ae08496c13c14689dc48ab740a882",
"sourceCodeHash": "0xf193a08ff3404656cfe7ab74fd31d9acafe127fe5717f25635e06b003c6d8094"
"initCodeHash": "0x215801f4d42de7076f376c4cb67d0a19431c793635b42b18687ce53508fb9c9b",
"sourceCodeHash": "0xfd830061ff6bddb8c3f8fe34bfd8714a4bb6785f44864e29b723ab56273533ea"
},
"src/legacy/DeployerWhitelist.sol:DeployerWhitelist": {
"initCodeHash": "0x2e0ef4c341367eb59cc6c25190c64eff441d3fe130189da91d4d126f6bdbc9b5",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ import { IBigStepper } from "interfaces/dispute/IBigStepper.sol";
/// before and after an upgrade.
contract OPContractsManagerStandardValidator is ISemver {
/// @notice The semantic version of the OPContractsManagerStandardValidator contract.
/// @custom:semver 2.10.4
string public constant version = "2.10.4";
/// @custom:semver 2.10.5
string public constant version = "2.10.5";

/// @notice The SuperchainConfig contract.
ISuperchainConfig public superchainConfig;
Expand Down Expand Up @@ -1096,7 +1096,7 @@ contract OPContractsManagerStandardValidator is ISemver {
// Note: Even if the devFeatureBitmap is on for ZK_DISPUTE_GAME, we treat the deployment pipeline and
// as extension, the factory as the source of truth for deciding whether to validate the ZK game.
// ZK is the only per-chain opt-in game type; mandatory game types fail loud in getGameImplementation()
// TODO: Once ZK is mandatory (not per-chain opt-in) post interop migration, remove this early return so chains
// TODO(#21529): Once ZK is mandatory (not per-chain opt-in), remove this early return so chains
// without ZKDisputeGame registered fail validation. Companion to the ZKDG-NOSHAPE TODO in
// StandardValidatorUtils.sol.
IDisputeGameFactory _factory = IDisputeGameFactory(_sysCfg.disputeGameFactory());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ contract StandardValidatorUtils {
internalRequire(address(dgf.gameImpls(GameTypes.SUPER_PERMISSIONED)) == address(0), "SPDG-NOSHAPE", _errors);
_errors =
internalRequire(address(dgf.gameImpls(GameTypes.SUPER_CANNON_KONA)) == address(0), "SCKDG-NOSHAPE", _errors);
// TODO: add ZKDG-NOSHAPE check here once the super-root ZKDisputeGame is ready to be used
// TODO(#21529): add ZKDG-NOSHAPE check here once the super-root ZKDisputeGame is ready to be used
// after interop migration. After that, ZK_DISPUTE_GAME must never be registered on non-super-root chains.
_errors = internalRequire(
address(dgf.gameImpls(GameTypes.PERMISSIONED_CANNON)) != address(0), "PDDG-NOSHAPE", _errors
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ library LibGameArgs {

assembly {
// skip length prefix
let base := add(_args, 0x20)
let base := add(_args, 32)
absolutePrestate := mload(base)
verifier := shr(96, mload(add(base, 32)))
maxChallengeDuration := shr(192, mload(add(base, 52)))
Expand Down
28 changes: 14 additions & 14 deletions packages/contracts-bedrock/src/dispute/zk/ZKDisputeGame.sol
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ import { IAnchorStateRegistry } from "interfaces/dispute/IAnchorStateRegistry.so
import { IDelayedWETH } from "interfaces/dispute/IDelayedWETH.sol";

/// @title ZKDisputeGame
/// @notice A ZK proof-based dispute game using the MCP (Modular Clone Proxy) pattern
/// with Clone-With-Immutable-Args (CWIA). Spec-compliant, permissionless
/// @notice A super-roots compatible ZK proof-based dispute game using the MCP (Modular Clone Proxy) pattern
/// with Clone-With-Immutable-Args (CWIA). Spec-compliant, interop-ready, permissionless
/// design that uses a generic IZKVerifier and DelayedWETH for bond custody.
/// @dev Derived from https://github.com/succinctlabs/op-succinct (at commit c13844a9bbc330cca69eef2538d8f8ec123e1653)
contract ZKDisputeGame is Clone, ISemver, IDisputeGame {
Expand Down Expand Up @@ -84,8 +84,8 @@ contract ZKDisputeGame is Clone, ISemver, IDisputeGame {
/// @custom:field claim The root claim being disputed.
struct ClaimData {
uint32 parentIndex; // 4 bytes
ProposalStatus status; // 1 byte |-- slot 1 (25 bytes)
address challenger; // 20 bytes
ProposalStatus status; // 1 byte
address challenger; // 20 bytes |-- slot 1 (25 bytes)
address prover; // 20 bytes
Timestamp deadline; // 8 bytes |-- slot 2 (28 bytes)
Claim claim; // 32 bytes |-- slot 3
Expand All @@ -111,8 +111,8 @@ contract ZKDisputeGame is Clone, ISemver, IDisputeGame {
////////////////////////////////////////////////////////////////

/// @notice Semantic version.
/// @custom:semver 2.0.0
string public constant version = "2.0.0";
/// @custom:semver 2.0.1
string public constant version = "2.0.1";

/// @notice The starting timestamp of the game.
Timestamp public createdAt;
Expand Down Expand Up @@ -209,49 +209,49 @@ contract ZKDisputeGame is Clone, ISemver, IDisputeGame {
////////////////////////////////////////////////////////////////

/// @notice Returns the absolute prestate commitment (ZK circuit identity) for the super-root ZK program.
/// @dev `clones-with-immutable-args` argument #6
/// @dev `clones-with-immutable-args` argument #5
/// @return absolutePrestate_ The absolute prestate vkey of the multi-chain super-root ZK circuit.
function absolutePrestate() public pure returns (bytes32 absolutePrestate_) {
absolutePrestate_ = _getArgBytes32(_preExtraDataByteCount() + _extraDataByteCount());
}

/// @notice Returns the ZK verifier contract.
/// @dev `clones-with-immutable-args` argument #7
/// @dev `clones-with-immutable-args` argument #6
/// @return verifier_ The ZK verifier contract used to validate proofs.
function verifier() public pure returns (IZKVerifier verifier_) {
verifier_ = IZKVerifier(_getArgAddress(_preExtraDataByteCount() + _extraDataByteCount() + 32));
}

/// @notice Returns the max challenge duration.
/// @dev `clones-with-immutable-args` argument #8
/// @dev `clones-with-immutable-args` argument #7
/// @return maxChallengeDuration_ The maximum time a proposal can remain unchallenged.
function maxChallengeDuration() public pure returns (Duration maxChallengeDuration_) {
maxChallengeDuration_ = Duration.wrap(_getArgUint64(_preExtraDataByteCount() + _extraDataByteCount() + 52));
}

/// @notice Returns the max prove duration.
/// @dev `clones-with-immutable-args` argument #9
/// @dev `clones-with-immutable-args` argument #8
/// @return maxProveDuration_ The maximum time a challenged proposal can remain unproven.
function maxProveDuration() public pure returns (Duration maxProveDuration_) {
maxProveDuration_ = Duration.wrap(_getArgUint64(_preExtraDataByteCount() + _extraDataByteCount() + 60));
}

/// @notice Returns the challenger bond amount.
/// @dev `clones-with-immutable-args` argument #10
/// @dev `clones-with-immutable-args` argument #9
/// @return challengerBond_ The required bond, in wei, for a challenger to challenge the proposal.
function challengerBond() public pure returns (uint256 challengerBond_) {
challengerBond_ = _getArgUint256(_preExtraDataByteCount() + _extraDataByteCount() + 68);
}

/// @notice Returns the anchor state registry contract.
/// @dev `clones-with-immutable-args` argument #11
/// @dev `clones-with-immutable-args` argument #10
/// @return registry_ The anchor state registry contract.
function anchorStateRegistry() public pure returns (IAnchorStateRegistry registry_) {
registry_ = IAnchorStateRegistry(_getArgAddress(_preExtraDataByteCount() + _extraDataByteCount() + 100));
}

/// @notice Returns the DelayedWETH contract used for bond custody.
/// @dev `clones-with-immutable-args` argument #12
/// @dev `clones-with-immutable-args` argument #11
/// @return weth_ The DelayedWETH contract used for bond custody.
function weth() public pure returns (IDelayedWETH weth_) {
weth_ = IDelayedWETH(payable(_getArgAddress(_preExtraDataByteCount() + _extraDataByteCount() + 120)));
Expand Down Expand Up @@ -596,7 +596,7 @@ contract ZKDisputeGame is Clone, ISemver, IDisputeGame {
status = GameStatus.DEFENDER_WINS;
normalModeCredit[gameCreator()] = totalBonds;
} else if (claimData.status == ProposalStatus.Challenged) {
// Claim is challenged, challenger wins, challenger wins everything
// Claim is challenged, prove deadline expired, challenger wins, challenger wins everything
status = GameStatus.CHALLENGER_WINS;
normalModeCredit[claimData.challenger] = totalBonds;
} else if (claimData.status == ProposalStatus.UnchallengedAndValidProofProvided) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { ISemver } from "interfaces/universal/ISemver.sol";
import { IStorageSetter } from "interfaces/universal/IStorageSetter.sol";
import { Claim, Duration } from "src/dispute/lib/LibUDT.sol";
import { GameTypes } from "src/dispute/lib/Types.sol";
import { LibGameArgs } from "src/dispute/lib/LibGameArgs.sol";
import { IAnchorStateRegistry } from "interfaces/dispute/IAnchorStateRegistry.sol";
import { IDelayedWETH } from "interfaces/dispute/IDelayedWETH.sol";
import { IZKVerifier } from "interfaces/dispute/zk/IZKVerifier.sol";
Expand Down Expand Up @@ -975,6 +976,16 @@ contract OPContractsManagerUtils_MakeGameArgs_Test is OPContractsManagerUtils_Te
address(delayedWETH)
);
assertEq(keccak256(result), keccak256(expected), "ZK game args CWIA layout mismatch");

// Decode the encoded args back through LibGameArgs and assert every field round-trips.
LibGameArgs.ZKGameArgs memory decoded = LibGameArgs.decodeZK(result);
assertEq(decoded.absolutePrestate, absolutePrestate.raw(), "absolutePrestate mismatch");
assertEq(decoded.verifier, address(verifier), "verifier mismatch");
assertEq(decoded.maxChallengeDuration, maxChallengeDuration.raw(), "maxChallengeDuration mismatch");
assertEq(decoded.maxProveDuration, maxProveDuration.raw(), "maxProveDuration mismatch");
assertEq(decoded.challengerBond, challengerBond, "challengerBond mismatch");
assertEq(decoded.anchorStateRegistry, address(anchorStateRegistry), "anchorStateRegistry mismatch");
assertEq(decoded.weth, address(delayedWETH), "weth mismatch");
}

/// @notice Tests that makeGameArgs reverts for an unsupported game type.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { ForgeArtifacts, StorageSlot } from "scripts/libraries/ForgeArtifacts.so
import { DeployUtils } from "scripts/libraries/DeployUtils.sol";

// Libraries
import { LibClone } from "@solady/utils/LibClone.sol";
import { DevFeatures } from "src/libraries/DevFeatures.sol";
import "src/dispute/lib/Types.sol";
import "src/dispute/lib/Errors.sol";
Expand Down Expand Up @@ -1063,6 +1064,33 @@ contract DisputeGameFactory_Create_ZkDisputeGame_Test is DisputeGameFactory_ZkDi
vm.prank(proposer);
disputeGameFactory.create{ value: 1 ether }(GameTypes.ZK_DISPUTE_GAME, rc, ed);
}

/// @notice Regression guard for the LibClone immutable-args length-overflow finding from the Solady audit
/// here: https://cantina.xyz/portfolio/018cf146-6e36-49a9-82b3-9ae39904f95a
/// CWIA clones must revert and not silently deploy a corrupted, codeless proxy that
/// permanently traps the bond when the appended args exceed the 2-byte length field.
function test_create_oversizedExtraData_reverts() public {
// Register both game implementations on the factory.
setupZKDisputeGame(defaultZKParams);
setupSuperFaultDisputeGame(Claim.wrap(bytes32(0)));

// Create a ton of extra data, far past the 2-byte CWIA length limit.
bytes memory oversizedExtraData = new bytes(0xe0000);
Claim rootClaim = Claim.wrap(keccak256("rootClaim"));
vm.deal(address(this), 10 ether);

// ZK dispute game: the clone must revert instead of deploying a codeless proxy.
vm.expectRevert(LibClone.DeploymentFailed.selector);
disputeGameFactory.create{ value: defaultZKParams.challengerBond }(
GameTypes.ZK_DISPUTE_GAME, rootClaim, oversizedExtraData
);

// Super fault dispute game: same guarantee.
vm.expectRevert(LibClone.DeploymentFailed.selector);
disputeGameFactory.create{ value: DEFAULT_DISPUTE_GAME_INIT_BOND }(
GameTypes.SUPER_CANNON, rootClaim, oversizedExtraData
);
}
}

/// @title DisputeGameFactory_SetImplementation_ZkDisputeGame_Test
Expand Down
Loading