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
35 changes: 16 additions & 19 deletions packages/contracts-bedrock/scripts/deploy/DeployOPChain.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -157,24 +157,23 @@ contract DeployOPChain is Script {
pure
returns (IOPContractsManagerV2.FullConfig memory config_)
{
// Only PERMISSIONED_CANNON is allowed for initial deployment since no prestate exists for permissionless games.
require(
_input.disputeGameType.raw() == GameTypes.PERMISSIONED_CANNON.raw(),
"DeployOPChain: only PERMISSIONED_CANNON game type is supported for initial deployment"
);

// Build dispute game configs - OPCMV2 requires exactly 3 configs: CANNON, PERMISSIONED_CANNON, CANNON_KONA
IOPContractsManagerUtils.DisputeGameConfig[] memory disputeGameConfigs =
new IOPContractsManagerUtils.DisputeGameConfig[](3);

// Determine which games should be enabled based on the starting respected game type
bool cannonEnabled = _input.disputeGameType.raw() == GameTypes.CANNON.raw();
bool permissionedCannonEnabled = true; // PERMISSIONED_CANNON must always be enabled
bool cannonKonaEnabled = _input.disputeGameType.raw() == GameTypes.CANNON_KONA.raw();

// Config 0: CANNON
IOPContractsManagerUtils.FaultDisputeGameConfig memory cannonConfig =
IOPContractsManagerUtils.FaultDisputeGameConfig({ absolutePrestate: _input.disputeAbsolutePrestate });

// Must be disabled for the initial deployment since no prestate exists for permissionless games.
disputeGameConfigs[0] = IOPContractsManagerUtils.DisputeGameConfig({
enabled: cannonEnabled,
initBond: cannonEnabled ? DEFAULT_INIT_BOND : 0,
enabled: false,
initBond: 0,
gameType: GameTypes.CANNON,
gameArgs: abi.encode(cannonConfig)
gameArgs: bytes("")
});

// Config 1: PERMISSIONED_CANNON (must be enabled)
Expand All @@ -186,21 +185,19 @@ contract DeployOPChain is Script {
});

disputeGameConfigs[1] = IOPContractsManagerUtils.DisputeGameConfig({
enabled: permissionedCannonEnabled,
enabled: true,
initBond: DEFAULT_INIT_BOND,
gameType: GameTypes.PERMISSIONED_CANNON,
gameArgs: abi.encode(pdgConfig)
});

// Config 2: CANNON_KONA
IOPContractsManagerUtils.FaultDisputeGameConfig memory cannonKonaConfig =
IOPContractsManagerUtils.FaultDisputeGameConfig({ absolutePrestate: _input.disputeAbsolutePrestate });

// Must be disabled for the initial deployment since no prestate exists for permissionless games.
disputeGameConfigs[2] = IOPContractsManagerUtils.DisputeGameConfig({
enabled: cannonKonaEnabled,
initBond: cannonKonaEnabled ? DEFAULT_INIT_BOND : 0,
enabled: false,
initBond: 0,
gameType: GameTypes.CANNON_KONA,
gameArgs: abi.encode(cannonKonaConfig)
gameArgs: bytes("")
});

config_ = IOPContractsManagerV2.FullConfig({
Expand All @@ -211,7 +208,7 @@ contract DeployOPChain is Script {
unsafeBlockSigner: _input.unsafeBlockSigner,
batcher: _input.batcher,
startingAnchorRoot: ScriptConstants.DEFAULT_OUTPUT_ROOT(),
startingRespectedGameType: _input.disputeGameType,
startingRespectedGameType: GameTypes.PERMISSIONED_CANNON,
basefeeScalar: _input.basefeeScalar,
blobBasefeeScalar: _input.blobBaseFeeScalar,
gasLimit: _input.gasLimit,
Expand Down
4 changes: 2 additions & 2 deletions packages/contracts-bedrock/snapshots/semver-lock.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@
"sourceCodeHash": "0xb3184aa5d95a82109e7134d1f61941b30e25f655b9849a0e303d04bbce0cde0b"
},
"src/L1/opcm/OPContractsManagerV2.sol:OPContractsManagerV2": {
"initCodeHash": "0xe9c4ba721d8bd3390b184e243d598a522b7efe8b7426ec4d0898529c5654e705",
"sourceCodeHash": "0x3898bab02581ae16ac529358dab8e80477fa34f1354ce8783cef3d36354d1810"
"initCodeHash": "0x5cbc998e57035d8658824e16dacaab8c702f9e18f482e16989b9420e5a7e8190",
"sourceCodeHash": "0x11678225efb1fb4593085febd8f438eeb4752c0ab3dfd2ee1c4fe47970dda953"
},
"src/L2/BaseFeeVault.sol:BaseFeeVault": {
"initCodeHash": "0x838bbd7f381e84e21887f72bd1da605bfc4588b3c39aed96cbce67c09335b3ee",
Expand Down
17 changes: 13 additions & 4 deletions packages/contracts-bedrock/src/L1/opcm/OPContractsManagerV2.sol
Original file line number Diff line number Diff line change
Expand Up @@ -147,9 +147,9 @@ contract OPContractsManagerV2 is ISemver, OPContractsManagerUtilsCaller {
/// - Major bump: New required sequential upgrade
/// - Minor bump: Replacement OPCM for same upgrade
/// - Patch bump: Development changes (expected for normal dev work)
/// @custom:semver 7.0.7
/// @custom:semver 7.0.8
function version() public pure returns (string memory) {
return "7.0.7";
return "7.0.8";
}

/// @param _standardValidator The standard validator for this OPCM release.
Expand Down Expand Up @@ -643,7 +643,7 @@ contract OPContractsManagerV2 is ISemver, OPContractsManagerUtilsCaller {

/// @notice Validates the deployment/upgrade config.
/// @param _cfg The full config.
function _assertValidFullConfig(FullConfig memory _cfg) internal pure {
function _assertValidFullConfig(FullConfig memory _cfg, bool _isInitialDeployment) internal pure {
// Start validating the dispute game configs. Put allowed game types here.
GameType[] memory validGameTypes = new GameType[](3);
validGameTypes[0] = GameTypes.CANNON;
Expand All @@ -667,6 +667,15 @@ contract OPContractsManagerV2 is ISemver, OPContractsManagerUtilsCaller {
if (!_cfg.disputeGameConfigs[i].enabled && _cfg.disputeGameConfigs[i].initBond != 0) {
revert OPContractsManagerV2_InvalidGameConfigs();
}

// During initial deployment, only PERMISSIONED_CANNON can be enabled, because no prestate exists for
// permissionless games.
if (
_isInitialDeployment && (validGameTypes[i].raw() != GameTypes.PERMISSIONED_CANNON.raw())
&& _cfg.disputeGameConfigs[i].enabled
) {
revert OPContractsManagerV2_InvalidGameConfigs();
}
}

// We currently REQUIRE that the PermissionedDisputeGame is enabled. We may be able to
Expand All @@ -691,7 +700,7 @@ contract OPContractsManagerV2 is ISemver, OPContractsManagerUtilsCaller {
returns (ChainContracts memory)
{
// Validate the config.
_assertValidFullConfig(_cfg);
_assertValidFullConfig(_cfg, _isInitialDeployment);

// Load the implementations.
IOPContractsManagerContainer.Implementations memory impls = implementations();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1030,10 +1030,10 @@ contract OPContractsManagerV2_Deploy_Test is OPContractsManagerV2_TestInit {
address initialProposer = DisputeGames.permissionedGameProposer(disputeGameFactory);
deployConfig.disputeGameConfigs.push(
IOPContractsManagerUtils.DisputeGameConfig({
enabled: true,
initBond: DEFAULT_DISPUTE_GAME_INIT_BOND, // Standard init bond
enabled: false,
initBond: 0,
gameType: GameTypes.CANNON,
gameArgs: abi.encode(IOPContractsManagerUtils.FaultDisputeGameConfig({ absolutePrestate: cannonPrestate }))
gameArgs: bytes("")
})
);
deployConfig.disputeGameConfigs.push(
Expand All @@ -1052,20 +1052,20 @@ contract OPContractsManagerV2_Deploy_Test is OPContractsManagerV2_TestInit {
);
deployConfig.disputeGameConfigs.push(
IOPContractsManagerUtils.DisputeGameConfig({
enabled: true,
initBond: DEFAULT_DISPUTE_GAME_INIT_BOND, // Standard init bond
enabled: false,
initBond: 0,
gameType: GameTypes.CANNON_KONA,
gameArgs: abi.encode(
IOPContractsManagerUtils.FaultDisputeGameConfig({ absolutePrestate: cannonKonaPrestate })
)
gameArgs: bytes("")
})
);
}

/// @notice Tests that the deploy function succeeds and passes standard validation.
function test_deploy_succeeds() public {
// Run the deploy and standard validator checks.
IOPContractsManagerV2.ChainContracts memory cts = runDeployV2(deployConfig);
// We expect PLDG-10 and CKDG-10 validator errors because CANNON and CANNON_KONA are
// disabled during initial deployment (no implementations registered).
IOPContractsManagerV2.ChainContracts memory cts = runDeployV2(deployConfig, bytes(""), "PLDG-10,CKDG-10");

// Verify key contracts are deployed.
assertTrue(address(cts.systemConfig) != address(0), "systemConfig not deployed");
Expand Down Expand Up @@ -1138,6 +1138,26 @@ contract OPContractsManagerV2_Deploy_Test is OPContractsManagerV2_TestInit {
deployConfig, abi.encodeWithSelector(IOPContractsManagerV2.OPContractsManagerV2_InvalidGameConfigs.selector)
);
}

function test_deploy_cannonGameEnabled_reverts() public {
deployConfig.disputeGameConfigs[0].enabled = true;
deployConfig.disputeGameConfigs[0].initBond = 1 ether;

// nosemgrep: sol-style-use-abi-encodecall
runDeployV2(
deployConfig, abi.encodeWithSelector(IOPContractsManagerV2.OPContractsManagerV2_InvalidGameConfigs.selector)
);
}

function test_deploy_cannonKonaGameEnabled_reverts() public {
deployConfig.disputeGameConfigs[2].enabled = true;
deployConfig.disputeGameConfigs[2].initBond = 1 ether;

// nosemgrep: sol-style-use-abi-encodecall
runDeployV2(
deployConfig, abi.encodeWithSelector(IOPContractsManagerV2.OPContractsManagerV2_InvalidGameConfigs.selector)
);
}
}

/// @title OPContractsManagerV2_DevFeatureBitmap_Test
Expand Down Expand Up @@ -1188,10 +1208,10 @@ contract OPContractsManagerV2_Migrate_Test is OPContractsManagerV2_TestInit {
IOPContractsManagerUtils.DisputeGameConfig[] memory dgConfigs =
new IOPContractsManagerUtils.DisputeGameConfig[](3);
dgConfigs[0] = IOPContractsManagerUtils.DisputeGameConfig({
enabled: true,
initBond: 0.08 ether,
enabled: false,
initBond: 0,
gameType: GameTypes.CANNON,
gameArgs: abi.encode(IOPContractsManagerUtils.FaultDisputeGameConfig({ absolutePrestate: cannonPrestate }))
gameArgs: bytes("")
});
dgConfigs[1] = IOPContractsManagerUtils.DisputeGameConfig({
enabled: true,
Expand All @@ -1206,10 +1226,10 @@ contract OPContractsManagerV2_Migrate_Test is OPContractsManagerV2_TestInit {
)
});
dgConfigs[2] = IOPContractsManagerUtils.DisputeGameConfig({
enabled: true,
initBond: 0.08 ether,
enabled: false,
initBond: 0,
gameType: GameTypes.CANNON_KONA,
gameArgs: abi.encode(IOPContractsManagerUtils.FaultDisputeGameConfig({ absolutePrestate: cannonKonaPrestate }))
gameArgs: bytes("")
});

// Set up the deploy config using struct literal for compile-time field checking.
Expand Down Expand Up @@ -1544,10 +1564,10 @@ contract OPContractsManagerV2_FeatBatchUpgrade_Test is OPContractsManagerV2_Test
address initialProposer = makeAddr("proposer");
baseConfig.disputeGameConfigs = new IOPContractsManagerUtils.DisputeGameConfig[](3);
baseConfig.disputeGameConfigs[0] = IOPContractsManagerUtils.DisputeGameConfig({
enabled: true,
initBond: DEFAULT_DISPUTE_GAME_INIT_BOND,
enabled: false,
initBond: 0,
gameType: GameTypes.CANNON,
gameArgs: abi.encode(IOPContractsManagerUtils.FaultDisputeGameConfig({ absolutePrestate: cannonPrestate }))
gameArgs: bytes("")
});
baseConfig.disputeGameConfigs[1] = IOPContractsManagerUtils.DisputeGameConfig({
enabled: true,
Expand All @@ -1562,10 +1582,10 @@ contract OPContractsManagerV2_FeatBatchUpgrade_Test is OPContractsManagerV2_Test
)
});
baseConfig.disputeGameConfigs[2] = IOPContractsManagerUtils.DisputeGameConfig({
enabled: true,
initBond: DEFAULT_DISPUTE_GAME_INIT_BOND,
enabled: false,
initBond: 0,
gameType: GameTypes.CANNON_KONA,
gameArgs: abi.encode(IOPContractsManagerUtils.FaultDisputeGameConfig({ absolutePrestate: cannonKonaPrestate }))
gameArgs: bytes("")
});

// 3. Deploy 15 separate chains using opcmV2.deploy().
Expand Down
111 changes: 19 additions & 92 deletions packages/contracts-bedrock/test/opcm/DeployOPChain.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -242,99 +242,28 @@ contract DeployOPChain_Test is DeployOPChain_TestBase {
_checkDeploymentAssertions(doo);
}

function test_run_cannonGameType_succeeds() public {
// Skip test if OPCM v2 is not enabled because OPCM v1 registers PERMISSIONED_CANNON only regardles of the game
// type.
function test_run_cannonGameType_reverts() public {
skipIfDevFeatureDisabled(DevFeatures.OPCM_V2);

deployOPChainInput.disputeGameType = GameTypes.CANNON;
DeployOPChain.Output memory doo = deployOPChain.run(deployOPChainInput);

// CANNON should be enabled with init bond
assertEq(
doo.disputeGameFactoryProxy.initBonds(GameTypes.CANNON),
deployOPChain.DEFAULT_INIT_BOND(),
"CANNON init bond"
);
assertNotEq(address(doo.disputeGameFactoryProxy.gameImpls(GameTypes.CANNON)), address(0), "CANNON impl");

// PERMISSIONED_CANNON must always be enabled
assertEq(
doo.disputeGameFactoryProxy.initBonds(GameTypes.PERMISSIONED_CANNON),
deployOPChain.DEFAULT_INIT_BOND(),
"PERMISSIONED_CANNON init bond"
);
assertNotEq(
address(doo.disputeGameFactoryProxy.gameImpls(GameTypes.PERMISSIONED_CANNON)),
address(0),
"PERMISSIONED_CANNON impl"
);

// CANNON_KONA should not be enabled
assertEq(doo.disputeGameFactoryProxy.initBonds(GameTypes.CANNON_KONA), 0, "CANNON_KONA init bond");
vm.expectRevert("DeployOPChain: only PERMISSIONED_CANNON game type is supported for initial deployment");
deployOPChain.run(deployOPChainInput);
}

function test_run_cannonKonaGameType_succeeds() public {
// Skip test if OPCM v2 is not enabled because OPCM v1 registers PERMISSIONED_CANNON only regardles of the game
// type.
function test_run_cannonKonaGameType_reverts() public {
skipIfDevFeatureDisabled(DevFeatures.OPCM_V2);

deployOPChainInput.disputeGameType = GameTypes.CANNON_KONA;
DeployOPChain.Output memory doo = deployOPChain.run(deployOPChainInput);

// CANNON_KONA should be enabled with init bond
assertEq(
doo.disputeGameFactoryProxy.initBonds(GameTypes.CANNON_KONA),
deployOPChain.DEFAULT_INIT_BOND(),
"CANNON_KONA init bond"
);
assertNotEq(
address(doo.disputeGameFactoryProxy.gameImpls(GameTypes.CANNON_KONA)), address(0), "CANNON_KONA impl"
);

// PERMISSIONED_CANNON must always be enabled in OPCM v2
assertEq(
doo.disputeGameFactoryProxy.initBonds(GameTypes.PERMISSIONED_CANNON),
deployOPChain.DEFAULT_INIT_BOND(),
"PERMISSIONED_CANNON init bond"
);
assertNotEq(
address(doo.disputeGameFactoryProxy.gameImpls(GameTypes.PERMISSIONED_CANNON)),
address(0),
"PERMISSIONED_CANNON impl"
);

// CANNON should not be enabled
assertEq(doo.disputeGameFactoryProxy.initBonds(GameTypes.CANNON), 0, "CANNON init bond");
vm.expectRevert("DeployOPChain: only PERMISSIONED_CANNON game type is supported for initial deployment");
deployOPChain.run(deployOPChainInput);
}

/// @notice Tests that faultDisputeGame is set to address(0) and permissionedDisputeGame is set to the correct
/// implementation for GameTypes.PERMISSIONED_CANNON.
function test_run_faultDisputeGamePermissionedCannon_succeeds() public {
skipIfDevFeatureDisabled(DevFeatures.OPCM_V2);

_assertDisputeGames(GameTypes.PERMISSIONED_CANNON);
}

/// @notice Tests that faultDisputeGame is set to address(0) when disputeGameType is GameTypes.CANNON.
function test_run_faultDisputeGameCannon_succeeds() public {
skipIfDevFeatureDisabled(DevFeatures.OPCM_V2);

_assertDisputeGames(GameTypes.CANNON);
}

/// @notice Tests that faultDisputeGame is set to address(0) when disputeGameType is GameTypes.CANNON_KONA.
function test_run_faultDisputeGameCannonKona_succeeds() public {
skipIfDevFeatureDisabled(DevFeatures.OPCM_V2);

_assertDisputeGames(GameTypes.CANNON_KONA);
}

/// @notice Helper function that runs DeployOPChain.run and asserts DeployOPChain.Output.faultDisputeGame is set to
/// address(0) and DeployOPChain.Output.permissionedDisputeGame is set to the correct implementation.
function _assertDisputeGames(GameType _gameType) internal {
deployOPChainInput.disputeGameType = _gameType;

deployOPChainInput.disputeGameType = GameTypes.PERMISSIONED_CANNON;
DeployOPChain.Output memory doo = deployOPChain.run(deployOPChainInput);

address expectedPermissioned = address(doo.disputeGameFactoryProxy.gameImpls(GameTypes.PERMISSIONED_CANNON));
Expand Down Expand Up @@ -382,25 +311,23 @@ contract DeployOPChain_Test is DeployOPChain_TestBase {
);
assertNotEq(address(doo.disputeGameFactoryProxy.gameImpls(GameTypes.PERMISSIONED_CANNON)), address(0));

// CANNON is only enabled if it's the starting game type
bool cannonEnabled = deployOPChainInput.disputeGameType.raw() == GameTypes.CANNON.raw();
// CANNON must be disabled for initial deployment
assertEq(doo.disputeGameFactoryProxy.initBonds(GameTypes.CANNON), 0, "CANNON init bond should be 0");
assertEq(
doo.disputeGameFactoryProxy.initBonds(GameTypes.CANNON),
cannonEnabled ? deployOPChain.DEFAULT_INIT_BOND() : 0
address(doo.disputeGameFactoryProxy.gameImpls(GameTypes.CANNON)),
address(0),
"CANNON impl should be the zero address"
);
if (cannonEnabled) {
assertNotEq(address(doo.disputeGameFactoryProxy.gameImpls(GameTypes.CANNON)), address(0));
}

// CANNON_KONA is only enabled if it's the starting game type
bool cannonKonaEnabled = deployOPChainInput.disputeGameType.raw() == GameTypes.CANNON_KONA.raw();
// CANNON_KONA must be disabled for initial deployment
assertEq(
doo.disputeGameFactoryProxy.initBonds(GameTypes.CANNON_KONA), 0, "CANNON_KONA init bond should be 0"
);
assertEq(
doo.disputeGameFactoryProxy.initBonds(GameTypes.CANNON_KONA),
cannonKonaEnabled ? deployOPChain.DEFAULT_INIT_BOND() : 0
address(doo.disputeGameFactoryProxy.gameImpls(GameTypes.CANNON_KONA)),
address(0),
"CANNON_KONA impl should be the zero address"
);
if (cannonKonaEnabled) {
assertNotEq(address(doo.disputeGameFactoryProxy.gameImpls(GameTypes.CANNON_KONA)), address(0));
}
}
}
}
Expand Down