diff --git a/op-deployer/pkg/deployer/integration_test/apply_test.go b/op-deployer/pkg/deployer/integration_test/apply_test.go index 64884131f5a8f..5a49ac06bcf31 100644 --- a/op-deployer/pkg/deployer/integration_test/apply_test.go +++ b/op-deployer/pkg/deployer/integration_test/apply_test.go @@ -914,15 +914,10 @@ func runEndToEndBootstrapAndApplyUpgradeTest(t *testing.T, afactsFS foundry.Stat // First, upgrade the superchain with V2 t.Run("upgrade superchain v2", func(t *testing.T) { superchainUpgradeConfig := embedded.UpgradeSuperchainConfigInput{ - Prank: superchainProxyAdminOwner, - Opcm: impls.OpcmV2, - SuperchainConfig: implementationsConfig.SuperchainConfigProxy, - ExtraInstructions: []embedded.ExtraInstruction{ - { - Key: "PermittedProxyDeployment", - Data: []byte("DelayedWETH"), - }, - }, + Prank: superchainProxyAdminOwner, + Opcm: impls.OpcmV2, + SuperchainConfig: implementationsConfig.SuperchainConfigProxy, + ExtraInstructions: []embedded.ExtraInstruction{}, } err := embedded.UpgradeSuperchainConfig(host, superchainUpgradeConfig) if err != nil { @@ -985,6 +980,7 @@ func runEndToEndBootstrapAndApplyUpgradeTest(t *testing.T, afactsFS foundry.Stat Key: "PermittedProxyDeployment", Data: []byte("DelayedWETH"), }, + // TODO(#18502): Remove the extra instruction for custom gas token after U18 ships. { Key: "overrides.cfg.useCustomGasToken", Data: make([]byte, 32), @@ -995,6 +991,77 @@ func runEndToEndBootstrapAndApplyUpgradeTest(t *testing.T, afactsFS foundry.Stat upgradeConfigBytes, err := json.Marshal(upgradeConfig) require.NoError(t, err, "UpgradeOPChainV2Input should marshal to JSON") + + // Verify input encoding + encodedData, err := upgradeConfig.EncodedUpgradeInputV2() + require.NoError(t, err, "Should encode UpgradeInputV2") + require.NotEmpty(t, encodedData, "Encoded data should not be empty") + + // Build expected hex encoding + // Structure breakdown: + // - Tuple offset (0x20) + // - SystemConfig address (0x034edd2a225f7f429a63e0f1d2084b9e0a93b538) + // - DisputeGameConfigs array offset (0x60) and ExtraInstructions array offset (0x340) + // - DisputeGameConfigs[]: 3 configs + // [0] Cannon: enabled=true, initBond=1e18, gameType=0, gameArgs="PRESTATE" + // [1] PermissionedCannon: enabled=true, initBond=1e18, gameType=1, gameArgs="PRESTATE"+proposer+challenger + // [2] CannonKona: enabled=false, initBond=0, gameType=0, gameArgs=empty + // - ExtraInstructions[]: 2 instructions + // [0] key="PermittedProxyDeployment", data="DelayedWETH" + // [1] key="overrides.cfg.useCustomGasToken", data=32 zero bytes + expected := "0000000000000000000000000000000000000000000000000000000000000020" + // offset to tuple + "000000000000000000000000034edd2a225f7f429a63e0f1d2084b9e0a93b538" + // systemConfig address + "0000000000000000000000000000000000000000000000000000000000000060" + // offset to disputeGameConfigs + "0000000000000000000000000000000000000000000000000000000000000340" + // offset to extraInstructions + "0000000000000000000000000000000000000000000000000000000000000003" + // disputeGameConfigs.length (3) + "0000000000000000000000000000000000000000000000000000000000000060" + // offset to disputeGameConfigs[0] + "0000000000000000000000000000000000000000000000000000000000000120" + // offset to disputeGameConfigs[1] + "0000000000000000000000000000000000000000000000000000000000000220" + // offset to disputeGameConfigs[2] + // DisputeGameConfigs[0] - Cannon + "0000000000000000000000000000000000000000000000000000000000000001" + // enabled=true + "0000000000000000000000000000000000000000000000000de0b6b3a7640000" + // initBond=1e18 + "0000000000000000000000000000000000000000000000000000000000000000" + // gameType=0 (Cannon) + "0000000000000000000000000000000000000000000000000000000000000080" + // offset to gameArgs + "0000000000000000000000000000000000000000000000000000000000000020" + // gameArgs.length (32 bytes) + "5052455354415445000000000000000000000000000000000000000000000000" + // gameArgs data "PRESTATE" + // DisputeGameConfigs[1] - PermissionedCannon + "0000000000000000000000000000000000000000000000000000000000000001" + // enabled=true + "0000000000000000000000000000000000000000000000000de0b6b3a7640000" + // initBond=1e18 + "0000000000000000000000000000000000000000000000000000000000000001" + // gameType=1 (PermissionedCannon) + "0000000000000000000000000000000000000000000000000000000000000080" + // offset to gameArgs + "0000000000000000000000000000000000000000000000000000000000000060" + // gameArgs.length (96 bytes) + "5052455354415445000000000000000000000000000000000000000000000000" + // gameArgs data "PRESTATE" + "0000000000000000000000005000000000000000000000000000000000000000" + // proposer address + "0000000000000000000000004300000000000000000000000000000000000000" + // challenger address + // DisputeGameConfigs[2] - CannonKona (disabled) + "0000000000000000000000000000000000000000000000000000000000000000" + // enabled=false + "0000000000000000000000000000000000000000000000000000000000000000" + // initBond=0 + "0000000000000000000000000000000000000000000000000000000000000008" + // gameType=8 (CannonKona) + "0000000000000000000000000000000000000000000000000000000000000080" + // offset to gameArgs + "0000000000000000000000000000000000000000000000000000000000000000" + // gameArgs.length (0) + // ExtraInstructions array + "0000000000000000000000000000000000000000000000000000000000000002" + // extraInstructions.length (2) + "0000000000000000000000000000000000000000000000000000000000000040" + // offset to extraInstructions[0] + "0000000000000000000000000000000000000000000000000000000000000100" + // offset to extraInstructions[1] + // ExtraInstructions[0] - PermittedProxyDeployment + "0000000000000000000000000000000000000000000000000000000000000040" + // offset to key + "0000000000000000000000000000000000000000000000000000000000000080" + // offset to data + "0000000000000000000000000000000000000000000000000000000000000018" + // key.length (24 bytes) + "5065726d697474656450726f78794465706c6f796d656e74000000000000000" + // "PermittedProxyDeployment" + "0" + // padding + "000000000000000000000000000000000000000000000000000000000000000b" + // data.length (11 bytes) + "44656c617965645745544800000000000000000000000000000000000000000" + // "DelayedWETH" + "0" + // padding + // ExtraInstructions[1] - useCustomGasToken override + "0000000000000000000000000000000000000000000000000000000000000040" + // offset to key + "0000000000000000000000000000000000000000000000000000000000000080" + // offset to data + "000000000000000000000000000000000000000000000000000000000000001f" + // key.length (31 bytes) + "6f76657272696465732e6366672e757365437573746f6d476173546f6b656e00" + // "overrides.cfg.useCustomGasToken" + "0000000000000000000000000000000000000000000000000000000000000020" + // data.length (32 bytes) + "0000000000000000000000000000000000000000000000000000000000000000" // data (32 zero bytes) + + require.Equal(t, expected, hex.EncodeToString(encodedData), "Encoded calldata should match expected structure") + err = embedded.DefaultUpgrader.Upgrade(host, upgradeConfigBytes) require.NoError(t, err, "OPCM V2 chain upgrade should succeed") }) diff --git a/op-deployer/pkg/deployer/upgrade/embedded/upgrade_test.go b/op-deployer/pkg/deployer/upgrade/embedded/upgrade_test.go index a6e3e45bf8382..50f3e900d4719 100644 --- a/op-deployer/pkg/deployer/upgrade/embedded/upgrade_test.go +++ b/op-deployer/pkg/deployer/upgrade/embedded/upgrade_test.go @@ -9,7 +9,7 @@ import ( "github.com/stretchr/testify/require" ) -func TestUpgradeOPChainInput_UpgradeInput(t *testing.T) { +func TestUpgradeOPChainInput_UpgradeInputV2(t *testing.T) { input := &UpgradeOPChainInput{ Prank: common.Address{0xaa}, Opcm: common.Address{0xbb}, diff --git a/packages/contracts-bedrock/scripts/deploy/UpgradeOPChain.s.sol b/packages/contracts-bedrock/scripts/deploy/UpgradeOPChain.s.sol index d26fa116559ed..d180f80ffdc23 100644 --- a/packages/contracts-bedrock/scripts/deploy/UpgradeOPChain.s.sol +++ b/packages/contracts-bedrock/scripts/deploy/UpgradeOPChain.s.sol @@ -27,6 +27,9 @@ contract UpgradeOPChainInput is BaseDeployIO { /// @param _sel The selector of the field to set. /// @param _value The value to set. function set(bytes4 _sel, OPContractsManager.OpChainConfig[] memory _value) public { + if (OPContractsManager(opcm()).isDevFeatureEnabled(DevFeatures.OPCM_V2)) { + revert("UpgradeOPCMInput: cannot set OPCM v1 upgrade input when OPCM v2 is enabled"); + } require(_value.length > 0, "UpgradeOPCMInput: cannot set empty array"); if (_sel == this.upgradeInput.selector) _upgradeInput = abi.encode(_value); @@ -40,6 +43,9 @@ contract UpgradeOPChainInput is BaseDeployIO { /// @param _sel The selector of the field to set. /// @param _value The value to set. function set(bytes4 _sel, OPContractsManagerV2.UpgradeInput memory _value) public { + if (!OPContractsManager(opcm()).isDevFeatureEnabled(DevFeatures.OPCM_V2)) { + revert("UpgradeOPCMInput: cannot set OPCM v2 upgrade input when OPCM v1 is enabled"); + } require(address(_value.systemConfig) != address(0), "UpgradeOPCMInput: cannot set zero address"); require(_value.disputeGameConfigs.length > 0, "UpgradeOPCMInput: cannot set empty dispute game configs array"); diff --git a/packages/contracts-bedrock/scripts/deploy/UpgradeSuperchainConfig.s.sol b/packages/contracts-bedrock/scripts/deploy/UpgradeSuperchainConfig.s.sol index ed30491e839e4..8cc0cb13123d2 100644 --- a/packages/contracts-bedrock/scripts/deploy/UpgradeSuperchainConfig.s.sol +++ b/packages/contracts-bedrock/scripts/deploy/UpgradeSuperchainConfig.s.sol @@ -92,10 +92,10 @@ contract DummyCaller { return (success, result); } } + /// @title DummyCallerV2 /// @notice This contract is used to mimic the contract that is used as the source of the delegatecall to the OPCM v2. /// Uses IOPContractsManagerV2.SuperchainUpgradeInput type for the upgrade input. - contract DummyCallerV2 { address internal _opcmAddr; diff --git a/packages/contracts-bedrock/test/opcm/UpgradeOPChain.t.sol b/packages/contracts-bedrock/test/opcm/UpgradeOPChain.t.sol index 35accd9af1cfc..16820d84740c6 100644 --- a/packages/contracts-bedrock/test/opcm/UpgradeOPChain.t.sol +++ b/packages/contracts-bedrock/test/opcm/UpgradeOPChain.t.sol @@ -23,62 +23,86 @@ import { ISystemConfig } from "interfaces/L1/ISystemConfig.sol"; contract UpgradeOPChainInput_Test is Test { UpgradeOPChainInput input; + MockOPCMV1 _mockOPCM; function setUp() public { input = new UpgradeOPChainInput(); + _mockOPCM = new MockOPCMV1(); + input.set(input.opcm.selector, address(_mockOPCM)); } + /// @notice This test verifies that the UpgradeOPChain script correctly reverts when the upgrade input is not + /// completely set. function test_getters_whenNotSet_reverts() public { + UpgradeOPChainInput freshInput = new UpgradeOPChainInput(); + vm.expectRevert("UpgradeOPCMInput: prank not set"); - input.prank(); + freshInput.prank(); vm.expectRevert("UpgradeOPCMInput: not set"); - input.opcm(); + freshInput.opcm(); vm.expectRevert("UpgradeOPCMInput: not set"); - input.upgradeInput(); + freshInput.upgradeInput(); } - function test_setAddress_succeeds() public { - address mockPrank = makeAddr("prank"); - address mockOPCM = makeAddr("opcm"); - - // Create mock contract at OPCM address - vm.etch(mockOPCM, hex"01"); + /// @notice This test verifies that the UpgradeOPChain script correctly sets the upgrade input with + /// the address type. + function testFuzz_setAddress_succeeds(address mockPrank, address mockOPCM) public { + vm.assume(mockPrank != address(0)); + vm.assume(mockOPCM != address(0)); - input.set(input.prank.selector, mockPrank); - input.set(input.opcm.selector, mockOPCM); + UpgradeOPChainInput freshInput = new UpgradeOPChainInput(); + freshInput.set(freshInput.prank.selector, mockPrank); + freshInput.set(freshInput.opcm.selector, mockOPCM); - assertEq(input.prank(), mockPrank); - assertEq(input.opcm(), mockOPCM); + assertEq(freshInput.prank(), mockPrank); + assertEq(freshInput.opcm(), mockOPCM); } - function test_setOpChainConfigs_succeeds() public { + /// @notice This test verifies that the UpgradeOPChain script correctly sets the upgrade input with + /// the OPContractsManager.OpChainConfig[] type. + function testFuzz_setOpChainConfigs_succeeds( + address systemConfig1, + address systemConfig2, + bytes32 prestate1, + bytes32 konaPrestate1, + bytes32 prestate2, + bytes32 konaPrestate2 + ) + public + { + // Assume non-zero addresses for system configs + vm.assume(systemConfig1 != address(0)); + vm.assume(systemConfig2 != address(0)); + // Assume not precompiles for system configs + assumeNotPrecompile(systemConfig1); + assumeNotPrecompile(systemConfig2); + // Ensure system configs don't collide with test contracts + vm.assume(systemConfig1 != address(input)); + vm.assume(systemConfig1 != address(_mockOPCM)); + vm.assume(systemConfig2 != address(input)); + vm.assume(systemConfig2 != address(_mockOPCM)); + // Create sample OpChainConfig array OPContractsManager.OpChainConfig[] memory configs = new OPContractsManager.OpChainConfig[](2); // Setup mock addresses and contracts for first config - address systemConfig1 = makeAddr("systemConfig1"); - address proxyAdmin1 = makeAddr("proxyAdmin1"); vm.etch(systemConfig1, hex"01"); - vm.etch(proxyAdmin1, hex"01"); configs[0] = OPContractsManager.OpChainConfig({ systemConfigProxy: ISystemConfig(systemConfig1), - cannonPrestate: Claim.wrap(bytes32(uint256(1))), - cannonKonaPrestate: Claim.wrap(bytes32(uint256(2))) + cannonPrestate: Claim.wrap(prestate1), + cannonKonaPrestate: Claim.wrap(konaPrestate1) }); // Setup mock addresses and contracts for second config - address systemConfig2 = makeAddr("systemConfig2"); - address proxyAdmin2 = makeAddr("proxyAdmin2"); vm.etch(systemConfig2, hex"01"); - vm.etch(proxyAdmin2, hex"01"); configs[1] = OPContractsManager.OpChainConfig({ systemConfigProxy: ISystemConfig(systemConfig2), - cannonPrestate: Claim.wrap(bytes32(uint256(2))), - cannonKonaPrestate: Claim.wrap(bytes32(uint256(3))) + cannonPrestate: Claim.wrap(prestate2), + cannonKonaPrestate: Claim.wrap(konaPrestate2) }); input.set(input.upgradeInput.selector, configs); @@ -89,28 +113,136 @@ contract UpgradeOPChainInput_Test is Test { // Additional verification of stored claims if needed OPContractsManager.OpChainConfig[] memory decodedConfigs = abi.decode(storedConfigs, (OPContractsManager.OpChainConfig[])); - assertEq(Claim.unwrap(decodedConfigs[0].cannonPrestate), bytes32(uint256(1))); - assertEq(Claim.unwrap(decodedConfigs[1].cannonPrestate), bytes32(uint256(2))); + assertEq(Claim.unwrap(decodedConfigs[0].cannonPrestate), prestate1); + assertEq(Claim.unwrap(decodedConfigs[1].cannonPrestate), prestate2); + assertEq(Claim.unwrap(decodedConfigs[0].cannonKonaPrestate), konaPrestate1); + assertEq(Claim.unwrap(decodedConfigs[1].cannonKonaPrestate), konaPrestate2); + } + + /// @notice This test verifies that the UpgradeOPChain script correctly reverts when setting the upgrade input with + /// a zero address. + function test_setAddress_withZeroAddress_reverts() public { + UpgradeOPChainInput freshInput = new UpgradeOPChainInput(); + + vm.expectRevert("UpgradeOPCMInput: cannot set zero address"); + freshInput.set(freshInput.prank.selector, address(0)); + + vm.expectRevert("UpgradeOPCMInput: cannot set zero address"); + freshInput.set(freshInput.opcm.selector, address(0)); + } + + /// @notice This test verifies that the UpgradeOPChain script correctly reverts when setting the upgrade input with + /// an empty array. + function test_setOpChainConfigs_withEmptyArray_reverts() public { + OPContractsManager.OpChainConfig[] memory emptyConfigs = new OPContractsManager.OpChainConfig[](0); + + vm.expectRevert("UpgradeOPCMInput: cannot set empty array"); + input.set(input.upgradeInput.selector, emptyConfigs); + } + + /// @notice This test verifies that the UpgradeOPChain script correctly reverts when setting the upgrade input with + /// an invalid selector. + function testFuzz_set_withInvalidSelector_reverts(bytes4 invalidSelector, address testAddr) public { + // Assume the selector is not one of the valid selectors + vm.assume(invalidSelector != input.prank.selector); + vm.assume(invalidSelector != input.opcm.selector); + vm.assume(invalidSelector != input.upgradeInput.selector); + vm.assume(testAddr != address(0)); + + vm.expectRevert("UpgradeOPCMInput: unknown selector"); + input.set(invalidSelector, testAddr); + + // Create a single config for testing invalid selector + OPContractsManager.OpChainConfig[] memory configs = new OPContractsManager.OpChainConfig[](1); + address mockSystemConfig = makeAddr("systemConfig"); + vm.etch(mockSystemConfig, hex"01"); + + configs[0] = OPContractsManager.OpChainConfig({ + systemConfigProxy: ISystemConfig(mockSystemConfig), + cannonPrestate: Claim.wrap(bytes32(uint256(1))), + cannonKonaPrestate: Claim.wrap(bytes32(uint256(2))) + }); + + vm.expectRevert("UpgradeOPCMInput: unknown selector"); + input.set(invalidSelector, configs); + } + + /// @notice This test verifies that the UpgradeOPChain script correctly reverts when setting the upgrade input with + /// OPCM v2 input when OPCM v1 is enabled. + function testFuzz_setUpgradeInputV2_onV1OPCM_reverts( + address systemConfig, + bool enabled, + uint256 initBond, + uint32 gameType + ) + public + { + vm.assume(systemConfig != address(0)); + vm.assume(initBond > 0); + + // Try to set V2 input when V1 is enabled + OPContractsManagerV2.DisputeGameConfig[] memory disputeGameConfigs = + new OPContractsManagerV2.DisputeGameConfig[](1); + disputeGameConfigs[0] = OPContractsManagerV2.DisputeGameConfig({ + enabled: enabled, + initBond: initBond, + gameType: GameType.wrap(gameType), + gameArgs: abi.encode("test") + }); + + OPContractsManagerV2.UpgradeInput memory upgradeInput = OPContractsManagerV2.UpgradeInput({ + systemConfig: ISystemConfig(systemConfig), + disputeGameConfigs: disputeGameConfigs, + extraInstructions: new IOPContractsManagerUtils.ExtraInstruction[](0) + }); + + vm.expectRevert("UpgradeOPCMInput: cannot set OPCM v2 upgrade input when OPCM v1 is enabled"); + input.set(input.upgradeInput.selector, upgradeInput); + } +} + +contract UpgradeOPChainInput_TestV2 is Test { + UpgradeOPChainInput input; + MockOPCMV2 mockOPCM; + + function setUp() public { + input = new UpgradeOPChainInput(); + mockOPCM = new MockOPCMV2(); + input.set(input.opcm.selector, address(mockOPCM)); } /// @notice Tests that the upgrade input can be set using the OPContractsManagerV2.UpgradeInput type. - function test_setUpgradeInputV2_succeeds() public { + function testFuzz_setUpgradeInputV2_succeeds( + address systemConfig, + bool enabled, + uint256 initBond, + uint32 gameType, + bytes memory gameArgs, + string memory extraKey, + bytes memory extraData + ) + public + { + // Assume non-zero address for system config + vm.assume(systemConfig != address(0)); + vm.assume(initBond > 0); + // Create sample UpgradeInputV2 OPContractsManagerV2.DisputeGameConfig[] memory disputeGameConfigs = new OPContractsManagerV2.DisputeGameConfig[](1); disputeGameConfigs[0] = OPContractsManagerV2.DisputeGameConfig({ - enabled: true, - initBond: 1000, - gameType: GameType.wrap(1), - gameArgs: abi.encode("test") + enabled: enabled, + initBond: initBond, + gameType: GameType.wrap(gameType), + gameArgs: gameArgs }); IOPContractsManagerUtils.ExtraInstruction[] memory extraInstructions = new IOPContractsManagerUtils.ExtraInstruction[](1); - extraInstructions[0] = IOPContractsManagerUtils.ExtraInstruction({ key: "test", data: abi.encode("test") }); + extraInstructions[0] = IOPContractsManagerUtils.ExtraInstruction({ key: extraKey, data: extraData }); OPContractsManagerV2.UpgradeInput memory upgradeInput = OPContractsManagerV2.UpgradeInput({ - systemConfig: ISystemConfig(makeAddr("systemConfig")), + systemConfig: ISystemConfig(systemConfig), disputeGameConfigs: disputeGameConfigs, extraInstructions: extraInstructions }); @@ -127,55 +259,65 @@ contract UpgradeOPChainInput_Test is Test { assertEq(address(decodedUpgradeInput.systemConfig), address(upgradeInput.systemConfig)); // Check dispute game configs match assertEq(decodedUpgradeInput.disputeGameConfigs.length, disputeGameConfigs.length); - assertEq(decodedUpgradeInput.disputeGameConfigs[0].enabled, disputeGameConfigs[0].enabled); - assertEq(decodedUpgradeInput.disputeGameConfigs[0].initBond, disputeGameConfigs[0].initBond); - assertEq( - GameType.unwrap(decodedUpgradeInput.disputeGameConfigs[0].gameType), - GameType.unwrap(disputeGameConfigs[0].gameType) - ); - assertEq( - keccak256(decodedUpgradeInput.disputeGameConfigs[0].gameArgs), keccak256(disputeGameConfigs[0].gameArgs) - ); + assertEq(decodedUpgradeInput.disputeGameConfigs[0].enabled, enabled); + assertEq(decodedUpgradeInput.disputeGameConfigs[0].initBond, initBond); + assertEq(GameType.unwrap(decodedUpgradeInput.disputeGameConfigs[0].gameType), gameType); + assertEq(keccak256(decodedUpgradeInput.disputeGameConfigs[0].gameArgs), keccak256(gameArgs)); // Check extra instructions match assertEq(decodedUpgradeInput.extraInstructions.length, extraInstructions.length); - assertEq(decodedUpgradeInput.extraInstructions[0].key, extraInstructions[0].key); - assertEq(keccak256(decodedUpgradeInput.extraInstructions[0].data), keccak256(extraInstructions[0].data)); + assertEq(decodedUpgradeInput.extraInstructions[0].key, extraKey); + assertEq(keccak256(decodedUpgradeInput.extraInstructions[0].data), keccak256(extraData)); } - function test_setAddress_withZeroAddress_reverts() public { - vm.expectRevert("UpgradeOPCMInput: cannot set zero address"); - input.set(input.prank.selector, address(0)); + /// @notice This test verifies that the UpgradeOPChain script correctly reverts when setting the upgrade input with + /// a zero system config. + function testFuzz_setUpgradeInputV2_withZeroSystemConfig_reverts() public { + OPContractsManagerV2.UpgradeInput memory upgradeInput = OPContractsManagerV2.UpgradeInput({ + systemConfig: ISystemConfig(address(0)), + disputeGameConfigs: new OPContractsManagerV2.DisputeGameConfig[](1), + extraInstructions: new IOPContractsManagerUtils.ExtraInstruction[](0) + }); vm.expectRevert("UpgradeOPCMInput: cannot set zero address"); - input.set(input.opcm.selector, address(0)); + input.set(input.upgradeInput.selector, upgradeInput); } - function test_setOpChainConfigs_withEmptyArray_reverts() public { - OPContractsManager.OpChainConfig[] memory emptyConfigs = new OPContractsManager.OpChainConfig[](0); + /// @notice This test verifies that the UpgradeOPChain script correctly reverts when setting the upgrade input with + /// an empty dispute game configs array. + function testFuzz_setUpgradeInputV2_withEmptyDisputeGameConfigs_reverts(address systemConfig) public { + vm.assume(systemConfig != address(0)); - vm.expectRevert("UpgradeOPCMInput: cannot set empty array"); - input.set(input.upgradeInput.selector, emptyConfigs); - } + OPContractsManagerV2.UpgradeInput memory upgradeInput = OPContractsManagerV2.UpgradeInput({ + systemConfig: ISystemConfig(systemConfig), + disputeGameConfigs: new OPContractsManagerV2.DisputeGameConfig[](0), + extraInstructions: new IOPContractsManagerUtils.ExtraInstruction[](0) + }); - function test_set_withInvalidSelector_reverts() public { - vm.expectRevert("UpgradeOPCMInput: unknown selector"); - input.set(bytes4(0xdeadbeef), makeAddr("test")); + vm.expectRevert("UpgradeOPCMInput: cannot set empty dispute game configs array"); + input.set(input.upgradeInput.selector, upgradeInput); + } - // Create a single config for testing invalid selector + /// @notice This test verifies that the UpgradeOPChain script correctly reverts when setting the upgrade input with + /// OPCM v1 input when OPCM v2 is enabled. + function testFuzz_setUpgradeInputV1_onV2OPCM_reverts( + address systemConfigProxy, + bytes32 cannonPrestate, + bytes32 cannonKonaPrestate + ) + public + { + vm.assume(systemConfigProxy != address(0)); + + // Try to set V1 input when V2 is enabled OPContractsManager.OpChainConfig[] memory configs = new OPContractsManager.OpChainConfig[](1); - address mockSystemConfig = makeAddr("systemConfig"); - address mockProxyAdmin = makeAddr("proxyAdmin"); - vm.etch(mockSystemConfig, hex"01"); - vm.etch(mockProxyAdmin, hex"01"); - configs[0] = OPContractsManager.OpChainConfig({ - systemConfigProxy: ISystemConfig(mockSystemConfig), - cannonPrestate: Claim.wrap(bytes32(uint256(1))), - cannonKonaPrestate: Claim.wrap(bytes32(uint256(2))) + systemConfigProxy: ISystemConfig(systemConfigProxy), + cannonPrestate: Claim.wrap(cannonPrestate), + cannonKonaPrestate: Claim.wrap(cannonKonaPrestate) }); - vm.expectRevert("UpgradeOPCMInput: unknown selector"); - input.set(bytes4(0xdeadbeef), configs); + vm.expectRevert("UpgradeOPCMInput: cannot set OPCM v1 upgrade input when OPCM v2 is enabled"); + input.set(input.upgradeInput.selector, configs); } } @@ -226,24 +368,36 @@ contract UpgradeOPChain_Test is Test { address indexed sysCfgProxy, bytes32 indexed absolutePrestate, bytes32 indexed cannonKonaPrestate ); - function setUp() public virtual { + function setUp() public { mockOPCM = new MockOPCMV1(); uoci = new UpgradeOPChainInput(); uoci.set(uoci.opcm.selector, address(mockOPCM)); + prank = makeAddr("prank"); + uoci.set(uoci.prank.selector, prank); + upgradeOPChain = new UpgradeOPChain(); + } + + /// @notice This test verifies that the UpgradeOPChain script correctly encodes and passes down the upgrade input + /// arguments to the OPCM contract's upgrade function. + /// @dev It does not test the actual upgrade functionality. + function testFuzz_upgrade_succeeds( + address systemConfigProxy, + bytes32 cannonPrestate, + bytes32 cannonKonaPrestate + ) + public + { + vm.assume(systemConfigProxy != address(0)); + config = OPContractsManager.OpChainConfig({ - systemConfigProxy: ISystemConfig(makeAddr("systemConfigProxy")), - cannonPrestate: Claim.wrap(keccak256("cannonPrestate")), - cannonKonaPrestate: Claim.wrap(keccak256("cannonKonaPrestate")) + systemConfigProxy: ISystemConfig(systemConfigProxy), + cannonPrestate: Claim.wrap(cannonPrestate), + cannonKonaPrestate: Claim.wrap(cannonKonaPrestate) }); OPContractsManager.OpChainConfig[] memory configs = new OPContractsManager.OpChainConfig[](1); configs[0] = config; uoci.set(uoci.upgradeInput.selector, configs); - prank = makeAddr("prank"); - uoci.set(uoci.prank.selector, prank); - upgradeOPChain = new UpgradeOPChain(); - } - function test_upgrade_succeeds() public { // UpgradeCalled should be emitted by the prank since it's a delegate call. vm.expectEmit(address(prank)); emit UpgradeCalled( @@ -277,12 +431,34 @@ contract UpgradeOPChain_TestV2 is Test { upgradeOPChain = new UpgradeOPChain(); } - function test_upgrade_succeeds() public { + /// @notice This test verifies that the UpgradeOPChain script correctly encodes and passes down the upgrade input + /// arguments to the OPCM contract's upgrade function. + /// @dev It does not test the actual upgrade functionality. + function testFuzz_upgrade_succeeds( + address systemConfig, + bool enabled, + uint256 initBond, + uint32 gameType, + bytes memory gameArgs + ) + public + { + vm.assume(systemConfig != address(0)); + // NOTE: Setting the upgrade input here to avoid `Copying of type struct OPContractsManagerV2.DisputeGameConfig // memory[] memory to storage not yet supported.` error. + OPContractsManagerV2.DisputeGameConfig[] memory disputeGameConfigs = + new OPContractsManagerV2.DisputeGameConfig[](1); + disputeGameConfigs[0] = OPContractsManagerV2.DisputeGameConfig({ + enabled: enabled, + initBond: initBond, + gameType: GameType.wrap(gameType), + gameArgs: gameArgs + }); + OPContractsManagerV2.UpgradeInput memory upgradeInput = OPContractsManagerV2.UpgradeInput({ - systemConfig: ISystemConfig(makeAddr("systemConfig")), - disputeGameConfigs: new OPContractsManagerV2.DisputeGameConfig[](1), + systemConfig: ISystemConfig(systemConfig), + disputeGameConfigs: disputeGameConfigs, extraInstructions: new IOPContractsManagerUtils.ExtraInstruction[](0) }); uoci.set(uoci.upgradeInput.selector, upgradeInput);