From 00ce042c7fd56e5e0dd885bc53da1b043e1c6f1e Mon Sep 17 00:00:00 2001 From: Maurelian Date: Thu, 28 Nov 2024 13:34:46 -0500 Subject: [PATCH 01/27] Add opcm upgrades spec --- specs/experimental/op-contracts-manager.md | 86 +++++++++++++--------- 1 file changed, 50 insertions(+), 36 deletions(-) diff --git a/specs/experimental/op-contracts-manager.md b/specs/experimental/op-contracts-manager.md index 503107f49..309ae3155 100644 --- a/specs/experimental/op-contracts-manager.md +++ b/specs/experimental/op-contracts-manager.md @@ -18,6 +18,7 @@ of governance approved [contract releases] can be found on the + **Table of Contents** - [Deployment](#deployment) @@ -39,29 +40,19 @@ of governance approved [contract releases] can be found on the ## Deployment -The OP Contracts Manager is a proxied contract deployed at `0xTODO`. It can be deployed as follows: - -TODO. +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. ## Interface -Version 1.0.0 of the OP Contracts Manager deploys the `op-contracts/v1.6.0` -contracts release. - -### `Proxy.sol` - -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 privileged methods of the OP Contracts Manager will be held by the L1 ProxyAdmin owner, as -specified by the [standard configuration]. +Version 1.0.0 of the OP Contracts Manager deploys the `op-contracts/v1.6.0` contracts release, +and is deployed at `0x9BC0A1eD534BFb31a6Be69e5b767Cba332f14347`. In the future this will +be tracked in the `superchain-registry`. ### `deploy` -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: +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: ```solidity struct Roles { @@ -85,10 +76,10 @@ 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: @@ -101,6 +92,37 @@ 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]. +### `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 +function upgrade(ISystemConfig[] _systemConfigs, NewChainConfig[] _newConfigs) public; +``` + +For each chain successfully upgraded, the following event is emitted: + +```solidity +event Upgraded(uint256 indexed l2ChainId, SystemConfig indexed systemConfig); +``` + +This method reverts if the upgrade is not successful for any of the chains. + +The high level logic of the upgrade method is as follows: + +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. + +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. + ### Getter Methods The following interface defines the available getter methods: @@ -153,13 +175,13 @@ 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. + 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. + 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. + if this property is not preserved in future versions of the OP Contracts Manager. ## Security Considerations @@ -170,10 +192,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 @@ -206,11 +228,3 @@ maximize compatibility. 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) - -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. From d503636f2d66c4ad3b908662d58fc40e966e4ec7 Mon Sep 17 00:00:00 2001 From: Maurelian Date: Thu, 28 Nov 2024 14:00:43 -0500 Subject: [PATCH 02/27] Reorganize OPCM spec around deploy vs. upgrade sections --- specs/experimental/op-contracts-manager.md | 133 ++++++++++++++------- 1 file changed, 91 insertions(+), 42 deletions(-) diff --git a/specs/experimental/op-contracts-manager.md b/specs/experimental/op-contracts-manager.md index 309ae3155..802514157 100644 --- a/specs/experimental/op-contracts-manager.md +++ b/specs/experimental/op-contracts-manager.md @@ -38,18 +38,25 @@ of governance approved [contract releases] can be found on the -## Deployment +## Overview 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. -## Interface +The OP Contracts Manager corresponding to each release can be used to: + +1. Deploy a new OP chain. +2. Upgrade the contracts for an existing OP chain from the previous release to the new release. + +## Deployment + +### Interface Version 1.0.0 of the OP Contracts Manager deploys the `op-contracts/v1.6.0` contracts release, and is deployed at `0x9BC0A1eD534BFb31a6Be69e5b767Cba332f14347`. In the future this will be tracked in the `superchain-registry`. -### `deploy` +#### `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: @@ -92,38 +99,7 @@ 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]. -### `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 -function upgrade(ISystemConfig[] _systemConfigs, NewChainConfig[] _newConfigs) public; -``` - -For each chain successfully upgraded, the following event is emitted: - -```solidity -event Upgraded(uint256 indexed l2ChainId, SystemConfig indexed systemConfig); -``` - -This method reverts if the upgrade is not successful for any of the chains. - -The high level logic of the upgrade method is as follows: - -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. - -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. - -### Getter Methods +#### Getter Methods The following interface defines the available getter methods: @@ -150,15 +126,15 @@ function implementation( function systemConfig(uint256 chainId) external view returns (SystemConfig); ``` -## Implementation +### Implementation -### Batch Inbox Address +#### 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 +#### Contract Deployments All contracts deployed by the OP Contracts Manager are deployed with CREATE2, with a salt equal to either: @@ -183,9 +159,63 @@ This provides the following benefits: - 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 +function upgrade(ISystemConfig[] _systemConfigs, NewChainConfig[] _newConfigs) public; +``` + +For each chain successfully upgraded, the following event is emitted: + +```solidity +event Upgraded(uint256 indexed l2ChainId, SystemConfig indexed systemConfig); +``` + +This method reverts if the upgrade is not successful for any of the chains. + +### Implementation + +The high level logic of the upgrade method is as follows: + +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. + +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. + +#### `NewChainConfig` struct + +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. + +In practice, this struct is likely to be have a unique name for each release of the OP Contracts +Manager. + +#### 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. + ## Security Considerations -### Chain ID Source of Truth +#### Chain ID Source of Truth One of the implicit restrictions on chain ID is that `deploy` can only be called once per chain ID, because contract addresses are a function of chain ID. However, @@ -204,7 +234,7 @@ uniqueness is not enforced by the OP Contracts Manager, and it is strongly recommended to only use chain IDs that are not already present in the [ethereum-lists/chains] repository. -### Chain ID Frontrunning +#### Chain ID Frontrunning Contract addresses for a chain are a function of chain ID, which implies you can counterfactually compute and use those chain addresses before the chain is @@ -212,7 +242,7 @@ deployed. However, this property should not be relied upon—new chain deploymen are permissionless, so you cannot guarantee usage of a given chain ID, as deploy transactions can be frontrun. -### Chain ID Value +#### Chain ID Value While not specific to OP Contracts Manager, when choosing a chain ID is important to consider that not all chain IDs are well supported by tools. For example, @@ -223,8 +253,27 @@ OP Contracts Manager does not consider factors such as these. The EVM supports 256-bit chain IDs, so OP Contracts Manager sticks with the full 256-bit range to maximize compatibility. -### Proxy Admin Owner +#### Proxy Admin Owner 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. + +#### Safely using `DELEGATECALL` + +Because the Upgrade Controller Safe will `DELEGATECALL` to the `OPCM.upgrade()` method, it is +critical that no storage writes occur. This should be enforced in multiple ways, including: + +- By static analysis of the `upgrade()` method 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. + +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. From 9558d4685658f8befb9f4ca820b1d3da6b228856 Mon Sep 17 00:00:00 2001 From: Maurelian Date: Thu, 28 Nov 2024 14:50:13 -0500 Subject: [PATCH 03/27] update OPCM getters --- specs/experimental/op-contracts-manager.md | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/specs/experimental/op-contracts-manager.md b/specs/experimental/op-contracts-manager.md index 802514157..fcfb00d53 100644 --- a/specs/experimental/op-contracts-manager.md +++ b/specs/experimental/op-contracts-manager.md @@ -115,15 +115,18 @@ struct Implementation { /// format `op-contracts/vX.Y.Z`. function latestRelease() external view returns (string memory); -/// @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); +/// @notice Returns the implementation contract addresses. +function implementations() public view returns (Implementations memory); /// @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); +function systemConfigs(uint256 chainId) external view returns (SystemConfig); + +/// @notice Maps an L2 chain ID to an L1 batch inbox address as defined by the standard +function chainIdToBatchInboxAddress(uint256 _l2ChainId) public pure returns (address); + +/// @notice Returns the blueprint contract addresses. +function blueprints() public view returns (Blueprints memory); ``` ### Implementation From 115c61eb697d6f488df739ae7302e21ec6e2ff19 Mon Sep 17 00:00:00 2001 From: Maurelian Date: Thu, 28 Nov 2024 15:16:57 -0500 Subject: [PATCH 04/27] Add `addGameType` section --- specs/experimental/op-contracts-manager.md | 103 ++++++++++++++------- 1 file changed, 71 insertions(+), 32 deletions(-) diff --git a/specs/experimental/op-contracts-manager.md b/specs/experimental/op-contracts-manager.md index fcfb00d53..0c6d5fcc2 100644 --- a/specs/experimental/op-contracts-manager.md +++ b/specs/experimental/op-contracts-manager.md @@ -47,6 +47,37 @@ The OP Contracts Manager corresponding to each release can be used to: 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 + +## Getter Methods + +The following interface defines the available getter methods: + +```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. +} + +/// @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); + +/// @notice Returns the implementation contract addresses. +function implementations() public view returns (Implementations memory); + +/// @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 systemConfigs(uint256 chainId) external view returns (SystemConfig); + +/// @notice Maps an L2 chain ID to an L1 batch inbox address as defined by the standard +function chainIdToBatchInboxAddress(uint256 _l2ChainId) public pure returns (address); + +/// @notice Returns the blueprint contract addresses. +function blueprints() public view returns (Blueprints memory); +``` ## Deployment @@ -99,36 +130,6 @@ 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 - -The following interface defines the available getter methods: - -```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. -} - -/// @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); - -/// @notice Returns the implementation contract addresses. -function implementations() public view returns (Implementations memory); - -/// @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 systemConfigs(uint256 chainId) external view returns (SystemConfig); - -/// @notice Maps an L2 chain ID to an L1 batch inbox address as defined by the standard -function chainIdToBatchInboxAddress(uint256 _l2ChainId) public pure returns (address); - -/// @notice Returns the blueprint contract addresses. -function blueprints() public view returns (Blueprints memory); -``` - ### Implementation #### Batch Inbox Address @@ -216,6 +217,44 @@ 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 NewGameConfig { + // fields will vary depending on the game type +} + +function addGameType(ISystemConfig[] _systemConfigs, NewGameConfig[] _newGames) public; +``` + +### Implementation + +The high level logic of the `addGameType` method is as follows (for each chain): + +1. Deploys the new game Creator contract for that game type. +2. Calls `setImplementation()` on the `DisputeGameFactory` +3. Calls `setAnchorState()` on the `AnchorStateRegistry` + +Note that in [Standard Configuration chains](../protocol/superchain-configuration.md): + +- `DisputeGameFactory.setImplementation()` is authorized to the Upgrade Controller +- `AnchorStateRegistry.setAnchorState()` is authorized to the Guardian + +Since the Guardian is its own Safe controlled by the Security Council, and the Upgrade Controller is +a 2 of 2 jointly controlled by the Security Council and Optimism Foundation, this is not impossible +to implement, but will be ugly and require new tooling and processes to do correctly. + ## Security Considerations #### Chain ID Source of Truth @@ -264,10 +303,10 @@ to ensure admin privileges are sufficiently secured. #### Safely using `DELEGATECALL` -Because the Upgrade Controller Safe will `DELEGATECALL` to the `OPCM.upgrade()` method, it is +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()` method during the development process. +- 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 From 064402135fd172a60b0445ab1d32592f195e7512 Mon Sep 17 00:00:00 2001 From: Maurelian Date: Thu, 28 Nov 2024 15:22:49 -0500 Subject: [PATCH 05/27] update toc --- specs/experimental/op-contracts-manager.md | 27 +++++++++++++++------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/specs/experimental/op-contracts-manager.md b/specs/experimental/op-contracts-manager.md index 0c6d5fcc2..569849b68 100644 --- a/specs/experimental/op-contracts-manager.md +++ b/specs/experimental/op-contracts-manager.md @@ -21,20 +21,31 @@ 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) + - [`NewChainConfig` struct](#newchainconfig-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) From 2d41c3d2fc6ba753be04a50f4e3f125d6e830aae Mon Sep 17 00:00:00 2001 From: Maurelian Date: Fri, 29 Nov 2024 14:29:05 -0500 Subject: [PATCH 06/27] feat: Update Deployed event to match impl --- specs/experimental/op-contracts-manager.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/experimental/op-contracts-manager.md b/specs/experimental/op-contracts-manager.md index 569849b68..d7d0db9f4 100644 --- a/specs/experimental/op-contracts-manager.md +++ b/specs/experimental/op-contracts-manager.md @@ -133,7 +133,7 @@ The `l2ChainId` has the following restrictions: 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: From b911eb474365ba6f477f178937cdc0acb0c2467d Mon Sep 17 00:00:00 2001 From: Maurelian Date: Tue, 3 Dec 2024 13:25:56 -0500 Subject: [PATCH 07/27] fix lint --- specs/experimental/op-contracts-manager.md | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/specs/experimental/op-contracts-manager.md b/specs/experimental/op-contracts-manager.md index d7d0db9f4..f26c54098 100644 --- a/specs/experimental/op-contracts-manager.md +++ b/specs/experimental/op-contracts-manager.md @@ -18,7 +18,6 @@ of governance approved [contract releases] can be found on the - **Table of Contents** - [Overview](#overview) @@ -268,7 +267,7 @@ to implement, but will be ugly and require new tooling and processes to do corre ## Security Considerations -#### Chain ID Source of Truth +### Chain ID Source of Truth One of the implicit restrictions on chain ID is that `deploy` can only be called once per chain ID, because contract addresses are a function of chain ID. However, @@ -287,7 +286,7 @@ uniqueness is not enforced by the OP Contracts Manager, and it is strongly recommended to only use chain IDs that are not already present in the [ethereum-lists/chains] repository. -#### Chain ID Frontrunning +### Chain ID Frontrunning Contract addresses for a chain are a function of chain ID, which implies you can counterfactually compute and use those chain addresses before the chain is @@ -295,7 +294,7 @@ deployed. However, this property should not be relied upon—new chain deploymen are permissionless, so you cannot guarantee usage of a given chain ID, as deploy transactions can be frontrun. -#### Chain ID Value +### Chain ID Value While not specific to OP Contracts Manager, when choosing a chain ID is important to consider that not all chain IDs are well supported by tools. For example, @@ -306,13 +305,13 @@ OP Contracts Manager does not consider factors such as these. The EVM supports 256-bit chain IDs, so OP Contracts Manager sticks with the full 256-bit range to maximize compatibility. -#### Proxy Admin Owner +### Proxy Admin Owner 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. -#### Safely using `DELEGATECALL` +### 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: From 34e18e4de0d27362e13f735a0bb3df3d79c8fd80 Mon Sep 17 00:00:00 2001 From: Maurelian Date: Wed, 4 Dec 2024 13:17:29 -0500 Subject: [PATCH 08/27] fix the spec for the addGameType function --- specs/experimental/op-contracts-manager.md | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/specs/experimental/op-contracts-manager.md b/specs/experimental/op-contracts-manager.md index f26c54098..f03aacf0e 100644 --- a/specs/experimental/op-contracts-manager.md +++ b/specs/experimental/op-contracts-manager.md @@ -18,6 +18,7 @@ of governance approved [contract releases] can be found on the + **Table of Contents** - [Overview](#overview) @@ -230,7 +231,7 @@ function, increasing the cost and reducing the number of OP Chains which can be ## 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 +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 @@ -252,18 +253,11 @@ function addGameType(ISystemConfig[] _systemConfigs, NewGameConfig[] _newGames) The high level logic of the `addGameType` method is as follows (for each chain): -1. Deploys the new game Creator contract for that game type. -2. Calls `setImplementation()` on the `DisputeGameFactory` -3. Calls `setAnchorState()` on the `AnchorStateRegistry` - -Note that in [Standard Configuration chains](../protocol/superchain-configuration.md): - -- `DisputeGameFactory.setImplementation()` is authorized to the Upgrade Controller -- `AnchorStateRegistry.setAnchorState()` is authorized to the Guardian - -Since the Guardian is its own Safe controlled by the Security Council, and the Upgrade Controller is -a 2 of 2 jointly controlled by the Security Council and Optimism Foundation, this is not impossible -to implement, but will be ugly and require new tooling and processes to do correctly. +1. The Upgrade Controller Safe will `DELEGATECALL` to the `OPCM.upgrade()` method. +1. A new Proxy contract will be deployed, with the implementation set to the `Creator` contract for that game type. +1. Calls `setImplementation()` on the `DisputeGameFactory` +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. ## Security Considerations From bb7ea4ec1c3f6613067c3b29a8ea22e0d7fc735d Mon Sep 17 00:00:00 2001 From: Maurelian Date: Fri, 6 Dec 2024 10:26:43 -0500 Subject: [PATCH 09/27] Apply suggestions from code review Co-authored-by: blaine --- specs/experimental/op-contracts-manager.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/experimental/op-contracts-manager.md b/specs/experimental/op-contracts-manager.md index f03aacf0e..ace9abde1 100644 --- a/specs/experimental/op-contracts-manager.md +++ b/specs/experimental/op-contracts-manager.md @@ -253,7 +253,7 @@ function addGameType(ISystemConfig[] _systemConfigs, NewGameConfig[] _newGames) The high level logic of the `addGameType` method is as follows (for each chain): -1. The Upgrade Controller Safe will `DELEGATECALL` to the `OPCM.upgrade()` method. +1. The Upgrade Controller Safe will `DELEGATECALL` to the `addGameType` method. 1. A new Proxy contract will be deployed, with the implementation set to the `Creator` contract for that game type. 1. Calls `setImplementation()` on the `DisputeGameFactory` 1. Calls `upgrade()` on the `AnchorStateRegistry` to set the new game type to add a new entry to the `anchors` mapping. From e1e004fa91f34c443c55f43b3c7f68d5016d8642 Mon Sep 17 00:00:00 2001 From: Maurelian Date: Fri, 6 Dec 2024 12:27:50 -0500 Subject: [PATCH 10/27] fixes --- specs/experimental/op-contracts-manager.md | 54 ++++++++++++---------- 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/specs/experimental/op-contracts-manager.md b/specs/experimental/op-contracts-manager.md index ace9abde1..f2fbbc684 100644 --- a/specs/experimental/op-contracts-manager.md +++ b/specs/experimental/op-contracts-manager.md @@ -65,17 +65,30 @@ The OP Contracts Manager corresponding to each release can be used to: The following interface defines the available getter methods: ```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. -} - /// @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); + /// @notice Represents the interface version so consumers know how to decode the DeployOutput struct + function OUTPUT_VERSION() 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 L1 smart contracts release deployed by this version of OPCM. + function l1ContractsRelease() external view returns (string 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) external view returns (address); + /// @notice Semver version specific to the OPContractsManager + function version() external view returns (string memory); + /// @notice Returns the implementation contract addresses. function implementations() public view returns (Implementations memory); @@ -104,21 +117,10 @@ The `deploy` method is used to deploy the full set of L1 contracts required to s chain that complies with the [standard configuration]. It has the following interface: ```solidity -struct Roles { - address proxyAdminOwner; - address systemConfigOwner; - address batcher; - address unsafeBlockSigner; - address proposer; - address challenger; -} - -function deploy( - uint256 l2ChainId, - uint32 basefeeScalar, - uint32 blobBasefeeScalar, - Roles roles -) external returns (SystemConfig) +/// @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: @@ -152,16 +154,18 @@ the batch inbox addresses. #### Contract Deployments All contracts deployed by the OP Contracts Manager are deployed with CREATE2, with a -salt equal to either: +salt equal to either of the following: -- The L2 chain ID, or -- `keccak256(bytes.concat(bytes32(uint256(l2ChainId)), contractName))`. +- `keccak256(abi.encode(l2ChainId, saltMixer))` +- `keccak256(bytes.concat(bytes32(uint256(l2ChainId)), saltMixer, contractName))`. 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. +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. @@ -253,7 +257,7 @@ function addGameType(ISystemConfig[] _systemConfigs, NewGameConfig[] _newGames) The high level logic of the `addGameType` method is as follows (for each chain): -1. The Upgrade Controller Safe will `DELEGATECALL` to the `addGameType` method. +1. The Upgrade Controller Safe will `DELEGATECALL` to the `OPCM.addGameType()` method. 1. A new Proxy contract will be deployed, with the implementation set to the `Creator` contract for that game type. 1. Calls `setImplementation()` on the `DisputeGameFactory` 1. Calls `upgrade()` on the `AnchorStateRegistry` to set the new game type to add a new entry to the `anchors` mapping. From 3cf1a9aecb92571cfa3e9d56378b4194f2e17181 Mon Sep 17 00:00:00 2001 From: Maurelian Date: Fri, 6 Dec 2024 14:07:07 -0500 Subject: [PATCH 11/27] Apply suggestions from code review --- specs/experimental/op-contracts-manager.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/specs/experimental/op-contracts-manager.md b/specs/experimental/op-contracts-manager.md index f2fbbc684..875ca57ea 100644 --- a/specs/experimental/op-contracts-manager.md +++ b/specs/experimental/op-contracts-manager.md @@ -190,13 +190,13 @@ all chains that it controls. It has the following interface: ```solidity -function upgrade(ISystemConfig[] _systemConfigs, NewChainConfig[] _newConfigs) public; +function upgrade(ISystemConfig[] _systemConfigs, IProxyAdmin[] _proxyAdmins, NewChainConfig[] _newConfigs) public; ``` For each chain successfully upgraded, the following event is emitted: ```solidity -event Upgraded(uint256 indexed l2ChainId, SystemConfig indexed systemConfig); +event Upgraded(uint256 indexed l2ChainId, SystemConfig indexed systemConfig, address indexed upgrader); ``` This method reverts if the upgrade is not successful for any of the chains. From 0b04390eca32d295e49da9f893f129572d4ae340 Mon Sep 17 00:00:00 2001 From: Maurelian Date: Fri, 6 Dec 2024 14:20:21 -0500 Subject: [PATCH 12/27] lint --- specs/protocol/configurability.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) 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
From 798f13a49622ffda25f0f5c508570cf8cafc6b02 Mon Sep 17 00:00:00 2001 From: Maurelian Date: Fri, 6 Dec 2024 14:29:43 -0500 Subject: [PATCH 13/27] fix lint --- specs/experimental/op-contracts-manager.md | 1 - 1 file changed, 1 deletion(-) diff --git a/specs/experimental/op-contracts-manager.md b/specs/experimental/op-contracts-manager.md index 875ca57ea..85023b54c 100644 --- a/specs/experimental/op-contracts-manager.md +++ b/specs/experimental/op-contracts-manager.md @@ -18,7 +18,6 @@ of governance approved [contract releases] can be found on the - **Table of Contents** - [Overview](#overview) From 176c6fe5886e617e79ce7988e5987ffa13fa6e2e Mon Sep 17 00:00:00 2001 From: Maurelian Date: Tue, 10 Dec 2024 12:21:48 -0500 Subject: [PATCH 14/27] remove info about specific deployments --- specs/experimental/op-contracts-manager.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/specs/experimental/op-contracts-manager.md b/specs/experimental/op-contracts-manager.md index 85023b54c..1fb18efb5 100644 --- a/specs/experimental/op-contracts-manager.md +++ b/specs/experimental/op-contracts-manager.md @@ -106,10 +106,6 @@ function blueprints() public view returns (Blueprints memory); ### Interface -Version 1.0.0 of the OP Contracts Manager deploys the `op-contracts/v1.6.0` contracts release, -and is deployed at `0x9BC0A1eD534BFb31a6Be69e5b767Cba332f14347`. In the future this will -be tracked in the `superchain-registry`. - #### `deploy` The `deploy` method is used to deploy the full set of L1 contracts required to setup a new OP Stack From de6c68b63988ab74ceeaa534b504c8b97577c68a Mon Sep 17 00:00:00 2001 From: Maurelian Date: Tue, 10 Dec 2024 12:24:43 -0500 Subject: [PATCH 15/27] fix interface --- specs/experimental/op-contracts-manager.md | 55 ++++++++-------------- 1 file changed, 19 insertions(+), 36 deletions(-) diff --git a/specs/experimental/op-contracts-manager.md b/specs/experimental/op-contracts-manager.md index 1fb18efb5..36c96d34c 100644 --- a/specs/experimental/op-contracts-manager.md +++ b/specs/experimental/op-contracts-manager.md @@ -64,42 +64,25 @@ The OP Contracts Manager corresponding to each release can be used to: The following interface defines the available getter methods: ```solidity -/// @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); - - /// @notice Represents the interface version so consumers know how to decode the DeployOutput struct - function OUTPUT_VERSION() 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 L1 smart contracts release deployed by this version of OPCM. - function l1ContractsRelease() external view returns (string 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) external view returns (address); - /// @notice Semver version specific to the OPContractsManager - function version() external view returns (string memory); - -/// @notice Returns the implementation contract addresses. -function implementations() public view returns (Implementations memory); - -/// @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 systemConfigs(uint256 chainId) external view returns (SystemConfig); - -/// @notice Maps an L2 chain ID to an L1 batch inbox address as defined by the standard -function chainIdToBatchInboxAddress(uint256 _l2ChainId) public pure returns (address); - -/// @notice Returns the blueprint contract addresses. -function blueprints() public view returns (Blueprints memory); +/// @notice Returns the latest approved release of the OP Stack contracts are named with the +/// format `op-contracts/vX.Y.Z`. +function l1ContractsRelease() external view returns (string memory); +/// @notice Represents the interface version so consumers know how to decode the DeployOutput struct +function OUTPUT_VERSION() 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); ``` ## Deployment From b6ccf284e0f5e7ca737d4f63cd2b9b60be9b6979 Mon Sep 17 00:00:00 2001 From: Maurelian Date: Tue, 10 Dec 2024 12:25:59 -0500 Subject: [PATCH 16/27] Improve readability of create2 input description --- specs/experimental/op-contracts-manager.md | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/specs/experimental/op-contracts-manager.md b/specs/experimental/op-contracts-manager.md index 36c96d34c..72e765308 100644 --- a/specs/experimental/op-contracts-manager.md +++ b/specs/experimental/op-contracts-manager.md @@ -131,16 +131,19 @@ the batch inbox addresses. #### Contract Deployments -All contracts deployed by the OP Contracts Manager are deployed with CREATE2, with a -salt equal to either of the following: +All contracts deployed by the OP Contracts Manager are deployed with CREATE2. -- `keccak256(abi.encode(l2ChainId, saltMixer))` -- `keccak256(bytes.concat(bytes32(uint256(l2ChainId)), saltMixer, contractName))`. +For singletons the following salt is used: -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. +``` +keccak256(abi.encode(l2ChainId, saltMixer)) +``` + +For `Proxy` contracts, the following salt is used: + +``` +keccak256(bytes.concat(bytes32(uint256(l2ChainId)), saltMixer, contractName)) +``` The `saltMixer` value is provided as a field in the `DeployInput` struct. From 544856928a7f9810e8aed736b6e1403644b554f4 Mon Sep 17 00:00:00 2001 From: Maurelian Date: Tue, 10 Dec 2024 12:27:23 -0500 Subject: [PATCH 17/27] add lang annotation to code fences --- specs/experimental/op-contracts-manager.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/specs/experimental/op-contracts-manager.md b/specs/experimental/op-contracts-manager.md index 72e765308..1343f4996 100644 --- a/specs/experimental/op-contracts-manager.md +++ b/specs/experimental/op-contracts-manager.md @@ -135,13 +135,13 @@ All contracts deployed by the OP Contracts Manager are deployed with CREATE2. For singletons the following salt is used: -``` +```solidity keccak256(abi.encode(l2ChainId, saltMixer)) ``` For `Proxy` contracts, the following salt is used: -``` +```solidity keccak256(bytes.concat(bytes32(uint256(l2ChainId)), saltMixer, contractName)) ``` From 0913d610477c9bf4f22cc7ebeeba315e74566430 Mon Sep 17 00:00:00 2001 From: Maurelian Date: Tue, 10 Dec 2024 12:37:03 -0500 Subject: [PATCH 18/27] Add DeployInput definition --- specs/experimental/op-contracts-manager.md | 31 ++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/specs/experimental/op-contracts-manager.md b/specs/experimental/op-contracts-manager.md index 1343f4996..e1161d40c 100644 --- a/specs/experimental/op-contracts-manager.md +++ b/specs/experimental/op-contracts-manager.md @@ -18,6 +18,7 @@ of governance approved [contract releases] can be found on the + **Table of Contents** - [Overview](#overview) @@ -171,6 +172,27 @@ all chains that it controls. It has the following interface: ```solidity +/// @notice The full set of inputs to deploy a new OP Stack chain. +struct DeployInput { + Roles roles; + uint32 basefeeScalar; + uint32 blobBasefeeScalar; + uint256 l2ChainId; + // The correct type is AnchorStateRegistry.StartingAnchorRoot[] memory, + // but OP Deployer does not yet support structs. + bytes startingAnchorRoots; + // The salt mixer is used as part of making the resulting salt unique. + string saltMixer; + uint64 gasLimit; + // Configurable dispute game parameters. + GameType disputeGameType; + Claim disputeAbsolutePrestate; + uint256 disputeMaxGameDepth; + uint256 disputeSplitDepth; + Duration disputeClockExtension; + Duration disputeMaxClockDuration; +} + function upgrade(ISystemConfig[] _systemConfigs, IProxyAdmin[] _proxyAdmins, NewChainConfig[] _newConfigs) public; ``` @@ -204,6 +226,15 @@ vary for each release of the OP Contracts Manager, based on what (if any) new pa In practice, this struct is likely to be have a unique name for each release 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 NewChainConfigForExampleUpgrade { + address systemConfigFoo; +} +``` + #### Requirements on the OP Chain contracts In general, all contracts used in an OP Chain SHOULD be proxied with a single shared implementation. From 9fee447df9fce86f55d4a3027d0b09feb410d523 Mon Sep 17 00:00:00 2001 From: Maurelian Date: Tue, 10 Dec 2024 14:11:21 -0500 Subject: [PATCH 19/27] fix DeployInput and Roles struct definition --- specs/experimental/op-contracts-manager.md | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/specs/experimental/op-contracts-manager.md b/specs/experimental/op-contracts-manager.md index e1161d40c..1ca14d978 100644 --- a/specs/experimental/op-contracts-manager.md +++ b/specs/experimental/op-contracts-manager.md @@ -172,25 +172,29 @@ all chains that it controls. It has the following interface: ```solidity -/// @notice The full set of inputs to deploy a new OP Stack chain. +struct Roles { + address opChainProxyAdminOwner; + address systemConfigOwner; + address batcher; + address unsafeBlockSigner; + address proposer; + address challenger; +} + struct DeployInput { Roles roles; uint32 basefeeScalar; uint32 blobBasefeeScalar; uint256 l2ChainId; - // The correct type is AnchorStateRegistry.StartingAnchorRoot[] memory, - // but OP Deployer does not yet support structs. bytes startingAnchorRoots; - // The salt mixer is used as part of making the resulting salt unique. string saltMixer; uint64 gasLimit; - // Configurable dispute game parameters. - GameType disputeGameType; - Claim disputeAbsolutePrestate; + uint32 disputeGameType; + bytes32 disputeAbsolutePrestate; uint256 disputeMaxGameDepth; uint256 disputeSplitDepth; - Duration disputeClockExtension; - Duration disputeMaxClockDuration; + uint64 disputeClockExtension; + uint64 disputeMaxClockDuration; } function upgrade(ISystemConfig[] _systemConfigs, IProxyAdmin[] _proxyAdmins, NewChainConfig[] _newConfigs) public; From 84f76b5af83a219cea098d693837702776f7078c Mon Sep 17 00:00:00 2001 From: Maurelian Date: Tue, 10 Dec 2024 17:01:21 -0500 Subject: [PATCH 20/27] Update addGameType spec --- specs/experimental/op-contracts-manager.md | 32 ++++++++++++++++------ 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/specs/experimental/op-contracts-manager.md b/specs/experimental/op-contracts-manager.md index 1ca14d978..acbd24d1a 100644 --- a/specs/experimental/op-contracts-manager.md +++ b/specs/experimental/op-contracts-manager.md @@ -262,22 +262,38 @@ The `addGameType` method is used to orchestrate the actions required to add a ne or more chains. ```solidity -struct NewGameConfig { - // fields will vary depending on the game type +struct PermissionlessGameConfig { + bytes32 absolutePrestate; } -function addGameType(ISystemConfig[] _systemConfigs, NewGameConfig[] _newGames) public; +function addGameType(ISystemConfig[] _systemConfigs, PermissionlessGameConfig[] _newGames) public; ``` ### Implementation The high level logic of the `addGameType` method is as follows (for each chain): -1. The Upgrade Controller Safe will `DELEGATECALL` to the `OPCM.addGameType()` method. -1. A new Proxy contract will be deployed, with the implementation set to the `Creator` contract for that game type. -1. Calls `setImplementation()` on the `DisputeGameFactory` -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. 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. + +```solidity +uint32 gameType; // constant value of 1 +bytes32 absolutePrestate; // Input as PermissionlessGameConfig.absolutePrestate +uint256 maxGameDepth; // Read from existing PermissionedGame +uint256 splitDepth; // Read from existing PermissionedGame +uint64 clockExtension; // Read from existing PermissionedGame +uint64 maxClockDuration; // Read from existing PermissionedGame +address vm; // Read from existing PermissionedGame +address weth; // Use address of newly deployed DelayedWeth contract +address anchorStateRegistry; // Read from existing PermissionedGame +uint256 l2ChainId; // Read from existing PermissionedGame +``` ## Security Considerations From 1e6e8812ea4ebc17c5ec3d586f591d0a855f207a Mon Sep 17 00:00:00 2001 From: Maurelian Date: Wed, 11 Dec 2024 13:17:19 -0500 Subject: [PATCH 21/27] feat: Update salt generation --- specs/experimental/op-contracts-manager.md | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/specs/experimental/op-contracts-manager.md b/specs/experimental/op-contracts-manager.md index acbd24d1a..334e4c826 100644 --- a/specs/experimental/op-contracts-manager.md +++ b/specs/experimental/op-contracts-manager.md @@ -132,18 +132,10 @@ the batch inbox addresses. #### Contract Deployments -All contracts deployed by the OP Contracts Manager are deployed with CREATE2. - -For singletons the following salt is used: - -```solidity -keccak256(abi.encode(l2ChainId, saltMixer)) -``` - -For `Proxy` contracts, the following salt is used: +All contracts deployed by the OP Contracts Manager are deployed with `CREATE2`, using the following salt: ```solidity -keccak256(bytes.concat(bytes32(uint256(l2ChainId)), saltMixer, contractName)) +keccak256(abi.encode(_l2ChainId, _saltMixer, _contractName)); ``` The `saltMixer` value is provided as a field in the `DeployInput` struct. From ccf96a3291dbdad89b2a365855c0fe28aff82338 Mon Sep 17 00:00:00 2001 From: Maurelian Date: Fri, 13 Dec 2024 13:44:35 -0500 Subject: [PATCH 22/27] Fix interface casing --- specs/experimental/op-contracts-manager.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/experimental/op-contracts-manager.md b/specs/experimental/op-contracts-manager.md index 334e4c826..a1cde13e1 100644 --- a/specs/experimental/op-contracts-manager.md +++ b/specs/experimental/op-contracts-manager.md @@ -69,7 +69,7 @@ The following interface defines the available getter methods: /// format `op-contracts/vX.Y.Z`. function l1ContractsRelease() external view returns (string memory); /// @notice Represents the interface version so consumers know how to decode the DeployOutput struct -function OUTPUT_VERSION() external view returns (uint256); +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 From 652c390924fd10f14c1a838c8bde4d28044a5291 Mon Sep 17 00:00:00 2001 From: Maurelian Date: Fri, 13 Dec 2024 14:02:03 -0500 Subject: [PATCH 23/27] lint --- specs/experimental/op-contracts-manager.md | 1 - 1 file changed, 1 deletion(-) diff --git a/specs/experimental/op-contracts-manager.md b/specs/experimental/op-contracts-manager.md index a1cde13e1..5d4af7802 100644 --- a/specs/experimental/op-contracts-manager.md +++ b/specs/experimental/op-contracts-manager.md @@ -18,7 +18,6 @@ of governance approved [contract releases] can be found on the - **Table of Contents** - [Overview](#overview) From d2c97301e4b66f22a555265000a222ba5fb01a11 Mon Sep 17 00:00:00 2001 From: Maurelian Date: Tue, 17 Dec 2024 10:46:22 -0500 Subject: [PATCH 24/27] Update specs/experimental/op-contracts-manager.md --- specs/experimental/op-contracts-manager.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/experimental/op-contracts-manager.md b/specs/experimental/op-contracts-manager.md index 5d4af7802..9a4a80363 100644 --- a/specs/experimental/op-contracts-manager.md +++ b/specs/experimental/op-contracts-manager.md @@ -66,7 +66,7 @@ The following interface defines the available getter methods: ```solidity /// @notice Returns the latest approved release of the OP Stack contracts are named with the /// format `op-contracts/vX.Y.Z`. -function l1ContractsRelease() external view returns (string memory); +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. From 413f6207f847bfd60658e8d2ba53a42244a0ba74 Mon Sep 17 00:00:00 2001 From: Maurelian Date: Wed, 18 Dec 2024 09:36:21 -0500 Subject: [PATCH 25/27] Add context on Upgrader role to intro --- specs/experimental/op-contracts-manager.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/specs/experimental/op-contracts-manager.md b/specs/experimental/op-contracts-manager.md index 9a4a80363..de8f07660 100644 --- a/specs/experimental/op-contracts-manager.md +++ b/specs/experimental/op-contracts-manager.md @@ -18,6 +18,7 @@ of governance approved [contract releases] can be found on the + **Table of Contents** - [Overview](#overview) @@ -59,6 +60,8 @@ The OP Contracts Manager corresponding to each release can be used to: 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 +Upgrades must be performed by the [Upgrade Controller](../protocol/stage-1.md#roles-for-stage-1) Safe for a chain. + ## Getter Methods The following interface defines the available getter methods: From f92ea22d061261edec912c83e3a0490d1fbf6573 Mon Sep 17 00:00:00 2001 From: Maurelian Date: Wed, 18 Dec 2024 09:56:35 -0500 Subject: [PATCH 26/27] Define IsthmusConfig and update addGameType config sources --- specs/experimental/op-contracts-manager.md | 31 +++++++++++----------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/specs/experimental/op-contracts-manager.md b/specs/experimental/op-contracts-manager.md index de8f07660..17ee7ed6a 100644 --- a/specs/experimental/op-contracts-manager.md +++ b/specs/experimental/op-contracts-manager.md @@ -216,7 +216,7 @@ This approach requires that all contracts have an `upgrade()` function which set 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. -#### `NewChainConfig` struct +#### `IsthmusConfig` struct 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. @@ -228,8 +228,9 @@ By way of example, if an upgrade is adding a new variable `address foo` to the ` an upgrade named `Example`, the struct could have the following definition: ```solidity -struct NewChainConfigForExampleUpgrade { - address systemConfigFoo; +struct IsthmusConfig { + uint32 public operatorFeeScalar; + uint64 public operatorFeeConstant; } ``` @@ -276,18 +277,18 @@ The high level logic of the `addGameType` method is as follows (for each chain): 1. Read the `DisputeGameFactory` address from the `SystemConfig`. 1. Call `DisputeGameFactory.setImplementation()` to register the new game. -```solidity -uint32 gameType; // constant value of 1 -bytes32 absolutePrestate; // Input as PermissionlessGameConfig.absolutePrestate -uint256 maxGameDepth; // Read from existing PermissionedGame -uint256 splitDepth; // Read from existing PermissionedGame -uint64 clockExtension; // Read from existing PermissionedGame -uint64 maxClockDuration; // Read from existing PermissionedGame -address vm; // Read from existing PermissionedGame -address weth; // Use address of newly deployed DelayedWeth contract -address anchorStateRegistry; // Read from existing PermissionedGame -uint256 l2ChainId; // Read from existing PermissionedGame -``` +| 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 From f3787aa8573d059fa14a39d12982b97a64e75972 Mon Sep 17 00:00:00 2001 From: Maurelian Date: Wed, 18 Dec 2024 09:57:44 -0500 Subject: [PATCH 27/27] lint --- specs/experimental/op-contracts-manager.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/specs/experimental/op-contracts-manager.md b/specs/experimental/op-contracts-manager.md index 17ee7ed6a..20a39760d 100644 --- a/specs/experimental/op-contracts-manager.md +++ b/specs/experimental/op-contracts-manager.md @@ -18,7 +18,6 @@ of governance approved [contract releases] can be found on the - **Table of Contents** - [Overview](#overview) @@ -33,7 +32,7 @@ of governance approved [contract releases] can be found on the - [Interface](#interface-1) - [`upgrade`](#upgrade) - [Implementation](#implementation-1) - - [`NewChainConfig` struct](#newchainconfig-struct) + - [`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)