From 432cfba4392734206b44c1efffd482d324023f01 Mon Sep 17 00:00:00 2001 From: Kelvin Fichter Date: Tue, 3 Feb 2026 13:10:01 -0500 Subject: [PATCH 1/3] fix: SuperchainConfig comments and test refactoring - Remove outdated warning comment from SuperchainConfig - Bump SuperchainConfig version to 2.4.1 - Fix comment in OPContractsManagerV2 - Refactor test helpers: move NeedsSuperchainConfigUpgrade to testutil package - Update add-game-type-v2 tests to deploy a full OP chain Co-Authored-By: Claude Opus 4.5 --- .../deployer/integration_test/apply_test.go | 51 +------ .../cli/manage_add_game_type_v2_test.go | 130 +++++++++--------- .../deployer/testutil/superchain_config.go | 50 +++++++ .../snapshots/semver-lock.json | 6 +- .../src/L1/SuperchainConfig.sol | 7 +- .../src/L1/opcm/OPContractsManagerV2.sol | 2 +- 6 files changed, 127 insertions(+), 119 deletions(-) create mode 100644 op-deployer/pkg/deployer/testutil/superchain_config.go diff --git a/op-deployer/pkg/deployer/integration_test/apply_test.go b/op-deployer/pkg/deployer/integration_test/apply_test.go index b24b661beac89..1f99156f5714d 100644 --- a/op-deployer/pkg/deployer/integration_test/apply_test.go +++ b/op-deployer/pkg/deployer/integration_test/apply_test.go @@ -5,14 +5,12 @@ import ( "context" "encoding/hex" "encoding/json" - "fmt" "log/slog" "math/big" "strings" "testing" "time" - "github.com/Masterminds/semver/v3" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/bootstrap" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/broadcaster" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/inspect" @@ -36,7 +34,6 @@ import ( "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/standard" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/state" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/testutil" - opbindings "github.com/ethereum-optimism/optimism/op-e2e/bindings" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/rpc" @@ -51,7 +48,6 @@ import ( "github.com/ethereum-optimism/optimism/op-chain-ops/genesis" "github.com/ethereum-optimism/optimism/op-core/predeploys" "github.com/ethereum-optimism/optimism/op-service/testlog" - "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/ethclient" @@ -818,7 +814,7 @@ func runEndToEndBootstrapAndApplyUpgradeTest(t *testing.T, afactsFS foundry.Stat require.NoError(t, err) defer versionClient.Close() - shouldUpgradeSuperchainConfig, err := needsSuperchainConfigUpgrade( + shouldUpgradeSuperchainConfig, err := testutil.NeedsSuperchainConfigUpgrade( ctx, versionClient, implementationsConfig.SuperchainConfigProxy, @@ -842,13 +838,18 @@ func runEndToEndBootstrapAndApplyUpgradeTest(t *testing.T, afactsFS foundry.Stat ) require.NoError(t, err) + opcmAddress := impls.Opcm + if deployer.IsDevFeatureEnabled(implementationsConfig.DevFeatureBitmap, deployer.OPCMV2DevFlag) { + opcmAddress = impls.OpcmV2 + } + // Only run the superchain config upgrade if the live superchain config is behind the freshly deployed // implementation. Running the script when versions match will revert and panic the test harness. if shouldUpgradeSuperchainConfig { t.Run("upgrade superchain config", func(t *testing.T) { upgradeConfig := embedded.UpgradeSuperchainConfigInput{ Prank: superchainProxyAdminOwner, - Opcm: impls.Opcm, + Opcm: opcmAddress, SuperchainConfig: implementationsConfig.SuperchainConfigProxy, } @@ -1051,44 +1052,6 @@ func runEndToEndBootstrapAndApplyUpgradeTest(t *testing.T, afactsFS foundry.Stat }) } -func needsSuperchainConfigUpgrade( - ctx context.Context, - client *ethclient.Client, - currentProxy, targetImpl common.Address, -) (bool, error) { - currentVersion, err := superchainConfigVersion(ctx, client, currentProxy) - if err != nil { - return false, fmt.Errorf("failed to fetch proxy superchain config version: %w", err) - } - - targetVersion, err := superchainConfigVersion(ctx, client, targetImpl) - if err != nil { - return false, fmt.Errorf("failed to fetch implementation superchain config version: %w", err) - } - - return currentVersion.LessThan(targetVersion), nil -} - -func superchainConfigVersion( - ctx context.Context, - client *ethclient.Client, - addr common.Address, -) (*semver.Version, error) { - contract, err := opbindings.NewSuperchainConfig(addr, client) - if err != nil { - return nil, fmt.Errorf("failed to bind superchain config at %s: %w", addr.Hex(), err) - } - versionStr, err := contract.Version(&bind.CallOpts{Context: ctx}) - if err != nil { - return nil, fmt.Errorf("failed to read version from %s: %w", addr.Hex(), err) - } - version, err := semver.NewVersion(versionStr) - if err != nil { - return nil, fmt.Errorf("failed to parse version %q from %s: %w", versionStr, addr.Hex(), err) - } - return version, nil -} - func setupGenesisChain(t *testing.T, l1ChainID uint64) (deployer.ApplyPipelineOpts, *state.Intent, *state.State) { lgr := testlog.Logger(t, slog.LevelDebug) diff --git a/op-deployer/pkg/deployer/integration_test/cli/manage_add_game_type_v2_test.go b/op-deployer/pkg/deployer/integration_test/cli/manage_add_game_type_v2_test.go index ae2ca691f2fc6..67e530443e715 100644 --- a/op-deployer/pkg/deployer/integration_test/cli/manage_add_game_type_v2_test.go +++ b/op-deployer/pkg/deployer/integration_test/cli/manage_add_game_type_v2_test.go @@ -12,19 +12,20 @@ import ( "time" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer" - "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/artifacts" - "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/bootstrap" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/broadcaster" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/integration_test/shared" - "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/standard" + "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/pipeline" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/testutil" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/upgrade/embedded" "github.com/ethereum-optimism/optimism/op-service/testlog" "github.com/ethereum-optimism/optimism/op-service/testutils" "github.com/ethereum-optimism/optimism/op-service/testutils/devnet" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/crypto" + "github.com/holiman/uint256" "github.com/stretchr/testify/require" + + "github.com/ethereum-optimism/optimism/op-chain-ops/devkeys" ) func TestManageAddGameTypeV2_CLI(t *testing.T) { @@ -109,8 +110,6 @@ func TestManageAddGameTypeV2_CLI(t *testing.T) { // Tests the manage add-game-type-v2 command, from the CLI to the actual contract execution through the Solidity scripts. func TestManageAddGameTypeV2_Integration(t *testing.T) { - // TODO(#18718): Update this to use an actual deployed OPCM V2 contract once we have one. - // For now, we manually deploy the OPCM V2 contract using bootstrap.Implementations. lgr := testlog.Logger(t, slog.LevelDebug) l1Rpc, stopL1, err := devnet.NewForkedSepolia(lgr) @@ -121,17 +120,12 @@ func TestManageAddGameTypeV2_Integration(t *testing.T) { runner := NewCLITestRunnerWithNetwork(t, WithL1RPC(l1Rpc.RPCUrl())) workDir := runner.GetWorkDir() - // Test values - using arbitrary addresses for testing - l1ProxyAdminOwner := deployer.DefaultL1ProxyAdminOwnerSepolia - systemConfigProxy := deployer.DefaultSystemConfigProxySepolia - - // Deploy the OPCM V2 contract. - opcmV2 := deployDependencies(t, runner) + // We deploy superchain, OPCM V2, and a fresh OP chain. + deployed := deployDependencies(t, runner) - // Run past upgrades before testing the V2 command. - // This is necessary when forking a network at a block before certain upgrades were executed. - _, afactsFS := testutil.LocalArtifacts(t) - shared.RunPastUpgradesWithRPC(t, runner.l1RPC, afactsFS, lgr, 11155111, l1ProxyAdminOwner, systemConfigProxy) + l1ProxyAdminOwner := deployed.proxyAdminOwner + systemConfigProxy := deployed.systemConfigProxy + opcmV2 := deployed.opcmV2 // FaultDisputeGameConfig just needs absolutePrestate (bytes32) testPrestate := common.Hash{'P', 'R', 'E', 'S', 'T', 'A', 'T', 'E'} @@ -247,64 +241,68 @@ func TestManageAddGameTypeV2_Integration(t *testing.T) { require.Equal(t, l1ProxyAdminOwner.Hex(), dump[0].To.Hex(), "calldata should be sent to prank address") } -// TODO(#18718): Remove this once we have a deployed OPCM V2 contract. -// deployDependencies deploys the superchain contracts and OPCM V2 implementation -// using the DeployImplementations script, and returns the OPCM V2 address -func deployDependencies(t *testing.T, runner *CLITestRunner) common.Address { +// deployedChain holds the addresses returned from deploying a fresh OP chain +type deployedChain struct { + opcmV2 common.Address + systemConfigProxy common.Address + proxyAdminOwner common.Address +} + +// deployDependencies deploys superchain, OPCM V2, and a fresh OP chain using ApplyPipeline. +// Returns addresses needed for testing the add-game-type-v2 command. +func deployDependencies(t *testing.T, runner *CLITestRunner) deployedChain { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute) defer cancel() testCacheDir := testutils.IsolatedTestDirWithAutoCleanup(t) - // First, deploy superchain contracts (required for OPCM deployment) - superchainProxyAdminOwner := common.Address{'S'} - superchainOut, err := bootstrap.Superchain(ctx, bootstrap.SuperchainConfig{ - L1RPCUrl: runner.l1RPC, - PrivateKey: runner.privateKeyHex, - ArtifactsLocator: artifacts.EmbeddedLocator, - Logger: runner.lgr, - SuperchainProxyAdminOwner: superchainProxyAdminOwner, - ProtocolVersionsOwner: common.Address{'P'}, - Guardian: common.Address{'G'}, - Paused: false, - RequiredProtocolVersion: params.ProtocolVersionV0{Major: 1}.Encode(), - RecommendedProtocolVersion: params.ProtocolVersionV0{Major: 2}.Encode(), - CacheDir: testCacheDir, - }) - require.NoError(t, err, "Failed to deploy superchain contracts") - - // Deploy implementations with OPCM V2 enabled - implOut, err := bootstrap.Implementations(ctx, bootstrap.ImplementationsConfig{ - L1RPCUrl: runner.l1RPC, - PrivateKey: runner.privateKeyHex, - ArtifactsLocator: artifacts.EmbeddedLocator, - Logger: runner.lgr, - WithdrawalDelaySeconds: standard.WithdrawalDelaySeconds, - MinProposalSizeBytes: standard.MinProposalSizeBytes, - ChallengePeriodSeconds: standard.ChallengePeriodSeconds, - ProofMaturityDelaySeconds: standard.ProofMaturityDelaySeconds, - DisputeGameFinalityDelaySeconds: standard.DisputeGameFinalityDelaySeconds, - MIPSVersion: int(standard.MIPSVersion), - DevFeatureBitmap: deployer.OPCMV2DevFlag, // Enable OPCM V2 - SuperchainConfigProxy: superchainOut.SuperchainConfigProxy, - ProtocolVersionsProxy: superchainOut.ProtocolVersionsProxy, - SuperchainProxyAdmin: superchainOut.SuperchainProxyAdmin, - L1ProxyAdminOwner: superchainProxyAdminOwner, - Challenger: common.Address{'C'}, - CacheDir: testCacheDir, - FaultGameMaxGameDepth: standard.DisputeMaxGameDepth, - FaultGameSplitDepth: standard.DisputeSplitDepth, - FaultGameClockExtension: standard.DisputeClockExtension, - FaultGameMaxClockDuration: standard.DisputeMaxClockDuration, + // Get the private key and devkeys + pk, err := crypto.HexToECDSA(runner.privateKeyHex) + require.NoError(t, err) + + dk, err := devkeys.NewMnemonicDevKeys(devkeys.TestMnemonic) + require.NoError(t, err) + + l1ChainID := big.NewInt(11155111) + + // We use the shared helper to create an intent and state + loc, _ := testutil.LocalArtifacts(t) + l2ChainID := uint256.NewInt(12345) // Test L2 chain ID + + intent, st := shared.NewIntent(t, l1ChainID, dk, l2ChainID, loc, loc, 30_000_000) + + // Ensure we are using OPCM V2 + intent.GlobalDeployOverrides = map[string]any{ + "devFeatureBitmap": deployer.OPCMV2DevFlag, + } + + // Deploy using ApplyPipeline with live target + err = deployer.ApplyPipeline(ctx, deployer.ApplyPipelineOpts{ + DeploymentTarget: deployer.DeploymentTargetLive, + L1RPCUrl: runner.l1RPC, + DeployerPrivateKey: pk, + Intent: intent, + State: st, + Logger: runner.lgr, + StateWriter: pipeline.NoopStateWriter(), + CacheDir: testCacheDir, }) - require.NoError(t, err, "Failed to deploy implementations") + require.NoError(t, err, "Failed to deploy OP chain") // Verify OPCM V2 was deployed - require.NotEqual(t, common.Address{}, implOut.OpcmV2, "OPCM V2 address should be set") - require.Equal(t, common.Address{}, implOut.Opcm, "OPCM V1 address should be zero when V2 is deployed") + require.NotEqual(t, common.Address{}, st.ImplementationsDeployment.OpcmV2Impl, "OPCM V2 address should be set") - t.Logf("Deployed OPCM V2 at address: %s", implOut.OpcmV2.Hex()) - t.Logf("SuperchainConfigProxy: %s", superchainOut.SuperchainConfigProxy.Hex()) + // Get the chain state + require.Len(t, st.Chains, 1, "Expected one chain to be deployed") + chainState := st.Chains[0] - return implOut.OpcmV2 + t.Logf("Deployed OPCM V2 at address: %s", st.ImplementationsDeployment.OpcmV2Impl.Hex()) + t.Logf("Deployed SystemConfigProxy at address: %s", chainState.SystemConfigProxy.Hex()) + t.Logf("ProxyAdminOwner: %s", intent.Chains[0].Roles.L1ProxyAdminOwner.Hex()) + + return deployedChain{ + opcmV2: st.ImplementationsDeployment.OpcmV2Impl, + systemConfigProxy: chainState.SystemConfigProxy, + proxyAdminOwner: intent.Chains[0].Roles.L1ProxyAdminOwner, + } } diff --git a/op-deployer/pkg/deployer/testutil/superchain_config.go b/op-deployer/pkg/deployer/testutil/superchain_config.go new file mode 100644 index 0000000000000..7e979ba649e2c --- /dev/null +++ b/op-deployer/pkg/deployer/testutil/superchain_config.go @@ -0,0 +1,50 @@ +package testutil + +import ( + "context" + "fmt" + + "github.com/Masterminds/semver/v3" + opbindings "github.com/ethereum-optimism/optimism/op-e2e/bindings" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/ethclient" +) + +func NeedsSuperchainConfigUpgrade( + ctx context.Context, + client *ethclient.Client, + currentProxy, targetImpl common.Address, +) (bool, error) { + currentVersion, err := superchainConfigVersion(ctx, client, currentProxy) + if err != nil { + return false, fmt.Errorf("failed to fetch proxy superchain config version: %w", err) + } + + targetVersion, err := superchainConfigVersion(ctx, client, targetImpl) + if err != nil { + return false, fmt.Errorf("failed to fetch implementation superchain config version: %w", err) + } + + return currentVersion.LessThan(targetVersion), nil +} + +func superchainConfigVersion( + ctx context.Context, + client *ethclient.Client, + addr common.Address, +) (*semver.Version, error) { + contract, err := opbindings.NewSuperchainConfig(addr, client) + if err != nil { + return nil, fmt.Errorf("failed to bind superchain config at %s: %w", addr.Hex(), err) + } + versionStr, err := contract.Version(&bind.CallOpts{Context: ctx}) + if err != nil { + return nil, fmt.Errorf("failed to read version from %s: %w", addr.Hex(), err) + } + version, err := semver.NewVersion(versionStr) + if err != nil { + return nil, fmt.Errorf("failed to parse version %q from %s: %w", versionStr, addr.Hex(), err) + } + return version, nil +} diff --git a/packages/contracts-bedrock/snapshots/semver-lock.json b/packages/contracts-bedrock/snapshots/semver-lock.json index 4f44d9eb50c28..0b305383d6ce0 100644 --- a/packages/contracts-bedrock/snapshots/semver-lock.json +++ b/packages/contracts-bedrock/snapshots/semver-lock.json @@ -44,8 +44,8 @@ "sourceCodeHash": "0x3b7b7a1023e6e87ce4680eee3cc4eebefc15b5ec80db3d39e824fbdd521762db" }, "src/L1/SuperchainConfig.sol:SuperchainConfig": { - "initCodeHash": "0xfb8c98028f1a0e70bb1afbbc532035ea71b0724883554eeaae62e1910a6c1cd9", - "sourceCodeHash": "0xbf344c4369b8cb00ec7a3108f72795747f3bc59ab5b37ac18cf21e72e2979dbf" + "initCodeHash": "0x7184a7d4ffb0e4624c8699d2573fa05eae028ca311e2f5bdd901e409cf6305ec", + "sourceCodeHash": "0x65c3a0c5d721408a6c8d66b817b4881820ea4119d6452796725fbf92ad9e3c8f" }, "src/L1/SystemConfig.sol:SystemConfig": { "initCodeHash": "0xd4ec112de4cf7173668374479b7405bab9c828e5b32c946ef8ab5cd021f9703b", @@ -53,7 +53,7 @@ }, "src/L1/opcm/OPContractsManagerV2.sol:OPContractsManagerV2": { "initCodeHash": "0x5f3548d6d5502669d34ff3104826d8498c3f74be2f6840a6acb9860e266d96a8", - "sourceCodeHash": "0xf7c02dec35e9c34e7e3e8f1fe939f7b84243064b423e38ba82fb06e389732cc7" + "sourceCodeHash": "0xe7c89192ae7881516997bdbcc952005d39d332f2144978fa2e64e944f256f026" }, "src/L2/BaseFeeVault.sol:BaseFeeVault": { "initCodeHash": "0x838bbd7f381e84e21887f72bd1da605bfc4588b3c39aed96cbce67c09335b3ee", diff --git a/packages/contracts-bedrock/src/L1/SuperchainConfig.sol b/packages/contracts-bedrock/src/L1/SuperchainConfig.sol index c0157fef35cfa..99c7c0dedd5c9 100644 --- a/packages/contracts-bedrock/src/L1/SuperchainConfig.sol +++ b/packages/contracts-bedrock/src/L1/SuperchainConfig.sol @@ -13,9 +13,6 @@ import { ISemver } from "interfaces/universal/ISemver.sol"; /// @custom:audit none This contracts is not yet audited. /// @title SuperchainConfig /// @notice The SuperchainConfig contract is used to manage configuration of global superchain values. -/// @dev WARNING: When upgrading this contract, any active pause states will be lost as the pause state -/// is stored in storage variables that are not preserved during upgrades. Therefore, this contract -/// should not be upgraded while the system is paused. contract SuperchainConfig is ProxyAdminOwnedBase, Initializable, ReinitializableBase, ISemver { /// @notice Thrown when a caller is not the guardian but tries to call a guardian-only function error SuperchainConfig_OnlyGuardian(); @@ -56,8 +53,8 @@ contract SuperchainConfig is ProxyAdminOwnedBase, Initializable, Reinitializable event ConfigUpdate(UpdateType indexed updateType, bytes data); /// @notice Semantic version. - /// @custom:semver 2.4.0 - string public constant version = "2.4.0"; + /// @custom:semver 2.4.1 + string public constant version = "2.4.1"; /// @notice Constructs the SuperchainConfig contract. constructor() ReinitializableBase(2) { diff --git a/packages/contracts-bedrock/src/L1/opcm/OPContractsManagerV2.sol b/packages/contracts-bedrock/src/L1/opcm/OPContractsManagerV2.sol index 24ac4a38b2b32..7fad38262fedc 100644 --- a/packages/contracts-bedrock/src/L1/opcm/OPContractsManagerV2.sol +++ b/packages/contracts-bedrock/src/L1/opcm/OPContractsManagerV2.sol @@ -181,7 +181,7 @@ contract OPContractsManagerV2 is ISemver, OPContractsManagerUtilsCaller { // If we expand the scope of this function to add other Superchain-wide contracts, we'll // probably want to start following a similar pattern to the chain upgrade flow. - // Upgrade the SuperchainConfig if it has changed. + // Upgrade the SuperchainConfig. _upgrade( IProxyAdmin(_inp.superchainConfig.proxyAdmin()), address(_inp.superchainConfig), From 6aa7d8ac06a252c5c0cfc0d0c46382472a621178 Mon Sep 17 00:00:00 2001 From: Kelvin Fichter Date: Tue, 3 Feb 2026 13:22:39 -0500 Subject: [PATCH 2/3] fix: semver lock --- packages/contracts-bedrock/snapshots/semver-lock.json | 4 ++-- .../contracts-bedrock/src/L1/opcm/OPContractsManagerV2.sol | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/contracts-bedrock/snapshots/semver-lock.json b/packages/contracts-bedrock/snapshots/semver-lock.json index 0b305383d6ce0..a66ada9df9502 100644 --- a/packages/contracts-bedrock/snapshots/semver-lock.json +++ b/packages/contracts-bedrock/snapshots/semver-lock.json @@ -52,8 +52,8 @@ "sourceCodeHash": "0xb3184aa5d95a82109e7134d1f61941b30e25f655b9849a0e303d04bbce0cde0b" }, "src/L1/opcm/OPContractsManagerV2.sol:OPContractsManagerV2": { - "initCodeHash": "0x5f3548d6d5502669d34ff3104826d8498c3f74be2f6840a6acb9860e266d96a8", - "sourceCodeHash": "0xe7c89192ae7881516997bdbcc952005d39d332f2144978fa2e64e944f256f026" + "initCodeHash": "0xe9c4ba721d8bd3390b184e243d598a522b7efe8b7426ec4d0898529c5654e705", + "sourceCodeHash": "0x3898bab02581ae16ac529358dab8e80477fa34f1354ce8783cef3d36354d1810" }, "src/L2/BaseFeeVault.sol:BaseFeeVault": { "initCodeHash": "0x838bbd7f381e84e21887f72bd1da605bfc4588b3c39aed96cbce67c09335b3ee", diff --git a/packages/contracts-bedrock/src/L1/opcm/OPContractsManagerV2.sol b/packages/contracts-bedrock/src/L1/opcm/OPContractsManagerV2.sol index 7fad38262fedc..478e2822525d5 100644 --- a/packages/contracts-bedrock/src/L1/opcm/OPContractsManagerV2.sol +++ b/packages/contracts-bedrock/src/L1/opcm/OPContractsManagerV2.sol @@ -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.6 + /// @custom:semver 7.0.7 function version() public pure returns (string memory) { - return "7.0.6"; + return "7.0.7"; } /// @param _standardValidator The standard validator for this OPCM release. From 8982b2e4630892e47c6228df9ce87aade6a41cf2 Mon Sep 17 00:00:00 2001 From: Kelvin Fichter Date: Tue, 3 Feb 2026 17:17:49 -0500 Subject: [PATCH 3/3] fix: broken test when applying superchain config upgrade --- .../integration_test/shared/shared.go | 6 +- .../pkg/deployer/manage/migrate_test.go | 223 +++++------------- 2 files changed, 67 insertions(+), 162 deletions(-) diff --git a/op-deployer/pkg/deployer/integration_test/shared/shared.go b/op-deployer/pkg/deployer/integration_test/shared/shared.go index 0987dc34107d9..1c3803ea95131 100644 --- a/op-deployer/pkg/deployer/integration_test/shared/shared.go +++ b/op-deployer/pkg/deployer/integration_test/shared/shared.go @@ -299,8 +299,8 @@ func buildV2OPCMUpgradeConfig(t *testing.T, prank, opcmAddr, systemConfigProxy c } } -// deployDummyCaller deploys DummyCaller at the prank address with the given OPCM address. -func deployDummyCaller(t *testing.T, rpcClient *rpc.Client, afactsFS foundry.StatDirFs, prank, opcmAddr common.Address) { +// DeployDummyCaller deploys DummyCaller at the prank address with the given OPCM address. +func DeployDummyCaller(t *testing.T, rpcClient *rpc.Client, afactsFS foundry.StatDirFs, prank, opcmAddr common.Address) { t.Helper() artifacts := &foundry.ArtifactsFS{FS: afactsFS} @@ -429,7 +429,7 @@ func RunPastUpgradesWithRPC(t *testing.T, l1RPCUrl string, afactsFS foundry.Stat // Process each OPCM upgrade: deploy DummyCaller with correct OPCM, run upgrade, broadcast for _, opcm := range toApply { // Deploy DummyCaller with this OPCM's address - deployDummyCaller(t, rpcClient, afactsFS, prank, opcm.Address) + DeployDummyCaller(t, rpcClient, afactsFS, prank, opcm.Address) // Create fresh broadcaster and host for this upgrade bcaster := NewImpersonationBroadcaster(lgr, ethClient, rpcClient, prank, networkChainID) diff --git a/op-deployer/pkg/deployer/manage/migrate_test.go b/op-deployer/pkg/deployer/manage/migrate_test.go index b6fab04f5fe8a..c540e6f2febc8 100644 --- a/op-deployer/pkg/deployer/manage/migrate_test.go +++ b/op-deployer/pkg/deployer/manage/migrate_test.go @@ -3,7 +3,6 @@ package manage import ( "context" "encoding/hex" - "encoding/json" "flag" "fmt" "log/slog" @@ -11,23 +10,20 @@ import ( "testing" "time" - "github.com/ethereum-optimism/optimism/op-chain-ops/script" + "github.com/ethereum-optimism/optimism/op-chain-ops/devkeys" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer" - "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/artifacts" - "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/bootstrap" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/broadcaster" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/integration_test/shared" - "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/standard" + "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/pipeline" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/testutil" - "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/upgrade/embedded" "github.com/ethereum-optimism/optimism/op-deployer/pkg/env" "github.com/ethereum-optimism/optimism/op-service/testlog" "github.com/ethereum-optimism/optimism/op-service/testutils" "github.com/ethereum-optimism/optimism/op-service/testutils/devnet" "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rpc" + "github.com/holiman/uint256" "github.com/stretchr/testify/require" "github.com/urfave/cli/v2" ) @@ -42,58 +38,16 @@ func TestInteropMigration(t *testing.T) { }) l1RPC := forkedL1.RPCUrl() - _, afactsFS := testutil.LocalArtifacts(t) + loc, afactsFS := testutil.LocalArtifacts(t) testCacheDir := testutils.IsolatedTestDirWithAutoCleanup(t) ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute) defer cancel() - pkHex, _, _ := shared.DefaultPrivkey(t) - - // Deploy superchain contracts first (required for OPCM deployment) - superchainProxyAdminOwner := common.Address{'S'} - superchainOut, err := bootstrap.Superchain(ctx, bootstrap.SuperchainConfig{ - L1RPCUrl: l1RPC, - PrivateKey: pkHex, - ArtifactsLocator: artifacts.EmbeddedLocator, - Logger: lgr, - SuperchainProxyAdminOwner: superchainProxyAdminOwner, - ProtocolVersionsOwner: common.Address{'P'}, - Guardian: common.Address{'G'}, - Paused: false, - RequiredProtocolVersion: params.ProtocolVersionV0{Major: 1}.Encode(), - RecommendedProtocolVersion: params.ProtocolVersionV0{Major: 2}.Encode(), - CacheDir: testCacheDir, - }) - require.NoError(t, err, "Failed to deploy superchain contracts") - - // Use a test SystemConfigProxy address - systemConfigProxy := common.HexToAddress("0x034edD2A225f7f429A63E0f1D2084B9E0A93b538") - l1ProxyAdminOwner := common.HexToAddress("0x1Eb2fFc903729a0F03966B917003800b145F56E2") - - cfg := bootstrap.ImplementationsConfig{ - L1RPCUrl: l1RPC, - PrivateKey: pkHex, - ArtifactsLocator: artifacts.EmbeddedLocator, - Logger: lgr, - MIPSVersion: int(standard.MIPSVersion), - WithdrawalDelaySeconds: standard.WithdrawalDelaySeconds, - MinProposalSizeBytes: standard.MinProposalSizeBytes, - ChallengePeriodSeconds: standard.ChallengePeriodSeconds, - ProofMaturityDelaySeconds: standard.ProofMaturityDelaySeconds, - DisputeGameFinalityDelaySeconds: standard.DisputeGameFinalityDelaySeconds, - DevFeatureBitmap: common.Hash{}, - SuperchainConfigProxy: superchainOut.SuperchainConfigProxy, - ProtocolVersionsProxy: superchainOut.ProtocolVersionsProxy, - SuperchainProxyAdmin: superchainOut.SuperchainProxyAdmin, - L1ProxyAdminOwner: superchainProxyAdminOwner, - Challenger: common.Address{'C'}, - CacheDir: testCacheDir, - FaultGameMaxGameDepth: standard.DisputeMaxGameDepth, - FaultGameSplitDepth: standard.DisputeSplitDepth, - FaultGameClockExtension: standard.DisputeClockExtension, - FaultGameMaxClockDuration: standard.DisputeMaxClockDuration, - } + _, pk, dk := shared.DefaultPrivkey(t) + + l1ChainID := big.NewInt(11155111) // Sepolia + l2ChainID := uint256.NewInt(12345) tests := []struct { name string @@ -105,41 +59,71 @@ func TestInteropMigration(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - // Deploy implementations with the specified dev feature - if tt.devFeature == (common.Hash{}) { - cfg.DevFeatureBitmap = deployer.EnableDevFeature(common.Hash{}, deployer.OptimismPortalInteropDevFlag) - } else { - cfg.DevFeatureBitmap = deployer.EnableDevFeature(tt.devFeature, deployer.OptimismPortalInteropDevFlag) + // Deploy a complete chain using ApplyPipeline - this ensures all addresses are properly connected + intent, st := shared.NewIntent(t, l1ChainID, dk, l2ChainID, loc, loc, 30_000_000) + + // Set dev features for this test + devBitmap := deployer.EnableDevFeature(tt.devFeature, deployer.OptimismPortalInteropDevFlag) + intent.GlobalDeployOverrides = map[string]any{ + "devFeatureBitmap": devBitmap, } - impls, err := bootstrap.Implementations(ctx, cfg) - require.NoError(t, err, "Failed to deploy implementations") + err := deployer.ApplyPipeline(ctx, deployer.ApplyPipelineOpts{ + DeploymentTarget: deployer.DeploymentTargetLive, + L1RPCUrl: l1RPC, + DeployerPrivateKey: pk, + Intent: intent, + State: st, + Logger: lgr, + StateWriter: pipeline.NoopStateWriter(), + CacheDir: testCacheDir, + }) + require.NoError(t, err, "Failed to deploy chain") + + // Get addresses from the deployed state + require.Len(t, st.Chains, 1, "Expected one chain to be deployed") + chainState := st.Chains[0] + systemConfigProxy := chainState.SystemConfigProxy + + // Get the L1ProxyAdminOwner from the intent + l1ProxyAdminOwner := intent.Chains[0].Roles.L1ProxyAdminOwner + + t.Logf("SystemConfigProxy: %s", systemConfigProxy.Hex()) + t.Logf("L1ProxyAdminOwner: %s", l1ProxyAdminOwner.Hex()) rpcClient, err := rpc.Dial(l1RPC) require.NoError(t, err) + var opcmAddr common.Address + if deployer.IsDevFeatureEnabled(tt.devFeature, deployer.OPCMV2DevFlag) { + require.NotEqual(t, common.Address{}, st.ImplementationsDeployment.OpcmV2Impl, "OPCM V2 address should be set") + opcmAddr = st.ImplementationsDeployment.OpcmV2Impl + t.Logf("OPCM V2: %s", opcmAddr.Hex()) + } else { + require.NotEqual(t, common.Address{}, st.ImplementationsDeployment.OpcmImpl, "OPCM V1 address should be set") + opcmAddr = st.ImplementationsDeployment.OpcmImpl + t.Logf("OPCM V1: %s", opcmAddr.Hex()) + } + + // Deploy DummyCaller at l1ProxyAdminOwner for the OPCM + shared.DeployDummyCaller(t, rpcClient, afactsFS, l1ProxyAdminOwner, opcmAddr) + bcast := new(broadcaster.CalldataBroadcaster) host, err := env.DefaultForkedScriptHost( ctx, bcast, lgr, - superchainProxyAdminOwner, + l1ProxyAdminOwner, afactsFS, rpcClient, ) require.NoError(t, err) var input InteropMigrationInput - var opcmAddr common.Address if deployer.IsDevFeatureEnabled(tt.devFeature, deployer.OPCMV2DevFlag) { // OPCM V2 path - require.NotEqual(t, common.Address{}, impls.OpcmV2, "OPCM V2 address should be set") - require.Equal(t, common.Address{}, impls.Opcm, "OPCM V1 address should be zero when V2 is deployed") - opcmAddr = impls.OpcmV2 - - // Upgrade the portal to OptimismPortalInterop - upgradeChainV2(t, host, l1ProxyAdminOwner, systemConfigProxy, impls.OpcmV2) + // Note: No need to call upgradeChainV2 since ApplyPipeline already deploys a fully initialized chain // Prepare game args for V2 - ABI encode the prestate bytes32Type, err := abi.NewType("bytes32", "", nil) @@ -178,12 +162,13 @@ func TestInteropMigration(t *testing.T) { } } else { // OPCM V1 path - require.NotEqual(t, common.Address{}, impls.Opcm, "OPCM V1 address should be set") - require.Equal(t, common.Address{}, impls.OpcmV2, "OPCM V2 address should be zero when V1 is deployed") - opcmAddr = impls.Opcm + // Note: No need to call upgradeChainV1 since ApplyPipeline already deploys a fully initialized chain - // Upgrade the portal to OptimismPortalInterop - upgradeChainV1(t, host, l1ProxyAdminOwner, systemConfigProxy, impls.Opcm) + // Get proposer and challenger from devkeys + proposer, err := dk.Address(devkeys.ProposerRole.Key(l1ChainID)) + require.NoError(t, err) + challenger, err := dk.Address(devkeys.ChallengerRole.Key(l1ChainID)) + require.NoError(t, err) input = InteropMigrationInput{ Prank: l1ProxyAdminOwner, @@ -195,8 +180,8 @@ func TestInteropMigration(t *testing.T) { L2SequenceNumber: big.NewInt(1), }, GameParameters: GameParameters{ - Proposer: common.Address{'A'}, - Challenger: common.Address{'B'}, + Proposer: proposer, + Challenger: challenger, MaxGameDepth: 73, SplitDepth: 30, InitBond: big.NewInt(1000000000000000000), // 1 ETH @@ -221,9 +206,9 @@ func TestInteropMigration(t *testing.T) { dump, err := bcast.Dump() require.NoError(t, err) - require.Len(t, dump, 2, "Should have two transactions") - require.True(t, dump[1].Value.ToInt().Cmp(common.Big0) == 0, "Transaction value should be zero") - require.Equal(t, l1ProxyAdminOwner, *dump[1].To, "Transaction should be sent to prank address") + require.Len(t, dump, 1, "Should have one transaction (migration)") + require.True(t, dump[0].Value.ToInt().Cmp(common.Big0) == 0, "Transaction value should be zero") + require.Equal(t, l1ProxyAdminOwner, *dump[0].To, "Transaction should be sent to prank address") }) } } @@ -535,83 +520,3 @@ func TestEncodedMigrateInputV2(t *testing.T) { require.Equal(t, expected, hex.EncodeToString(data)) } - -// upgradeChainV1 upgrades a chain via OPCM V1 using ChainConfigs array. -func upgradeChainV1(t *testing.T, host *script.Host, proxyAdminOwner common.Address, systemConfigProxy common.Address, opcm common.Address) { - testPrestate := common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000abc") - testKonaPrestate := common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000fed") - - upgradeConfig := embedded.UpgradeOPChainInput{ - Prank: proxyAdminOwner, - Opcm: opcm, - ChainConfigs: []embedded.OPChainConfig{ - { - SystemConfigProxy: systemConfigProxy, - CannonPrestate: testPrestate, - CannonKonaPrestate: testKonaPrestate, - }, - }, - } - - upgradeConfigBytes, err := json.Marshal(upgradeConfig) - require.NoError(t, err, "UpgradeOPChainInput should marshal to JSON") - err = embedded.DefaultUpgrader.Upgrade(host, upgradeConfigBytes) - require.NoError(t, err, "OPCM V1 chain upgrade should succeed") -} - -// Upgrades a chain via OPCM V2 to ensure the OptimismPortal is upgraded to OptimismPortalInterop. -func upgradeChainV2(t *testing.T, host *script.Host, proxyAdminOwner common.Address, systemConfigProxy common.Address, opcm common.Address) { - // ABI-encode game args for FaultDisputeGameConfig{absolutePrestate} - - // FaultDisputeGameConfig just needs absolutePrestate (bytes32) - testPrestate := common.Hash{'P', 'R', 'E', 'S', 'T', 'A', 'T', 'E'} - - // PermissionedDisputeGameConfig needs absolutePrestate, proposer, challenger - testProposer := common.Address{'P'} - testChallenger := common.Address{'C'} - - upgradeConfig := embedded.UpgradeOPChainInput{ - Prank: proxyAdminOwner, - Opcm: opcm, - UpgradeInputV2: &embedded.UpgradeInputV2{ - SystemConfig: systemConfigProxy, - DisputeGameConfigs: []embedded.DisputeGameConfig{ - { - Enabled: true, - InitBond: big.NewInt(1000000000000000000), - GameType: embedded.GameTypeCannon, - FaultDisputeGameConfig: &embedded.FaultDisputeGameConfig{ - AbsolutePrestate: testPrestate, - }, - }, - { - Enabled: true, - InitBond: big.NewInt(1000000000000000000), - GameType: embedded.GameTypePermissionedCannon, - PermissionedDisputeGameConfig: &embedded.PermissionedDisputeGameConfig{ - AbsolutePrestate: testPrestate, - Proposer: testProposer, - Challenger: testChallenger, - }, - }, - { - Enabled: false, - InitBond: big.NewInt(0), - GameType: embedded.GameTypeCannonKona, - // Disabled games don't need args - }, - }, - ExtraInstructions: []embedded.ExtraInstruction{ - { - Key: "PermittedProxyDeployment", - Data: []byte("DelayedWETH"), - }, - }, - }, - } - - upgradeConfigBytes, err := json.Marshal(upgradeConfig) - require.NoError(t, err, "UpgradeOPChainV2Input should marshal to JSON") - err = embedded.DefaultUpgrader.Upgrade(host, upgradeConfigBytes) - require.NoError(t, err, "OPCM V2 chain upgrade should succeed") -}