diff --git a/specs/experimental/op-contracts-manager.md b/specs/experimental/op-contracts-manager.md index 503107f49..20a39760d 100644 --- a/specs/experimental/op-contracts-manager.md +++ b/specs/experimental/op-contracts-manager.md @@ -20,80 +20,102 @@ of governance approved [contract releases] can be found on the **Table of Contents** +- [Overview](#overview) +- [Getter Methods](#getter-methods) - [Deployment](#deployment) -- [Interface](#interface) - - [`Proxy.sol`](#proxysol) - - [`deploy`](#deploy) - - [Getter Methods](#getter-methods) -- [Implementation](#implementation) - - [Batch Inbox Address](#batch-inbox-address) - - [Contract Deployments](#contract-deployments) + - [Interface](#interface) + - [`deploy`](#deploy) + - [Implementation](#implementation) + - [Batch Inbox Address](#batch-inbox-address) + - [Contract Deployments](#contract-deployments) +- [Upgrading](#upgrading) + - [Interface](#interface-1) + - [`upgrade`](#upgrade) + - [Implementation](#implementation-1) + - [`IsthmusConfig` struct](#isthmusconfig-struct) + - [Requirements on the OP Chain contracts](#requirements-on-the-op-chain-contracts) +- [Adding game types](#adding-game-types) + - [Interface](#interface-2) + - [`addGameType`](#addgametype) + - [Implementation](#implementation-2) - [Security Considerations](#security-considerations) - [Chain ID Source of Truth](#chain-id-source-of-truth) - [Chain ID Frontrunning](#chain-id-frontrunning) - [Chain ID Value](#chain-id-value) - [Proxy Admin Owner](#proxy-admin-owner) - - [Upgradeability (ABI Changes)](#upgradeability-abi-changes) + - [Safely using `DELEGATECALL`](#safely-using-delegatecall) + - [Atomicity of upgrades](#atomicity-of-upgrades) -## Deployment +## Overview -The OP Contracts Manager is a proxied contract deployed at `0xTODO`. It can be deployed as follows: +The OP Contracts Manager refers to a series of contracts, of which a new singleton is deployed +for each new release of the OP Stack contracts. -TODO. +The OP Contracts Manager corresponding to each release can be used to: -## Interface +1. Deploy a new OP chain. +2. Upgrade the contracts for an existing OP chain from the previous release to the new release. +3. Orchestrate adding a new game type on a per-chain basis -Version 1.0.0 of the OP Contracts Manager deploys the `op-contracts/v1.6.0` -contracts release. +Upgrades must be performed by the [Upgrade Controller](../protocol/stage-1.md#roles-for-stage-1) Safe for a chain. -### `Proxy.sol` +## Getter Methods -The OP Contracts Manager is a proxied contract using the standard `Proxy.sol` contract that lives in -the Optimism monorepo. Therefore the OP Contracts Manager will have the same interface as the -`Proxy.sol`, in addition to other methods defined in this specification. +The following interface defines the available getter methods: -The privileged methods of the OP Contracts Manager will be held by the L1 ProxyAdmin owner, as -specified by the [standard configuration]. +```solidity +/// @notice Returns the latest approved release of the OP Stack contracts are named with the +/// format `op-contracts/vX.Y.Z`. +function release() external view returns (string memory); +/// @notice Represents the interface version so consumers know how to decode the DeployOutput struct +function outputVersion() external view returns (uint256); +/// @notice Addresses of the Blueprint contracts. +function blueprints() external view returns (Blueprints memory); +/// @notice Maps an L2 chain ID to an L1 batch inbox address +function chainIdToBatchInboxAddress(uint256 _l2ChainId) external pure returns (address); +/// @notice Addresses of the latest implementation contracts. +function implementations() external view returns (Implementations memory); +/// @notice Address of the ProtocolVersions contract shared by all chains. +function protocolVersions() external view returns (address); +/// @notice Address of the SuperchainConfig contract shared by all chains. +function superchainConfig() external view returns (address); +/// @notice Maps an L2 Chain ID to the SystemConfig for that chain. +function systemConfigs(uint256 _l2ChainId) external view returns (address); +/// @notice Semver version specific to the OPContractsManager +function version() external view returns (string memory); +``` -### `deploy` +## Deployment -The `deploy` method is the only non-view method in the contract. It is used to -deploy the full set of L1 contracts required to setup a new OP Stack chain that -complies with the [standard configuration]. It has the following interface: +### Interface -```solidity -struct Roles { - address proxyAdminOwner; - address systemConfigOwner; - address batcher; - address unsafeBlockSigner; - address proposer; - address challenger; -} +#### `deploy` + +The `deploy` method is used to deploy the full set of L1 contracts required to setup a new OP Stack +chain that complies with the [standard configuration]. It has the following interface: -function deploy( - uint256 l2ChainId, - uint32 basefeeScalar, - uint32 blobBasefeeScalar, - Roles roles -) external returns (SystemConfig) +```solidity +/// @notice Deploys a new OP Chain +/// @param _input DeployInput containing chain specific config information. +/// @return DeployOutput containing the new addresses. +function deploy(DeployInput calldata _input) external returns (DeployOutput memory) ``` The `l2ChainId` has the following restrictions: - It must not be equal to 0. - It must not be equal to the chain ID of the chain the OP Contracts Manager is -deployed on. + deployed on. - It must not be equal to a chain ID that is already present in the -[ethereum-lists/chains] repository. This is not enforced onchain, but may matter -for future versions of OP Contracts Manager that handle upgrades. + [ethereum-lists/chains] repository. This is not enforced onchain, but may matter + for future versions of OP Contracts Manager that handle upgrades. On success, the following event is emitted: ```solidity -event Deployed(uint256 indexed l2ChainId, SystemConfig indexed systemConfig); +event Deployed(uint256 indexed outputVersion, uint256 indexed l2ChainId, address indexed deployer, bytes deployOutput); ``` This method reverts on failure. This occurs when: @@ -101,65 +123,171 @@ This method reverts on failure. This occurs when: - The input `l2ChainId` does not comply with the restrictions above. - The resulting configuration is not compliant with the [standard configuration]. -### Getter Methods +### Implementation -The following interface defines the available getter methods: +#### Batch Inbox Address + +The chain's [Batch Inbox] address is computed at deploy time using the recommend approach defined +in the [standard configuration]. This improves UX by removing an input, and ensures uniqueness of +the batch inbox addresses. + +#### Contract Deployments + +All contracts deployed by the OP Contracts Manager are deployed with `CREATE2`, using the following salt: ```solidity -/// @notice The logic address and initializer selector for an implementation contract. -struct Implementation { - address logic; // Address containing the deployed logic contract. - bytes4 initializer; // Function selector for the initializer. +keccak256(abi.encode(_l2ChainId, _saltMixer, _contractName)); +``` + +The `saltMixer` value is provided as a field in the `DeployInput` struct. + +This provides the following benefits: + +- Contract addresses for a chain can be derived as a function of chain ID without any RPC calls. +- Chain ID uniqueness is enforced for free, as a deploy using the same chain ID + will result in attempting to deploy to the same address, which is prohibited by + the EVM. + - This property is contingent on the proxy and `AddressManager` code not + changing when OP Contracts Manager is upgraded. Both of these are not planned to + change. + - The OP Contracts Manager is not responsible for enforcing chain ID uniqueness, so it is acceptable + if this property is not preserved in future versions of the OP Contracts Manager. + +## Upgrading + +### Interface + +#### `upgrade` + +The `upgrade` method is used by the Upgrade Controller to upgrade the full set of L1 contracts for +all chains that it controls. + +It has the following interface: + +```solidity +struct Roles { + address opChainProxyAdminOwner; + address systemConfigOwner; + address batcher; + address unsafeBlockSigner; + address proposer; + address challenger; } -/// @notice Returns the latest approved release of the OP Stack contracts. -/// @notice Release strings follow semver and are named with the -/// format `op-contracts/vX.Y.Z`. -function latestRelease() external view returns (string memory); +struct DeployInput { + Roles roles; + uint32 basefeeScalar; + uint32 blobBasefeeScalar; + uint256 l2ChainId; + bytes startingAnchorRoots; + string saltMixer; + uint64 gasLimit; + uint32 disputeGameType; + bytes32 disputeAbsolutePrestate; + uint256 disputeMaxGameDepth; + uint256 disputeSplitDepth; + uint64 disputeClockExtension; + uint64 disputeMaxClockDuration; +} + +function upgrade(ISystemConfig[] _systemConfigs, IProxyAdmin[] _proxyAdmins, NewChainConfig[] _newConfigs) public; +``` -/// @notice Maps a release version to a contract name to its implementation data. -function implementation( - string memory release, - string memory contractName -) external view returns (Implementation memory); +For each chain successfully upgraded, the following event is emitted: -/// @notice Maps an L2 Chain ID to the SystemConfig address for that chain. -/// @notice All contracts for a chain can be found from its SystemConfig. -function systemConfig(uint256 chainId) external view returns (SystemConfig); +```solidity +event Upgraded(uint256 indexed l2ChainId, SystemConfig indexed systemConfig, address indexed upgrader); ``` -## Implementation +This method reverts if the upgrade is not successful for any of the chains. -### Batch Inbox Address +### Implementation -The chain's [Batch Inbox] address is computed at deploy time using the recommend approach defined -in the [standard configuration]. This improves UX by removing an input, and ensures uniqueness of -the batch inbox addresses. +The high level logic of the upgrade method is as follows: -### Contract Deployments +1. The Upgrade Controller Safe will `DELEGATECALL` to the `OPCM.upgrade()` method. +2. For each `_systemConfig`, the list of addresses in the chain is retrieved. +3. For each address, a two step upgrade is used where: + 1. the first upgrade is to an `InitializerResetter` which resets the `initialized` value. + 1. the implementation is updated to the final address and `upgrade()` is called on that address. -All contracts deployed by the OP Contracts Manager are deployed with CREATE2, with a -salt equal to either: +This approach requires that all contracts have an `upgrade()` function which sets the `initialized` +value to `true`. The `upgrade` function body should be empty unless it is used to set a new state +variable added to that contract since the last upgrade. -- The L2 chain ID, or -- `keccak256(bytes.concat(bytes32(uint256(l2ChainId)), contractName))`. +#### `IsthmusConfig` struct -The former is used when only a single instance of a given contract is deployed for a chain. -The latter is used when deploying multiple instances of a given contract for a chain, -which is the case of all `Proxy` contracts. For these, the `contractName` -is the name of the implementation contract that will be used with the proxy. +This struct is used to pass the new chain configuration to the `upgrade` method, and so it will +vary for each release of the OP Contracts Manager, based on what (if any) new parameters are added. -This provides the following benefits: +In practice, this struct is likely to be have a unique name for each release of the OP Contracts +Manager. -- Contract addresses for a chain can be derived as a function of chain ID without any RPC calls. -- Chain ID uniqueness is enforced for free, as a deploy using the same chain ID -will result in attempting to deploy to the same address, which is prohibited by -the EVM. - - This property is contingent on the proxy and `AddressManager` code not - changing when OP Contracts Manager is upgraded. Both of these are not planned to - change. - - The OP Contracts Manager is not responsible for enforcing chain ID uniqueness, so it is acceptable - if this property is not preserved in future versions of the OP Contracts Manager. +By way of example, if an upgrade is adding a new variable `address foo` to the `SystemConfig` contract, for +an upgrade named `Example`, the struct could have the following definition: + +```solidity +struct IsthmusConfig { + uint32 public operatorFeeScalar; + uint64 public operatorFeeConstant; +} +``` + +#### Requirements on the OP Chain contracts + +In general, all contracts used in an OP Chain SHOULD be proxied with a single shared implementation. +This means that all values which are not constant across OP Chains SHOULD be held in storage rather +than the bytecode of the implementation. + +Any contracts which do not meet this requirement will need to be deployed by the `upgrade()` +function, increasing the cost and reducing the number of OP Chains which can be atomically upgraded. + +## Adding game types + +Because different OP Chains within a Superchain may use different dispute game types, and are +expected to move from a permissioned to permissionless game over time, an `addGameType()` method is +provided to enable adding a new game type to multiple games at once. + +### Interface + +#### `addGameType` + +The `addGameType` method is used to orchestrate the actions required to add a new game type to one +or more chains. + +```solidity +struct PermissionlessGameConfig { + bytes32 absolutePrestate; +} + +function addGameType(ISystemConfig[] _systemConfigs, PermissionlessGameConfig[] _newGames) public; +``` + +### Implementation + +The high level logic of the `addGameType` method is as follows (for each chain): + +1. Deploy and initialize new `DelayedWethProxy` for the new game type, reusing the existing implementation +1. Deploy a new `FaultDisputeGame` contract. The source of the constructor args is indicated below this list, + the value which is not available onchain is the `absolutePrestate`. +1. Calls `upgrade()` on the `AnchorStateRegistry` to set the new game type to + add a new entry to the `anchors` mapping. The `upgrade()` method should + revert if it would overwrite an existing entry. +1. Read the `DisputeGameFactory` address from the `SystemConfig`. +1. Call `DisputeGameFactory.setImplementation()` to register the new game. + +| Name | Type | Description | Source | +| ------------------- | ------- | -------------------------------------------------- | ------------------------------------------ | +| gameType | uint32 | Constant value of 1 indicating the game type | Hardcoded constant | +| absolutePrestate | bytes32 | Initial state of the game | Input in `PermissionlessGameConfig` struct | +| maxGameDepth | uint256 | Maximum depth of the game tree | Copied from existing `PermissionedGame` | +| splitDepth | uint256 | Depth at which the game tree splits | Copied from existing `PermissionedGame` | +| clockExtension | uint64 | Time extension granted for moves | Copied from existing `PermissionedGame` | +| maxClockDuration | uint64 | Maximum duration of the game clock | Copied from existing `PermissionedGame` | +| vm | address | Virtual machine contract address | Copied from existing `PermissionedGame` | +| weth | address | Address of the newly deployed DelayedWeth contract | Newly deployed contract | +| anchorStateRegistry | address | Registry contract address | Copied from existing `PermissionedGame` | +| l2ChainId | uint256 | Chain ID of the L2 network | Copied from existing `PermissionedGame` | ## Security Considerations @@ -170,10 +298,10 @@ once per chain ID, because contract addresses are a function of chain ID. Howeve future versions of OP Contracts Manager may: - Change the Proxy code used, which would allow a duplicate chain ID to be deployed -if there is only the implicit check. + if there is only the implicit check. - Manage upgrades, which will require "registering" existing pre-OP Contracts Manager -chains in the OP Contracts Manager. Registration will be a privileged action, and the [superchain registry] will be -used as the source of truth for registrations. + chains in the OP Contracts Manager. Registration will be a privileged action, and the [superchain registry] will be + used as the source of truth for registrations. This means, for example, if deploying a chain with a chain ID of 10—which is OP Mainnet's chain ID—deployment will execute successfully, but the entry in OP @@ -207,10 +335,21 @@ The proxy admin owner is a very powerful role, as it allows upgrading protocol contracts. When choosing the initial proxy admin owner, a Safe is recommended to ensure admin privileges are sufficiently secured. -### Upgradeability (ABI Changes) +### Safely using `DELEGATECALL` + +Because a Safe will `DELEGATECALL` to the `upgrade()` and `addGameType()` methods, it is +critical that no storage writes occur. This should be enforced in multiple ways, including: + +- By static analysis of the `upgrade()` and `addGameType()` methods during the development process. +- By simulating and verifying the state changes which occur in the Upgrade Controller Safe prior to execution. + +### Atomicity of upgrades + +Although atomicity of a superchain upgrade is not essential for many types of upgrade, it will +at times be necessary. It is certainly always desirable for operational reasons. -This contract is upgradeable, and breaking changes are expected, as upgrades -are required to update the contracts release that is deployed. This is because -the required inputs to the `deploy` method may change as new contract releases -are supported. Therefore, if calling this contract from another contract, be -sure to account for future breaking changes to the ABI. +For this reason, efficiency should be kept in mind when designing the upgrade path. When the size of +the superchain reaches a size that nears the block gas limit, upgrades may need to be broken up into +stages, so that components which must be upgrade atomically can be. For example, all +`OptimismPortal` contracts may need to be upgraded in one transaction, followed by another +transaction which upgrades all `L1CrossDomainMessenger` contracts. diff --git a/specs/protocol/configurability.md b/specs/protocol/configurability.md index 267cb3521..ce5e411b9 100644 --- a/specs/protocol/configurability.md +++ b/specs/protocol/configurability.md @@ -87,7 +87,7 @@ see [Batcher Transaction](../glossary.md#batcher-transaction)).
**Requirement:** Current convention is versionByte || keccak256(bytes32(chainId))\[:19\], where || denotes concatenation, `versionByte` is `0x00`, and `chainId` is a `uint256`.
-**Notes:** It is recommended, but not required, to follow this convention. +**Notes:** It is recommended, but not required, to follow this convention. ### [Batcher Hash](./system-config.md#batcherhash-bytes32) @@ -130,7 +130,7 @@ to finalize withdrawals.
**Requirement:** [`CANNON` ( `0`)](https://github.com/ethereum-optimism/optimism/blob/op-contracts/v1.5.0/packages/contracts-bedrock/src/dispute/lib/Types.sol#L28)
**Notes:** The game type may be changed to [`PERMISSIONED_CANNON` ( -`1`)]() +`1`)](https://github.com/ethereum-optimism/optimism/blob/op-contracts/v1.5.0/packages/contracts-bedrock/src/dispute/lib/Types.sol#L31) as a fallback to permissioned proposals, in the event of a failure in the Fault Proof system.
### Fault Game Max Depth @@ -288,7 +288,7 @@ contracts deployed on layer 1.
### Resource Config | Config Property | Standard Config Requirement | -|-----------------------------|-----------------------------| +| --------------------------- | --------------------------- | | maxResourceLimit | $2*10^7$ | | elasticityMultiplier | $10$ | | baseFeeMaxChangeDenominator | $8$ | @@ -360,7 +360,7 @@ from the latest `op-contracts/vX.Y.X` release of source code in [ProxyAdmin.sol](https://github.com/ethereum-optimism/optimism/blob/op-contracts/v1.3.0/packages/contracts-bedrock/src/universal/ProxyAdmin.sol) from the latest `op-contracts/vX.Y.X` release of source code in [Optimism repository](https://github.com/ethereum-optimism/optimism). Predeploy -address: [0x4200000000000000000000000000000000000018](https://docs.optimism.io/chain/addresses#op-mainnet-l2).
+address: [0x4200000000000000000000000000000000000018](https://docs.optimism.io/chain/addresses#op-mainnet-l2).
**Notes:** Governance-controlled, high security.
### L2 ProxyAdmin owner @@ -379,7 +379,7 @@ Address: **Description:** Account authorized to change values in the SystemConfig contract. All configuration is stored on L1 and picked up by L2 as part of the [derivation](./derivation.md) of the L2 chain.
-**Administrator:**
+**Administrator:** [L1 Proxy Admin](#admin-roles)
**Administers:** [Batch submitter address](#service-roles), [Sequencer P2P / Unsafe head signer](#service-roles), [Fee Margin](#consensus-parameters), [Gas limit](#consensus-parameters), [System Config Owner](#admin-roles)
**Requirement:** Chain Governor or Servicer