Commit de31478b authored by Matt Solomon's avatar Matt Solomon Committed by GitHub

OPSM: Begin implementing OP Stack Manager code and it's deployment (#11623)

* begin supporting specifying versions in OPSM

* add deploy logic

* deploy OPSM along with implementations

* scaffold opsm interface between scripts

* fixes

* mvp of DeployOPChain

* start working on an e2e opsm test, currently reverts

* fix tests

* test cleanup

* rename opsmSingleton to opsm

* chore: remove unused imports

* refactor: switch from 'new' to blueprints, 50% code size reduction

* fix semgrep

* feat: add OPSM interop tests

* test: add missing specs

* add DisputeGameFactory deployment

* chore: update snapshots

* fix opsm interop support

* chore: update snapshots

* Update packages/contracts-bedrock/test/DeployOPChain.t.sol

* fix: add L1StandardBridge setter and initialization

* chore: small clarification of deploy flow

* Update packages/contracts-bedrock/scripts/DeployImplementations.s.sol
Co-authored-by: default avatarBlaine Malone <blainemalone01@gmail.com>

* chore: add todos

* fix: change bytes32 to string

* rename addrs to opChainAddrs for clarity

* test: fix assertion string numbering

* chore: update semver lock:

---------
Co-authored-by: default avatarBlaine Malone <blainemalone01@gmail.com>
parent 53fa7b9e
......@@ -15,6 +15,7 @@ import { AnchorStateRegistry } from "src/dispute/AnchorStateRegistry.sol";
import { FaultDisputeGame } from "src/dispute/FaultDisputeGame.sol";
import { PermissionedDisputeGame } from "src/dispute/PermissionedDisputeGame.sol";
import { OPStackManager } from "src/L1/OPStackManager.sol";
import { OptimismPortal2 } from "src/L1/OptimismPortal2.sol";
import { SystemConfig } from "src/L1/SystemConfig.sol";
import { L1CrossDomainMessenger } from "src/L1/L1CrossDomainMessenger.sol";
......@@ -38,6 +39,7 @@ contract DeployOPChainInput {
uint32 basefeeScalar;
uint32 blobBaseFeeScalar;
uint256 l2ChainId;
OPStackManager opsm;
}
bool public inputSet = false;
......@@ -59,6 +61,8 @@ contract DeployOPChainInput {
require(_input.roles.unsafeBlockSigner != address(0), "DeployOPChainInput: null unsafeBlockSigner");
require(_input.roles.proposer != address(0), "DeployOPChainInput: null proposer");
require(_input.roles.challenger != address(0), "DeployOPChainInput: null challenger");
require(_input.l2ChainId != 0 && _input.l2ChainId != block.chainid, "DeployOPChainInput: invalid l2ChainId");
require(address(_input.opsm) != address(0), "DeployOPChainInput: null opsm");
inputSet = true;
inputs = _input;
......@@ -117,6 +121,11 @@ contract DeployOPChainInput {
assertInputSet();
return inputs.l2ChainId;
}
function opsm() public view returns (OPStackManager) {
assertInputSet();
return inputs.opsm;
}
}
contract DeployOPChainOutput {
......@@ -298,10 +307,66 @@ contract DeployOPChain is Script {
return dso.output();
}
function run(DeployOPChainInput _dsi, DeployOPChainOutput _dso) public view {
function run(DeployOPChainInput _dsi, DeployOPChainOutput _dso) public {
require(_dsi.inputSet(), "DeployOPChain: input not set");
// TODO call OP Stack Manager deploy method
OPStackManager opsm = _dsi.opsm();
OPStackManager.Roles memory roles = OPStackManager.Roles({
opChainProxyAdminOwner: _dsi.opChainProxyAdminOwner(),
systemConfigOwner: _dsi.systemConfigOwner(),
batcher: _dsi.batcher(),
unsafeBlockSigner: _dsi.unsafeBlockSigner(),
proposer: _dsi.proposer(),
challenger: _dsi.challenger()
});
OPStackManager.DeployInput memory deployInput = OPStackManager.DeployInput({
roles: roles,
basefeeScalar: _dsi.basefeeScalar(),
blobBasefeeScalar: _dsi.blobBaseFeeScalar(),
l2ChainId: _dsi.l2ChainId()
});
vm.broadcast(msg.sender);
OPStackManager.DeployOutput memory deployOutput = opsm.deploy(deployInput);
vm.label(address(deployOutput.opChainProxyAdmin), "opChainProxyAdmin");
vm.label(address(deployOutput.addressManager), "addressManager");
vm.label(address(deployOutput.l1ERC721BridgeProxy), "l1ERC721BridgeProxy");
vm.label(address(deployOutput.systemConfigProxy), "systemConfigProxy");
vm.label(address(deployOutput.optimismMintableERC20FactoryProxy), "optimismMintableERC20FactoryProxy");
vm.label(address(deployOutput.l1StandardBridgeProxy), "l1StandardBridgeProxy");
vm.label(address(deployOutput.l1CrossDomainMessengerProxy), "l1CrossDomainMessengerProxy");
vm.label(address(deployOutput.optimismPortalProxy), "optimismPortalProxy");
vm.label(address(deployOutput.disputeGameFactoryProxy), "disputeGameFactoryProxy");
vm.label(address(deployOutput.disputeGameFactoryImpl), "disputeGameFactoryImpl");
vm.label(address(deployOutput.anchorStateRegistryProxy), "anchorStateRegistryProxy");
vm.label(address(deployOutput.anchorStateRegistryImpl), "anchorStateRegistryImpl");
vm.label(address(deployOutput.faultDisputeGame), "faultDisputeGame");
vm.label(address(deployOutput.permissionedDisputeGame), "permissionedDisputeGame");
vm.label(address(deployOutput.delayedWETHPermissionedGameProxy), "delayedWETHPermissionedGameProxy");
vm.label(address(deployOutput.delayedWETHPermissionlessGameProxy), "delayedWETHPermissionlessGameProxy");
_dso.set(_dso.opChainProxyAdmin.selector, address(deployOutput.opChainProxyAdmin));
_dso.set(_dso.addressManager.selector, address(deployOutput.addressManager));
_dso.set(_dso.l1ERC721BridgeProxy.selector, address(deployOutput.l1ERC721BridgeProxy));
_dso.set(_dso.systemConfigProxy.selector, address(deployOutput.systemConfigProxy));
_dso.set(
_dso.optimismMintableERC20FactoryProxy.selector, address(deployOutput.optimismMintableERC20FactoryProxy)
);
_dso.set(_dso.l1StandardBridgeProxy.selector, address(deployOutput.l1StandardBridgeProxy));
_dso.set(_dso.l1CrossDomainMessengerProxy.selector, address(deployOutput.l1CrossDomainMessengerProxy));
_dso.set(_dso.optimismPortalProxy.selector, address(deployOutput.optimismPortalProxy));
_dso.set(_dso.disputeGameFactoryProxy.selector, address(deployOutput.disputeGameFactoryProxy));
_dso.set(_dso.disputeGameFactoryImpl.selector, address(deployOutput.disputeGameFactoryImpl));
_dso.set(_dso.anchorStateRegistryProxy.selector, address(deployOutput.anchorStateRegistryProxy));
_dso.set(_dso.anchorStateRegistryImpl.selector, address(deployOutput.anchorStateRegistryImpl));
_dso.set(_dso.faultDisputeGame.selector, address(deployOutput.faultDisputeGame));
_dso.set(_dso.permissionedDisputeGame.selector, address(deployOutput.permissionedDisputeGame));
_dso.set(_dso.delayedWETHPermissionedGameProxy.selector, address(deployOutput.delayedWETHPermissionedGameProxy));
_dso.set(
_dso.delayedWETHPermissionlessGameProxy.selector, address(deployOutput.delayedWETHPermissionlessGameProxy)
);
_dso.checkOutput();
}
......
......@@ -7,7 +7,7 @@ pragma solidity ^0.8.13;
// since Solidity does not have great array UX.
//
// This library was generated using the `generator.py` script from the linked repo with the length
// set to 10, and then everything except the `addresses` functions was removed.
// set accordingly, and then everything except the `addresses` functions was removed.
library Solarray {
function addresses(address a) internal pure returns (address[] memory) {
address[] memory arr = new address[](1);
......@@ -189,6 +189,38 @@ library Solarray {
return arr;
}
function addresses(
address a,
address b,
address c,
address d,
address e,
address f,
address g,
address h,
address i,
address j,
address k
)
internal
pure
returns (address[] memory)
{
address[] memory arr = new address[](11);
arr[0] = a;
arr[1] = b;
arr[2] = c;
arr[3] = d;
arr[4] = e;
arr[5] = f;
arr[6] = g;
arr[7] = h;
arr[8] = i;
arr[9] = j;
arr[10] = k;
return arr;
}
function extend(address[] memory arr1, address[] memory arr2) internal pure returns (address[] memory newArr) {
uint256 length1 = arr1.length;
uint256 length2 = arr2.length;
......
......@@ -32,8 +32,8 @@
"sourceCodeHash": "0xde4df0f9633dc0cdb1c9f634003ea5b0f7c5c1aebc407bc1b2f44c0ecf938649"
},
"src/L1/OPStackManager.sol": {
"initCodeHash": "0x67bf02405bf1ca7d78c4215c350ad9c5c7b4cece35d9fab837f279d65f995c5d",
"sourceCodeHash": "0x8e272e707e383d516b8f1cce0ea29ff46a0eb448c8386fa146e6a43f3100042a"
"initCodeHash": "0xe1eab75651e3d81ad20ca01b1e7d373b25d716ee5f8841a56e56b4531a6e0e70",
"sourceCodeHash": "0x5182a2678dadb200dd255ecdfa395e5f7b1e1e27288e78ddf8802ab51ed2dd81"
},
"src/L1/OptimismPortal.sol": {
"initCodeHash": "0x6bf59539298b20221de6c51db21016be8d3278bdbe0be1cdd49638dc828e003e",
......
[]
\ No newline at end of file
[
{
"bytes": "160",
"label": "blueprint",
"offset": 0,
"slot": "0",
"type": "struct OPStackManager.Blueprints"
},
{
"bytes": "32",
"label": "latestRelease",
"offset": 0,
"slot": "5",
"type": "string"
},
{
"bytes": "32",
"label": "implementations",
"offset": 0,
"slot": "6",
"type": "mapping(string => mapping(string => struct OPStackManager.Implementation))"
},
{
"bytes": "32",
"label": "systemConfigs",
"offset": 0,
"slot": "7",
"type": "mapping(uint256 => contract SystemConfig)"
}
]
\ No newline at end of file
[
{
"bytes": "160",
"label": "blueprint",
"offset": 0,
"slot": "0",
"type": "struct OPStackManager.Blueprints"
},
{
"bytes": "32",
"label": "latestRelease",
"offset": 0,
"slot": "5",
"type": "string"
},
{
"bytes": "32",
"label": "implementations",
"offset": 0,
"slot": "6",
"type": "mapping(string => mapping(string => struct OPStackManager.Implementation))"
},
{
"bytes": "32",
"label": "systemConfigs",
"offset": 0,
"slot": "7",
"type": "mapping(uint256 => contract SystemConfig)"
}
]
\ No newline at end of file
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;
import { OPStackManager } from "src/L1/OPStackManager.sol";
import { SuperchainConfig } from "src/L1/SuperchainConfig.sol";
import { ProtocolVersions } from "src/L1/ProtocolVersions.sol";
import { ResourceMetering } from "src/L1/ResourceMetering.sol";
import { SystemConfig } from "src/L1/SystemConfig.sol";
import { SystemConfigInterop } from "src/L1/SystemConfigInterop.sol";
/// @custom:proxied TODO this is not proxied yet.
contract OPStackManagerInterop is OPStackManager {
constructor(
SuperchainConfig _superchainConfig,
ProtocolVersions _protocolVersions,
Blueprints memory _blueprints
)
OPStackManager(_superchainConfig, _protocolVersions, _blueprints)
{ }
// The `SystemConfigInterop` contract has an extra `address _dependencyManager` argument
// that we must account for.
function encodeSystemConfigInitializer(
bytes4 selector,
DeployInput memory _input,
DeployOutput memory _output
)
internal
pure
virtual
override
returns (bytes memory)
{
(ResourceMetering.ResourceConfig memory referenceResourceConfig, SystemConfig.Addresses memory opChainAddrs) =
defaultSystemConfigParams(selector, _input, _output);
// TODO For now we assume that the dependency manager is the same as the proxy admin owner.
// This is currently undefined since it's not part of the standard config, so we may need
// to update where this value is pulled from in the future. To support a different dependency
// manager in this contract without an invasive change of redefining the `Roles` struct,
// we will make the change described in https://github.com/ethereum-optimism/optimism/issues/11783.
address dependencyManager = address(_input.roles.opChainProxyAdminOwner);
return abi.encodeWithSelector(
selector,
_input.roles.systemConfigOwner,
_input.basefeeScalar,
_input.blobBasefeeScalar,
bytes32(uint256(uint160(_input.roles.batcher))), // batcherHash
30_000_000, // gasLimit TODO make this configurable?
_input.roles.unsafeBlockSigner,
referenceResourceConfig,
chainIdToBatchInboxAddress(_input.l2ChainId),
opChainAddrs,
dependencyManager
);
}
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;
// Testing utilities
import { Test } from "forge-std/Test.sol";
// Target contract
import { DeployOPChainInput } from "scripts/DeployOPChain.s.sol";
import { DeployOPChain_TestBase } from "test/DeployOPChain.t.sol";
import { OPStackManager } from "src/L1/OPStackManager.sol";
import { SuperchainConfig } from "src/L1/SuperchainConfig.sol";
import { ProtocolVersions } from "src/L1/ProtocolVersions.sol";
// Exposes internal functions for testing.
contract OPStackManager_Harness is OPStackManager {
constructor(
SuperchainConfig _superchainConfig,
ProtocolVersions _protocolVersions,
Blueprints memory _blueprints
)
OPStackManager(_superchainConfig, _protocolVersions, _blueprints)
{ }
function chainIdToBatchInboxAddress_exposed(uint256 l2ChainId) public pure returns (address) {
return super.chainIdToBatchInboxAddress(l2ChainId);
}
......@@ -17,38 +28,68 @@ contract OPStackManager_Harness is OPStackManager {
// Unlike other test suites, we intentionally do not inherit from CommonTest or Setup. This is
// because OPStackManager acts as a deploy script, so we start from a clean slate here and
// work OPStackManager's deployment into the existing test setup, instead of using the existing
// test setup to deploy OPStackManager.
contract OPStackManager_Init is Test {
OPStackManager opsm;
// Default dummy parameters for the deploy function.
OPStackManager.Roles roles;
uint256 l2ChainId = 1234;
uint32 basefeeScalar = 1;
uint32 blobBasefeeScalar = 1;
function setUp() public {
opsm = new OPStackManager();
// test setup to deploy OPStackManager. We do however inherit from DeployOPChain_TestBase so
// we can use its setup to deploy the implementations similarly to how a real deployment would
// happen.
contract OPStackManager_Deploy_Test is DeployOPChain_TestBase {
// This helper function is used to convert the input struct type defined in DeployOPChain.s.sol
// to the input struct type defined in OPStackManager.sol.
function toOPSMDeployInput(DeployOPChainInput.Input memory input)
internal
pure
returns (OPStackManager.DeployInput memory)
{
return OPStackManager.DeployInput({
roles: OPStackManager.Roles({
opChainProxyAdminOwner: input.roles.opChainProxyAdminOwner,
systemConfigOwner: input.roles.systemConfigOwner,
batcher: input.roles.batcher,
unsafeBlockSigner: input.roles.unsafeBlockSigner,
proposer: input.roles.proposer,
challenger: input.roles.challenger
}),
basefeeScalar: input.basefeeScalar,
blobBasefeeScalar: input.blobBaseFeeScalar,
l2ChainId: input.l2ChainId
});
}
}
contract OPStackManager_Deploy_Test is OPStackManager_Init {
function test_deploy_l2ChainIdEqualsZero_reverts() public {
deployOPChainInput.l2ChainId = 0;
vm.expectRevert(OPStackManager.InvalidChainId.selector);
opsm.deploy(0, basefeeScalar, blobBasefeeScalar, roles);
deployImplementationsOutput.opsm.deploy(toOPSMDeployInput(deployOPChainInput));
}
function test_deploy_l2ChainIdEqualsCurrentChainId_reverts() public {
deployOPChainInput.l2ChainId = block.chainid;
vm.expectRevert(OPStackManager.InvalidChainId.selector);
opsm.deploy(block.chainid, basefeeScalar, blobBasefeeScalar, roles);
deployImplementationsOutput.opsm.deploy(toOPSMDeployInput(deployOPChainInput));
}
function test_deploy_succeeds() public {
deployImplementationsOutput.opsm.deploy(toOPSMDeployInput(deployOPChainInput));
}
}
// These tests use the harness which exposes internal functions for testing.
contract OPStackManager_InternalMethods_Test is Test {
function test_calculatesBatchInboxAddress_succeeds() public {
OPStackManager_Harness opsmHarness = new OPStackManager_Harness();
OPStackManager_Harness opsmHarness;
function setUp() public {
opsmHarness = new OPStackManager_Harness({
_superchainConfig: SuperchainConfig(makeAddr("superchainConfig")),
_protocolVersions: ProtocolVersions(makeAddr("protocolVersions")),
_blueprints: OPStackManager.Blueprints({
addressManager: makeAddr("addressManager"),
proxy: makeAddr("proxy"),
proxyAdmin: makeAddr("proxyAdmin"),
l1ChugSplashProxy: makeAddr("l1ChugSplashProxy"),
resolvedDelegateProxy: makeAddr("resolvedDelegateProxy")
})
});
}
function test_calculatesBatchInboxAddress_succeeds() public view {
// These test vectors were calculated manually:
// 1. Compute the bytes32 encoding of the chainId: bytes32(uint256(chainId));
// 2. Hash it and manually take the first 19 bytes, and prefixed it with 0x00.
......
......@@ -2,7 +2,6 @@
pragma solidity ^0.8.15;
import { CommonTest } from "test/setup/CommonTest.sol";
import { Executables } from "scripts/libraries/Executables.sol";
import { console2 as console } from "forge-std/console2.sol";
import { ProtocolVersions } from "src/L1/ProtocolVersions.sol";
import { OptimismPortal } from "src/L1/OptimismPortal.sol";
......@@ -823,7 +822,25 @@ contract Specification_Test is CommonTest {
// OPStackManager
_addSpec({ _name: "OPStackManager", _sel: _getSel("version()") });
_addSpec({ _name: "OPStackManager", _sel: _getSel("superchainConfig()") });
_addSpec({ _name: "OPStackManager", _sel: _getSel("protocolVersions()") });
_addSpec({ _name: "OPStackManager", _sel: _getSel("latestRelease()") });
_addSpec({ _name: "OPStackManager", _sel: _getSel("implementations(string,string)") });
_addSpec({ _name: "OPStackManager", _sel: _getSel("systemConfigs(uint256)") });
_addSpec({ _name: "OPStackManager", _sel: OPStackManager.setRelease.selector });
_addSpec({ _name: "OPStackManager", _sel: OPStackManager.deploy.selector });
_addSpec({ _name: "OPStackManager", _sel: OPStackManager.blueprints.selector });
// OPStackManagerInterop
_addSpec({ _name: "OPStackManagerInterop", _sel: _getSel("version()") });
_addSpec({ _name: "OPStackManagerInterop", _sel: _getSel("superchainConfig()") });
_addSpec({ _name: "OPStackManagerInterop", _sel: _getSel("protocolVersions()") });
_addSpec({ _name: "OPStackManagerInterop", _sel: _getSel("latestRelease()") });
_addSpec({ _name: "OPStackManagerInterop", _sel: _getSel("implementations(string,string)") });
_addSpec({ _name: "OPStackManagerInterop", _sel: _getSel("systemConfigs(uint256)") });
_addSpec({ _name: "OPStackManagerInterop", _sel: OPStackManager.setRelease.selector });
_addSpec({ _name: "OPStackManagerInterop", _sel: OPStackManager.deploy.selector });
_addSpec({ _name: "OPStackManagerInterop", _sel: OPStackManager.blueprints.selector });
// DeputyGuardianModule
_addSpec({
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment