Skip to content

Commit

Permalink
op-deployer: Bootstrap superchain command
Browse files Browse the repository at this point in the history
Fixes #13265.
  • Loading branch information
mslipper committed Dec 6, 2024
1 parent f21f95e commit 0c09dc8
Show file tree
Hide file tree
Showing 4 changed files with 375 additions and 1 deletion.
57 changes: 57 additions & 0 deletions op-deployer/pkg/deployer/bootstrap/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ const (
DelayedWethProxyFlagName = "delayed-weth-proxy"
DelayedWethImplFlagName = "delayed-weth-impl"
ProxyOwnerFlagName = "proxy-owner"
SuperchainProxyAdminOwnerFlagName = "superchain-proxy-admin-owner"
ProtocolVersionsOwnerFlagName = "protocol-versions-owner"
GuardianFlagName = "guardian"
PausedFlagName = "paused"
RequiredProtocolVersionFlagName = "required-protocol-version"
RecommendedProtocolVersionFlagName = "recommended-protocol-version"
)

var (
Expand Down Expand Up @@ -176,6 +182,39 @@ var (
EnvVars: deployer.PrefixEnvVar("PROXY_OWNER"),
Value: common.Address{}.Hex(),
}
SuperchainProxyAdminOwnerFlag = &cli.StringFlag{
Name: SuperchainProxyAdminOwnerFlagName,
Usage: "Owner address for the superchain proxy admin",
EnvVars: deployer.PrefixEnvVar("SUPERCHAIN_PROXY_ADMIN_OWNER"),
Value: common.Address{}.Hex(),
}
ProtocolVersionsOwnerFlag = &cli.StringFlag{
Name: ProtocolVersionsOwnerFlagName,
Usage: "Owner address for protocol versions",
EnvVars: deployer.PrefixEnvVar("PROTOCOL_VERSIONS_OWNER"),
Value: common.Address{}.Hex(),
}
GuardianFlag = &cli.StringFlag{
Name: GuardianFlagName,
Usage: "Guardian address",
EnvVars: deployer.PrefixEnvVar("GUARDIAN"),
Value: common.Address{}.Hex(),
}
PausedFlag = &cli.BoolFlag{
Name: PausedFlagName,
Usage: "Initial paused state",
EnvVars: deployer.PrefixEnvVar("PAUSED"),
}
RequiredProtocolVersionFlag = &cli.StringFlag{
Name: RequiredProtocolVersionFlagName,
Usage: "Required protocol version (semver)",
EnvVars: deployer.PrefixEnvVar("REQUIRED_PROTOCOL_VERSION"),
}
RecommendedProtocolVersionFlag = &cli.StringFlag{
Name: RecommendedProtocolVersionFlagName,
Usage: "Recommended protocol version (semver)",
EnvVars: deployer.PrefixEnvVar("RECOMMENDED_PROTOCOL_VERSION"),
}
)

