diff --git a/packages/contracts-bedrock/scripts/deploy/DeployImplementations.s.sol b/packages/contracts-bedrock/scripts/deploy/DeployImplementations.s.sol index e94a4c1859392..ba5cc301e15b7 100644 --- a/packages/contracts-bedrock/scripts/deploy/DeployImplementations.s.sol +++ b/packages/contracts-bedrock/scripts/deploy/DeployImplementations.s.sol @@ -746,10 +746,10 @@ contract DeployImplementations is Script { uint256 mipsVersion = _dii.mipsVersion(); IPreimageOracle preimageOracle = IPreimageOracle(address(_dio.preimageOracleSingleton())); - // We want to ensure that the OPCM for upgrade 13 is deployed with Mips32 on production networks. - if (mipsVersion != 1) { + // We want to ensure that the OPCM for upgrade 14 is deployed with Mips64 on production networks. + if (mipsVersion != 2) { if (block.chainid == Chains.Mainnet || block.chainid == Chains.Sepolia) { - revert("DeployImplementations: Only Mips32 should be deployed on Mainnet or Sepolia"); + revert("DeployImplementations: Only Mips64 should be deployed on Mainnet or Sepolia"); } } diff --git a/packages/contracts-bedrock/src/L1/OPContractsManager14.sol b/packages/contracts-bedrock/src/L1/OPContractsManager14.sol new file mode 100644 index 0000000000000..2f7fcc7c81002 --- /dev/null +++ b/packages/contracts-bedrock/src/L1/OPContractsManager14.sol @@ -0,0 +1,157 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.15; + +import { OPContractsManager } from "./OPContractsManager.sol"; +// Libraries +import { Blueprint } from "src/libraries/Blueprint.sol"; +import { Claim, GameType, GameTypes } from "src/dispute/lib/Types.sol"; +// Interfaces +import { IBigStepper } from "interfaces/dispute/IBigStepper.sol"; +import { IDisputeGame } from "interfaces/dispute/IDisputeGame.sol"; +import { ISystemConfig } from "interfaces/L1/ISystemConfig.sol"; +import { IDisputeGameFactory } from "interfaces/dispute/IDisputeGameFactory.sol"; +import { IFaultDisputeGame } from "interfaces/dispute/IFaultDisputeGame.sol"; +import { IPermissionedDisputeGame } from "interfaces/dispute/IPermissionedDisputeGame.sol"; +import { IProtocolVersions } from "interfaces/L1/IProtocolVersions.sol"; +import { ISuperchainConfig } from "interfaces/L1/ISuperchainConfig.sol"; +import { IProxyAdmin } from "interfaces/universal/IProxyAdmin.sol"; + +/// @title OPContractsManager14 +/// @notice Represents the new OPContractsManager for Upgrade 14 +contract OPContractsManager14 is OPContractsManager { + function version() public pure override returns (string memory) { + return string.concat(super.version(), "+upgrade14.1"); + } + + constructor( + ISuperchainConfig _superchainConfig, + IProxyAdmin _superchainProxyAdmin, + string memory _l1ContractsRelease, + Blueprints memory _blueprints, + Implementations memory _implementations, + address _upgradeController + ) + OPContractsManager( + _superchainConfig, + 0x0, // No longer required for upgrade 14 + _superchainProxyAdmin, + _l1ContractsRelease, + _blueprints, + _implementations, + _upgradeController + ) + { } + + /// @notice Upgrades a set of chains to the latest implementation contracts + /// @param _opChainConfigs Array of OpChain structs, one per chain to upgrade + /// @dev This function is intended to be called via DELEGATECALL from the Upgrade Controller Safe + function upgrade(OpChainConfig[] memory _opChainConfigs) external override { + if (address(this) == address(thisOPCM)) revert OnlyDelegatecall(); + + // If this is delegatecalled by the upgrade controller, set isRC to false first, else, continue execution. + if (address(this) == upgradeController) { + // Set isRC to false. + // This function asserts that the caller is the upgrade controller. + thisOPCM.setRC(false); + } + + Implementations memory impls = getImplementations(); + Blueprints memory bps = getBlueprints(); + + for (uint256 i = 0; i < _opChainConfigs.length; i++) { + assertValidOpChainConfig(_opChainConfigs[i]); + ISystemConfig.Addresses memory opChainAddrs = _opChainConfigs[i].systemConfigProxy.getAddresses(); + + // -------- Discover and Upgrade Proofs Contracts -------- + + // All chains have the Permissioned Dispute Game. + IPermissionedDisputeGame permissionedDisputeGame = IPermissionedDisputeGame( + address( + getGameImplementation( + IDisputeGameFactory(opChainAddrs.disputeGameFactory), GameTypes.PERMISSIONED_CANNON + ) + ) + ); + // We're also going to need the l2ChainId below, so we cache it in the outer scope. + uint256 l2ChainId = getL2ChainId(IFaultDisputeGame(address(permissionedDisputeGame))); + + // Now retrieve the permissionless game. If it exists, replace its implementation. + IFaultDisputeGame permissionlessDisputeGame = IFaultDisputeGame( + address(getGameImplementation(IDisputeGameFactory(opChainAddrs.disputeGameFactory), GameTypes.CANNON)) + ); + + if (address(permissionlessDisputeGame) != address(0)) { + // Deploy and set a new permissionless game to update its prestate + deployAndSetNewGameImpl({ + _l2ChainId: l2ChainId, + _disputeGame: IDisputeGame(address(permissionlessDisputeGame)), + _gameType: GameTypes.CANNON, + _opChainConfig: _opChainConfigs[i], + _implementations: impls, + _blueprints: bps, + _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)); + } + } + + /// @notice Deploys and sets a new dispute game implementation + /// @param _l2ChainId The L2 chain ID + /// @param _disputeGame The current dispute game implementation + /// @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, + GameType _gameType, + OpChainConfig memory _opChainConfig, + Blueprints memory _blueprints, + Implementations memory _implementations, + ISystemConfig.Addresses memory _opChainAddrs + ) + internal + { + // Get the constructor params for the game + IFaultDisputeGame.GameConstructorParams memory params = + getGameConstructorParams(IFaultDisputeGame(address(_disputeGame))); + + // Set the new vm value. + params.vm = IBigStepper(_implementations.mipsImpl); + // Set the new absolute prestate + if (Claim.unwrap(_opChainConfig.absolutePrestate) == bytes32(0)) { + revert PrestateNotSet(); + } + params.absolutePrestate = _opChainConfig.absolutePrestate; + + IDisputeGame newGame; + if (GameType.unwrap(_gameType) == GameType.unwrap(GameTypes.PERMISSIONED_CANNON)) { + address proposer = getProposer(IPermissionedDisputeGame(address(_disputeGame))); + address challenger = getChallenger(IPermissionedDisputeGame(address(_disputeGame))); + newGame = IDisputeGame( + Blueprint.deployFrom( + _blueprints.permissionedDisputeGame1, + _blueprints.permissionedDisputeGame2, + computeSalt(_l2ChainId, reusableSaltMixer(_opChainConfig), "PermissionedDisputeGame"), + encodePermissionedFDGConstructor(params, proposer, challenger) + ) + ); + } else { + newGame = IDisputeGame( + Blueprint.deployFrom( + _blueprints.permissionlessDisputeGame1, + _blueprints.permissionlessDisputeGame2, + computeSalt(_l2ChainId, reusableSaltMixer(_opChainConfig), "PermissionlessDisputeGame"), + encodePermissionlessFDGConstructor(params) + ) + ); + } + setDGFImplementation(IDisputeGameFactory(_opChainAddrs.disputeGameFactory), _gameType, IDisputeGame(newGame)); + } +} diff --git a/packages/contracts-bedrock/test/opcm/DeployImplementations.t.sol b/packages/contracts-bedrock/test/opcm/DeployImplementations.t.sol index d2fc138e91f76..85680343ccc95 100644 --- a/packages/contracts-bedrock/test/opcm/DeployImplementations.t.sol +++ b/packages/contracts-bedrock/test/opcm/DeployImplementations.t.sol @@ -432,14 +432,14 @@ contract DeployImplementations_Test is Test { function test_run_deployMipsV1OnMainnetOrSepolia_reverts() public { setDefaults(); - dii.set(dii.mipsVersion.selector, 2); + dii.set(dii.mipsVersion.selector, 1); vm.chainId(Chains.Mainnet); - vm.expectRevert("DeployImplementations: Only Mips32 should be deployed on Mainnet or Sepolia"); + vm.expectRevert("DeployImplementations: Only Mips64 should be deployed on Mainnet or Sepolia"); deployImplementations.run(dii, dio); vm.chainId(Chains.Sepolia); - vm.expectRevert("DeployImplementations: Only Mips32 should be deployed on Mainnet or Sepolia"); + vm.expectRevert("DeployImplementations: Only Mips64 should be deployed on Mainnet or Sepolia"); deployImplementations.run(dii, dio); } }