Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

op-deployer: Bootstrap superchain command #13294

Merged
merged 2 commits into from
Dec 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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" {
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