var OPCMFlags = []cli.Flag{
Expand Down Expand Up @@ -239,6 +278,18 @@ var ProxyFlags = []cli.Flag{
ProxyOwnerFlag,
}

var SuperchainFlags = []cli.Flag{
deployer.L1RPCURLFlag,
deployer.PrivateKeyFlag,
ArtifactsLocatorFlag,
SuperchainProxyAdminOwnerFlag,
ProtocolVersionsOwnerFlag,
GuardianFlag,
PausedFlag,
RequiredProtocolVersionFlag,
RecommendedProtocolVersionFlag,
}

var Commands = []*cli.Command{
{
Name: "opcm",
Expand Down Expand Up @@ -285,4 +336,10 @@ var Commands = []*cli.Command{
Flags: cliapp.ProtectFlags(ProxyFlags),
Action: ProxyCLI,
},
{
Name: "superchain",
Usage: "Bootstrap the Superchain configuration",
Flags: cliapp.ProtectFlags(SuperchainFlags),
Action: SuperchainCLI,
},
}
6 changes: 5 additions & 1 deletion op-deployer/pkg/deployer/bootstrap/opcm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import (

var networks = []string{"mainnet", "sepolia"}

var versions = []string{"v1.8.0-rc.3"}
var versions = []string{"v1.8.0-rc.3", "v1.6.0"}

func TestOPCMLiveChain(t *testing.T) {
for _, network := range networks {
Expand All @@ -27,6 +27,10 @@ func TestOPCMLiveChain(t *testing.T) {
t.Skip("v1.8.0-rc.3 not supported on mainnet yet")
}

if version == "v1.6.0" && network == "sepolia" {
t.Skip("v1.6.0 not supported")
}

envVar := strings.ToUpper(network) + "_RPC_URL"
rpcURL := os.Getenv(envVar)
require.NotEmpty(t, rpcURL, "must specify RPC url via %s env var", envVar)
Expand Down
218 changes: 218 additions & 0 deletions op-deployer/pkg/deployer/bootstrap/superchain.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
package bootstrap

import (
"context"
"crypto/ecdsa"
"fmt"
"strings"

"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer"
"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/artifacts"
"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/broadcaster"
"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/opcm"
"github.com/ethereum-optimism/optimism/op-deployer/pkg/env"
opcrypto "github.com/ethereum-optimism/optimism/op-service/crypto"
"github.com/ethereum-optimism/optimism/op-service/ctxinterrupt"
"github.com/ethereum-optimism/optimism/op-service/ioutil"
"github.com/ethereum-optimism/optimism/op-service/jsonutil"
oplog "github.com/ethereum-optimism/optimism/op-service/log"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rpc"
"github.com/urfave/cli/v2"
)

type SuperchainConfig struct {
L1RPCUrl string
PrivateKey string
Logger log.Logger
ArtifactsLocator *artifacts.Locator

privateKeyECDSA *ecdsa.PrivateKey

SuperchainProxyAdminOwner common.Address
ProtocolVersionsOwner common.Address
Guardian common.Address
Paused bool
RequiredProtocolVersion params.ProtocolVersion
RecommendedProtocolVersion params.ProtocolVersion
}

func (c *SuperchainConfig) Check() error {
if c.L1RPCUrl == "" {
return fmt.Errorf("l1RPCUrl must be specified")
}

if c.PrivateKey == "" {
return fmt.Errorf("private key must be specified")
}

privECDSA, err := crypto.HexToECDSA(strings.TrimPrefix(c.PrivateKey, "0x"))
if err != nil {
return fmt.Errorf("failed to parse private key: %w", err)
}
c.privateKeyECDSA = privECDSA

if c.Logger == nil {
return fmt.Errorf("logger must be specified")
}

if c.ArtifactsLocator == nil {
return fmt.Errorf("artifacts locator must be specified")
}

if c.SuperchainProxyAdminOwner == (common.Address{}) {
return fmt.Errorf("superchain proxy admin owner must be specified")
}

if c.ProtocolVersionsOwner == (common.Address{}) {
return fmt.Errorf("protocol versions owner must be specified")
}

if c.Guardian == (common.Address{}) {
return fmt.Errorf("guardian must be specified")
}

return nil
}

func SuperchainCLI(cliCtx *cli.Context) error {
logCfg := oplog.ReadCLIConfig(cliCtx)
l := oplog.NewLogger(oplog.AppOut(cliCtx), logCfg)
oplog.SetGlobalLogHandler(l.Handler())

l1RPCUrl := cliCtx.String(deployer.L1RPCURLFlagName)
privateKey := cliCtx.String(deployer.PrivateKeyFlagName)
artifactsURLStr := cliCtx.String(ArtifactsLocatorFlagName)
artifactsLocator := new(artifacts.Locator)
if err := artifactsLocator.UnmarshalText([]byte(artifactsURLStr)); err != nil {
return fmt.Errorf("failed to parse artifacts URL: %w", err)
}

superchainProxyAdminOwner := common.HexToAddress(cliCtx.String(SuperchainProxyAdminOwnerFlagName))
protocolVersionsOwner := common.HexToAddress(cliCtx.String(ProtocolVersionsOwnerFlagName))
guardian := common.HexToAddress(cliCtx.String(GuardianFlagName))
paused := cliCtx.Bool(PausedFlagName)
requiredVersionStr := cliCtx.String(RequiredProtocolVersionFlagName)
recommendedVersionStr := cliCtx.String(RecommendedProtocolVersionFlagName)

cfg := SuperchainConfig{
L1RPCUrl: l1RPCUrl,
PrivateKey: privateKey,
Logger: l,
ArtifactsLocator: artifactsLocator,
SuperchainProxyAdminOwner: superchainProxyAdminOwner,
ProtocolVersionsOwner: protocolVersionsOwner,
Guardian: guardian,
Paused: paused,
}

if err := cfg.RequiredProtocolVersion.UnmarshalText([]byte(requiredVersionStr)); err != nil {
return fmt.Errorf("failed to parse required protocol version: %w", err)
}
if err := cfg.RecommendedProtocolVersion.UnmarshalText([]byte(recommendedVersionStr)); err != nil {
return fmt.Errorf("failed to parse required protocol version: %w", err)
}

ctx := ctxinterrupt.WithCancelOnInterrupt(cliCtx.Context)

dso, err := Superchain(ctx, cfg)
if err != nil {
return fmt.Errorf("failed to deploy superchain: %w", err)
}

if err := jsonutil.WriteJSON(dso, ioutil.ToStdOut()); err != nil {
return fmt.Errorf("failed to write output: %w", err)
}
return nil
}

func Superchain(ctx context.Context, cfg SuperchainConfig) (opcm.DeploySuperchainOutput, error) {
var dso opcm.DeploySuperchainOutput

if err := cfg.Check(); err != nil {
return dso, fmt.Errorf("invalid config for Superchain: %w", err)
}

lgr := cfg.Logger
progressor := func(curr, total int64) {
lgr.Info("artifacts download progress", "current", curr, "total", total)
}

artifactsFS, cleanup, err := artifacts.Download(ctx, cfg.ArtifactsLocator, progressor)
if err != nil {
return dso, fmt.Errorf("failed to download artifacts: %w", err)
}
defer func() {
if err := cleanup(); err != nil {
lgr.Warn("failed to clean up artifacts", "err", err)
}
}()

l1Client, err := ethclient.Dial(cfg.L1RPCUrl)
if err != nil {
return dso, fmt.Errorf("failed to connect to L1 RPC: %w", err)
}

chainID, err := l1Client.ChainID(ctx)
if err != nil {
return dso, fmt.Errorf("failed to get chain ID: %w", err)
}

signer := opcrypto.SignerFnFromBind(opcrypto.PrivateKeySignerFn(cfg.privateKeyECDSA, chainID))
chainDeployer := crypto.PubkeyToAddress(cfg.privateKeyECDSA.PublicKey)

bcaster, err := broadcaster.NewKeyedBroadcaster(broadcaster.KeyedBroadcasterOpts{
Logger: lgr,
ChainID: chainID,
Client: l1Client,
Signer: signer,
From: chainDeployer,
})
if err != nil {
return dso, fmt.Errorf("failed to create broadcaster: %w", err)
}

l1RPC, err := rpc.Dial(cfg.L1RPCUrl)
if err != nil {
return dso, fmt.Errorf("failed to connect to L1 RPC: %w", err)
}

l1Host, err := env.DefaultForkedScriptHost(
ctx,
bcaster,
lgr,
chainDeployer,
artifactsFS,
l1RPC,
)
if err != nil {
return dso, fmt.Errorf("failed to create script host: %w", err)
}

dso, err = opcm.DeploySuperchain(
l1Host,
opcm.DeploySuperchainInput{
SuperchainProxyAdminOwner: cfg.SuperchainProxyAdminOwner,
ProtocolVersionsOwner: cfg.ProtocolVersionsOwner,
Guardian: cfg.Guardian,
Paused: cfg.Paused,
RequiredProtocolVersion: cfg.RequiredProtocolVersion,
RecommendedProtocolVersion: cfg.RecommendedProtocolVersion,
},
)
if err != nil {
return dso, fmt.Errorf("error deploying superchain: %w", err)
}

if _, err := bcaster.Broadcast(ctx); err != nil {
return dso, fmt.Errorf("failed to broadcast: %w", err)
}

lgr.Info("deployed superchain configuration")

return dso, nil
}
Loading

0 comments on commit 0c09dc8

Please sign in to comment.