Commit 308ce74c authored by Maurelian's avatar Maurelian Committed by GitHub

OPSM: Deploy Permissioned Game (#12064)

* chore: fix semver lock

* fix: no permissionless root, remove hash from 0xdead

* fix: use 0xdead root properly

* feat: add remaining fault proof support

* chore: Update semver-lock

* fix: Remove extra anchor root definition and restore aritfactsFs argument

* feat: Add wip big blueprint code

* Don't wrap input to deployBigBytecode with preamble

* fix: off by one in deployBigBytecode

* feat: more gas efficient blueprint deployment for permissioned game

* Get the big deployments working

* perf: more efficient preamble parsing

* chore: snapshots + fix revert

* test: skip FaultDisputeGameAddress since we don't deploy it yet

* chore: cleanup

---------
Co-authored-by: default avatarMatt Solomon <matt@mattsolomon.dev>
Co-authored-by: default avatarMatthew Slipper <me@matthewslipper.com>
parent ec3f6344
...@@ -27,6 +27,8 @@ import ( ...@@ -27,6 +27,8 @@ import (
const TestParams = ` const TestParams = `
participants: participants:
- el_type: geth - el_type: geth
el_extra_params:
- "--gcmode=archive"
cl_type: lighthouse cl_type: lighthouse
network_params: network_params:
prefunded_accounts: '{ "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266": { "balance": "1000000ETH" } }' prefunded_accounts: '{ "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266": { "balance": "1000000ETH" } }'
...@@ -41,6 +43,7 @@ network_params: ...@@ -41,6 +43,7 @@ network_params:
}' }'
network_id: "77799777" network_id: "77799777"
seconds_per_slot: 3 seconds_per_slot: 3
genesis_delay: 0
` `
type deployerKey struct{} type deployerKey struct{}
...@@ -56,7 +59,7 @@ func (d *deployerKey) String() string { ...@@ -56,7 +59,7 @@ func (d *deployerKey) String() string {
func TestEndToEndApply(t *testing.T) { func TestEndToEndApply(t *testing.T) {
kurtosisutil.Test(t) kurtosisutil.Test(t)
lgr := testlog.Logger(t, slog.LevelInfo) lgr := testlog.Logger(t, slog.LevelDebug)
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
defer cancel() defer cancel()
...@@ -189,6 +192,10 @@ func TestEndToEndApply(t *testing.T) { ...@@ -189,6 +192,10 @@ func TestEndToEndApply(t *testing.T) {
{"DelayedWETHPermissionlessGameProxyAddress", chainState.DelayedWETHPermissionlessGameProxyAddress}, {"DelayedWETHPermissionlessGameProxyAddress", chainState.DelayedWETHPermissionlessGameProxyAddress},
} }
for _, addr := range chainAddrs { for _, addr := range chainAddrs {
// TODO Delete this `if`` block once FaultDisputeGameAddress is deployed.
if addr.name == "FaultDisputeGameAddress" {
continue
}
t.Run(fmt.Sprintf("chain %s - %s", chainState.ID, addr.name), func(t *testing.T) { t.Run(fmt.Sprintf("chain %s - %s", chainState.ID, addr.name), func(t *testing.T) {
code, err := l1Client.CodeAt(ctx, addr.addr, nil) code, err := l1Client.CodeAt(ctx, addr.addr, nil)
require.NoError(t, err) require.NoError(t, err)
......
...@@ -10,6 +10,7 @@ import { ISuperchainConfig } from "src/L1/interfaces/ISuperchainConfig.sol"; ...@@ -10,6 +10,7 @@ import { ISuperchainConfig } from "src/L1/interfaces/ISuperchainConfig.sol";
import { Constants } from "src/libraries/Constants.sol"; import { Constants } from "src/libraries/Constants.sol";
import { Predeploys } from "src/libraries/Predeploys.sol"; import { Predeploys } from "src/libraries/Predeploys.sol";
import { Bytes } from "src/libraries/Bytes.sol";
import { ProxyAdmin } from "src/universal/ProxyAdmin.sol"; import { ProxyAdmin } from "src/universal/ProxyAdmin.sol";
import { Proxy } from "src/universal/Proxy.sol"; import { Proxy } from "src/universal/Proxy.sol";
...@@ -23,6 +24,7 @@ import { IPreimageOracle } from "src/cannon/interfaces/IPreimageOracle.sol"; ...@@ -23,6 +24,7 @@ import { IPreimageOracle } from "src/cannon/interfaces/IPreimageOracle.sol";
import { MIPS } from "src/cannon/MIPS.sol"; import { MIPS } from "src/cannon/MIPS.sol";
import { DisputeGameFactory } from "src/dispute/DisputeGameFactory.sol"; import { DisputeGameFactory } from "src/dispute/DisputeGameFactory.sol";
import { AnchorStateRegistry } from "src/dispute/AnchorStateRegistry.sol"; import { AnchorStateRegistry } from "src/dispute/AnchorStateRegistry.sol";
import { PermissionedDisputeGame } from "src/dispute/PermissionedDisputeGame.sol";
import { SuperchainConfig } from "src/L1/SuperchainConfig.sol"; import { SuperchainConfig } from "src/L1/SuperchainConfig.sol";
import { ProtocolVersions } from "src/L1/ProtocolVersions.sol"; import { ProtocolVersions } from "src/L1/ProtocolVersions.sol";
...@@ -514,10 +516,11 @@ contract DeployImplementations is Script { ...@@ -514,10 +516,11 @@ contract DeployImplementations is Script {
blueprints.l1ChugSplashProxy = deployBytecode(Blueprint.blueprintDeployerBytecode(type(L1ChugSplashProxy).creationCode), salt); blueprints.l1ChugSplashProxy = deployBytecode(Blueprint.blueprintDeployerBytecode(type(L1ChugSplashProxy).creationCode), salt);
blueprints.resolvedDelegateProxy = deployBytecode(Blueprint.blueprintDeployerBytecode(type(ResolvedDelegateProxy).creationCode), salt); blueprints.resolvedDelegateProxy = deployBytecode(Blueprint.blueprintDeployerBytecode(type(ResolvedDelegateProxy).creationCode), salt);
blueprints.anchorStateRegistry = deployBytecode(Blueprint.blueprintDeployerBytecode(type(AnchorStateRegistry).creationCode), salt); blueprints.anchorStateRegistry = deployBytecode(Blueprint.blueprintDeployerBytecode(type(AnchorStateRegistry).creationCode), salt);
(blueprints.permissionedDisputeGame1, blueprints.permissionedDisputeGame2) = deployBigBytecode(type(PermissionedDisputeGame).creationCode, salt);
vm.stopBroadcast(); vm.stopBroadcast();
// forgefmt: disable-end // forgefmt: disable-end
OPStackManager.ImplementationSetter[] memory setters = new OPStackManager.ImplementationSetter[](7); OPStackManager.ImplementationSetter[] memory setters = new OPStackManager.ImplementationSetter[](9);
setters[0] = OPStackManager.ImplementationSetter({ setters[0] = OPStackManager.ImplementationSetter({
name: "L1ERC721Bridge", name: "L1ERC721Bridge",
info: OPStackManager.Implementation(address(_dio.l1ERC721BridgeImpl()), L1ERC721Bridge.initialize.selector) info: OPStackManager.Implementation(address(_dio.l1ERC721BridgeImpl()), L1ERC721Bridge.initialize.selector)
...@@ -543,13 +546,22 @@ contract DeployImplementations is Script { ...@@ -543,13 +546,22 @@ contract DeployImplementations is Script {
name: "L1StandardBridge", name: "L1StandardBridge",
info: OPStackManager.Implementation(address(_dio.l1StandardBridgeImpl()), L1StandardBridge.initialize.selector) info: OPStackManager.Implementation(address(_dio.l1StandardBridgeImpl()), L1StandardBridge.initialize.selector)
}); });
setters[6] = OPStackManager.ImplementationSetter({ setters[6] = OPStackManager.ImplementationSetter({
name: "DisputeGameFactory", name: "DisputeGameFactory",
info: OPStackManager.Implementation( info: OPStackManager.Implementation(
address(_dio.disputeGameFactoryImpl()), DisputeGameFactory.initialize.selector address(_dio.disputeGameFactoryImpl()), DisputeGameFactory.initialize.selector
) )
}); });
setters[7] = OPStackManager.ImplementationSetter({
name: "DelayedWETH",
info: OPStackManager.Implementation(address(_dio.delayedWETHImpl()), DelayedWETH.initialize.selector)
});
setters[8] = OPStackManager.ImplementationSetter({
name: "MIPS",
// MIPS is a singleton for all chains, so it doesn't need to be initialized, so the
// selector is just `bytes4(0)`.
info: OPStackManager.Implementation(address(_dio.mipsSingleton()), bytes4(0))
});
// This call contains a broadcast to deploy OPSM which is proxied. // This call contains a broadcast to deploy OPSM which is proxied.
OPStackManager opsmProxy = createOPSMContract(_dii, _dio, blueprints, release, setters); OPStackManager opsmProxy = createOPSMContract(_dii, _dio, blueprints, release, setters);
...@@ -617,14 +629,14 @@ contract DeployImplementations is Script { ...@@ -617,14 +629,14 @@ contract DeployImplementations is Script {
// The fault proofs contracts are configured as follows: // The fault proofs contracts are configured as follows:
// | Contract | Proxied | Deployment | MCP Ready | // | Contract | Proxied | Deployment | MCP Ready |
// |-------------------------|---------|-----------------------------------|------------| // |-------------------------|---------|-----------------------------------|------------|
// | DisputeGameFactory | Yes | Bespoke | Yes | X // | DisputeGameFactory | Yes | Bespoke | Yes |
// | AnchorStateRegistry | Yes | Bespoke | No | X // | AnchorStateRegistry | Yes | Bespoke | No |
// | FaultDisputeGame | No | Bespoke | No | Todo // | FaultDisputeGame | No | Bespoke | No | Not yet supported by OPCM
// | PermissionedDisputeGame | No | Bespoke | No | Todo // | PermissionedDisputeGame | No | Bespoke | No |
// | DelayedWETH | Yes | Two bespoke (one per DisputeGame) | No | Todo: Proxies. // | DelayedWETH | Yes | Two bespoke (one per DisputeGame) | No |
// | PreimageOracle | No | Shared | N/A | X // | PreimageOracle | No | Shared | N/A |
// | MIPS | No | Shared | N/A | X // | MIPS | No | Shared | N/A |
// | OptimismPortal2 | Yes | Shared | No | X // | OptimismPortal2 | Yes | Shared | No |
// //
// This script only deploys the shared contracts. The bespoke contracts are deployed by // This script only deploys the shared contracts. The bespoke contracts are deployed by
// `DeployOPChain.s.sol`. When the shared contracts are proxied, the contracts deployed here are // `DeployOPChain.s.sol`. When the shared contracts are proxied, the contracts deployed here are
...@@ -731,6 +743,26 @@ contract DeployImplementations is Script { ...@@ -731,6 +743,26 @@ contract DeployImplementations is Script {
} }
require(newContract_ != address(0), "DeployImplementations: create2 failed"); require(newContract_ != address(0), "DeployImplementations: create2 failed");
} }
function deployBigBytecode(
bytes memory _bytecode,
bytes32 _salt
)
public
returns (address newContract1_, address newContract2_)
{
// Preamble needs 3 bytes.
uint256 maxInitCodeSize = 24576 - 3;
require(_bytecode.length > maxInitCodeSize, "DeployImplementations: Use deployBytecode instead");
bytes memory part1Slice = Bytes.slice(_bytecode, 0, maxInitCodeSize);
bytes memory part1 = Blueprint.blueprintDeployerBytecode(part1Slice);
bytes memory part2Slice = Bytes.slice(_bytecode, maxInitCodeSize, _bytecode.length - maxInitCodeSize);
bytes memory part2 = Blueprint.blueprintDeployerBytecode(part2Slice);
newContract1_ = deployBytecode(part1, _salt);
newContract2_ = deployBytecode(part2, _salt);
}
} }
// Similar to how DeploySuperchain.s.sol contains a lot of comments to thoroughly document the script // Similar to how DeploySuperchain.s.sol contains a lot of comments to thoroughly document the script
......
...@@ -11,6 +11,7 @@ import { BaseDeployIO } from "scripts/utils/BaseDeployIO.sol"; ...@@ -11,6 +11,7 @@ import { BaseDeployIO } from "scripts/utils/BaseDeployIO.sol";
import { IResourceMetering } from "src/L1/interfaces/IResourceMetering.sol"; import { IResourceMetering } from "src/L1/interfaces/IResourceMetering.sol";
import { ISuperchainConfig } from "src/L1/interfaces/ISuperchainConfig.sol"; import { ISuperchainConfig } from "src/L1/interfaces/ISuperchainConfig.sol";
import { IBigStepper } from "src/dispute/interfaces/IBigStepper.sol";
import { Constants } from "src/libraries/Constants.sol"; import { Constants } from "src/libraries/Constants.sol";
import { Predeploys } from "src/libraries/Predeploys.sol"; import { Predeploys } from "src/libraries/Predeploys.sol";
...@@ -23,7 +24,7 @@ import { DisputeGameFactory } from "src/dispute/DisputeGameFactory.sol"; ...@@ -23,7 +24,7 @@ import { DisputeGameFactory } from "src/dispute/DisputeGameFactory.sol";
import { AnchorStateRegistry } from "src/dispute/AnchorStateRegistry.sol"; import { AnchorStateRegistry } from "src/dispute/AnchorStateRegistry.sol";
import { FaultDisputeGame } from "src/dispute/FaultDisputeGame.sol"; import { FaultDisputeGame } from "src/dispute/FaultDisputeGame.sol";
import { PermissionedDisputeGame } from "src/dispute/PermissionedDisputeGame.sol"; import { PermissionedDisputeGame } from "src/dispute/PermissionedDisputeGame.sol";
import { GameType, GameTypes, Hash, OutputRoot } from "src/dispute/lib/Types.sol"; import { Claim, GameType, GameTypes, Hash, OutputRoot } from "src/dispute/lib/Types.sol";
import { OPStackManager } from "src/L1/OPStackManager.sol"; import { OPStackManager } from "src/L1/OPStackManager.sol";
import { OptimismPortal2 } from "src/L1/OptimismPortal2.sol"; import { OptimismPortal2 } from "src/L1/OptimismPortal2.sol";
...@@ -201,7 +202,7 @@ contract DeployOPChainOutput is BaseDeployIO { ...@@ -201,7 +202,7 @@ contract DeployOPChainOutput is BaseDeployIO {
address(_disputeGameFactoryProxy), address(_disputeGameFactoryProxy),
address(_anchorStateRegistryProxy), address(_anchorStateRegistryProxy),
address(_anchorStateRegistryImpl), address(_anchorStateRegistryImpl),
address(_faultDisputeGame), // address(_faultDisputeGame),
address(_permissionedDisputeGame), address(_permissionedDisputeGame),
address(_delayedWETHPermissionedGameProxy), address(_delayedWETHPermissionedGameProxy),
address(_delayedWETHPermissionlessGameProxy) address(_delayedWETHPermissionlessGameProxy)
...@@ -289,8 +290,8 @@ contract DeployOPChainOutput is BaseDeployIO { ...@@ -289,8 +290,8 @@ contract DeployOPChainOutput is BaseDeployIO {
// -------- Deployment Assertions -------- // -------- Deployment Assertions --------
function assertValidDeploy(DeployOPChainInput _doi) internal { function assertValidDeploy(DeployOPChainInput _doi) internal {
assertValidAnchorStateRegistryProxy(_doi);
assertValidAnchorStateRegistryImpl(_doi); assertValidAnchorStateRegistryImpl(_doi);
assertValidAnchorStateRegistryProxy(_doi);
assertValidDelayedWETHs(_doi); assertValidDelayedWETHs(_doi);
assertValidDisputeGameFactory(_doi); assertValidDisputeGameFactory(_doi);
assertValidL1CrossDomainMessenger(_doi); assertValidL1CrossDomainMessenger(_doi);
...@@ -298,9 +299,23 @@ contract DeployOPChainOutput is BaseDeployIO { ...@@ -298,9 +299,23 @@ contract DeployOPChainOutput is BaseDeployIO {
assertValidL1StandardBridge(_doi); assertValidL1StandardBridge(_doi);
assertValidOptimismMintableERC20Factory(_doi); assertValidOptimismMintableERC20Factory(_doi);
assertValidOptimismPortal(_doi); assertValidOptimismPortal(_doi);
assertValidPermissionedDisputeGame(_doi);
assertValidSystemConfig(_doi); assertValidSystemConfig(_doi);
// TODO Other FP assertions like the dispute games, anchor state registry, etc. }
// TODO add initialization assertions
function assertValidPermissionedDisputeGame(DeployOPChainInput _doi) internal view {
PermissionedDisputeGame game = permissionedDisputeGame();
require(GameType.unwrap(game.gameType()) == GameType.unwrap(GameTypes.PERMISSIONED_CANNON), "DPG-10");
require(Claim.unwrap(game.absolutePrestate()) == bytes32(hex"dead"), "DPG-20");
OPStackManager opsm = _doi.opsmProxy();
(address mips,) = opsm.implementations(opsm.latestRelease(), "MIPS");
require(game.vm() == IBigStepper(mips), "DPG-30");
require(address(game.weth()) == address(delayedWETHPermissionedGameProxy()), "DPG-40");
require(address(game.anchorStateRegistry()) == address(anchorStateRegistryProxy()), "DPG-50");
require(game.l2ChainId() == _doi.l2ChainId(), "DPG-60");
} }
function assertValidAnchorStateRegistryProxy(DeployOPChainInput) internal { function assertValidAnchorStateRegistryProxy(DeployOPChainInput) internal {
...@@ -436,7 +451,14 @@ contract DeployOPChainOutput is BaseDeployIO { ...@@ -436,7 +451,14 @@ contract DeployOPChainOutput is BaseDeployIO {
} }
function assertValidDisputeGameFactory(DeployOPChainInput) internal view { function assertValidDisputeGameFactory(DeployOPChainInput) internal view {
// TODO add in once FP support is added. DisputeGameFactory factory = disputeGameFactoryProxy();
DeployUtils.assertInitialized({ _contractAddress: address(factory), _slot: 0, _offset: 0 });
require(
address(factory.gameImpls(GameTypes.PERMISSIONED_CANNON)) == address(permissionedDisputeGame()), "DF-10"
);
require(factory.owner() == address(opChainProxyAdmin()), "DF-20");
} }
function assertValidDelayedWETHs(DeployOPChainInput) internal view { function assertValidDelayedWETHs(DeployOPChainInput) internal view {
...@@ -480,7 +502,7 @@ contract DeployOPChain is Script { ...@@ -480,7 +502,7 @@ contract DeployOPChain is Script {
vm.label(address(deployOutput.disputeGameFactoryProxy), "disputeGameFactoryProxy"); vm.label(address(deployOutput.disputeGameFactoryProxy), "disputeGameFactoryProxy");
vm.label(address(deployOutput.anchorStateRegistryProxy), "anchorStateRegistryProxy"); vm.label(address(deployOutput.anchorStateRegistryProxy), "anchorStateRegistryProxy");
vm.label(address(deployOutput.anchorStateRegistryImpl), "anchorStateRegistryImpl"); vm.label(address(deployOutput.anchorStateRegistryImpl), "anchorStateRegistryImpl");
vm.label(address(deployOutput.faultDisputeGame), "faultDisputeGame"); // vm.label(address(deployOutput.faultDisputeGame), "faultDisputeGame");
vm.label(address(deployOutput.permissionedDisputeGame), "permissionedDisputeGame"); vm.label(address(deployOutput.permissionedDisputeGame), "permissionedDisputeGame");
vm.label(address(deployOutput.delayedWETHPermissionedGameProxy), "delayedWETHPermissionedGameProxy"); vm.label(address(deployOutput.delayedWETHPermissionedGameProxy), "delayedWETHPermissionedGameProxy");
vm.label(address(deployOutput.delayedWETHPermissionlessGameProxy), "delayedWETHPermissionlessGameProxy"); vm.label(address(deployOutput.delayedWETHPermissionlessGameProxy), "delayedWETHPermissionlessGameProxy");
...@@ -498,7 +520,7 @@ contract DeployOPChain is Script { ...@@ -498,7 +520,7 @@ contract DeployOPChain is Script {
_doo.set(_doo.disputeGameFactoryProxy.selector, address(deployOutput.disputeGameFactoryProxy)); _doo.set(_doo.disputeGameFactoryProxy.selector, address(deployOutput.disputeGameFactoryProxy));
_doo.set(_doo.anchorStateRegistryProxy.selector, address(deployOutput.anchorStateRegistryProxy)); _doo.set(_doo.anchorStateRegistryProxy.selector, address(deployOutput.anchorStateRegistryProxy));
_doo.set(_doo.anchorStateRegistryImpl.selector, address(deployOutput.anchorStateRegistryImpl)); _doo.set(_doo.anchorStateRegistryImpl.selector, address(deployOutput.anchorStateRegistryImpl));
_doo.set(_doo.faultDisputeGame.selector, address(deployOutput.faultDisputeGame)); // _doo.set(_doo.faultDisputeGame.selector, address(deployOutput.faultDisputeGame));
_doo.set(_doo.permissionedDisputeGame.selector, address(deployOutput.permissionedDisputeGame)); _doo.set(_doo.permissionedDisputeGame.selector, address(deployOutput.permissionedDisputeGame));
_doo.set(_doo.delayedWETHPermissionedGameProxy.selector, address(deployOutput.delayedWETHPermissionedGameProxy)); _doo.set(_doo.delayedWETHPermissionedGameProxy.selector, address(deployOutput.delayedWETHPermissionedGameProxy));
_doo.set( _doo.set(
......
...@@ -32,8 +32,8 @@ ...@@ -32,8 +32,8 @@
"sourceCodeHash": "0xde4df0f9633dc0cdb1c9f634003ea5b0f7c5c1aebc407bc1b2f44c0ecf938649" "sourceCodeHash": "0xde4df0f9633dc0cdb1c9f634003ea5b0f7c5c1aebc407bc1b2f44c0ecf938649"
}, },
"src/L1/OPStackManager.sol": { "src/L1/OPStackManager.sol": {
"initCodeHash": "0x4bffecbd95e63f9bd04ab8e3c6a804cc25e0cd151ebeb7f8d6b9330332e6eb20", "initCodeHash": "0x5b451782192b8429f6822c88270c4f0dbd10342518c5695ecf4dff7b5ebfb4e4",
"sourceCodeHash": "0x850f1eacc77f1a5c680625196618bc4b4332cb68924d9eddd57c749bedcd7c94" "sourceCodeHash": "0x4a9c242ce96471437ec97662d2365a7bda376db765c630a41cbe238811f1df51"
}, },
"src/L1/OptimismPortal.sol": { "src/L1/OptimismPortal.sol": {
"initCodeHash": "0xbe2c0c81b3459014f287d8c89cdc0d27dde5d1f44e5d024fa1e4773ddc47c190", "initCodeHash": "0xbe2c0c81b3459014f287d8c89cdc0d27dde5d1f44e5d024fa1e4773ddc47c190",
......
...@@ -50,6 +50,16 @@ ...@@ -50,6 +50,16 @@
"internalType": "address", "internalType": "address",
"name": "anchorStateRegistry", "name": "anchorStateRegistry",
"type": "address" "type": "address"
},
{
"internalType": "address",
"name": "permissionedDisputeGame1",
"type": "address"
},
{
"internalType": "address",
"name": "permissionedDisputeGame2",
"type": "address"
} }
], ],
"internalType": "struct OPStackManager.Blueprints", "internalType": "struct OPStackManager.Blueprints",
...@@ -298,6 +308,16 @@ ...@@ -298,6 +308,16 @@
"internalType": "address", "internalType": "address",
"name": "anchorStateRegistry", "name": "anchorStateRegistry",
"type": "address" "type": "address"
},
{
"internalType": "address",
"name": "permissionedDisputeGame1",
"type": "address"
},
{
"internalType": "address",
"name": "permissionedDisputeGame2",
"type": "address"
} }
], ],
"internalType": "struct OPStackManager.Blueprints", "internalType": "struct OPStackManager.Blueprints",
...@@ -499,6 +519,11 @@ ...@@ -499,6 +519,11 @@
"name": "EmptyInitcode", "name": "EmptyInitcode",
"type": "error" "type": "error"
}, },
{
"inputs": [],
"name": "IdentityPrecompileCallFailed",
"type": "error"
},
{ {
"inputs": [], "inputs": [],
"name": "InvalidChainId", "name": "InvalidChainId",
......
...@@ -50,6 +50,16 @@ ...@@ -50,6 +50,16 @@
"internalType": "address", "internalType": "address",
"name": "anchorStateRegistry", "name": "anchorStateRegistry",
"type": "address" "type": "address"
},
{
"internalType": "address",
"name": "permissionedDisputeGame1",
"type": "address"
},
{
"internalType": "address",
"name": "permissionedDisputeGame2",
"type": "address"
} }
], ],
"internalType": "struct OPStackManager.Blueprints", "internalType": "struct OPStackManager.Blueprints",
...@@ -298,6 +308,16 @@ ...@@ -298,6 +308,16 @@
"internalType": "address", "internalType": "address",
"name": "anchorStateRegistry", "name": "anchorStateRegistry",
"type": "address" "type": "address"
},
{
"internalType": "address",
"name": "permissionedDisputeGame1",
"type": "address"
},
{
"internalType": "address",
"name": "permissionedDisputeGame2",
"type": "address"
} }
], ],
"internalType": "struct OPStackManager.Blueprints", "internalType": "struct OPStackManager.Blueprints",
...@@ -499,6 +519,11 @@ ...@@ -499,6 +519,11 @@
"name": "EmptyInitcode", "name": "EmptyInitcode",
"type": "error" "type": "error"
}, },
{
"inputs": [],
"name": "IdentityPrecompileCallFailed",
"type": "error"
},
{ {
"inputs": [], "inputs": [],
"name": "InvalidChainId", "name": "InvalidChainId",
......
...@@ -13,32 +13,39 @@ ...@@ -13,32 +13,39 @@
"slot": "0", "slot": "0",
"type": "bool" "type": "bool"
}, },
{
"bytes": "192",
"label": "blueprint",
"offset": 0,
"slot": "1",
"type": "struct OPStackManager.Blueprints"
},
{ {
"bytes": "32", "bytes": "32",
"label": "latestRelease", "label": "latestRelease",
"offset": 0, "offset": 0,
"slot": "7", "slot": "1",
"type": "string" "type": "string"
}, },
{ {
"bytes": "32", "bytes": "32",
"label": "implementations", "label": "implementations",
"offset": 0, "offset": 0,
"slot": "8", "slot": "2",
"type": "mapping(string => mapping(string => struct OPStackManager.Implementation))" "type": "mapping(string => mapping(string => struct OPStackManager.Implementation))"
}, },
{ {
"bytes": "32", "bytes": "32",
"label": "systemConfigs", "label": "systemConfigs",
"offset": 0, "offset": 0,
"slot": "9", "slot": "3",
"type": "mapping(uint256 => contract SystemConfig)" "type": "mapping(uint256 => contract SystemConfig)"
},
{
"bytes": "256",
"label": "blueprint",
"offset": 0,
"slot": "4",
"type": "struct OPStackManager.Blueprints"
},
{
"bytes": "1600",
"label": "__gap",
"offset": 0,
"slot": "12",
"type": "uint256[50]"
} }
] ]
\ No newline at end of file
...@@ -13,32 +13,39 @@ ...@@ -13,32 +13,39 @@
"slot": "0", "slot": "0",
"type": "bool" "type": "bool"
}, },
{
"bytes": "192",
"label": "blueprint",
"offset": 0,
"slot": "1",
"type": "struct OPStackManager.Blueprints"
},
{ {
"bytes": "32", "bytes": "32",
"label": "latestRelease", "label": "latestRelease",
"offset": 0, "offset": 0,
"slot": "7", "slot": "1",
"type": "string" "type": "string"
}, },
{ {
"bytes": "32", "bytes": "32",
"label": "implementations", "label": "implementations",
"offset": 0, "offset": 0,
"slot": "8", "slot": "2",
"type": "mapping(string => mapping(string => struct OPStackManager.Implementation))" "type": "mapping(string => mapping(string => struct OPStackManager.Implementation))"
}, },
{ {
"bytes": "32", "bytes": "32",
"label": "systemConfigs", "label": "systemConfigs",
"offset": 0, "offset": 0,
"slot": "9", "slot": "3",
"type": "mapping(uint256 => contract SystemConfig)" "type": "mapping(uint256 => contract SystemConfig)"
},
{
"bytes": "256",
"label": "blueprint",
"offset": 0,
"slot": "4",
"type": "struct OPStackManager.Blueprints"
},
{
"bytes": "1600",
"label": "__gap",
"offset": 0,
"slot": "12",
"type": "uint256[50]"
} }
] ]
\ No newline at end of file
...@@ -20,6 +20,9 @@ library Blueprint { ...@@ -20,6 +20,9 @@ library Blueprint {
/// @notice Thrown when parsing a blueprint preamble and the resulting initcode is empty. /// @notice Thrown when parsing a blueprint preamble and the resulting initcode is empty.
error EmptyInitcode(); error EmptyInitcode();
/// @notice Thrown when call to the identity precompile fails.
error IdentityPrecompileCallFailed();
/// @notice Thrown when parsing a blueprint preamble and the bytecode does not contain the expected prefix bytes. /// @notice Thrown when parsing a blueprint preamble and the bytecode does not contain the expected prefix bytes.
error NotABlueprint(); error NotABlueprint();
...@@ -56,7 +59,7 @@ library Blueprint { ...@@ -56,7 +59,7 @@ library Blueprint {
/// @notice Given bytecode as a sequence of bytes, parse the blueprint preamble and deconstruct /// @notice Given bytecode as a sequence of bytes, parse the blueprint preamble and deconstruct
/// the bytecode into the ERC version, preamble data and initcode. Reverts if the bytecode is /// the bytecode into the ERC version, preamble data and initcode. Reverts if the bytecode is
/// not a valid blueprint contract according to ERC-5202. /// not a valid blueprint contract according to ERC-5202.
function parseBlueprintPreamble(bytes memory _bytecode) internal pure returns (Preamble memory) { function parseBlueprintPreamble(bytes memory _bytecode) internal view returns (Preamble memory) {
if (_bytecode.length < 2 || _bytecode[0] != 0xFE || _bytecode[1] != 0x71) { if (_bytecode.length < 2 || _bytecode[0] != 0xFE || _bytecode[1] != 0x71) {
revert NotABlueprint(); revert NotABlueprint();
} }
...@@ -77,18 +80,34 @@ library Blueprint { ...@@ -77,18 +80,34 @@ library Blueprint {
bytes memory preambleData = new bytes(dataLength); bytes memory preambleData = new bytes(dataLength);
if (nLengthBytes != 0) { if (nLengthBytes != 0) {
uint256 dataStart = 3 + nLengthBytes; uint256 dataStart = 3 + nLengthBytes;
// This loop is very small, so not worth using the identity precompile like we do with initcode below.
for (uint256 i = 0; i < dataLength; i++) { for (uint256 i = 0; i < dataLength; i++) {
preambleData[i] = _bytecode[dataStart + i]; preambleData[i] = _bytecode[dataStart + i];
} }
} }
// Parsing the initcode byte-by-byte is too costly for long initcode, so we perform a staticcall
// to the identity precompile at address(0x04) to copy the initcode.
uint256 initcodeStart = 3 + nLengthBytes + dataLength; uint256 initcodeStart = 3 + nLengthBytes + dataLength;
bytes memory initcode = new bytes(_bytecode.length - initcodeStart); uint256 initcodeLength = _bytecode.length - initcodeStart;
for (uint256 i = 0; i < initcode.length; i++) { if (initcodeLength == 0) revert EmptyInitcode();
initcode[i] = _bytecode[initcodeStart + i];
bytes memory initcode = new bytes(initcodeLength);
bool success;
assembly ("memory-safe") {
// Calculate the memory address of the input data (initcode) within _bytecode.
// - add(_bytecode, 32): Moves past the length field to the start of _bytecode's data.
// - add(..., initcodeStart): Adds the offset to reach the initcode within _bytecode.
let inputData := add(add(_bytecode, 32), initcodeStart)
// Calculate the memory address for the output data in initcode.
let outputData := add(initcode, 32)
// Perform the staticcall to the identity precompile.
success := staticcall(gas(), 0x04, inputData, initcodeLength, outputData, initcodeLength)
} }
if (initcode.length == 0) revert EmptyInitcode();
if (!success) revert IdentityPrecompileCallFailed();
return Preamble(ercVersion, preambleData, initcode); return Preamble(ercVersion, preambleData, initcode);
} }
...@@ -112,6 +131,32 @@ library Blueprint { ...@@ -112,6 +131,32 @@ library Blueprint {
if (newContract_ == address(0)) revert DeploymentFailed(); if (newContract_ == address(0)) revert DeploymentFailed();
} }
/// @notice Parses the code at two target addresses as individual blueprints, concatentates them and then deploys
/// the resulting initcode with the given `_data` appended, i.e. `_data` is the ABI-encoded constructor arguments.
function deployFrom(
address _target1,
address _target2,
bytes32 _salt,
bytes memory _data
)
internal
returns (address newContract_)
{
Preamble memory preamble1 = parseBlueprintPreamble(address(_target1).code);
if (preamble1.ercVersion != 0) revert UnsupportedERCVersion(preamble1.ercVersion);
if (preamble1.preambleData.length != 0) revert UnexpectedPreambleData(preamble1.preambleData);
Preamble memory preamble2 = parseBlueprintPreamble(address(_target2).code);
if (preamble2.ercVersion != 0) revert UnsupportedERCVersion(preamble2.ercVersion);
if (preamble2.preambleData.length != 0) revert UnexpectedPreambleData(preamble2.preambleData);
bytes memory initcode = bytes.concat(preamble1.initcode, preamble2.initcode, _data);
assembly ("memory-safe") {
newContract_ := create2(0, add(initcode, 0x20), mload(initcode), _salt)
}
if (newContract_ == address(0)) revert DeploymentFailed();
}
/// @notice Convert a bytes array to a uint256. /// @notice Convert a bytes array to a uint256.
function bytesToUint(bytes memory _b) internal pure returns (uint256) { function bytesToUint(bytes memory _b) internal pure returns (uint256) {
if (_b.length > 32) revert BytesArrayTooLong(); if (_b.length > 32) revert BytesArrayTooLong();
......
...@@ -22,7 +22,7 @@ contract BlueprintHarness { ...@@ -22,7 +22,7 @@ contract BlueprintHarness {
return Blueprint.blueprintDeployerBytecode(_initcode); return Blueprint.blueprintDeployerBytecode(_initcode);
} }
function parseBlueprintPreamble(bytes memory _bytecode) public pure returns (Blueprint.Preamble memory) { function parseBlueprintPreamble(bytes memory _bytecode) public view returns (Blueprint.Preamble memory) {
return Blueprint.parseBlueprintPreamble(_bytecode); return Blueprint.parseBlueprintPreamble(_bytecode);
} }
......
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