diff --git a/packages/contracts-bedrock/interfaces/L1/IOPContractsManager.sol b/packages/contracts-bedrock/interfaces/L1/IOPContractsManager.sol index 67f3e2a5e9bdb..6b7bac8297129 100644 --- a/packages/contracts-bedrock/interfaces/L1/IOPContractsManager.sol +++ b/packages/contracts-bedrock/interfaces/L1/IOPContractsManager.sol @@ -229,23 +229,8 @@ interface IOPContractsManager { /// @notice Thrown when an invalid `l2ChainId` is provided to `deploy`. error InvalidChainId(); - /// @notice Thrown when a role's address is not valid (opChainProxyAdminOwner). - error InvalidRoleAddressPAO(); - - /// @notice Thrown when a role's address is not valid (systemConfigOwner). - error InvalidRoleAddressSCO(); - - /// @notice Thrown when a role's address is not valid (batcher). - error InvalidRoleAddressBatcher(); - - /// @notice Thrown when a role's address is not valid (unsafeBlockSigner). - error InvalidRoleAddressUBS(); - - /// @notice Thrown when a role's address is not valid (proposer). - error InvalidRoleAddressProposer(); - - /// @notice Thrown when a role's address is not valid (challenger). - error InvalidRoleAddressChallenger(); + /// @notice Thrown when a role's address is not valid. + error InvalidRoleAddress(string role); /// @notice Thrown when the latest release is not set upon initialization. error LatestReleaseNotSet(); diff --git a/packages/contracts-bedrock/interfaces/L1/IOPContractsManagerLegacyUpgrade.sol b/packages/contracts-bedrock/interfaces/L1/IOPContractsManagerLegacyUpgrade.sol new file mode 100644 index 0000000000000..057139005a96a --- /dev/null +++ b/packages/contracts-bedrock/interfaces/L1/IOPContractsManagerLegacyUpgrade.sol @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import { ISystemConfig } from "interfaces/L1/ISystemConfig.sol"; +import { IProxyAdmin } from "interfaces/universal/IProxyAdmin.sol"; +import { Claim } from "src/dispute/lib/Types.sol"; + +/// @title IOPContractsManagerLegacyUpgrade +/// @notice Interface for the legacy OPContractsManager upgrade function. +/// This interface is used to test Upgrade 13 and 14 paths and can be safely removed +/// after those upgrades are completed. Only difference in the new struct is the added +/// disputeGameUsesSuperRoots boolean. +interface IOPContractsManagerLegacyUpgrade { + struct OpChainConfig { + ISystemConfig systemConfigProxy; + IProxyAdmin proxyAdmin; + Claim absolutePrestate; + } + + function upgrade(OpChainConfig[] memory _opChainConfigs) external; +} diff --git a/packages/contracts-bedrock/interfaces/L1/IOPPrestateUpdater.sol b/packages/contracts-bedrock/interfaces/L1/IOPPrestateUpdater.sol deleted file mode 100644 index 33533cf96a975..0000000000000 --- a/packages/contracts-bedrock/interfaces/L1/IOPPrestateUpdater.sol +++ /dev/null @@ -1,157 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -// Libraries -import { GameType } from "src/dispute/lib/Types.sol"; - -// Interfaces -import { IProxyAdmin } from "interfaces/universal/IProxyAdmin.sol"; -import { ISuperchainConfig } from "interfaces/L1/ISuperchainConfig.sol"; -import { IProtocolVersions } from "interfaces/L1/IProtocolVersions.sol"; -import { ISystemConfig } from "interfaces/L1/ISystemConfig.sol"; -import { IOPContractsManager } from "interfaces/L1/IOPContractsManager.sol"; -import { IDisputeGame } from "interfaces/dispute/IDisputeGame.sol"; - -interface IOPPrestateUpdater { - // -------- Constants and Variables -------- - - function version() external pure returns (string memory); - - /// @notice Address of the SuperchainConfig contract shared by all chains. - function superchainConfig() external view returns (ISuperchainConfig); - - /// @notice Address of the ProtocolVersions contract shared by all chains. - function protocolVersions() external view returns (IProtocolVersions); - - /// @notice Address of the ProxyAdmin contract shared by all chains. - function superchainProxyAdmin() external view returns (IProxyAdmin); - - /// @notice L1 smart contracts release deployed by this version of OPCM. This is used in opcm to signal which - /// version of the L1 smart contracts is deployed. It takes the format of `op-contracts/vX.Y.Z`. - function l1ContractsRelease() external view returns (string memory); - - // -------- Events -------- - - /// @notice Emitted when a new OP Stack chain is deployed. - /// @param l2ChainId Chain ID of the new chain. - /// @param deployer Address that deployed the chain. - /// @param deployOutput ABI-encoded output of the deployment. - event Deployed(uint256 indexed l2ChainId, address indexed deployer, bytes deployOutput); - - /// @notice Emitted when a chain is upgraded - /// @param systemConfig Address of the chain's SystemConfig contract - /// @param upgrader Address that initiated the upgrade - event Upgraded(uint256 indexed l2ChainId, ISystemConfig indexed systemConfig, address indexed upgrader); - - /// @notice Emitted when a new game type is added to a chain - /// @param l2ChainId Chain ID of the chain - /// @param gameType Type of the game being added - /// @param newDisputeGame Address of the deployed dispute game - /// @param oldDisputeGame Address of the old dispute game - event GameTypeAdded(uint256 indexed l2ChainId, GameType indexed gameType, IDisputeGame newDisputeGame, IDisputeGame oldDisputeGame); - - // -------- Errors -------- - - error BytesArrayTooLong(); - error DeploymentFailed(); - error EmptyInitcode(); - error IdentityPrecompileCallFailed(); - error NotABlueprint(); - error ReservedBitsSet(); - error UnexpectedPreambleData(bytes data); - error UnsupportedERCVersion(uint8 version); - error OnlyUpgradeController(); - error PrestateNotSet(); - - /// @notice Thrown when an address is the zero address. - error AddressNotFound(address who); - - /// @notice Throw when a contract address has no code. - error AddressHasNoCode(address who); - - /// @notice Thrown when a release version is already set. - error AlreadyReleased(); - - /// @notice Thrown when an invalid `l2ChainId` is provided to `deploy`. - error InvalidChainId(); - - /// @notice Thrown when a role's address is not valid (opChainProxyAdminOwner). - error InvalidRoleAddressPAO(); - - /// @notice Thrown when a role's address is not valid (systemConfigOwner). - error InvalidRoleAddressSCO(); - - /// @notice Thrown when a role's address is not valid (batcher). - error InvalidRoleAddressBatcher(); - - /// @notice Thrown when a role's address is not valid (unsafeBlockSigner). - error InvalidRoleAddressUBS(); - - /// @notice Thrown when a role's address is not valid (proposer). - error InvalidRoleAddressProposer(); - - /// @notice Thrown when a role's address is not valid (challenger). - error InvalidRoleAddressChallenger(); - - /// @notice Thrown when the latest release is not set upon initialization. - error LatestReleaseNotSet(); - - /// @notice Thrown when the starting anchor root is not provided. - error InvalidStartingAnchorRoot(); - - /// @notice Thrown when certain methods are called outside of a DELEGATECALL. - error OnlyDelegatecall(); - - /// @notice Thrown when game configs passed to addGameType are invalid. - error InvalidGameConfigs(); - - /// @notice Thrown when the SuperchainConfig of the chain does not match the SuperchainConfig of this OPCM. - error SuperchainConfigMismatch(ISystemConfig systemConfig); - - error SuperchainProxyAdminMismatch(); - - /// @notice Thrown when a function from the parent (OPCM) is not implemented. - error NotImplemented(); - - /// @notice Thrown when the prestate of a permissioned disputed game is 0. - error PrestateRequired(); - - // -------- Methods -------- - - function __constructor__( - ISuperchainConfig _superchainConfig, - IProtocolVersions _protocolVersions, - IOPContractsManager.Blueprints memory _blueprints - ) - external; - - function deploy(IOPContractsManager.DeployInput calldata _input) external returns (IOPContractsManager.DeployOutput memory); - - /// @notice Upgrades the implementation of all proxies in the specified chains - /// @param _opChainConfigs The chains to upgrade - function upgrade(IOPContractsManager.OpChainConfig[] memory _opChainConfigs) external; - - /// @notice addGameType deploys a new dispute game and links it to the DisputeGameFactory. The inputted _gameConfigs - /// must be added in ascending GameType order. - function addGameType(IOPContractsManager.AddGameInput[] memory _gameConfigs) external returns (IOPContractsManager.AddGameOutput[] memory); - - /// @notice Maps an L2 chain ID to an L1 batch inbox address as defined by the standard - /// configuration's convention. This convention is `versionByte || keccak256(bytes32(chainId))[:19]`, - /// where || denotes concatenation`, versionByte is 0x00, and chainId is a uint256. - /// https://specs.optimism.io/protocol/configurability.html#consensus-parameters - function chainIdToBatchInboxAddress(uint256 _l2ChainId) external pure returns (address); - - /// @notice Returns the blueprint contract addresses. - function blueprints() external view returns (IOPContractsManager.Blueprints memory); - - /// @notice Returns the implementation contract addresses. - function implementations() external view returns (IOPContractsManager.Implementations memory); - - function upgradeController() external view returns (address); - - function isRC() external view returns (bool); - - function setRC(bool _isRC) external; - - function updatePrestate(IOPContractsManager.OpChainConfig[] memory _prestateUpdateInputs) external; -} diff --git a/packages/contracts-bedrock/justfile b/packages/contracts-bedrock/justfile index 5f6c8a97a745c..1908267920fe3 100644 --- a/packages/contracts-bedrock/justfile +++ b/packages/contracts-bedrock/justfile @@ -65,7 +65,7 @@ test-dev *ARGS: build-go-ffi # Default block number for the forked upgrade path. export sepoliaBlockNumber := "7701807" -export mainnetBlockNumber := "21971446" +export mainnetBlockNumber := "21983965" export pinnedBlockNumber := if env_var_or_default("FORK_BASE_CHAIN", "") == "mainnet" { mainnetBlockNumber diff --git a/packages/contracts-bedrock/scripts/checks/interfaces/main.go b/packages/contracts-bedrock/scripts/checks/interfaces/main.go index 211730d3adc1f..0ecc45e19034b 100644 --- a/packages/contracts-bedrock/scripts/checks/interfaces/main.go +++ b/packages/contracts-bedrock/scripts/checks/interfaces/main.go @@ -26,6 +26,9 @@ var excludeContracts = []string{ // EAS "IEAS", "ISchemaResolver", "ISchemaRegistry", + // Misc stuff that can be ignored + "IOPContractsManagerLegacyUpgrade", + // TODO: Interfaces that need to be fixed "IInitializable", "IOptimismMintableERC20", "ILegacyMintableERC20", "KontrolCheatsBase", "ISystemConfigInterop", "IResolvedDelegateProxy", diff --git a/packages/contracts-bedrock/snapshots/abi/OPContractsManager.json b/packages/contracts-bedrock/snapshots/abi/OPContractsManager.json index 78f613a279507..92ae15a241750 100644 --- a/packages/contracts-bedrock/snapshots/abi/OPContractsManager.json +++ b/packages/contracts-bedrock/snapshots/abi/OPContractsManager.json @@ -662,6 +662,11 @@ "internalType": "Claim", "name": "absolutePrestate", "type": "bytes32" + }, + { + "internalType": "bool", + "name": "disputeGameUsesSuperRoots", + "type": "bool" } ], "internalType": "struct OPContractsManager.OpChainConfig[]", @@ -738,33 +743,14 @@ "type": "error" }, { - "inputs": [], - "name": "InvalidRoleAddressBatcher", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidRoleAddressChallenger", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidRoleAddressPAO", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidRoleAddressProposer", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidRoleAddressSCO", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidRoleAddressUBS", + "inputs": [ + { + "internalType": "string", + "name": "role", + "type": "string" + } + ], + "name": "InvalidRoleAddress", "type": "error" }, { diff --git a/packages/contracts-bedrock/snapshots/abi/OPContractsManagerDeployer.json b/packages/contracts-bedrock/snapshots/abi/OPContractsManagerDeployer.json index 2b0073e0e1296..600ddfba513b5 100644 --- a/packages/contracts-bedrock/snapshots/abi/OPContractsManagerDeployer.json +++ b/packages/contracts-bedrock/snapshots/abi/OPContractsManagerDeployer.json @@ -504,4 +504,4 @@ "name": "UnsupportedERCVersion", "type": "error" } -] +] \ No newline at end of file diff --git a/packages/contracts-bedrock/snapshots/abi/OPContractsManagerDeployerInterop.json b/packages/contracts-bedrock/snapshots/abi/OPContractsManagerDeployerInterop.json index 2b0073e0e1296..600ddfba513b5 100644 --- a/packages/contracts-bedrock/snapshots/abi/OPContractsManagerDeployerInterop.json +++ b/packages/contracts-bedrock/snapshots/abi/OPContractsManagerDeployerInterop.json @@ -504,4 +504,4 @@ "name": "UnsupportedERCVersion", "type": "error" } -] +] \ No newline at end of file diff --git a/packages/contracts-bedrock/snapshots/abi/OPContractsManagerGameTypeAdder.json b/packages/contracts-bedrock/snapshots/abi/OPContractsManagerGameTypeAdder.json index a2088238caf93..663ec41ae920a 100644 --- a/packages/contracts-bedrock/snapshots/abi/OPContractsManagerGameTypeAdder.json +++ b/packages/contracts-bedrock/snapshots/abi/OPContractsManagerGameTypeAdder.json @@ -311,6 +311,11 @@ "internalType": "Claim", "name": "absolutePrestate", "type": "bytes32" + }, + { + "internalType": "bool", + "name": "disputeGameUsesSuperRoots", + "type": "bool" } ], "internalType": "struct OPContractsManager.OpChainConfig[]", diff --git a/packages/contracts-bedrock/snapshots/abi/OPContractsManagerUpgrader.json b/packages/contracts-bedrock/snapshots/abi/OPContractsManagerUpgrader.json index 8efb6ad77e6d9..195f8d2acbc87 100644 --- a/packages/contracts-bedrock/snapshots/abi/OPContractsManagerUpgrader.json +++ b/packages/contracts-bedrock/snapshots/abi/OPContractsManagerUpgrader.json @@ -208,6 +208,11 @@ "internalType": "Claim", "name": "absolutePrestate", "type": "bytes32" + }, + { + "internalType": "bool", + "name": "disputeGameUsesSuperRoots", + "type": "bool" } ], "internalType": "struct OPContractsManager.OpChainConfig[]", diff --git a/packages/contracts-bedrock/snapshots/semver-lock.json b/packages/contracts-bedrock/snapshots/semver-lock.json index 0818d19fa94d9..9d1dd675cc2ea 100644 --- a/packages/contracts-bedrock/snapshots/semver-lock.json +++ b/packages/contracts-bedrock/snapshots/semver-lock.json @@ -16,24 +16,16 @@ "sourceCodeHash": "0x44797707aea8c63dec049a02d69ea056662a06e5cf320028ab8b388634bf1c67" }, "src/L1/OPContractsManager.sol": { - "initCodeHash": "0xfc5622ed6411ec01e7bcfb3c9250b130b8ca25e74ee40e70e4017fefa34cddf7", - "sourceCodeHash": "0x1f53a1e174479aa244ad78fc171d4cbeb8529505a57af59f89435837e2803bcc" - }, - "src/L1/OPContractsManagerInterop.sol": { - "initCodeHash": "0x6855bfe2f22b4ce53c62fca0d32029cc39f0a2ab2ded43808bb6c493acadc781", - "sourceCodeHash": "0x79b3ae68d83f021d9118f8a1943c1d0773e7e01a19f580f6b90ad4c00370cff5" - }, - "src/L1/OPPrestateUpdater.sol": { - "initCodeHash": "0x840b42954a8921e8216a93ace6d2a047e28ef8df8f426181b1b229d2d5818fc1", - "sourceCodeHash": "0x985c33cc1933d68de38d6c13afc1ac42ef7e455cb06bb255c1fed22b06b97a62" + "initCodeHash": "0x5cbd9da550ffc2fdd17434145798cbfd5cf8140c474319671bf339f43cbb994c", + "sourceCodeHash": "0x1af10fb0f17db1dfc8b02844f36577bffe54c942f6f8f09a0b75fa9ba38654d9" }, "src/L1/OptimismPortal2.sol": { - "initCodeHash": "0xac11984d6c3e58fd02e33509540ee329c4f5caedb94e422f8ab3a7a95ce7a546", - "sourceCodeHash": "0x79652f4f6aebc11dd4ad201dcf9a436d94db76e2edef07b12486c3de9cd804d2" + "initCodeHash": "0x22eddc89e6acc58ab5c22927d303a1be8d05a21220e6d314883cba2bca4ffb56", + "sourceCodeHash": "0x03da70e6c4b48c9e58722a0ea41c9cb7ca6bf12ce26f8118316a4168b8715b98" }, "src/L1/OptimismPortalInterop.sol": { - "initCodeHash": "0x8d76300f7a79686e522d4cac0582f4f7ec77b798f881747d0403762fa56b3a71", - "sourceCodeHash": "0x1b2ce1f2e3125bd449501b9d228b207d31f2bc39c7f23262c674e0d1f47c0d0b" + "initCodeHash": "0xd77aedf895785318db654a0a7f72f80f0a83a70d32e8b479f3fa3ffa1e50265f", + "sourceCodeHash": "0xcd94e7641d43620f4169b083b2e597c2db2eb679b44ccaf5c7eeae6f63d2774a" }, "src/L1/ProtocolVersions.sol": { "initCodeHash": "0x5a76c8530cb24cf23d3baacc6eefaac226382af13f1e2a35535d2ec2b0573b29", @@ -44,12 +36,12 @@ "sourceCodeHash": "0xfd56e63e76b1f203cceeb9bbb14396ae803cbbbf7e80ca0ee11fb586321812af" }, "src/L1/SystemConfig.sol": { - "initCodeHash": "0x93e732c31e59dc78d6414ad12fcc0cbe4537d3a69a2ca34ff8713b0b51679b19", - "sourceCodeHash": "0x538518cf61bda80bc3f0b94e4fe236b65e53fff397fd43958e695705c57e02d9" + "initCodeHash": "0x471ac69544fdee81d0734b87151297ad4cf91ca1d43a2c95e9e644c0424eed65", + "sourceCodeHash": "0x8c3ccb8f3718c00c3f9bf7c866a22d87ea18a87a6e9454c31d1c97638107434c" }, "src/L1/SystemConfigInterop.sol": { - "initCodeHash": "0xe3651f84f0fecf6c4b60801d8168da55bfe3889912548fcd6b935c9f6dff91b3", - "sourceCodeHash": "0xe3031507a78ed71352c0c1523272881a48c8d6bd98d5322ce3a7cb8061eeade3" + "initCodeHash": "0xcdca84b074ddfd6b4e4df1a57d3500c1d48ecf88852af47f6351e9ae24b6fc2a", + "sourceCodeHash": "0x71914fa1408befaef3a06a67404e0ab580d9d945e068ba23063ea6588f0f68a6" }, "src/L2/BaseFeeVault.sol": { "initCodeHash": "0xc403d4c555d8e69a2699e01d192ae7327136701fa02da10a6d75a584b3c364c9", @@ -243,4 +235,4 @@ "initCodeHash": "0x2bfce526f82622288333d53ca3f43a0a94306ba1bab99241daa845f8f4b18bd4", "sourceCodeHash": "0xf49d7b0187912a6bb67926a3222ae51121e9239495213c975b3b4b217ee57a1b" } -} +} \ No newline at end of file diff --git a/packages/contracts-bedrock/snapshots/storageLayout/SystemConfig.json b/packages/contracts-bedrock/snapshots/storageLayout/SystemConfig.json index 8332aa216cb4e..3d1796e5e4063 100644 --- a/packages/contracts-bedrock/snapshots/storageLayout/SystemConfig.json +++ b/packages/contracts-bedrock/snapshots/storageLayout/SystemConfig.json @@ -110,5 +110,12 @@ "offset": 12, "slot": "106", "type": "uint64" + }, + { + "bytes": "32", + "label": "l2ChainId", + "offset": 0, + "slot": "107", + "type": "uint256" } -] +] \ No newline at end of file diff --git a/packages/contracts-bedrock/snapshots/storageLayout/SystemConfigInterop.json b/packages/contracts-bedrock/snapshots/storageLayout/SystemConfigInterop.json index 8332aa216cb4e..3d1796e5e4063 100644 --- a/packages/contracts-bedrock/snapshots/storageLayout/SystemConfigInterop.json +++ b/packages/contracts-bedrock/snapshots/storageLayout/SystemConfigInterop.json @@ -110,5 +110,12 @@ "offset": 12, "slot": "106", "type": "uint64" + }, + { + "bytes": "32", + "label": "l2ChainId", + "offset": 0, + "slot": "107", + "type": "uint256" } -] +] \ No newline at end of file diff --git a/packages/contracts-bedrock/src/L1/OPContractsManager.sol b/packages/contracts-bedrock/src/L1/OPContractsManager.sol index 9853e1aaf50ab..903e026afd1ae 100644 --- a/packages/contracts-bedrock/src/L1/OPContractsManager.sol +++ b/packages/contracts-bedrock/src/L1/OPContractsManager.sol @@ -5,7 +5,7 @@ pragma solidity 0.8.15; import { Blueprint } from "src/libraries/Blueprint.sol"; import { Constants } from "src/libraries/Constants.sol"; import { Bytes } from "src/libraries/Bytes.sol"; -import { Claim, Duration, GameType, GameTypes, OutputRoot } from "src/dispute/lib/Types.sol"; +import { Claim, Duration, GameType, GameTypes, OutputRoot, Hash } from "src/dispute/lib/Types.sol"; import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; // Interfaces @@ -525,34 +525,12 @@ contract OPContractsManagerUpgrader is OPContractsManagerBase { /// @dev This function is intended to be called via DELEGATECALL from the Upgrade Controller Safe function upgrade(OPContractsManager.OpChainConfig[] memory _opChainConfigs) external virtual { OPContractsManager.Implementations memory impls = getImplementations(); - OPContractsManager.Blueprints memory bps = getBlueprints(); for (uint256 i = 0; i < _opChainConfigs.length; i++) { assertValidOpChainConfig(_opChainConfigs[i]); ISystemConfig.Addresses memory opChainAddrs = _opChainConfigs[i].systemConfigProxy.getAddresses(); - // -------- Upgrade SystemConfig to Isthmus implementation -------- - upgradeTo( - _opChainConfigs[i].proxyAdmin, address(_opChainConfigs[i].systemConfigProxy), impls.systemConfigImpl - ); - - // -------- Upgrade Contracts Stored in SystemConfig -------- - - // OptimismPortal and L1CrossDomainMessenger are being upgraded to include the fixes - // for EIP-7623 (minimum gas limits for L1 -> L2 messages). - upgradeTo(_opChainConfigs[i].proxyAdmin, opChainAddrs.optimismPortal, impls.optimismPortalImpl); - upgradeTo( - _opChainConfigs[i].proxyAdmin, opChainAddrs.l1CrossDomainMessenger, impls.l1CrossDomainMessengerImpl - ); - - // L1ERC721Bridge and L1StandardBridge are being upgraded to include the tweaks to the - // EOA checking code for EIP-7702 (code length == 23). - upgradeTo(_opChainConfigs[i].proxyAdmin, opChainAddrs.l1ERC721Bridge, impls.l1ERC721BridgeImpl); - upgradeTo(_opChainConfigs[i].proxyAdmin, opChainAddrs.l1StandardBridge, impls.l1StandardBridgeImpl); - - // -------- Discover and Upgrade Proofs Contracts -------- - - // All chains have the Permissioned Dispute Game. + // All chains have the PermissionedDisputeGame, grab that. IPermissionedDisputeGame permissionedDisputeGame = IPermissionedDisputeGame( address( getGameImplementation( @@ -561,37 +539,116 @@ contract OPContractsManagerUpgrader is OPContractsManagerBase { ) ); - // We're also going to need the l2ChainId below, so we cache it in the outer scope. + // Grab the L2 chain ID from the PermissionedDisputeGame. uint256 l2ChainId = getL2ChainId(IFaultDisputeGame(address(permissionedDisputeGame))); - deployAndSetNewGameImpl({ - _l2ChainId: l2ChainId, - _disputeGame: IDisputeGame(address(permissionedDisputeGame)), - _gameType: GameTypes.PERMISSIONED_CANNON, - _opChainConfig: _opChainConfigs[i], - _implementations: impls, - _blueprints: bps, - _opChainAddrs: opChainAddrs - }); + // Grab the current respectedGameType from the OptimismPortal contract before the upgrade. + GameType respectedGameType = IOptimismPortal2(payable(opChainAddrs.optimismPortal)).respectedGameType(); + + // Grab the current SuperchainConfig from the OptimismPortal contract before the upgrade. + ISuperchainConfig superchainConfig = + IOptimismPortal2(payable(opChainAddrs.optimismPortal)).superchainConfig(); - // Now retrieve the permissionless game. If it exists, replace its implementation. - IFaultDisputeGame permissionlessDisputeGame = IFaultDisputeGame( - address(getGameImplementation(IDisputeGameFactory(opChainAddrs.disputeGameFactory), GameTypes.CANNON)) + // Start by upgrading the SystemConfig contract to have the l2ChainId. + upgradeToAndCall( + _opChainConfigs[i].proxyAdmin, + address(_opChainConfigs[i].systemConfigProxy), + impls.systemConfigImpl, + abi.encodeCall(ISystemConfig.upgrade, (l2ChainId)) ); - if (address(permissionlessDisputeGame) != address(0)) { - // Deploy and set a new permissionless game to update its prestate + // Separate context to avoid stack too deep. + IAnchorStateRegistry newAnchorStateRegistryProxy; + { + // Deploy a new AnchorStateRegistry contract. + // We use the SOT suffix to avoid CREATE2 conflicts with the existing ASR. + newAnchorStateRegistryProxy = IAnchorStateRegistry( + deployProxy({ + _l2ChainId: l2ChainId, + _proxyAdmin: _opChainConfigs[i].proxyAdmin, + _saltMixer: reusableSaltMixer(_opChainConfigs[i]), + _contractName: "AnchorStateRegistry-SOT" + }) + ); + + // Separate context to avoid stack too deep. + { + // Get the existing anchor root from the old AnchorStateRegistry contract. + // Get the AnchorStateRegistry from the PermissionedDisputeGame. + (Hash root, uint256 l2BlockNumber) = getAnchorStateRegistry( + IFaultDisputeGame(address(permissionedDisputeGame)) + ).anchors(respectedGameType); + + // Upgrade and initialize the AnchorStateRegistry contract. + // Since this is a net-new contract, we need to initialize it. + upgradeToAndCall( + _opChainConfigs[i].proxyAdmin, + address(newAnchorStateRegistryProxy), + impls.anchorStateRegistryImpl, + abi.encodeCall( + IAnchorStateRegistry.initialize, + ( + superchainConfig, + IDisputeGameFactory(opChainAddrs.disputeGameFactory), + OutputRoot({ root: root, l2BlockNumber: l2BlockNumber }), + respectedGameType + ) + ) + ); + } + } + + // Separate context to avoid stack too deep. + { + // Upgrade the OptimismPortal contract. + upgradeToAndCall( + _opChainConfigs[i].proxyAdmin, + opChainAddrs.optimismPortal, + impls.optimismPortalImpl, + abi.encodeCall( + IOptimismPortal2.upgrade, + (newAnchorStateRegistryProxy, _opChainConfigs[i].disputeGameUsesSuperRoots) + ) + ); + } + + // We also need to redeploy the dispute games because the AnchorStateRegistry is new. + // Separate context to avoid stack too deep. + { + // Deploy and set a new permissioned game to update its prestate. deployAndSetNewGameImpl({ _l2ChainId: l2ChainId, - _disputeGame: IDisputeGame(address(permissionlessDisputeGame)), - _gameType: GameTypes.CANNON, + _disputeGame: IDisputeGame(address(permissionedDisputeGame)), + _newAnchorStateRegistryProxy: newAnchorStateRegistryProxy, + _gameType: GameTypes.PERMISSIONED_CANNON, _opChainConfig: _opChainConfigs[i], - _implementations: impls, - _blueprints: bps, _opChainAddrs: opChainAddrs }); } + // Separate context to avoid stack too deep. + { + // Now retrieve the permissionless game. + IFaultDisputeGame permissionlessDisputeGame = IFaultDisputeGame( + address( + getGameImplementation(IDisputeGameFactory(opChainAddrs.disputeGameFactory), GameTypes.CANNON) + ) + ); + + // If it exists, replace its implementation. + if (address(permissionlessDisputeGame) != address(0)) { + // Deploy and set a new permissionless game to update its prestate + deployAndSetNewGameImpl({ + _l2ChainId: l2ChainId, + _disputeGame: IDisputeGame(address(permissionlessDisputeGame)), + _newAnchorStateRegistryProxy: newAnchorStateRegistryProxy, + _gameType: GameTypes.CANNON, + _opChainConfig: _opChainConfigs[i], + _opChainAddrs: opChainAddrs + }); + } + } + // Emit the upgraded event with the address of the caller. Since this will be a delegatecall, // the caller will be the value of the ADDRESS opcode. emit Upgraded(l2ChainId, _opChainConfigs[i].systemConfigProxy, address(this)); @@ -620,28 +677,30 @@ contract OPContractsManagerUpgrader is OPContractsManagerBase { /// @notice Deploys and sets a new dispute game implementation /// @param _l2ChainId The L2 chain ID /// @param _disputeGame The current dispute game implementation + /// @param _newAnchorStateRegistryProxy The new anchor state registry proxy /// @param _gameType The type of game to deploy /// @param _opChainConfig The OP chain configuration - /// @param _blueprints The blueprint addresses - /// @param _implementations The implementation addresses /// @param _opChainAddrs The OP chain addresses function deployAndSetNewGameImpl( uint256 _l2ChainId, IDisputeGame _disputeGame, + IAnchorStateRegistry _newAnchorStateRegistryProxy, GameType _gameType, OPContractsManager.OpChainConfig memory _opChainConfig, - OPContractsManager.Blueprints memory _blueprints, - OPContractsManager.Implementations memory _implementations, ISystemConfig.Addresses memory _opChainAddrs ) internal { + OPContractsManager.Blueprints memory bps = getBlueprints(); + OPContractsManager.Implementations memory impls = getImplementations(); + // Get the constructor params for the game IFaultDisputeGame.GameConstructorParams memory params = getGameConstructorParams(IFaultDisputeGame(address(_disputeGame))); // Modify the params with the new vm values. - params.vm = IBigStepper(_implementations.mipsImpl); + params.anchorStateRegistry = IAnchorStateRegistry(address(_newAnchorStateRegistryProxy)); + params.vm = IBigStepper(impls.mipsImpl); if (Claim.unwrap(_opChainConfig.absolutePrestate) == bytes32(0)) { revert OPContractsManager.PrestateNotSet(); } @@ -653,8 +712,8 @@ contract OPContractsManagerUpgrader is OPContractsManagerBase { address challenger = getChallenger(IPermissionedDisputeGame(address(_disputeGame))); newGame = IDisputeGame( Blueprint.deployFrom( - _blueprints.permissionedDisputeGame1, - _blueprints.permissionedDisputeGame2, + bps.permissionedDisputeGame1, + bps.permissionedDisputeGame2, computeSalt(_l2ChainId, reusableSaltMixer(_opChainConfig), "PermissionedDisputeGame"), encodePermissionedFDGConstructor(params, proposer, challenger) ) @@ -662,8 +721,8 @@ contract OPContractsManagerUpgrader is OPContractsManagerBase { } else { newGame = IDisputeGame( Blueprint.deployFrom( - _blueprints.permissionlessDisputeGame1, - _blueprints.permissionlessDisputeGame2, + bps.permissionlessDisputeGame1, + bps.permissionlessDisputeGame2, computeSalt(_l2ChainId, reusableSaltMixer(_opChainConfig), "PermissionlessDisputeGame"), encodePermissionlessFDGConstructor(params) ) @@ -805,7 +864,7 @@ contract OPContractsManagerDeployer is OPContractsManagerBase { output.opChainProxyAdmin, address(output.l1ERC721BridgeProxy), implementation.l1ERC721BridgeImpl, data ); - data = encodeOptimismPortalInitializer(output, _superchainConfig); + data = encodeOptimismPortalInitializer(_input, output, _superchainConfig); upgradeToAndCall( output.opChainProxyAdmin, address(output.optimismPortalProxy), implementation.optimismPortalImpl, data ); @@ -954,6 +1013,7 @@ contract OPContractsManagerDeployer is OPContractsManagerBase { /// @notice Helper method for encoding the OptimismPortal initializer data. function encodeOptimismPortalInitializer( + OPContractsManager.DeployInput memory _input, OPContractsManager.DeployOutput memory _output, ISuperchainConfig _superchainConfig ) @@ -965,10 +1025,10 @@ contract OPContractsManagerDeployer is OPContractsManagerBase { return abi.encodeCall( IOptimismPortal2.initialize, ( - _output.disputeGameFactoryProxy, _output.systemConfigProxy, _superchainConfig, - GameTypes.PERMISSIONED_CANNON + _output.anchorStateRegistryProxy, + _input.disputeGameUsesSuperRoots ) ); } @@ -997,7 +1057,8 @@ contract OPContractsManagerDeployer is OPContractsManagerBase { _input.roles.unsafeBlockSigner, referenceResourceConfig, chainIdToBatchInboxAddress(_input.l2ChainId), - opChainAddrs + opChainAddrs, + _input.l2ChainId ) ); } @@ -1057,7 +1118,7 @@ contract OPContractsManagerDeployer is OPContractsManagerBase { OutputRoot memory startingAnchorRoot = abi.decode(_input.startingAnchorRoot, (OutputRoot)); return abi.encodeCall( IAnchorStateRegistry.initialize, - (_superchainConfig, _output.disputeGameFactoryProxy, _output.optimismPortalProxy, startingAnchorRoot) + (_superchainConfig, _output.disputeGameFactoryProxy, startingAnchorRoot, GameTypes.PERMISSIONED_CANNON) ); } @@ -1113,7 +1174,8 @@ contract OPContractsManagerDeployerInterop is OPContractsManagerDeployer { referenceResourceConfig, chainIdToBatchInboxAddress(_input.l2ChainId), opChainAddrs, - dependencyManager + dependencyManager, + _input.l2ChainId ) ); } @@ -1236,9 +1298,9 @@ contract OPContractsManager is ISemver { // -------- Constants and Variables -------- - /// @custom:semver 1.9.0 + /// @custom:semver 1.10.0 function version() public pure virtual returns (string memory) { - return "1.9.0"; + return "1.10.0"; } OPContractsManagerGameTypeAdder public immutable opcmGameTypeAdder; @@ -1292,23 +1354,8 @@ contract OPContractsManager is ISemver { /// @notice Thrown when an invalid `l2ChainId` is provided to `deploy`. error InvalidChainId(); - /// @notice Thrown when a role's address is not valid (opChainProxyAdminOwner). - error InvalidRoleAddressPAO(); - - /// @notice Thrown when a role's address is not valid (systemConfigOwner). - error InvalidRoleAddressSCO(); - - /// @notice Thrown when a role's address is not valid (batcher). - error InvalidRoleAddressBatcher(); - - /// @notice Thrown when a role's address is not valid (unsafeBlockSigner). - error InvalidRoleAddressUBS(); - - /// @notice Thrown when a role's address is not valid (proposer). - error InvalidRoleAddressProposer(); - - /// @notice Thrown when a role's address is not valid (challenger). - error InvalidRoleAddressChallenger(); + /// @notice Thrown when a role's address is not valid. + error InvalidRoleAddress(string role); /// @notice Thrown when the latest release is not set upon initialization. error LatestReleaseNotSet(); diff --git a/packages/contracts-bedrock/src/L1/OPContractsManagerInterop.sol b/packages/contracts-bedrock/src/L1/OPContractsManagerInterop.sol deleted file mode 100644 index 6358bc741d715..0000000000000 --- a/packages/contracts-bedrock/src/L1/OPContractsManagerInterop.sol +++ /dev/null @@ -1,80 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.15; - -// Contracts -import { OPContractsManager } from "src/L1/OPContractsManager.sol"; - -// Interfaces -import { ISuperchainConfig } from "interfaces/L1/ISuperchainConfig.sol"; -import { IProtocolVersions } from "interfaces/L1/IProtocolVersions.sol"; -import { IResourceMetering } from "interfaces/L1/IResourceMetering.sol"; -import { ISystemConfig } from "interfaces/L1/ISystemConfig.sol"; -import { ISystemConfigInterop } from "interfaces/L1/ISystemConfigInterop.sol"; -import { IProxyAdmin } from "interfaces/universal/IProxyAdmin.sol"; - -contract OPContractsManagerInterop is OPContractsManager { - /// @custom:semver +interop.11 - function version() public pure override returns (string memory) { - return string.concat(super.version(), "+interop.11"); - } - - constructor( - ISuperchainConfig _superchainConfig, - IProtocolVersions _protocolVersions, - IProxyAdmin _superchainProxyAdmin, - string memory _l1ContractsRelease, - Blueprints memory _blueprints, - Implementations memory _implementations, - address _upgradeController - ) - OPContractsManager( - _superchainConfig, - _protocolVersions, - _superchainProxyAdmin, - _l1ContractsRelease, - _blueprints, - _implementations, - _upgradeController - ) - { } - - // The `SystemConfigInterop` contract has an extra `address _dependencyManager` argument - // that we must account for. - function encodeSystemConfigInitializer( - DeployInput memory _input, - DeployOutput memory _output - ) - internal - view - virtual - override - returns (bytes memory) - { - (IResourceMetering.ResourceConfig memory referenceResourceConfig, ISystemConfig.Addresses memory opChainAddrs) = - defaultSystemConfigParams(_input, _output); - - // TODO For now we assume that the dependency manager is the same as system config owner. - // This is currently undefined since it's not part of the standard config, so we may need - // to update where this value is pulled from in the future. To support a different dependency - // manager in this contract without an invasive change of redefining the `Roles` struct, - // we will make the change described in https://github.com/ethereum-optimism/optimism/issues/11783. - address dependencyManager = address(_input.roles.systemConfigOwner); - - return abi.encodeCall( - ISystemConfigInterop.initialize, - ( - _input.roles.systemConfigOwner, - _input.basefeeScalar, - _input.blobBasefeeScalar, - bytes32(uint256(uint160(_input.roles.batcher))), // batcherHash - _input.gasLimit, - _input.roles.unsafeBlockSigner, - referenceResourceConfig, - chainIdToBatchInboxAddress(_input.l2ChainId), - opChainAddrs, - dependencyManager, - _input.l2ChainId - ) - ); - } -} diff --git a/packages/contracts-bedrock/src/L1/OPPrestateUpdater.sol b/packages/contracts-bedrock/src/L1/OPPrestateUpdater.sol deleted file mode 100644 index dd6f50989fec0..0000000000000 --- a/packages/contracts-bedrock/src/L1/OPPrestateUpdater.sol +++ /dev/null @@ -1,172 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.15; - -// Contracts -import { OPContractsManager } from "src/L1/OPContractsManager.sol"; - -// Interfaces -import { ISuperchainConfig } from "interfaces/L1/ISuperchainConfig.sol"; -import { IProtocolVersions } from "interfaces/L1/IProtocolVersions.sol"; -import { IPermissionedDisputeGame } from "interfaces/dispute/IPermissionedDisputeGame.sol"; -import { IDisputeGameFactory } from "interfaces/dispute/IDisputeGameFactory.sol"; -import { IFaultDisputeGame } from "interfaces/dispute/IFaultDisputeGame.sol"; -import { IDelayedWETH } from "interfaces/dispute/IDelayedWETH.sol"; -import { IProxyAdmin } from "interfaces/universal/IProxyAdmin.sol"; - -// Libraries -import { Claim, GameTypes } from "src/dispute/lib/Types.sol"; - -/// @title OPPrestateUpdater -/// @notice A custom implementation of OPContractsManager that enables updating the prestate hash -/// for the permissioned and fault dispute games on a set of chains. -contract OPPrestateUpdater is OPContractsManager { - /// @notice Thrown when a function from the parent (OPCM) is not implemented. - error NotImplemented(); - - /// @notice Thrown when the prestate of a permissioned disputed game is 0. - error PrestateRequired(); - - // @return Version string - /// @custom:semver 1.7.1 - function version() public pure override returns (string memory) { - return "1.7.1"; - } - - // @notice Constructs the OPPrestateUpdater contract - // @param _superchainConfig Address of the SuperchainConfig contract - // @param _protocolVersions Address of the ProtocolVersions contract - // @param _blueprints Addresses of Blueprint contracts - constructor( - ISuperchainConfig _superchainConfig, - IProtocolVersions _protocolVersions, - Blueprints memory _blueprints - ) - OPContractsManager( - _superchainConfig, - _protocolVersions, - IProxyAdmin(address(0)), - "", - _blueprints, - Implementations( - address(0), //superchainConfigImpl - address(0), //protocolVersionsImpl - address(0), //l1ERC721BridgeImpl - address(0), //optimismPortalImpl - address(0), //systemConfigImpl - address(0), //optimismMintableERC20FactoryImpl - address(0), //l1CrossDomainMessengerImpl - address(0), //l1StandardBridgeImpl - address(0), //disputeGameFactoryImpl - address(0), //anchorStateRegistryImpl - address(0), //delayedWETHImpl - address(0) // mipsImpl - ), - address(0) - ) - { } - - /// @notice Overrides the l1ContractsRelease function to return "none", as this OPCM - /// is not releasing new contracts. - function l1ContractsRelease() external pure override returns (string memory) { - return "none"; - } - - function deploy(DeployInput memory _input) external pure override returns (DeployOutput memory) { - _input; // Silence warning - revert NotImplemented(); - } - - function upgrade(OpChainConfig[] memory _opChainConfigs) external pure override { - _opChainConfigs; // Silence warning - revert NotImplemented(); - } - - function addGameType(AddGameInput[] memory _gameConfigs) public pure override returns (AddGameOutput[] memory) { - _gameConfigs; // Silence warning - revert NotImplemented(); - } - - /// @notice Updates the prestate hash for a new game type while keeping all other parameters the same - /// @param _prestateUpdateInputs The new prestate hash to use - function updatePrestate(OpChainConfig[] memory _prestateUpdateInputs) external { - // Loop through each chain and prestate hash - for (uint256 i = 0; i < _prestateUpdateInputs.length; i++) { - if (Claim.unwrap(_prestateUpdateInputs[i].absolutePrestate) == bytes32(0)) { - revert PrestateRequired(); - } - - // Get the DisputeGameFactory and existing game implementations - IDisputeGameFactory dgf = - IDisputeGameFactory(_prestateUpdateInputs[i].systemConfigProxy.disputeGameFactory()); - IFaultDisputeGame fdg = IFaultDisputeGame(address(getGameImplementation(dgf, GameTypes.CANNON))); - IPermissionedDisputeGame pdg = - IPermissionedDisputeGame(address(getGameImplementation(dgf, GameTypes.PERMISSIONED_CANNON))); - - // All chains must have a permissioned game, but not all chains must have a fault dispute game. - // Whether a chain has a fault dispute game determines how many AddGameInput objects are needed. - bool hasFDG = address(fdg) != address(0); - - AddGameInput[] memory inputs = new AddGameInput[](hasFDG ? 2 : 1); - AddGameInput memory pdgInput; - AddGameInput memory fdgInput; - - // Get the existing game parameters and init bond for the permissioned game - IFaultDisputeGame.GameConstructorParams memory pdgParams = - getGameConstructorParams(IFaultDisputeGame(address(pdg))); - uint256 initBond = dgf.initBonds(GameTypes.PERMISSIONED_CANNON); - - string memory saltMixer = reusableSaltMixer(_prestateUpdateInputs[i]); - // Create game input with updated prestate but same other params - pdgInput = AddGameInput({ - disputeAbsolutePrestate: _prestateUpdateInputs[i].absolutePrestate, - saltMixer: saltMixer, - systemConfig: _prestateUpdateInputs[i].systemConfigProxy, - proxyAdmin: _prestateUpdateInputs[i].proxyAdmin, - delayedWETH: IDelayedWETH(payable(address(pdgParams.weth))), - disputeGameType: pdgParams.gameType, - disputeMaxGameDepth: pdgParams.maxGameDepth, - disputeSplitDepth: pdgParams.splitDepth, - disputeClockExtension: pdgParams.clockExtension, - disputeMaxClockDuration: pdgParams.maxClockDuration, - initialBond: initBond, - vm: pdgParams.vm, - permissioned: true - }); - - // If a fault dispute game exists, create a new game with the same parameters but updated prestate. - if (hasFDG) { - // Get the existing game parameters and init bond for the fault dispute game - IFaultDisputeGame.GameConstructorParams memory fdgParams = - getGameConstructorParams(IFaultDisputeGame(address(fdg))); - initBond = dgf.initBonds(GameTypes.CANNON); - - // Create game input with updated prestate but same other params - fdgInput = AddGameInput({ - disputeAbsolutePrestate: _prestateUpdateInputs[i].absolutePrestate, - saltMixer: saltMixer, - systemConfig: _prestateUpdateInputs[i].systemConfigProxy, - proxyAdmin: _prestateUpdateInputs[i].proxyAdmin, - delayedWETH: IDelayedWETH(payable(address(fdgParams.weth))), - disputeGameType: fdgParams.gameType, - disputeMaxGameDepth: fdgParams.maxGameDepth, - disputeSplitDepth: fdgParams.splitDepth, - disputeClockExtension: fdgParams.clockExtension, - disputeMaxClockDuration: fdgParams.maxClockDuration, - initialBond: initBond, - vm: fdgParams.vm, - permissioned: false - }); - } - - // Game inputs must be ordered with increasing game type values. So FDG is first if it exists. - if (hasFDG) { - inputs[0] = fdgInput; - inputs[1] = pdgInput; - } else { - inputs[0] = pdgInput; - } - // Add the new game type with updated prestate - super.addGameType(inputs); - } - } -} diff --git a/packages/contracts-bedrock/src/L1/OptimismPortalInterop.sol b/packages/contracts-bedrock/src/L1/OptimismPortalInterop.sol index 532f67ac0be5a..7a56632256fe3 100644 --- a/packages/contracts-bedrock/src/L1/OptimismPortalInterop.sol +++ b/packages/contracts-bedrock/src/L1/OptimismPortalInterop.sol @@ -23,9 +23,9 @@ contract OptimismPortalInterop is OptimismPortal2 { /// @param _proofMaturityDelaySeconds The proof maturity delay in seconds. constructor(uint256 _proofMaturityDelaySeconds) OptimismPortal2(_proofMaturityDelaySeconds) { } - /// @custom:semver +interop.3 + /// @custom:semver +interop.4 function version() public pure override returns (string memory) { - return string.concat(super.version(), "+interop.3"); + return string.concat(super.version(), "+interop.4"); } /// @notice Sets static configuration options for the L2 system. diff --git a/packages/contracts-bedrock/src/L1/SystemConfig.sol b/packages/contracts-bedrock/src/L1/SystemConfig.sol index 1828f3fb23291..092767efb54bd 100644 --- a/packages/contracts-bedrock/src/L1/SystemConfig.sol +++ b/packages/contracts-bedrock/src/L1/SystemConfig.sol @@ -140,9 +140,9 @@ contract SystemConfig is OwnableUpgradeable, ReinitializableBase, ISemver { event ConfigUpdate(uint256 indexed version, UpdateType indexed updateType, bytes data); /// @notice Semantic version. - /// @custom:semver 2.5.0 + /// @custom:semver 2.6.0 function version() public pure virtual returns (string memory) { - return "2.5.0"; + return "2.6.0"; } /// @notice Constructs the SystemConfig contract. diff --git a/packages/contracts-bedrock/src/L1/SystemConfigInterop.sol b/packages/contracts-bedrock/src/L1/SystemConfigInterop.sol index dbb9a3dbb2562..fc4135c088326 100644 --- a/packages/contracts-bedrock/src/L1/SystemConfigInterop.sol +++ b/packages/contracts-bedrock/src/L1/SystemConfigInterop.sol @@ -67,9 +67,9 @@ contract SystemConfigInterop is SystemConfig { Storage.setAddress(DEPENDENCY_MANAGER_SLOT, _dependencyManager); } - /// @custom:semver +interop.1 + /// @custom:semver +interop.2 function version() public pure override returns (string memory) { - return string.concat(super.version(), "+interop.1"); + return string.concat(super.version(), "+interop.2"); } /// @notice Adds a chain to the interop dependency set. Can only be called by the dependency manager. diff --git a/packages/contracts-bedrock/test/L1/OPContractsManager.t.sol b/packages/contracts-bedrock/test/L1/OPContractsManager.t.sol index 26119404693fe..82c90379fd553 100644 --- a/packages/contracts-bedrock/test/L1/OPContractsManager.t.sol +++ b/packages/contracts-bedrock/test/L1/OPContractsManager.t.sol @@ -27,6 +27,7 @@ import { IOptimismMintableERC20Factory } from "interfaces/universal/IOptimismMin import { IL1CrossDomainMessenger } from "interfaces/L1/IL1CrossDomainMessenger.sol"; import { IMIPS } from "interfaces/cannon/IMIPS.sol"; import { IOptimismPortal2 } from "interfaces/L1/IOptimismPortal2.sol"; +import { IProxy } from "interfaces/universal/IProxy.sol"; import { IProxyAdmin } from "interfaces/universal/IProxyAdmin.sol"; import { ISuperchainConfig } from "interfaces/L1/ISuperchainConfig.sol"; import { IProtocolVersions } from "interfaces/L1/IProtocolVersions.sol"; @@ -44,6 +45,7 @@ import { IOPContractsManagerUpgrader, IOPContractsManagerContractsContainer } from "interfaces/L1/IOPContractsManager.sol"; +import { IOPContractsManagerLegacyUpgrade } from "interfaces/L1/IOPContractsManagerLegacyUpgrade.sol"; import { ISemver } from "interfaces/universal/ISemver.sol"; // Contracts @@ -285,6 +287,29 @@ contract OPContractsManager_Upgrade_Harness is CommonTest { faultDisputeGame = IFaultDisputeGame(address(artifacts.mustGetAddress("FaultDisputeGame"))); } + /// @notice Converts the new OpChainConfig struct to the legacy OpChainConfig struct format. + /// This function is used to test Upgrade 13 and 14 paths and can be safely removed + /// after those upgrades are completed. Only difference in the new struct is the added + /// disputeGameUsesSuperRoots boolean. + /// @param _opChainConfigs The new OpChainConfig structs to convert. + /// @return The legacy OpChainConfig structs. + function convertToLegacyConfigs(IOPContractsManager.OpChainConfig[] memory _opChainConfigs) + public + pure + returns (IOPContractsManagerLegacyUpgrade.OpChainConfig[] memory) + { + IOPContractsManagerLegacyUpgrade.OpChainConfig[] memory legacyConfigs = + new IOPContractsManagerLegacyUpgrade.OpChainConfig[](_opChainConfigs.length); + for (uint256 i = 0; i < _opChainConfigs.length; i++) { + legacyConfigs[i] = IOPContractsManagerLegacyUpgrade.OpChainConfig({ + systemConfigProxy: _opChainConfigs[i].systemConfigProxy, + proxyAdmin: _opChainConfigs[i].proxyAdmin, + absolutePrestate: _opChainConfigs[i].absolutePrestate + }); + } + return legacyConfigs; + } + function expectEmitUpgraded(address impl, address proxy) public { vm.expectEmit(proxy); emit Upgraded(impl); @@ -351,7 +376,8 @@ contract OPContractsManager_Upgrade_Harness is CommonTest { vm.etch(_delegateCaller, vm.getDeployedCode("test/mocks/Callers.sol:DelegateCaller")); DelegateCaller(_delegateCaller).dcForward( - address(deployedOPCM), abi.encodeCall(IOPContractsManager.upgrade, (opChainConfigs)) + address(deployedOPCM), + abi.encodeCall(IOPContractsManagerLegacyUpgrade.upgrade, (convertToLegacyConfigs(opChainConfigs))) ); VmSafe.Gas memory gas = vm.lastCallGas(); @@ -398,7 +424,9 @@ contract OPContractsManager_Upgrade_Harness is CommonTest { } function runUpgrade14UpgradeAndChecks(address _delegateCaller) public { - IOPContractsManager.Implementations memory impls = opcm.implementations(); + // TODO(#14665): Replace this address with once the final OPCM is deployed for Upgrade 14. + IOPContractsManager deployedOPCM = IOPContractsManager(address(0x3A1f523a4bc09cd344A2745a108Bb0398288094F)); + IOPContractsManager.Implementations memory impls = deployedOPCM.implementations(); // sanity check IPermissionedDisputeGame oldPDG = @@ -429,7 +457,8 @@ contract OPContractsManager_Upgrade_Harness is CommonTest { vm.etch(_delegateCaller, vm.getDeployedCode("test/mocks/Callers.sol:DelegateCaller")); DelegateCaller(_delegateCaller).dcForward( - address(opcm), abi.encodeCall(IOPContractsManager.upgrade, (opChainConfigs)) + address(deployedOPCM), + abi.encodeCall(IOPContractsManagerLegacyUpgrade.upgrade, (convertToLegacyConfigs(opChainConfigs))) ); VmSafe.Gas memory gas = vm.lastCallGas(); @@ -460,9 +489,124 @@ contract OPContractsManager_Upgrade_Harness is CommonTest { } } + function runUpgrade15UpgradeAndChecks(address _delegateCaller) public { + IOPContractsManager.Implementations memory impls = opcm.implementations(); + + // Predict the address of the new AnchorStateRegistry proxy. + // Subcontext to avoid stack too deep. + address newAsrProxy; + { + // Compute the salt using the system config address. + bytes32 salt = keccak256( + abi.encode( + l2ChainId, + string.concat(string(bytes.concat(bytes32(uint256(uint160(address(systemConfig))))))), + "AnchorStateRegistry-SOT" + ) + ); + + // Use the actual proxy instead of the local code so we can reuse this test. + address proxyBp = opcm.blueprints().proxy; + Blueprint.Preamble memory preamble = Blueprint.parseBlueprintPreamble(proxyBp.code); + bytes memory initCode = bytes.concat(preamble.initcode, abi.encode(proxyAdmin)); + newAsrProxy = vm.computeCreate2Address(salt, keccak256(initCode), _delegateCaller); + vm.label(newAsrProxy, "NewAnchorStateRegistryProxy"); + } + + // Grab the PermissionedDisputeGame and FaultDisputeGame implementations before upgrade. + address oldPDGImpl = address(disputeGameFactory.gameImpls(GameTypes.PERMISSIONED_CANNON)); + address oldFDGImpl = address(disputeGameFactory.gameImpls(GameTypes.CANNON)); + IPermissionedDisputeGame oldPDG = IPermissionedDisputeGame(oldPDGImpl); + IFaultDisputeGame oldFDG = IFaultDisputeGame(oldFDGImpl); + + // Expect the SystemConfig and OptimismPortal to be upgraded. + expectEmitUpgraded(impls.systemConfigImpl, address(systemConfig)); + expectEmitUpgraded(impls.optimismPortalImpl, address(optimismPortal2)); + + // We always expect the PermissionedDisputeGame to be deployed. We don't yet know the + // address of the new permissionedGame which will be deployed by the + // OPContractsManager.upgrade() call, so ignore the first topic. + vm.expectEmit(false, true, true, true, address(disputeGameFactory)); + emit ImplementationSet(address(0), GameTypes.PERMISSIONED_CANNON); + + // If the old FaultDisputeGame exists, we expect it to be upgraded. + if (address(oldFDG) != address(0)) { + // Ignore the first topic for the same reason as the previous comment. + vm.expectEmit(false, true, true, true, address(disputeGameFactory)); + emit ImplementationSet(address(0), GameTypes.CANNON); + } + + vm.expectEmit(address(_delegateCaller)); + emit Upgraded(l2ChainId, systemConfig, address(_delegateCaller)); + + // Temporarily replace the upgrader with a DelegateCaller so we can test the upgrade, + // then reset its code to the original code. + bytes memory delegateCallerCode = address(_delegateCaller).code; + vm.etch(_delegateCaller, vm.getDeployedCode("test/mocks/Callers.sol:DelegateCaller")); + + // Execute the upgrade. + // We use the new format here, not the legacy one. + DelegateCaller(_delegateCaller).dcForward( + address(opcm), abi.encodeCall(IOPContractsManager.upgrade, (opChainConfigs)) + ); + + // Less than 90% of the gas target of 20M to account for the gas used by using Safe. + VmSafe.Gas memory gas = vm.lastCallGas(); + assertLt(gas.gasTotalUsed, 0.9 * 20_000_000, "Upgrade exceeds gas target of 15M"); + + // Reset the upgrader's code to the original code. + vm.etch(_delegateCaller, delegateCallerCode); + + // Grab the new implementations. + address newPDGImpl = address(disputeGameFactory.gameImpls(GameTypes.PERMISSIONED_CANNON)); + IPermissionedDisputeGame pdg = IPermissionedDisputeGame(newPDGImpl); + address newFDGImpl = address(disputeGameFactory.gameImpls(GameTypes.CANNON)); + IFaultDisputeGame fdg = IFaultDisputeGame(newFDGImpl); + + // Check that the PermissionedDisputeGame is upgraded to the expected version, references + // the correct anchor state and has the mipsImpl. Although Upgrade 15 doesn't actually + // change any of this, we might as well check it again. + assertEq(ISemver(address(pdg)).version(), "1.4.1"); + assertEq(address(pdg.vm()), impls.mipsImpl); + assertEq(pdg.l2ChainId(), oldPDG.l2ChainId()); + + // If the old FaultDisputeGame exists, we expect it to be upgraded. Check same as above. + if (address(oldFDG) != address(0)) { + assertEq(ISemver(address(fdg)).version(), "1.4.1"); + assertEq(address(fdg.vm()), impls.mipsImpl); + assertEq(fdg.l2ChainId(), oldFDG.l2ChainId()); + } + + // Make sure that the SystemConfig is upgraded to the right version. It must also have the + // right l2ChainId and must be properly initialized. + assertEq(ISemver(address(systemConfig)).version(), "2.6.0"); + assertEq(impls.systemConfigImpl, EIP1967Helper.getImplementation(address(systemConfig))); + assertEq(systemConfig.l2ChainId(), l2ChainId); + DeployUtils.assertInitialized({ _contractAddress: address(systemConfig), _isProxy: true, _slot: 0, _offset: 0 }); + + // Make sure that the OptimismPortal is upgraded to the right version. It must also have a + // reference to the new AnchorStateRegistry. + assertEq(ISemver(address(optimismPortal2)).version(), "4.0.0"); + assertEq(impls.optimismPortalImpl, EIP1967Helper.getImplementation(address(optimismPortal2))); + assertEq(address(optimismPortal2.anchorStateRegistry()), address(newAsrProxy)); + DeployUtils.assertInitialized({ + _contractAddress: address(optimismPortal2), + _isProxy: true, + _slot: 0, + _offset: 0 + }); + + // Make sure the new AnchorStateRegistry has the right version and is initialized. + assertEq(ISemver(address(newAsrProxy)).version(), "3.0.0"); + vm.prank(address(proxyAdmin)); + assertEq(IProxy(payable(newAsrProxy)).admin(), address(proxyAdmin)); + DeployUtils.assertInitialized({ _contractAddress: address(newAsrProxy), _isProxy: true, _slot: 0, _offset: 0 }); + } + function runUpgradeTestAndChecks(address _delegateCaller) public { runV200UpgradeAndChecks(_delegateCaller); runUpgrade14UpgradeAndChecks(_delegateCaller); + runUpgrade15UpgradeAndChecks(_delegateCaller); } } @@ -1012,7 +1156,7 @@ contract OPContractsManager_UpdatePrestate_Test is Test { }), optimismPortalImpl: DeployUtils.create1({ _name: "OptimismPortal2", - _args: DeployUtils.encodeConstructor(abi.encodeCall(IOptimismPortal2.__constructor__, (1, 1))) + _args: DeployUtils.encodeConstructor(abi.encodeCall(IOptimismPortal2.__constructor__, (1))) }), systemConfigImpl: DeployUtils.create1({ _name: "SystemConfig", @@ -1036,7 +1180,7 @@ contract OPContractsManager_UpdatePrestate_Test is Test { }), anchorStateRegistryImpl: DeployUtils.create1({ _name: "AnchorStateRegistry", - _args: DeployUtils.encodeConstructor(abi.encodeCall(IAnchorStateRegistry.__constructor__, ())) + _args: DeployUtils.encodeConstructor(abi.encodeCall(IAnchorStateRegistry.__constructor__, (1))) }), delayedWETHImpl: DeployUtils.create1({ _name: "DelayedWETH", @@ -1128,6 +1272,7 @@ contract OPContractsManager_UpdatePrestate_Test is Test { l2ChainId: 100, saltMixer: "hello", gasLimit: 30_000_000, + disputeGameUsesSuperRoots: false, disputeGameType: GameType.wrap(1), disputeAbsolutePrestate: Claim.wrap( bytes32(hex"038512e02c4c3f7bdaec27d00edf55b7155e0905301e1a88083e4e0a6764d54c") @@ -1149,7 +1294,10 @@ contract OPContractsManager_UpdatePrestate_Test is Test { function test_updatePrestate_pdgOnlyWithValidInput_succeeds() public { IOPContractsManager.OpChainConfig[] memory inputs = new IOPContractsManager.OpChainConfig[](1); inputs[0] = IOPContractsManager.OpChainConfig( - chainDeployOutput.systemConfigProxy, chainDeployOutput.opChainProxyAdmin, Claim.wrap(bytes32(hex"ABBA")) + chainDeployOutput.systemConfigProxy, + chainDeployOutput.opChainProxyAdmin, + Claim.wrap(bytes32(hex"ABBA")), + false ); address proxyAdminOwner = chainDeployOutput.opChainProxyAdmin.owner(); @@ -1180,7 +1328,10 @@ contract OPContractsManager_UpdatePrestate_Test is Test { IOPContractsManager.OpChainConfig[] memory inputs = new IOPContractsManager.OpChainConfig[](1); inputs[0] = IOPContractsManager.OpChainConfig( - chainDeployOutput.systemConfigProxy, chainDeployOutput.opChainProxyAdmin, Claim.wrap(bytes32(hex"ABBA")) + chainDeployOutput.systemConfigProxy, + chainDeployOutput.opChainProxyAdmin, + Claim.wrap(bytes32(hex"ABBA")), + false ); address proxyAdminOwner = chainDeployOutput.opChainProxyAdmin.owner(); @@ -1217,7 +1368,8 @@ contract OPContractsManager_UpdatePrestate_Test is Test { inputs[0] = IOPContractsManager.OpChainConfig({ systemConfigProxy: chainDeployOutput.systemConfigProxy, proxyAdmin: chainDeployOutput.opChainProxyAdmin, - absolutePrestate: Claim.wrap(bytes32(0)) + absolutePrestate: Claim.wrap(bytes32(0)), + disputeGameUsesSuperRoots: false }); address proxyAdminOwner = chainDeployOutput.opChainProxyAdmin.owner(); diff --git a/packages/contracts-bedrock/test/L1/OPPrestateUpdater.t.sol b/packages/contracts-bedrock/test/L1/OPPrestateUpdater.t.sol deleted file mode 100644 index 563e21d16be6c..0000000000000 --- a/packages/contracts-bedrock/test/L1/OPPrestateUpdater.t.sol +++ /dev/null @@ -1,395 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.15; - -// Testing -import { Test } from "forge-std/Test.sol"; -import { DelegateCaller } from "test/mocks/Callers.sol"; - -// Scripts -import { DeployUtils } from "scripts/libraries/DeployUtils.sol"; - -// Libraries -import { Blueprint } from "src/libraries/Blueprint.sol"; - -// Interfaces -import { IL1ERC721Bridge } from "interfaces/L1/IL1ERC721Bridge.sol"; -import { IL1StandardBridge } from "interfaces/L1/IL1StandardBridge.sol"; -import { IL1CrossDomainMessenger } from "interfaces/L1/IL1CrossDomainMessenger.sol"; -import { IOptimismMintableERC20Factory } from "interfaces/universal/IOptimismMintableERC20Factory.sol"; -import { IAnchorStateRegistry } from "interfaces/dispute/IAnchorStateRegistry.sol"; -import { IMIPS } from "interfaces/cannon/IMIPS.sol"; -import { IOptimismPortal2 } from "interfaces/L1/IOptimismPortal2.sol"; -import { IProxyAdmin } from "interfaces/universal/IProxyAdmin.sol"; -import { ISuperchainConfig } from "interfaces/L1/ISuperchainConfig.sol"; -import { IProtocolVersions } from "interfaces/L1/IProtocolVersions.sol"; -import { IPreimageOracle } from "interfaces/cannon/IPreimageOracle.sol"; -import { IPermissionedDisputeGame } from "interfaces/dispute/IPermissionedDisputeGame.sol"; -import { IDelayedWETH } from "interfaces/dispute/IDelayedWETH.sol"; -import { IOPContractsManager } from "interfaces/L1/IOPContractsManager.sol"; -import { IDisputeGameFactory } from "interfaces/dispute/IDisputeGameFactory.sol"; -import { ISystemConfig } from "interfaces/L1/ISystemConfig.sol"; -import { IOPPrestateUpdater } from "interfaces/L1/IOPPrestateUpdater.sol"; - -// Contracts -import { OPContractsManager } from "src/L1/OPContractsManager.sol"; -import { OPPrestateUpdater } from "src/L1/OPPrestateUpdater.sol"; -import { Blueprint } from "src/libraries/Blueprint.sol"; -import { IBigStepper } from "interfaces/dispute/IBigStepper.sol"; -import { GameType, Duration, Hash, Claim } from "src/dispute/lib/LibUDT.sol"; -import { OutputRoot, GameTypes } from "src/dispute/lib/Types.sol"; - -contract OPPrestateUpdater_Test is Test { - IOPContractsManager internal opcm; - OPPrestateUpdater internal prestateUpdater; - - OPContractsManager.OpChainConfig[] internal opChainConfigs; - OPContractsManager.AddGameInput[] internal gameInput; - - IOPContractsManager.DeployOutput internal chainDeployOutput; - - function setUp() public { - IProxyAdmin superchainProxyAdmin = IProxyAdmin(makeAddr("superchainProxyAdmin")); - ISuperchainConfig superchainConfigProxy = ISuperchainConfig(makeAddr("superchainConfig")); - IProtocolVersions protocolVersionsProxy = IProtocolVersions(makeAddr("protocolVersions")); - bytes32 salt = hex"01"; - IOPContractsManager.Blueprints memory blueprints; - - (blueprints.addressManager,) = Blueprint.create(vm.getCode("AddressManager"), salt); - (blueprints.proxy,) = Blueprint.create(vm.getCode("Proxy"), salt); - (blueprints.proxyAdmin,) = Blueprint.create(vm.getCode("ProxyAdmin"), salt); - (blueprints.l1ChugSplashProxy,) = Blueprint.create(vm.getCode("L1ChugSplashProxy"), salt); - (blueprints.resolvedDelegateProxy,) = Blueprint.create(vm.getCode("ResolvedDelegateProxy"), salt); - (blueprints.permissionedDisputeGame1, blueprints.permissionedDisputeGame2) = - Blueprint.create(vm.getCode("PermissionedDisputeGame"), salt); - (blueprints.permissionlessDisputeGame1, blueprints.permissionlessDisputeGame2) = - Blueprint.create(vm.getCode("FaultDisputeGame"), salt); - - IPreimageOracle oracle = IPreimageOracle( - DeployUtils.create1({ - _name: "PreimageOracle", - _args: DeployUtils.encodeConstructor(abi.encodeCall(IPreimageOracle.__constructor__, (126000, 86400))) - }) - ); - - IOPContractsManager.Implementations memory impls = IOPContractsManager.Implementations({ - superchainConfigImpl: DeployUtils.create1({ - _name: "SuperchainConfig", - _args: DeployUtils.encodeConstructor(abi.encodeCall(ISuperchainConfig.__constructor__, ())) - }), - protocolVersionsImpl: DeployUtils.create1({ - _name: "ProtocolVersions", - _args: DeployUtils.encodeConstructor(abi.encodeCall(IProtocolVersions.__constructor__, ())) - }), - l1ERC721BridgeImpl: DeployUtils.create1({ - _name: "L1ERC721Bridge", - _args: DeployUtils.encodeConstructor(abi.encodeCall(IL1ERC721Bridge.__constructor__, ())) - }), - optimismPortalImpl: DeployUtils.create1({ - _name: "OptimismPortal2", - _args: DeployUtils.encodeConstructor(abi.encodeCall(IOptimismPortal2.__constructor__, (1))) - }), - systemConfigImpl: DeployUtils.create1({ - _name: "SystemConfig", - _args: DeployUtils.encodeConstructor(abi.encodeCall(ISystemConfig.__constructor__, ())) - }), - optimismMintableERC20FactoryImpl: DeployUtils.create1({ - _name: "OptimismMintableERC20Factory", - _args: DeployUtils.encodeConstructor(abi.encodeCall(IOptimismMintableERC20Factory.__constructor__, ())) - }), - l1CrossDomainMessengerImpl: DeployUtils.create1({ - _name: "L1CrossDomainMessenger", - _args: DeployUtils.encodeConstructor(abi.encodeCall(IL1CrossDomainMessenger.__constructor__, ())) - }), - l1StandardBridgeImpl: DeployUtils.create1({ - _name: "L1StandardBridge", - _args: DeployUtils.encodeConstructor(abi.encodeCall(IL1StandardBridge.__constructor__, ())) - }), - disputeGameFactoryImpl: DeployUtils.create1({ - _name: "DisputeGameFactory", - _args: DeployUtils.encodeConstructor(abi.encodeCall(IDisputeGameFactory.__constructor__, ())) - }), - anchorStateRegistryImpl: DeployUtils.create1({ - _name: "AnchorStateRegistry", - _args: DeployUtils.encodeConstructor(abi.encodeCall(IAnchorStateRegistry.__constructor__, (1))) - }), - delayedWETHImpl: DeployUtils.create1({ - _name: "DelayedWETH", - _args: DeployUtils.encodeConstructor(abi.encodeCall(IDelayedWETH.__constructor__, (3))) - }), - mipsImpl: DeployUtils.create1({ - _name: "MIPS", - _args: DeployUtils.encodeConstructor(abi.encodeCall(IMIPS.__constructor__, (oracle))) - }) - }); - - vm.etch(address(superchainConfigProxy), hex"01"); - vm.etch(address(protocolVersionsProxy), hex"01"); - - opcm = IOPContractsManager( - DeployUtils.createDeterministic({ - _name: "OPContractsManager", - _args: DeployUtils.encodeConstructor( - abi.encodeCall( - IOPContractsManager.__constructor__, - ( - superchainConfigProxy, - protocolVersionsProxy, - superchainProxyAdmin, - "dev", - blueprints, - impls, - address(this) - ) - ) - ), - _salt: DeployUtils.DEFAULT_SALT - }) - ); - - chainDeployOutput = opcm.deploy( - IOPContractsManager.DeployInput({ - roles: IOPContractsManager.Roles({ - opChainProxyAdminOwner: address(this), - systemConfigOwner: address(this), - batcher: address(this), - unsafeBlockSigner: address(this), - proposer: address(this), - challenger: address(this) - }), - basefeeScalar: 1, - blobBasefeeScalar: 1, - startingAnchorRoot: abi.encode( - OutputRoot({ - root: Hash.wrap(0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef), - l2BlockNumber: 0 - }) - ), - l2ChainId: 100, - saltMixer: "hello", - gasLimit: 30_000_000, - disputeGameUsesSuperRoots: false, - disputeGameType: GameType.wrap(1), - disputeAbsolutePrestate: Claim.wrap( - bytes32(hex"038512e02c4c3f7bdaec27d00edf55b7155e0905301e1a88083e4e0a6764d54c") - ), - disputeMaxGameDepth: 73, - disputeSplitDepth: 30, - disputeClockExtension: Duration.wrap(10800), - disputeMaxClockDuration: Duration.wrap(302400) - }) - ); - - prestateUpdater = OPPrestateUpdater( - DeployUtils.createDeterministic({ - _name: "OPPrestateUpdater", - _args: DeployUtils.encodeConstructor( - abi.encodeCall( - IOPPrestateUpdater.__constructor__, - (ISuperchainConfig(address(this)), IProtocolVersions(address(this)), blueprints) - ) - ), - _salt: DeployUtils.DEFAULT_SALT - }) - ); - } - - function test_semver_works() public view { - assertNotEq(abi.encode(prestateUpdater.version()), abi.encode(0)); - } - - function test_updatePrestate_pdgOnlyWithValidInput_succeeds() public { - OPContractsManager.OpChainConfig[] memory inputs = new OPContractsManager.OpChainConfig[](1); - inputs[0] = OPContractsManager.OpChainConfig( - chainDeployOutput.systemConfigProxy, - chainDeployOutput.opChainProxyAdmin, - Claim.wrap(bytes32(hex"ABBA")), - false - ); - address proxyAdminOwner = chainDeployOutput.opChainProxyAdmin.owner(); - - vm.etch(address(proxyAdminOwner), vm.getDeployedCode("test/mocks/Callers.sol:DelegateCaller")); - DelegateCaller(proxyAdminOwner).dcForward( - address(prestateUpdater), abi.encodeCall(OPPrestateUpdater.updatePrestate, (inputs)) - ); - - IPermissionedDisputeGame pdg = IPermissionedDisputeGame( - address( - IDisputeGameFactory(chainDeployOutput.systemConfigProxy.disputeGameFactory()).gameImpls( - GameTypes.PERMISSIONED_CANNON - ) - ) - ); - - assertEq(pdg.absolutePrestate().raw(), inputs[0].absolutePrestate.raw(), "pdg prestate mismatch"); - - // Ensure that the WETH contract is not reverting - pdg.weth().balanceOf(address(0)); - } - - function test_updatePrestate_bothGamesWithValidInput_succeeds() public { - // Also add a permissionless game - IOPContractsManager.AddGameInput memory input = newGameInputFactory({ permissioned: false }); - input.disputeGameType = GameTypes.CANNON; - addGameType(input); - - OPContractsManager.OpChainConfig[] memory inputs = new OPContractsManager.OpChainConfig[](1); - inputs[0] = OPContractsManager.OpChainConfig( - chainDeployOutput.systemConfigProxy, - chainDeployOutput.opChainProxyAdmin, - Claim.wrap(bytes32(hex"ABBA")), - false - ); - address proxyAdminOwner = chainDeployOutput.opChainProxyAdmin.owner(); - - vm.etch(address(proxyAdminOwner), vm.getDeployedCode("test/mocks/Callers.sol:DelegateCaller")); - DelegateCaller(proxyAdminOwner).dcForward( - address(prestateUpdater), abi.encodeCall(OPPrestateUpdater.updatePrestate, (inputs)) - ); - - IPermissionedDisputeGame pdg = IPermissionedDisputeGame( - address( - IDisputeGameFactory(chainDeployOutput.systemConfigProxy.disputeGameFactory()).gameImpls( - GameTypes.PERMISSIONED_CANNON - ) - ) - ); - IPermissionedDisputeGame fdg = IPermissionedDisputeGame( - address( - IDisputeGameFactory(chainDeployOutput.systemConfigProxy.disputeGameFactory()).gameImpls( - GameTypes.CANNON - ) - ) - ); - - assertEq(pdg.absolutePrestate().raw(), inputs[0].absolutePrestate.raw(), "pdg prestate mismatch"); - assertEq(fdg.absolutePrestate().raw(), inputs[0].absolutePrestate.raw(), "fdg prestate mismatch"); - - // Ensure that the WETH contracts are not reverting - pdg.weth().balanceOf(address(0)); - fdg.weth().balanceOf(address(0)); - } - - function test_updatePrestate_whenPDGPrestateIsZero_reverts() public { - OPPrestateUpdater.OpChainConfig[] memory inputs = new OPPrestateUpdater.OpChainConfig[](1); - inputs[0] = OPContractsManager.OpChainConfig({ - systemConfigProxy: chainDeployOutput.systemConfigProxy, - proxyAdmin: chainDeployOutput.opChainProxyAdmin, - absolutePrestate: Claim.wrap(bytes32(0)), - disputeGameUsesSuperRoots: false - }); - - address proxyAdminOwner = chainDeployOutput.opChainProxyAdmin.owner(); - vm.etch(address(proxyAdminOwner), vm.getDeployedCode("test/mocks/Callers.sol:DelegateCaller")); - - vm.expectRevert(OPPrestateUpdater.PrestateRequired.selector); - DelegateCaller(proxyAdminOwner).dcForward( - address(prestateUpdater), abi.encodeCall(OPPrestateUpdater.updatePrestate, (inputs)) - ); - } - - function test_deploy_notImplemented_reverts() public { - OPContractsManager.DeployInput memory input = OPContractsManager.DeployInput({ - roles: OPContractsManager.Roles({ - opChainProxyAdminOwner: address(0), - systemConfigOwner: address(0), - batcher: address(0), - unsafeBlockSigner: address(0), - proposer: address(0), - challenger: address(0) - }), - basefeeScalar: 0, - blobBasefeeScalar: 0, - l2ChainId: 0, - startingAnchorRoot: bytes(abi.encode(0)), - saltMixer: "", - gasLimit: 0, - disputeGameUsesSuperRoots: false, - disputeGameType: GameType.wrap(0), - disputeAbsolutePrestate: Claim.wrap(0), - disputeMaxGameDepth: 0, - disputeSplitDepth: 0, - disputeClockExtension: Duration.wrap(0), - disputeMaxClockDuration: Duration.wrap(0) - }); - - vm.expectRevert(OPPrestateUpdater.NotImplemented.selector); - prestateUpdater.deploy(input); - } - - function test_upgrade_notImplemented_reverts() public { - opChainConfigs.push( - OPContractsManager.OpChainConfig({ - systemConfigProxy: ISystemConfig(address(0)), - proxyAdmin: IProxyAdmin(address(0)), - absolutePrestate: Claim.wrap(0), - disputeGameUsesSuperRoots: false - }) - ); - - vm.expectRevert(OPPrestateUpdater.NotImplemented.selector); - prestateUpdater.upgrade(opChainConfigs); - } - - function test_addGameType_notImplemented_reverts() public { - gameInput.push( - OPContractsManager.AddGameInput({ - saltMixer: "hello", - systemConfig: ISystemConfig(address(0)), - proxyAdmin: IProxyAdmin(address(0)), - delayedWETH: IDelayedWETH(payable(address(0))), - disputeGameType: GameType.wrap(2000), - disputeAbsolutePrestate: Claim.wrap(bytes32(hex"deadbeef1234")), - disputeMaxGameDepth: 73, - disputeSplitDepth: 30, - disputeClockExtension: Duration.wrap(10800), - disputeMaxClockDuration: Duration.wrap(302400), - initialBond: 1 ether, - vm: IBigStepper(address(0)), - permissioned: true - }) - ); - - vm.expectRevert(OPPrestateUpdater.NotImplemented.selector); - prestateUpdater.addGameType(gameInput); - } - - function test_l1ContractsRelease_works() public view { - string memory result = "none"; - - assertEq(result, prestateUpdater.l1ContractsRelease()); - } - - function addGameType(IOPContractsManager.AddGameInput memory input) - internal - returns (IOPContractsManager.AddGameOutput memory) - { - IOPContractsManager.AddGameInput[] memory inputs = new IOPContractsManager.AddGameInput[](1); - inputs[0] = input; - - (bool success, bytes memory rawGameOut) = - address(opcm).delegatecall(abi.encodeCall(IOPContractsManager.addGameType, (inputs))); - assertTrue(success, "addGameType failed"); - - IOPContractsManager.AddGameOutput[] memory addGameOutAll = - abi.decode(rawGameOut, (IOPContractsManager.AddGameOutput[])); - return addGameOutAll[0]; - } - - function newGameInputFactory(bool permissioned) internal view returns (IOPContractsManager.AddGameInput memory) { - return IOPContractsManager.AddGameInput({ - saltMixer: "hello", - systemConfig: chainDeployOutput.systemConfigProxy, - proxyAdmin: chainDeployOutput.opChainProxyAdmin, - delayedWETH: IDelayedWETH(payable(address(0))), - disputeGameType: GameType.wrap(2000), - disputeAbsolutePrestate: Claim.wrap(bytes32(hex"deadbeef1234")), - disputeMaxGameDepth: 73, - disputeSplitDepth: 30, - disputeClockExtension: Duration.wrap(10800), - disputeMaxClockDuration: Duration.wrap(302400), - initialBond: 1 ether, - vm: IBigStepper(address(opcm.implementations().mipsImpl)), - permissioned: permissioned - }); - } -} diff --git a/packages/contracts-bedrock/test/L1/OptimismPortal2.t.sol b/packages/contracts-bedrock/test/L1/OptimismPortal2.t.sol index c6f02f3766856..e5fc247994fd5 100644 --- a/packages/contracts-bedrock/test/L1/OptimismPortal2.t.sol +++ b/packages/contracts-bedrock/test/L1/OptimismPortal2.t.sol @@ -54,7 +54,6 @@ contract OptimismPortal2_Test is CommonTest { assertEq(address(opImpl.anchorStateRegistry()), address(0)); assertEq(address(opImpl.systemConfig()), address(0)); assertEq(address(opImpl.superchainConfig()), address(0)); - assertEq(opImpl.respectedGameType().raw(), deploy.cfg().respectedGameType()); // TODO(opcm upgrades): remove skip once upgrade path is implemented returnIfForkTest("OptimismPortal2_Test: l2Sender is nonzero on OP mainnet"); diff --git a/packages/contracts-bedrock/test/setup/ForkLive.s.sol b/packages/contracts-bedrock/test/setup/ForkLive.s.sol index 80b516f4ee09e..2fcee28ee5532 100644 --- a/packages/contracts-bedrock/test/setup/ForkLive.s.sol +++ b/packages/contracts-bedrock/test/setup/ForkLive.s.sol @@ -24,7 +24,7 @@ import { ISystemConfig } from "interfaces/L1/ISystemConfig.sol"; import { IProxyAdmin } from "interfaces/universal/IProxyAdmin.sol"; import { IOPContractsManager } from "interfaces/L1/IOPContractsManager.sol"; import { IAnchorStateRegistry } from "interfaces/dispute/IAnchorStateRegistry.sol"; - +import { IOPContractsManagerLegacyUpgrade } from "interfaces/L1/IOPContractsManagerLegacyUpgrade.sol"; /// @title ForkLive /// @notice This script is called by Setup.sol as a preparation step for the foundry test suite, and is run as an /// alternative to Deploy.s.sol, when `FORK_TEST=true` is set in the env. @@ -34,6 +34,7 @@ import { IAnchorStateRegistry } from "interfaces/dispute/IAnchorStateRegistry.so /// Therefore this script can only be run against a fork of a production network which is listed in the /// superchain-registry. /// This contract must not have constructor logic because it is set into state using `etch`. + contract ForkLive is Deployer { using stdToml for string; @@ -185,10 +186,34 @@ contract ForkLive is Deployer { // then reset its code to the original code. bytes memory upgraderCode = address(upgrader).code; vm.etch(upgrader, vm.getDeployedCode("test/mocks/Callers.sol:DelegateCaller")); + + // Some upgrades require the legacy format. + IOPContractsManagerLegacyUpgrade.OpChainConfig[] memory legacyConfigs = + new IOPContractsManagerLegacyUpgrade.OpChainConfig[](opChains.length); + for (uint256 i = 0; i < opChains.length; i++) { + legacyConfigs[i] = IOPContractsManagerLegacyUpgrade.OpChainConfig({ + systemConfigProxy: opChains[i].systemConfigProxy, + proxyAdmin: opChains[i].proxyAdmin, + absolutePrestate: opChains[i].absolutePrestate + }); + } + + // Start by doing Upgrade 13. DelegateCaller(upgrader).dcForward( - address(0x026b2F158255Beac46c1E7c6b8BbF29A4b6A7B76), abi.encodeCall(IOPContractsManager.upgrade, (opChains)) + address(0x026b2F158255Beac46c1E7c6b8BbF29A4b6A7B76), + abi.encodeCall(IOPContractsManagerLegacyUpgrade.upgrade, (legacyConfigs)) ); + + // Then do Upgrade 14. + DelegateCaller(upgrader).dcForward( + address(0x3A1f523a4bc09cd344A2745a108Bb0398288094F), + abi.encodeCall(IOPContractsManagerLegacyUpgrade.upgrade, (legacyConfigs)) + ); + + // Then do the final upgrade. DelegateCaller(upgrader).dcForward(address(opcm), abi.encodeCall(IOPContractsManager.upgrade, (opChains))); + + // Reset the upgrader to the original code. vm.etch(upgrader, upgraderCode); console.log("ForkLive: Saving newly deployed contracts");