From 4f7720935f43f024c2f015edc2fdff7abb630ac0 Mon Sep 17 00:00:00 2001 From: Matthew Slipper Date: Sat, 14 Sep 2024 02:01:16 -0600 Subject: [PATCH] Port deployers, add end-to-end contract deployments This PR ports over the deployers in `interopgen` into `op-deployer`, and updates `op-deployer` to support end-to end contract deployments for both the Superchain and individual OP Chains. This PR includes a couple of bugfixes for things I discovered along the way: 1. The script host is updated to bump the nonce of the address calling the CREATE2 deployer when broadcasting. This fixes a chain/simulation mismatch that blocked contracts from being deployed. 2. The DeployImplementations contract used a fixed CREATE2 salt, which caused problems on repeated deployments. I updated the contract to pull the nonce from the environment as we do elsewhere. Builds on https://github.com/ethereum-optimism/optimism/pull/11910. --- op-chain-ops/deployer/apply.go | 21 +++- op-chain-ops/deployer/apply_test.go | 94 +++++++++++++-- op-chain-ops/deployer/broadcaster/discard.go | 20 ++++ op-chain-ops/deployer/broadcaster/keyed.go | 56 ++++++--- .../opsm}/implementations.go | 38 +++--- .../deployers => deployer/opsm}/opchain.go | 27 ++--- op-chain-ops/deployer/opsm/superchain.go | 57 ++------- op-chain-ops/deployer/pipeline/host.go | 70 +++++++++++ .../deployer/pipeline/implementations.go | 101 ++++++++++++++++ op-chain-ops/deployer/pipeline/init.go | 10 ++ op-chain-ops/deployer/pipeline/opchain.go | 110 ++++++++++++++++++ op-chain-ops/deployer/pipeline/superchain.go | 59 +++++----- op-chain-ops/deployer/state/intent.go | 33 +++--- op-chain-ops/deployer/state/state.go | 58 ++++++++- op-chain-ops/interopgen/deploy.go | 12 +- .../interopgen/deployers/superchain.go | 70 ----------- op-chain-ops/script/script.go | 15 ++- op-chain-ops/script/script_test.go | 2 +- .../scripts/DeployImplementations.s.sol | 3 +- 19 files changed, 621 insertions(+), 235 deletions(-) create mode 100644 op-chain-ops/deployer/broadcaster/discard.go rename op-chain-ops/{interopgen/deployers => deployer/opsm}/implementations.go (67%) rename op-chain-ops/{interopgen/deployers => deployer/opsm}/opchain.go (76%) create mode 100644 op-chain-ops/deployer/pipeline/host.go create mode 100644 op-chain-ops/deployer/pipeline/implementations.go create mode 100644 op-chain-ops/deployer/pipeline/opchain.go delete mode 100644 op-chain-ops/interopgen/deployers/superchain.go diff --git a/op-chain-ops/deployer/apply.go b/op-chain-ops/deployer/apply.go index d39628cc63632..e604eb8a9ffe8 100644 --- a/op-chain-ops/deployer/apply.go +++ b/op-chain-ops/deployer/apply.go @@ -126,19 +126,32 @@ func Apply(ctx context.Context, cfg ApplyConfig) error { return nil } +type pipelineStage struct { + name string + stage pipeline.Stage +} + func ApplyPipeline( ctx context.Context, env *pipeline.Env, intent *state.Intent, st *state.State, ) error { - pline := []struct { - name string - stage pipeline.Stage - }{ + pline := []pipelineStage{ {"init", pipeline.Init}, {"deploy-superchain", pipeline.DeploySuperchain}, + {"deploy-implementations", pipeline.DeployImplementations}, + } + + for _, chain := range intent.Chains { + pline = append(pline, pipelineStage{ + fmt.Sprintf("deploy-opchain-%s", chain.ID.Hex()), + func(ctx context.Context, env *pipeline.Env, intent *state.Intent, st *state.State) error { + return pipeline.DeployOPChain(ctx, env, intent, st, chain.ID) + }, + }) } + for _, stage := range pline { if err := stage.stage(ctx, env, intent, st); err != nil { return fmt.Errorf("error in pipeline stage: %w", err) diff --git a/op-chain-ops/deployer/apply_test.go b/op-chain-ops/deployer/apply_test.go index 68a500b969de6..00cdb51ae9f43 100644 --- a/op-chain-ops/deployer/apply_test.go +++ b/op-chain-ops/deployer/apply_test.go @@ -10,6 +10,8 @@ import ( "path" "testing" + "github.com/holiman/uint256" + "github.com/ethereum-optimism/optimism/op-chain-ops/deployer/pipeline" "github.com/ethereum-optimism/optimism/op-chain-ops/deployer/state" "github.com/ethereum-optimism/optimism/op-chain-ops/devkeys" @@ -27,6 +29,15 @@ participants: cl_type: lighthouse network_params: prefunded_accounts: '{ "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266": { "balance": "1000000ETH" } }' + additional_preloaded_contracts: '{ + "0x4e59b44847b379578588920cA78FbF26c0B4956C": { + balance: "0ETH", + code: "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03601600081602082378035828234f58015156039578182fd5b8082525050506014600cf3", + storage: {}, + nonce: 0, + secretKey: "0x" + } + }' network_id: "77799777" seconds_per_slot: 3 ` @@ -42,7 +53,7 @@ func (d *deployerKey) String() string { } func TestEndToEndApply(t *testing.T) { - kurtosisutil.Test(t) + //kurtosisutil.Test(t) lgr := testlog.Logger(t, slog.LevelInfo) @@ -77,6 +88,8 @@ func TestEndToEndApply(t *testing.T) { require.NoError(t, err) signer := opcrypto.SignerFnFromBind(opcrypto.PrivateKeySignerFn(pk, l1ChainID)) + id := uint256.NewInt(1) + addrFor := func(key devkeys.Key) common.Address { addr, err := dk.Address(key) require.NoError(t, err) @@ -99,6 +112,20 @@ func TestEndToEndApply(t *testing.T) { UseFaultProofs: true, FundDevAccounts: true, ContractArtifactsURL: (*state.ArtifactsURL)(artifactsURL), + Chains: []state.ChainIntent{ + { + ID: id.Bytes32(), + Roles: state.ChainRoles{ + ProxyAdminOwner: addrFor(devkeys.L2ProxyAdminOwnerRole.Key(l1ChainID)), + SystemConfigOwner: addrFor(devkeys.SystemConfigOwner.Key(l1ChainID)), + GovernanceTokenOwner: addrFor(devkeys.L2ProxyAdminOwnerRole.Key(l1ChainID)), + UnsafeBlockSigner: addrFor(devkeys.SequencerP2PRole.Key(l1ChainID)), + Batcher: addrFor(devkeys.BatcherRole.Key(l1ChainID)), + Proposer: addrFor(devkeys.ProposerRole.Key(l1ChainID)), + Challenger: addrFor(devkeys.ChallengerRole.Key(l1ChainID)), + }, + }, + }, } st := &state.State{ Version: 1, @@ -111,16 +138,63 @@ func TestEndToEndApply(t *testing.T) { st, )) - addrs := []common.Address{ - st.SuperchainDeployment.ProxyAdminAddress, - st.SuperchainDeployment.SuperchainConfigProxyAddress, - st.SuperchainDeployment.SuperchainConfigImplAddress, - st.SuperchainDeployment.ProtocolVersionsProxyAddress, - st.SuperchainDeployment.ProtocolVersionsImplAddress, + addrs := []struct { + name string + addr common.Address + }{ + {"SuperchainProxyAdmin", st.SuperchainDeployment.ProxyAdminAddress}, + {"SuperchainConfigProxy", st.SuperchainDeployment.SuperchainConfigProxyAddress}, + {"SuperchainConfigImpl", st.SuperchainDeployment.SuperchainConfigImplAddress}, + {"ProtocolVersionsProxy", st.SuperchainDeployment.ProtocolVersionsProxyAddress}, + {"ProtocolVersionsImpl", st.SuperchainDeployment.ProtocolVersionsImplAddress}, + {"Opsm", st.ImplementationsDeployment.OpsmAddress}, + {"DelayedWETHImpl", st.ImplementationsDeployment.DelayedWETHImplAddress}, + {"OptimismPortalImpl", st.ImplementationsDeployment.OptimismPortalImplAddress}, + {"PreimageOracleSingleton", st.ImplementationsDeployment.PreimageOracleSingletonAddress}, + {"MipsSingleton", st.ImplementationsDeployment.MipsSingletonAddress}, + {"SystemConfigImpl", st.ImplementationsDeployment.SystemConfigImplAddress}, + {"L1CrossDomainMessengerImpl", st.ImplementationsDeployment.L1CrossDomainMessengerImplAddress}, + {"L1ERC721BridgeImpl", st.ImplementationsDeployment.L1ERC721BridgeImplAddress}, + {"L1StandardBridgeImpl", st.ImplementationsDeployment.L1StandardBridgeImplAddress}, + {"OptimismMintableERC20FactoryImpl", st.ImplementationsDeployment.OptimismMintableERC20FactoryImplAddress}, + {"DisputeGameFactoryImpl", st.ImplementationsDeployment.DisputeGameFactoryImplAddress}, } for _, addr := range addrs { - code, err := l1Client.CodeAt(ctx, addr, nil) - require.NoError(t, err) - require.NotEmpty(t, code) + t.Run(addr.name, func(t *testing.T) { + code, err := l1Client.CodeAt(ctx, addr.addr, nil) + require.NoError(t, err) + require.NotEmpty(t, code, "contracts %s at %s has no code", addr.name, addr.addr) + }) + } + + for _, chainState := range st.Chains { + chainAddrs := []struct { + name string + addr common.Address + }{ + {"ProxyAdminAddress", chainState.ProxyAdminAddress}, + {"AddressManagerAddress", chainState.AddressManagerAddress}, + {"L1ERC721BridgeProxyAddress", chainState.L1ERC721BridgeProxyAddress}, + {"SystemConfigProxyAddress", chainState.SystemConfigProxyAddress}, + {"OptimismMintableERC20FactoryProxyAddress", chainState.OptimismMintableERC20FactoryProxyAddress}, + {"L1StandardBridgeProxyAddress", chainState.L1StandardBridgeProxyAddress}, + {"L1CrossDomainMessengerProxyAddress", chainState.L1CrossDomainMessengerProxyAddress}, + {"OptimismPortalProxyAddress", chainState.OptimismPortalProxyAddress}, + {"DisputeGameFactoryProxyAddress", chainState.DisputeGameFactoryProxyAddress}, + {"DisputeGameFactoryImplAddress", chainState.DisputeGameFactoryImplAddress}, + {"AnchorStateRegistryProxyAddress", chainState.AnchorStateRegistryProxyAddress}, + {"AnchorStateRegistryImplAddress", chainState.AnchorStateRegistryImplAddress}, + {"FaultDisputeGameAddress", chainState.FaultDisputeGameAddress}, + {"PermissionedDisputeGameAddress", chainState.PermissionedDisputeGameAddress}, + {"DelayedWETHPermissionedGameProxyAddress", chainState.DelayedWETHPermissionedGameProxyAddress}, + {"DelayedWETHPermissionlessGameProxyAddress", chainState.DelayedWETHPermissionlessGameProxyAddress}, + } + for _, addr := range chainAddrs { + t.Run(fmt.Sprintf("chain %s - %s", chainState.ID, addr.name), func(t *testing.T) { + code, err := l1Client.CodeAt(ctx, addr.addr, nil) + require.NoError(t, err) + require.NotEmpty(t, code, "contracts %s at %s for chain %s has no code", addr.name, addr.addr, chainState.ID) + }) + } } } diff --git a/op-chain-ops/deployer/broadcaster/discard.go b/op-chain-ops/deployer/broadcaster/discard.go new file mode 100644 index 0000000000000..42f5b1b0a9642 --- /dev/null +++ b/op-chain-ops/deployer/broadcaster/discard.go @@ -0,0 +1,20 @@ +package broadcaster + +import ( + "context" + + "github.com/ethereum-optimism/optimism/op-chain-ops/script" +) + +type discardBroadcaster struct { +} + +func DiscardBroadcaster() Broadcaster { + return &discardBroadcaster{} +} + +func (d *discardBroadcaster) Broadcast(ctx context.Context) ([]BroadcastResult, error) { + return nil, nil +} + +func (d *discardBroadcaster) Hook(bcast script.Broadcast) {} diff --git a/op-chain-ops/deployer/broadcaster/keyed.go b/op-chain-ops/deployer/broadcaster/keyed.go index 8c34cb1ab531e..2784c4d455be7 100644 --- a/op-chain-ops/deployer/broadcaster/keyed.go +++ b/op-chain-ops/deployer/broadcaster/keyed.go @@ -20,13 +20,14 @@ import ( ) const ( - GasPadFactor = 1.5 + GasPadFactor = 2.0 ) type KeyedBroadcaster struct { lgr log.Logger mgr txmgr.TxManager bcasts []script.Broadcast + client *ethclient.Client } type KeyedBroadcasterOpts struct { @@ -84,8 +85,9 @@ func NewKeyedBroadcaster(cfg KeyedBroadcasterOpts) (*KeyedBroadcaster, error) { } return &KeyedBroadcaster{ - lgr: cfg.Logger, - mgr: mgr, + lgr: cfg.Logger, + mgr: mgr, + client: cfg.Client, }, nil } @@ -98,8 +100,13 @@ func (t *KeyedBroadcaster) Broadcast(ctx context.Context) ([]BroadcastResult, er futures := make([]<-chan txmgr.SendResponse, len(t.bcasts)) ids := make([]common.Hash, len(t.bcasts)) + latestBlock, err := t.client.BlockByNumber(ctx, nil) + if err != nil { + return nil, fmt.Errorf("failed to get latest block: %w", err) + } + for i, bcast := range t.bcasts { - futures[i], ids[i] = t.broadcast(ctx, bcast) + futures[i], ids[i] = t.broadcast(ctx, bcast, latestBlock.GasLimit()) t.lgr.Info( "transaction broadcasted", "id", ids[i], @@ -107,7 +114,7 @@ func (t *KeyedBroadcaster) Broadcast(ctx context.Context) ([]BroadcastResult, er ) } - var err *multierror.Error + var txErr *multierror.Error var completed int for i, fut := range futures { bcastRes := <-fut @@ -122,7 +129,7 @@ func (t *KeyedBroadcaster) Broadcast(ctx context.Context) ([]BroadcastResult, er if bcastRes.Receipt.Status == 0 { failErr := fmt.Errorf("transaction failed: %s", outRes.Receipt.TxHash.String()) - err = multierror.Append(err, failErr) + txErr = multierror.Append(txErr, failErr) outRes.Err = failErr t.lgr.Error( "transaction failed on chain", @@ -144,7 +151,7 @@ func (t *KeyedBroadcaster) Broadcast(ctx context.Context) ([]BroadcastResult, er ) } } else { - err = multierror.Append(err, bcastRes.Err) + txErr = multierror.Append(txErr, bcastRes.Err) outRes.Err = bcastRes.Err t.lgr.Error( "transaction failed", @@ -157,12 +164,13 @@ func (t *KeyedBroadcaster) Broadcast(ctx context.Context) ([]BroadcastResult, er results = append(results, outRes) } - return results, err.ErrorOrNil() + return results, txErr.ErrorOrNil() } -func (t *KeyedBroadcaster) broadcast(ctx context.Context, bcast script.Broadcast) (<-chan txmgr.SendResponse, common.Hash) { - id := bcast.ID() +func (t *KeyedBroadcaster) broadcast(ctx context.Context, bcast script.Broadcast, blockGasLimit uint64) (<-chan txmgr.SendResponse, common.Hash) { + ch := make(chan txmgr.SendResponse, 1) + id := bcast.ID() value := ((*uint256.Int)(bcast.Value)).ToBig() var candidate txmgr.TxCandidate switch bcast.Type { @@ -172,27 +180,45 @@ func (t *KeyedBroadcaster) broadcast(ctx context.Context, bcast script.Broadcast TxData: bcast.Input, To: to, Value: value, - GasLimit: padGasLimit(bcast.Input, bcast.GasUsed, false), + GasLimit: padGasLimit(bcast.Input, bcast.GasUsed, false, blockGasLimit), } case script.BroadcastCreate: candidate = txmgr.TxCandidate{ TxData: bcast.Input, To: nil, - GasLimit: padGasLimit(bcast.Input, bcast.GasUsed, true), + GasLimit: padGasLimit(bcast.Input, bcast.GasUsed, true, blockGasLimit), + } + case script.BroadcastCreate2: + txData := make([]byte, len(bcast.Salt)+len(bcast.Input)) + copy(txData, bcast.Salt[:]) + copy(txData[len(bcast.Salt):], bcast.Input) + + candidate = txmgr.TxCandidate{ + TxData: txData, + To: &script.DeterministicDeployerAddress, + Value: value, + GasLimit: padGasLimit(bcast.Input, bcast.GasUsed, true, blockGasLimit), } } - ch := make(chan txmgr.SendResponse, 1) t.mgr.SendAsync(ctx, candidate, ch) return ch, id } -func padGasLimit(data []byte, gasUsed uint64, creation bool) uint64 { +// padGasLimit calculates the gas limit for a transaction based on the intrinsic gas and the gas used by +// the underlying call. Values are multiplied by a pad factor to account for any discrepancies. The output +// is clamped to the block gas limit since Geth will reject transactions that exceed it before letting them +// into the mempool. +func padGasLimit(data []byte, gasUsed uint64, creation bool, blockGasLimit uint64) uint64 { intrinsicGas, err := core.IntrinsicGas(data, nil, creation, true, true, false) // This method never errors - we should look into it if it does. if err != nil { panic(err) } - return uint64(float64(intrinsicGas+gasUsed) * GasPadFactor) + limit := uint64(float64(intrinsicGas+gasUsed) * GasPadFactor) + if limit > blockGasLimit { + return blockGasLimit + } + return limit } diff --git a/op-chain-ops/interopgen/deployers/implementations.go b/op-chain-ops/deployer/opsm/implementations.go similarity index 67% rename from op-chain-ops/interopgen/deployers/implementations.go rename to op-chain-ops/deployer/opsm/implementations.go index 29a1fb6dee99b..d5b576fb9cb6a 100644 --- a/op-chain-ops/interopgen/deployers/implementations.go +++ b/op-chain-ops/deployer/opsm/implementations.go @@ -1,4 +1,4 @@ -package deployers +package opsm import ( "fmt" @@ -19,8 +19,9 @@ type DeployImplementationsInput struct { Release string SuperchainConfigProxy common.Address ProtocolVersionsProxy common.Address - SuperchainProxyAdmin common.Address UseInterop bool // if true, deploy Interop implementations + + SuperchainProxyAdmin common.Address } func (input *DeployImplementationsInput) InputSet() bool { @@ -49,21 +50,24 @@ type DeployImplementationsScript struct { Run func(input, output common.Address) error } -func DeployImplementations(l1Host *script.Host, input *DeployImplementationsInput) (*DeployImplementationsOutput, error) { - output := &DeployImplementationsOutput{} - inputAddr := l1Host.NewScriptAddress() - outputAddr := l1Host.NewScriptAddress() +func DeployImplementations( + host *script.Host, + input DeployImplementationsInput, +) (DeployImplementationsOutput, error) { + var output DeployImplementationsOutput + inputAddr := host.NewScriptAddress() + outputAddr := host.NewScriptAddress() - cleanupInput, err := script.WithPrecompileAtAddress[*DeployImplementationsInput](l1Host, inputAddr, input) + cleanupInput, err := script.WithPrecompileAtAddress[*DeployImplementationsInput](host, inputAddr, &input) if err != nil { - return nil, fmt.Errorf("failed to insert DeployImplementationsInput precompile: %w", err) + return output, fmt.Errorf("failed to insert DeployImplementationsInput precompile: %w", err) } defer cleanupInput() - cleanupOutput, err := script.WithPrecompileAtAddress[*DeployImplementationsOutput](l1Host, outputAddr, output, + cleanupOutput, err := script.WithPrecompileAtAddress[*DeployImplementationsOutput](host, outputAddr, &output, script.WithFieldSetter[*DeployImplementationsOutput]) if err != nil { - return nil, fmt.Errorf("failed to insert DeployImplementationsOutput precompile: %w", err) + return output, fmt.Errorf("failed to insert DeployImplementationsOutput precompile: %w", err) } defer cleanupOutput() @@ -71,9 +75,9 @@ func DeployImplementations(l1Host *script.Host, input *DeployImplementationsInpu if input.UseInterop { implContract = "DeployImplementationsInterop" } - deployScript, cleanupDeploy, err := script.WithScript[DeployImplementationsScript](l1Host, "DeployImplementations.s.sol", implContract) + deployScript, cleanupDeploy, err := script.WithScript[DeployImplementationsScript](host, "DeployImplementations.s.sol", implContract) if err != nil { - return nil, fmt.Errorf("failed to load %s script: %w", implContract, err) + return output, fmt.Errorf("failed to load %s script: %w", implContract, err) } defer cleanupDeploy() @@ -81,8 +85,8 @@ func DeployImplementations(l1Host *script.Host, input *DeployImplementationsInpu if input.UseInterop { opsmContract = "OPStackManagerInterop" } - if err := l1Host.RememberOnLabel("OPStackManager", opsmContract+".sol", opsmContract); err != nil { - return nil, fmt.Errorf("failed to link OPStackManager label: %w", err) + if err := host.RememberOnLabel("OPStackManager", opsmContract+".sol", opsmContract); err != nil { + return output, fmt.Errorf("failed to link OPStackManager label: %w", err) } // So we can see in detail where the SystemConfig interop initializer fails @@ -90,12 +94,12 @@ func DeployImplementations(l1Host *script.Host, input *DeployImplementationsInpu if input.UseInterop { sysConfig = "SystemConfigInterop" } - if err := l1Host.RememberOnLabel("SystemConfigImpl", sysConfig+".sol", sysConfig); err != nil { - return nil, fmt.Errorf("failed to link SystemConfig label: %w", err) + if err := host.RememberOnLabel("SystemConfigImpl", sysConfig+".sol", sysConfig); err != nil { + return output, fmt.Errorf("failed to link SystemConfig label: %w", err) } if err := deployScript.Run(inputAddr, outputAddr); err != nil { - return nil, fmt.Errorf("failed to run %s script: %w", implContract, err) + return output, fmt.Errorf("failed to run %s script: %w", implContract, err) } return output, nil diff --git a/op-chain-ops/interopgen/deployers/opchain.go b/op-chain-ops/deployer/opsm/opchain.go similarity index 76% rename from op-chain-ops/interopgen/deployers/opchain.go rename to op-chain-ops/deployer/opsm/opchain.go index 184ccc946bf08..6bab3fdbfe754 100644 --- a/op-chain-ops/interopgen/deployers/opchain.go +++ b/op-chain-ops/deployer/opsm/opchain.go @@ -1,4 +1,4 @@ -package deployers +package opsm import ( "fmt" @@ -35,6 +35,7 @@ type DeployOPChainOutput struct { OptimismMintableERC20FactoryProxy common.Address L1StandardBridgeProxy common.Address L1CrossDomainMessengerProxy common.Address + // Fault proof contracts below. OptimismPortalProxy common.Address DisputeGameFactoryProxy common.Address @@ -55,33 +56,33 @@ type DeployOPChainScript struct { Run func(input, output common.Address) error } -func DeployOPChain(l1Host *script.Host, input *DeployOPChainInput) (*DeployOPChainOutput, error) { - output := &DeployOPChainOutput{} - inputAddr := l1Host.NewScriptAddress() - outputAddr := l1Host.NewScriptAddress() +func DeployOPChain(host *script.Host, input DeployOPChainInput) (DeployOPChainOutput, error) { + var dco DeployOPChainOutput + inputAddr := host.NewScriptAddress() + outputAddr := host.NewScriptAddress() - cleanupInput, err := script.WithPrecompileAtAddress[*DeployOPChainInput](l1Host, inputAddr, input) + cleanupInput, err := script.WithPrecompileAtAddress[*DeployOPChainInput](host, inputAddr, &input) if err != nil { - return nil, fmt.Errorf("failed to insert DeployOPChainInput precompile: %w", err) + return dco, fmt.Errorf("failed to insert DeployOPChainInput precompile: %w", err) } defer cleanupInput() - cleanupOutput, err := script.WithPrecompileAtAddress[*DeployOPChainOutput](l1Host, outputAddr, output, + cleanupOutput, err := script.WithPrecompileAtAddress[*DeployOPChainOutput](host, outputAddr, &dco, script.WithFieldSetter[*DeployOPChainOutput]) if err != nil { - return nil, fmt.Errorf("failed to insert DeployOPChainOutput precompile: %w", err) + return dco, fmt.Errorf("failed to insert DeployOPChainOutput precompile: %w", err) } defer cleanupOutput() - deployScript, cleanupDeploy, err := script.WithScript[DeployOPChainScript](l1Host, "DeployOPChain.s.sol", "DeployOPChain") + deployScript, cleanupDeploy, err := script.WithScript[DeployOPChainScript](host, "DeployOPChain.s.sol", "DeployOPChain") if err != nil { - return nil, fmt.Errorf("failed to load DeployOPChain script: %w", err) + return dco, fmt.Errorf("failed to load DeployOPChain script: %w", err) } defer cleanupDeploy() if err := deployScript.Run(inputAddr, outputAddr); err != nil { - return nil, fmt.Errorf("failed to run DeployOPChain script: %w", err) + return dco, fmt.Errorf("failed to run DeployOPChain script: %w", err) } - return output, nil + return dco, nil } diff --git a/op-chain-ops/deployer/opsm/superchain.go b/op-chain-ops/deployer/opsm/superchain.go index 99adb77817e16..0505d34760a97 100644 --- a/op-chain-ops/deployer/opsm/superchain.go +++ b/op-chain-ops/deployer/opsm/superchain.go @@ -1,11 +1,9 @@ package opsm import ( - "context" "fmt" "math/big" - "github.com/ethereum-optimism/optimism/op-chain-ops/deployer/broadcaster" "github.com/ethereum-optimism/optimism/op-chain-ops/foundry" "github.com/ethereum-optimism/optimism/op-chain-ops/script" opcrypto "github.com/ethereum-optimism/optimism/op-service/crypto" @@ -29,11 +27,11 @@ func (dsi *DeploySuperchainInput) InputSet() bool { } type DeploySuperchainOutput struct { - SuperchainProxyAdmin common.Address `toml:"superchainProxyAdmin"` - SuperchainConfigImpl common.Address `toml:"superchainConfigImpl"` - SuperchainConfigProxy common.Address `toml:"superchainConfigProxy"` - ProtocolVersionsImpl common.Address `toml:"protocolVersionsImpl"` - ProtocolVersionsProxy common.Address `toml:"protocolVersionsProxy"` + SuperchainProxyAdmin common.Address + SuperchainConfigImpl common.Address + SuperchainConfigProxy common.Address + ProtocolVersionsImpl common.Address + ProtocolVersionsProxy common.Address } func (output *DeploySuperchainOutput) CheckOutput() error { @@ -54,46 +52,13 @@ type DeploySuperchainOpts struct { Logger log.Logger } -func DeploySuperchainForge(ctx context.Context, opts DeploySuperchainOpts) (DeploySuperchainOutput, error) { +func DeploySuperchain(h *script.Host, input DeploySuperchainInput) (DeploySuperchainOutput, error) { var dso DeploySuperchainOutput - bcaster, err := broadcaster.NewKeyedBroadcaster(broadcaster.KeyedBroadcasterOpts{ - Logger: opts.Logger, - ChainID: opts.ChainID, - Client: opts.Client, - Signer: opts.Signer, - From: opts.Deployer, - }) - if err != nil { - return dso, fmt.Errorf("failed to create broadcaster: %w", err) - } - - scriptCtx := script.DefaultContext - scriptCtx.Sender = opts.Deployer - scriptCtx.Origin = opts.Deployer - artifacts := &foundry.ArtifactsFS{FS: opts.ArtifactsFS} - h := script.NewHost( - opts.Logger, - artifacts, - nil, - scriptCtx, - script.WithBroadcastHook(bcaster.Hook), - script.WithIsolatedBroadcasts(), - ) - - if err := h.EnableCheats(); err != nil { - return dso, fmt.Errorf("failed to enable cheats: %w", err) - } - - nonce, err := opts.Client.NonceAt(ctx, opts.Deployer, nil) - if err != nil { - return dso, fmt.Errorf("failed to get deployer nonce: %w", err) - } - inputAddr := h.NewScriptAddress() outputAddr := h.NewScriptAddress() - cleanupInput, err := script.WithPrecompileAtAddress[*DeploySuperchainInput](h, inputAddr, &opts.Input) + cleanupInput, err := script.WithPrecompileAtAddress[*DeploySuperchainInput](h, inputAddr, &input) if err != nil { return dso, fmt.Errorf("failed to insert DeploySuperchainInput precompile: %w", err) } @@ -116,17 +81,9 @@ func DeploySuperchainForge(ctx context.Context, opts DeploySuperchainOpts) (Depl } defer cleanupDeploy() - h.SetNonce(opts.Deployer, nonce) - - opts.Logger.Info("deployer nonce", "nonce", nonce) - if err := deployScript.Run(inputAddr, outputAddr); err != nil { return dso, fmt.Errorf("failed to run DeploySuperchain script: %w", err) } - if _, err := bcaster.Broadcast(ctx); err != nil { - return dso, fmt.Errorf("failed to broadcast transactions: %w", err) - } - return dso, nil } diff --git a/op-chain-ops/deployer/pipeline/host.go b/op-chain-ops/deployer/pipeline/host.go new file mode 100644 index 0000000000000..f62a2ee8bfae0 --- /dev/null +++ b/op-chain-ops/deployer/pipeline/host.go @@ -0,0 +1,70 @@ +package pipeline + +import ( + "context" + "fmt" + "math/big" + + "github.com/ethereum-optimism/optimism/op-chain-ops/deployer/broadcaster" + "github.com/ethereum-optimism/optimism/op-chain-ops/foundry" + "github.com/ethereum-optimism/optimism/op-chain-ops/script" + opcrypto "github.com/ethereum-optimism/optimism/op-service/crypto" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/ethereum/go-ethereum/log" +) + +type CallScriptBroadcastOpts struct { + L1ChainID *big.Int + Logger log.Logger + ArtifactsFS foundry.StatDirFs + Deployer common.Address + Signer opcrypto.SignerFn + Client *ethclient.Client + Handler func(host *script.Host) error +} + +func CallScriptBroadcast( + ctx context.Context, + opts CallScriptBroadcastOpts, +) error { + bcaster, err := broadcaster.NewKeyedBroadcaster(broadcaster.KeyedBroadcasterOpts{ + Logger: opts.Logger, + ChainID: opts.L1ChainID, + Client: opts.Client, + Signer: opts.Signer, + From: opts.Deployer, + }) + if err != nil { + return fmt.Errorf("failed to create broadcaster: %w", err) + } + + scriptCtx := script.DefaultContext + scriptCtx.Sender = opts.Deployer + scriptCtx.Origin = opts.Deployer + artifacts := &foundry.ArtifactsFS{FS: opts.ArtifactsFS} + h := script.NewHost( + opts.Logger, + artifacts, + nil, + scriptCtx, + script.WithBroadcastHook(bcaster.Hook), + script.WithIsolatedBroadcasts(), + script.WithCreate2Deployer(), + ) + + if err := h.EnableCheats(); err != nil { + return fmt.Errorf("failed to enable cheats: %w", err) + } + + err = opts.Handler(h) + if err != nil { + return fmt.Errorf("failed to run handler: %w", err) + } + + if _, err := bcaster.Broadcast(ctx); err != nil { + return fmt.Errorf("failed to broadcast: %w", err) + } + + return nil +} diff --git a/op-chain-ops/deployer/pipeline/implementations.go b/op-chain-ops/deployer/pipeline/implementations.go new file mode 100644 index 0000000000000..8c6855765e262 --- /dev/null +++ b/op-chain-ops/deployer/pipeline/implementations.go @@ -0,0 +1,101 @@ +package pipeline + +import ( + "context" + "fmt" + "math/big" + "os" + + "github.com/ethereum-optimism/optimism/op-chain-ops/deployer/opsm" + "github.com/ethereum-optimism/optimism/op-chain-ops/deployer/state" + "github.com/ethereum-optimism/optimism/op-chain-ops/foundry" + "github.com/ethereum-optimism/optimism/op-chain-ops/script" +) + +func DeployImplementations(ctx context.Context, env *Env, intent *state.Intent, st *state.State) error { + lgr := env.Logger.New("stage", "deploy-implementations") + + if !shouldDeployImplementations(intent, st) { + lgr.Info("implementations deployment not needed") + return nil + } + + lgr.Info("deploying implementations") + + var artifactsFS foundry.StatDirFs + var err error + if intent.ContractArtifactsURL.Scheme == "file" { + fs := os.DirFS(intent.ContractArtifactsURL.Path) + artifactsFS = fs.(foundry.StatDirFs) + } else { + return fmt.Errorf("only file:// artifacts URLs are supported") + } + + var dump *foundry.ForgeAllocs + var dio opsm.DeployImplementationsOutput + err = CallScriptBroadcast( + ctx, + CallScriptBroadcastOpts{ + L1ChainID: big.NewInt(int64(intent.L1ChainID)), + Logger: lgr, + ArtifactsFS: artifactsFS, + Deployer: env.Deployer, + Signer: env.Signer, + Client: env.L1Client, + Handler: func(host *script.Host) error { + host.SetEnvVar("IMPL_SALT", st.Create2Salt.Hex()[2:]) + host.ImportState(st.SuperchainDeployment.StateDump) + dio, err = opsm.DeployImplementations( + host, + opsm.DeployImplementationsInput{ + WithdrawalDelaySeconds: big.NewInt(604800), + MinProposalSizeBytes: big.NewInt(126000), + ChallengePeriodSeconds: big.NewInt(86400), + ProofMaturityDelaySeconds: big.NewInt(604800), + DisputeGameFinalityDelaySeconds: big.NewInt(302400), + Release: "op-contracts/v1.6.0", + SuperchainConfigProxy: st.SuperchainDeployment.SuperchainConfigProxyAddress, + ProtocolVersionsProxy: st.SuperchainDeployment.ProtocolVersionsProxyAddress, + SuperchainProxyAdmin: st.SuperchainDeployment.ProxyAdminAddress, + UseInterop: false, + }, + ) + if err != nil { + return fmt.Errorf("error deploying implementations: %w", err) + } + dump, err = host.StateDump() + if err != nil { + return fmt.Errorf("error dumping state: %w", err) + } + return nil + }, + }, + ) + if err != nil { + return fmt.Errorf("error deploying implementations: %w", err) + } + + st.ImplementationsDeployment = &state.ImplementationsDeployment{ + OpsmAddress: dio.Opsm, + DelayedWETHImplAddress: dio.DelayedWETHImpl, + OptimismPortalImplAddress: dio.OptimismPortalImpl, + PreimageOracleSingletonAddress: dio.PreimageOracleSingleton, + MipsSingletonAddress: dio.MipsSingleton, + SystemConfigImplAddress: dio.SystemConfigImpl, + L1CrossDomainMessengerImplAddress: dio.L1CrossDomainMessengerImpl, + L1ERC721BridgeImplAddress: dio.L1ERC721BridgeImpl, + L1StandardBridgeImplAddress: dio.L1StandardBridgeImpl, + OptimismMintableERC20FactoryImplAddress: dio.OptimismMintableERC20FactoryImpl, + DisputeGameFactoryImplAddress: dio.DisputeGameFactoryImpl, + StateDump: dump, + } + if err := env.WriteState(st); err != nil { + return err + } + + return nil +} + +func shouldDeployImplementations(intent *state.Intent, st *state.State) bool { + return st.SuperchainDeployment == nil +} diff --git a/op-chain-ops/deployer/pipeline/init.go b/op-chain-ops/deployer/pipeline/init.go index 9f4227f856b00..55cbe9793d6ad 100644 --- a/op-chain-ops/deployer/pipeline/init.go +++ b/op-chain-ops/deployer/pipeline/init.go @@ -2,8 +2,11 @@ package pipeline import ( "context" + "crypto/rand" "fmt" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum-optimism/optimism/op-chain-ops/deployer/state" ) @@ -20,6 +23,13 @@ func Init(ctx context.Context, env *Env, intent *state.Intent, st *state.State) return fmt.Errorf("unsupported state version: %d", st.Version) } + if st.Create2Salt == (common.Hash{}) { + _, err := rand.Read(st.Create2Salt[:]) + if err != nil { + return fmt.Errorf("failed to generate CREATE2 salt: %w", err) + } + } + // If the state has never been applied, we don't need to perform // any additional checks. if st.AppliedIntent == nil { diff --git a/op-chain-ops/deployer/pipeline/opchain.go b/op-chain-ops/deployer/pipeline/opchain.go new file mode 100644 index 0000000000000..6a27ded2d520b --- /dev/null +++ b/op-chain-ops/deployer/pipeline/opchain.go @@ -0,0 +1,110 @@ +package pipeline + +import ( + "context" + "fmt" + "math/big" + "os" + + "github.com/ethereum-optimism/optimism/op-chain-ops/deployer/opsm" + "github.com/ethereum-optimism/optimism/op-chain-ops/deployer/state" + "github.com/ethereum-optimism/optimism/op-chain-ops/foundry" + "github.com/ethereum-optimism/optimism/op-chain-ops/script" + "github.com/ethereum/go-ethereum/common" +) + +func DeployOPChain(ctx context.Context, env *Env, intent *state.Intent, st *state.State, chainID common.Hash) error { + lgr := env.Logger.New("stage", "deploy-opchain") + + if !shouldDeployOPChain(intent, st, chainID) { + lgr.Info("opchain deployment not needed") + return nil + } + + lgr.Info("deploying OP chain", "id", chainID.Hex()) + + var artifactsFS foundry.StatDirFs + var err error + if intent.ContractArtifactsURL.Scheme == "file" { + fs := os.DirFS(intent.ContractArtifactsURL.Path) + artifactsFS = fs.(foundry.StatDirFs) + } else { + return fmt.Errorf("only file:// artifacts URLs are supported") + } + + thisIntent, err := intent.Chain(chainID) + if err != nil { + return fmt.Errorf("failed to get chain intent: %w", err) + } + + var dco opsm.DeployOPChainOutput + err = CallScriptBroadcast( + ctx, + CallScriptBroadcastOpts{ + L1ChainID: big.NewInt(int64(intent.L1ChainID)), + Logger: lgr, + ArtifactsFS: artifactsFS, + Deployer: env.Deployer, + Signer: env.Signer, + Client: env.L1Client, + Handler: func(host *script.Host) error { + host.ImportState(st.ImplementationsDeployment.StateDump) + dco, err = opsm.DeployOPChain( + host, + opsm.DeployOPChainInput{ + OpChainProxyAdminOwner: thisIntent.Roles.ProxyAdminOwner, + SystemConfigOwner: thisIntent.Roles.SystemConfigOwner, + Batcher: thisIntent.Roles.Batcher, + UnsafeBlockSigner: thisIntent.Roles.UnsafeBlockSigner, + Proposer: thisIntent.Roles.Proposer, + Challenger: thisIntent.Roles.Challenger, + BasefeeScalar: 1368, + BlobBaseFeeScalar: 801949, + L2ChainId: chainID.Big(), + Opsm: st.ImplementationsDeployment.OpsmAddress, + }, + ) + return err + }, + }, + ) + if err != nil { + return fmt.Errorf("error deploying OP chain: %w", err) + } + + st.Chains = append(st.Chains, state.ChainState{ + ID: chainID, + + ProxyAdminAddress: dco.OpChainProxyAdmin, + AddressManagerAddress: dco.AddressManager, + L1ERC721BridgeProxyAddress: dco.L1ERC721BridgeProxy, + SystemConfigProxyAddress: dco.SystemConfigProxy, + OptimismMintableERC20FactoryProxyAddress: dco.OptimismMintableERC20FactoryProxy, + L1StandardBridgeProxyAddress: dco.L1StandardBridgeProxy, + L1CrossDomainMessengerProxyAddress: dco.L1CrossDomainMessengerProxy, + OptimismPortalProxyAddress: dco.OptimismPortalProxy, + DisputeGameFactoryProxyAddress: dco.DisputeGameFactoryProxy, + DisputeGameFactoryImplAddress: dco.DisputeGameFactoryImpl, + AnchorStateRegistryProxyAddress: dco.AnchorStateRegistryProxy, + AnchorStateRegistryImplAddress: dco.AnchorStateRegistryImpl, + FaultDisputeGameAddress: dco.FaultDisputeGame, + PermissionedDisputeGameAddress: dco.PermissionedDisputeGame, + DelayedWETHPermissionedGameProxyAddress: dco.DelayedWETHPermissionedGameProxy, + DelayedWETHPermissionlessGameProxyAddress: dco.DelayedWETHPermissionlessGameProxy, + }) + if err := env.WriteState(st); err != nil { + return err + } + + return nil +} + +func shouldDeployOPChain(intent *state.Intent, st *state.State, chainID common.Hash) bool { + for _, chain := range st.Chains { + if chain.ID == chainID { + return false + } + } + + return true +} diff --git a/op-chain-ops/deployer/pipeline/superchain.go b/op-chain-ops/deployer/pipeline/superchain.go index 5b44b2ee01c36..3a91c867ff3a1 100644 --- a/op-chain-ops/deployer/pipeline/superchain.go +++ b/op-chain-ops/deployer/pipeline/superchain.go @@ -6,14 +6,14 @@ import ( "math/big" "os" + "github.com/ethereum-optimism/optimism/op-chain-ops/script" + "github.com/ethereum-optimism/optimism/op-chain-ops/deployer/opsm" "github.com/ethereum-optimism/optimism/op-chain-ops/deployer/state" "github.com/ethereum-optimism/optimism/op-chain-ops/foundry" "github.com/ethereum-optimism/optimism/op-node/rollup" ) -const DefaultContractsBedrockRepo = "us-docker.pkg.dev/oplabs-tools-artifacts/images/contracts-bedrock" - func DeploySuperchain(ctx context.Context, env *Env, intent *state.Intent, st *state.State) error { lgr := env.Logger.New("stage", "deploy-superchain") @@ -33,23 +33,38 @@ func DeploySuperchain(ctx context.Context, env *Env, intent *state.Intent, st *s return fmt.Errorf("only file:// artifacts URLs are supported") } - dso, err := opsm.DeploySuperchainForge( + var dump *foundry.ForgeAllocs + var dso opsm.DeploySuperchainOutput + err = CallScriptBroadcast( ctx, - opsm.DeploySuperchainOpts{ - Input: opsm.DeploySuperchainInput{ - ProxyAdminOwner: intent.SuperchainRoles.ProxyAdminOwner, - ProtocolVersionsOwner: intent.SuperchainRoles.ProtocolVersionsOwner, - Guardian: intent.SuperchainRoles.Guardian, - Paused: false, - RequiredProtocolVersion: rollup.OPStackSupport, - RecommendedProtocolVersion: rollup.OPStackSupport, - }, + CallScriptBroadcastOpts{ + L1ChainID: big.NewInt(int64(intent.L1ChainID)), + Logger: lgr, ArtifactsFS: artifactsFS, - ChainID: big.NewInt(int64(intent.L1ChainID)), - Client: env.L1Client, - Signer: env.Signer, Deployer: env.Deployer, - Logger: lgr, + Signer: env.Signer, + Client: env.L1Client, + Handler: func(host *script.Host) error { + dso, err = opsm.DeploySuperchain( + host, + opsm.DeploySuperchainInput{ + ProxyAdminOwner: intent.SuperchainRoles.ProxyAdminOwner, + ProtocolVersionsOwner: intent.SuperchainRoles.ProtocolVersionsOwner, + Guardian: intent.SuperchainRoles.Guardian, + Paused: false, + RequiredProtocolVersion: rollup.OPStackSupport, + RecommendedProtocolVersion: rollup.OPStackSupport, + }, + ) + if err != nil { + return fmt.Errorf("failed to deploy superchain: %w", err) + } + dump, err = host.StateDump() + if err != nil { + return fmt.Errorf("error dumping state: %w", err) + } + return nil + }, }, ) if err != nil { @@ -62,8 +77,8 @@ func DeploySuperchain(ctx context.Context, env *Env, intent *state.Intent, st *s SuperchainConfigImplAddress: dso.SuperchainConfigImpl, ProtocolVersionsProxyAddress: dso.ProtocolVersionsProxy, ProtocolVersionsImplAddress: dso.ProtocolVersionsImpl, + StateDump: dump, } - if err := env.WriteState(st); err != nil { return err } @@ -72,13 +87,5 @@ func DeploySuperchain(ctx context.Context, env *Env, intent *state.Intent, st *s } func shouldDeploySuperchain(intent *state.Intent, st *state.State) bool { - if st.AppliedIntent == nil { - return true - } - - if st.SuperchainDeployment == nil { - return true - } - - return false + return st.SuperchainDeployment == nil } diff --git a/op-chain-ops/deployer/state/intent.go b/op-chain-ops/deployer/state/intent.go index 5184a3bb28efe..3c41169e1d457 100644 --- a/op-chain-ops/deployer/state/intent.go +++ b/op-chain-ops/deployer/state/intent.go @@ -24,7 +24,7 @@ type Intent struct { ContractArtifactsURL *ArtifactsURL `json:"contractArtifactsURL" toml:"contractArtifactsURL"` - Chains []Chain `json:"chains" toml:"chains"` + Chains []ChainIntent `json:"chains" toml:"chains"` } func (c Intent) L1ChainIDBig() *big.Int { @@ -62,14 +62,14 @@ func (c Intent) Check() error { return nil } -func (c Intent) Chain(id uint64) (Chain, error) { +func (c Intent) Chain(id common.Hash) (ChainIntent, error) { for i := range c.Chains { if c.Chains[i].ID == id { return c.Chains[i], nil } } - return Chain{}, fmt.Errorf("chain %d not found", id) + return ChainIntent{}, fmt.Errorf("chain %d not found", id) } func (c Intent) WriteToFile(path string) error { @@ -84,32 +84,33 @@ type SuperchainRoles struct { Guardian common.Address `json:"guardian" toml:"guardian"` } -type Chain struct { - ID uint64 `json:"id"` +type ChainIntent struct { + ID common.Hash `json:"id" toml:"id"` - Roles ChainRoles `json:"roles"` + Roles ChainRoles `json:"roles" toml:"roles"` - Overrides map[string]any `json:"overrides"` + Overrides map[string]any `json:"overrides" toml:"overrides"` } type ChainRoles struct { - ProxyAdminOwner common.Address `json:"proxyAdminOwner"` + ProxyAdminOwner common.Address `json:"proxyAdminOwner" toml:"proxyAdminOwner"` - SystemConfigOwner common.Address `json:"systemConfigOwner"` + SystemConfigOwner common.Address `json:"systemConfigOwner" toml:"systemConfigOwner"` - GovernanceTokenOwner common.Address `json:"governanceTokenOwner"` + GovernanceTokenOwner common.Address `json:"governanceTokenOwner" toml:"governanceTokenOwner"` - UnsafeBlockSigner common.Address `json:"unsafeBlockSigner"` + UnsafeBlockSigner common.Address `json:"unsafeBlockSigner" toml:"unsafeBlockSigner"` - Batcher common.Address `json:"batcher"` + Batcher common.Address `json:"batcher" toml:"batcher"` - Proposer common.Address `json:"proposer"` + Proposer common.Address `json:"proposer" toml:"proposer"` - Challenger common.Address `json:"challenger"` + Challenger common.Address `json:"challenger" toml:"challenger"` } -func (c *Chain) Check() error { - if c.ID == 0 { +func (c *ChainIntent) Check() error { + var emptyHash common.Hash + if c.ID == emptyHash { return fmt.Errorf("id must be set") } diff --git a/op-chain-ops/deployer/state/state.go b/op-chain-ops/deployer/state/state.go index 19addf79938df..315f25b64b767 100644 --- a/op-chain-ops/deployer/state/state.go +++ b/op-chain-ops/deployer/state/state.go @@ -1,6 +1,7 @@ package state import ( + "github.com/ethereum-optimism/optimism/op-chain-ops/foundry" "github.com/ethereum-optimism/optimism/op-service/ioutil" "github.com/ethereum-optimism/optimism/op-service/jsonutil" "github.com/ethereum/go-ethereum/common" @@ -12,6 +13,9 @@ type State struct { // Version versions the state so we can update it later. Version int `json:"version"` + // Create2Salt is the salt used for CREATE2 deployments. + Create2Salt common.Hash `json:"create2Salt"` + // AppliedIntent contains the chain intent that was last // successfully applied. It is diffed against new intent // in order to determine what deployment steps to take. @@ -22,6 +26,13 @@ type State struct { // deployment. It only contains the proxies because the implementations // can be looked up on chain. SuperchainDeployment *SuperchainDeployment `json:"superchainDeployment"` + + // ImplementationsDeployment contains the addresses of the common implementation + // contracts required for the Superchain to function. + ImplementationsDeployment *ImplementationsDeployment `json:"implementationsDeployment"` + + // Chains contains data about L2 chain deployments. + Chains []ChainState `json:"opChainDeployments"` } func (s State) WriteToFile(path string) error { @@ -29,9 +40,46 @@ func (s State) WriteToFile(path string) error { } type SuperchainDeployment struct { - ProxyAdminAddress common.Address `json:"proxyAdminAddress"` - SuperchainConfigProxyAddress common.Address `json:"superchainConfigProxyAddress"` - SuperchainConfigImplAddress common.Address `json:"superchainConfigImplAddress"` - ProtocolVersionsProxyAddress common.Address `json:"protocolVersionsProxyAddress"` - ProtocolVersionsImplAddress common.Address `json:"protocolVersionsImplAddress"` + ProxyAdminAddress common.Address `json:"proxyAdminAddress"` + SuperchainConfigProxyAddress common.Address `json:"superchainConfigProxyAddress"` + SuperchainConfigImplAddress common.Address `json:"superchainConfigImplAddress"` + ProtocolVersionsProxyAddress common.Address `json:"protocolVersionsProxyAddress"` + ProtocolVersionsImplAddress common.Address `json:"protocolVersionsImplAddress"` + StateDump *foundry.ForgeAllocs `json:"stateDump"` +} + +type ImplementationsDeployment struct { + OpsmAddress common.Address `json:"opsmAddress"` + DelayedWETHImplAddress common.Address `json:"delayedWETHImplAddress"` + OptimismPortalImplAddress common.Address `json:"optimismPortalImplAddress"` + PreimageOracleSingletonAddress common.Address `json:"preimageOracleSingletonAddress"` + MipsSingletonAddress common.Address `json:"mipsSingletonAddress"` + SystemConfigImplAddress common.Address `json:"systemConfigImplAddress"` + L1CrossDomainMessengerImplAddress common.Address `json:"l1CrossDomainMessengerImplAddress"` + L1ERC721BridgeImplAddress common.Address `json:"l1ERC721BridgeImplAddress"` + L1StandardBridgeImplAddress common.Address `json:"l1StandardBridgeImplAddress"` + OptimismMintableERC20FactoryImplAddress common.Address `json:"optimismMintableERC20FactoryImplAddress"` + DisputeGameFactoryImplAddress common.Address `json:"disputeGameFactoryImplAddress"` + StateDump *foundry.ForgeAllocs `json:"stateDump"` +} + +type ChainState struct { + ID common.Hash `json:"id"` + + ProxyAdminAddress common.Address `json:"proxyAdminAddress"` + AddressManagerAddress common.Address `json:"addressManagerAddress"` + L1ERC721BridgeProxyAddress common.Address `json:"l1ERC721BridgeProxyAddress"` + SystemConfigProxyAddress common.Address `json:"systemConfigProxyAddress"` + OptimismMintableERC20FactoryProxyAddress common.Address `json:"optimismMintableERC20FactoryProxyAddress"` + L1StandardBridgeProxyAddress common.Address `json:"l1StandardBridgeProxyAddress"` + L1CrossDomainMessengerProxyAddress common.Address `json:"l1CrossDomainMessengerProxyAddress"` + OptimismPortalProxyAddress common.Address `json:"optimismPortalProxyAddress"` + DisputeGameFactoryProxyAddress common.Address `json:"disputeGameFactoryProxyAddress"` + DisputeGameFactoryImplAddress common.Address `json:"disputeGameFactoryImplAddress"` + AnchorStateRegistryProxyAddress common.Address `json:"anchorStateRegistryProxyAddress"` + AnchorStateRegistryImplAddress common.Address `json:"anchorStateRegistryImplAddress"` + FaultDisputeGameAddress common.Address `json:"faultDisputeGameAddress"` + PermissionedDisputeGameAddress common.Address `json:"permissionedDisputeGameAddress"` + DelayedWETHPermissionedGameProxyAddress common.Address `json:"delayedWETHPermissionedGameProxyAddress"` + DelayedWETHPermissionlessGameProxyAddress common.Address `json:"delayedWETHPermissionlessGameProxyAddress"` } diff --git a/op-chain-ops/interopgen/deploy.go b/op-chain-ops/interopgen/deploy.go index 89982a8763375..65cb474d9f2be 100644 --- a/op-chain-ops/interopgen/deploy.go +++ b/op-chain-ops/interopgen/deploy.go @@ -5,6 +5,8 @@ import ( "fmt" "math/big" + "github.com/ethereum-optimism/optimism/op-chain-ops/deployer/opsm" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" @@ -147,7 +149,7 @@ func prepareInitialL1(l1Host *script.Host, cfg *L1Config) (*L1Deployment, error) func deploySuperchainToL1(l1Host *script.Host, superCfg *SuperchainConfig) (*SuperchainDeployment, error) { l1Host.SetTxOrigin(superCfg.Deployer) - superDeployment, err := deployers.DeploySuperchain(l1Host, &deployers.DeploySuperchainInput{ + superDeployment, err := opsm.DeploySuperchain(l1Host, opsm.DeploySuperchainInput{ ProxyAdminOwner: superCfg.ProxyAdminOwner, ProtocolVersionsOwner: superCfg.ProtocolVersionsOwner, Guardian: superCfg.SuperchainConfigGuardian, @@ -159,7 +161,7 @@ func deploySuperchainToL1(l1Host *script.Host, superCfg *SuperchainConfig) (*Sup return nil, fmt.Errorf("failed to deploy Superchain contracts: %w", err) } - implementationsDeployment, err := deployers.DeployImplementations(l1Host, &deployers.DeployImplementationsInput{ + implementationsDeployment, err := opsm.DeployImplementations(l1Host, opsm.DeployImplementationsInput{ WithdrawalDelaySeconds: superCfg.Implementations.FaultProof.WithdrawalDelaySeconds, MinProposalSizeBytes: superCfg.Implementations.FaultProof.MinProposalSizeBytes, ChallengePeriodSeconds: superCfg.Implementations.FaultProof.ChallengePeriodSeconds, @@ -178,7 +180,7 @@ func deploySuperchainToL1(l1Host *script.Host, superCfg *SuperchainConfig) (*Sup // Collect deployment addresses // This could all be automatic once we have better output-contract typing/scripting return &SuperchainDeployment{ - Implementations: Implementations(*implementationsDeployment), + Implementations: Implementations(implementationsDeployment), ProxyAdmin: superDeployment.SuperchainProxyAdmin, ProtocolVersions: superDeployment.ProtocolVersionsImpl, ProtocolVersionsProxy: superDeployment.ProtocolVersionsProxy, @@ -194,7 +196,7 @@ func deployL2ToL1(l1Host *script.Host, superCfg *SuperchainConfig, superDeployme l1Host.SetTxOrigin(cfg.Deployer) - output, err := deployers.DeployOPChain(l1Host, &deployers.DeployOPChainInput{ + output, err := opsm.DeployOPChain(l1Host, opsm.DeployOPChainInput{ OpChainProxyAdminOwner: cfg.ProxyAdminOwner, SystemConfigOwner: cfg.SystemConfigOwner, Batcher: cfg.BatchSenderAddress, @@ -212,7 +214,7 @@ func deployL2ToL1(l1Host *script.Host, superCfg *SuperchainConfig, superDeployme // Collect deployment addresses return &L2Deployment{ - L2OpchainDeployment: L2OpchainDeployment(*output), + L2OpchainDeployment: L2OpchainDeployment(output), }, nil } diff --git a/op-chain-ops/interopgen/deployers/superchain.go b/op-chain-ops/interopgen/deployers/superchain.go deleted file mode 100644 index 60e9805d6711f..0000000000000 --- a/op-chain-ops/interopgen/deployers/superchain.go +++ /dev/null @@ -1,70 +0,0 @@ -package deployers - -import ( - "fmt" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/params" - - "github.com/ethereum-optimism/optimism/op-chain-ops/script" -) - -type DeploySuperchainInput struct { - ProxyAdminOwner common.Address // TODO(#11783): also used as interop-dependency-set owner - ProtocolVersionsOwner common.Address - Guardian common.Address - Paused bool - RequiredProtocolVersion params.ProtocolVersion - RecommendedProtocolVersion params.ProtocolVersion -} - -func (input *DeploySuperchainInput) InputSet() bool { - return true -} - -type DeploySuperchainOutput struct { - SuperchainProxyAdmin common.Address - SuperchainConfigImpl common.Address - SuperchainConfigProxy common.Address - ProtocolVersionsImpl common.Address - ProtocolVersionsProxy common.Address -} - -func (output *DeploySuperchainOutput) CheckOutput() error { - return nil -} - -type DeploySuperchainScript struct { - Run func(input, output common.Address) error -} - -func DeploySuperchain(l1Host *script.Host, input *DeploySuperchainInput) (*DeploySuperchainOutput, error) { - output := &DeploySuperchainOutput{} - inputAddr := l1Host.NewScriptAddress() - outputAddr := l1Host.NewScriptAddress() - - cleanupInput, err := script.WithPrecompileAtAddress[*DeploySuperchainInput](l1Host, inputAddr, input) - if err != nil { - return nil, fmt.Errorf("failed to insert DeploySuperchainInput precompile: %w", err) - } - defer cleanupInput() - - cleanupOutput, err := script.WithPrecompileAtAddress[*DeploySuperchainOutput](l1Host, outputAddr, output, - script.WithFieldSetter[*DeploySuperchainOutput]) - if err != nil { - return nil, fmt.Errorf("failed to insert DeploySuperchainOutput precompile: %w", err) - } - defer cleanupOutput() - - deployScript, cleanupDeploy, err := script.WithScript[DeploySuperchainScript](l1Host, "DeploySuperchain.s.sol", "DeploySuperchain") - if err != nil { - return nil, fmt.Errorf("failed to load DeploySuperchain script: %w", err) - } - defer cleanupDeploy() - - if err := deployScript.Run(inputAddr, outputAddr); err != nil { - return nil, fmt.Errorf("failed to run DeploySuperchain script: %w", err) - } - - return output, nil -} diff --git a/op-chain-ops/script/script.go b/op-chain-ops/script/script.go index 5bbc0942a52b2..333d7509426ee 100644 --- a/op-chain-ops/script/script.go +++ b/op-chain-ops/script/script.go @@ -386,6 +386,17 @@ func (h *Host) GetNonce(addr common.Address) uint64 { return h.state.GetNonce(addr) } +func (h *Host) ImportState(allocs *foundry.ForgeAllocs) { + for addr, alloc := range allocs.Accounts { + h.state.SetBalance(addr, uint256.MustFromBig(alloc.Balance), tracing.BalanceChangeUnspecified) + h.state.SetNonce(addr, alloc.Nonce) + h.state.SetCode(addr, alloc.Code) + for key, value := range alloc.Storage { + h.state.SetState(addr, key, value) + } + } +} + // getPrecompile overrides any accounts during runtime, to insert special precompiles, if activated. func (h *Host) getPrecompile(rules params.Rules, original vm.PrecompiledContract, addr common.Address) vm.PrecompiledContract { if p, ok := h.precompiles[addr]; ok { @@ -436,8 +447,8 @@ func (h *Host) onEnter(depth int, typ byte, from common.Address, to common.Addre return } - // Bump nonce value, such that a broadcast Call appears like a tx - if parentCallFrame.LastOp == vm.CALL { + // Bump nonce value, such that a broadcast Call or CREATE2 appears like a tx + if parentCallFrame.LastOp == vm.CALL || parentCallFrame.LastOp == vm.CREATE2 { sender := parentCallFrame.Ctx.Address() if parentCallFrame.Prank.Sender != nil { sender = *parentCallFrame.Prank.Sender diff --git a/op-chain-ops/script/script_test.go b/op-chain-ops/script/script_test.go index 53415d1f0583f..2d515bfdf02f9 100644 --- a/op-chain-ops/script/script_test.go +++ b/op-chain-ops/script/script_test.go @@ -166,5 +166,5 @@ func TestScriptBroadcast(t *testing.T) { require.EqualValues(t, 2, h.GetNonce(coffeeAddr)) // This is zero because the deterministic deployer is the // address that actually deploys the contract using CREATE2. - require.EqualValues(t, 0, h.GetNonce(cafeAddr)) + require.EqualValues(t, 1, h.GetNonce(cafeAddr)) } diff --git a/packages/contracts-bedrock/scripts/DeployImplementations.s.sol b/packages/contracts-bedrock/scripts/DeployImplementations.s.sol index f68db7511f5a6..5cb6fe9d0c6d9 100644 --- a/packages/contracts-bedrock/scripts/DeployImplementations.s.sol +++ b/packages/contracts-bedrock/scripts/DeployImplementations.s.sol @@ -33,6 +33,7 @@ import { OptimismPortalInterop } from "src/L1/OptimismPortalInterop.sol"; import { SystemConfigInterop } from "src/L1/SystemConfigInterop.sol"; import { Blueprint } from "src/libraries/Blueprint.sol"; +import { Config } from "scripts/libraries/Config.sol"; import { DeployUtils } from "scripts/libraries/DeployUtils.sol"; import { Solarray } from "scripts/libraries/Solarray.sol"; @@ -338,7 +339,7 @@ contract DeployImplementations is Script { // First we deploy the blueprints for the singletons deployed by OPSM. // forgefmt: disable-start - bytes32 salt = bytes32(0); + bytes32 salt = keccak256(bytes(Config.implSalt())); OPStackManager.Blueprints memory blueprints; vm.startBroadcast(msg.sender);