Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -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");
}
}

Expand Down
157 changes: 157 additions & 0 deletions packages/contracts-bedrock/src/L1/OPContractsManager14.sol
Original file line number Diff line number Diff line change
@@ -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);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To confirm my understanding, this MIPS contract is the shared singleton instance right?

That would mean that after this upgrade every chain would point to the same MIPS contract instance and thus the same PreimageOracle instance. Currently a bunch of chains have their own deployment of MIPS and PreimageOracle which makes monitoring more complex so would be wonderful if that got fixed as part of this upgrade. Existing games would keep pointing to whatever MIPS/PreimageOracle they were using previously so there's no compatibility concerns here, I'm just checking my understanding to ensure we really do get everything switched to using the singletons.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes - this is my understanding. @maurelian or @mslipper - can you confirm?

// 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));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
Expand Down