Skip to content

Commit

Permalink
Cleanups for production, add standard addresses (#12169)
Browse files Browse the repository at this point in the history
* Cleanups for production, add standard addresses

This PR:

- Cleans up the intent file to be more suitable for production deployments
- Fixes various bugs encountered while preparing `op-deployer` for use against predeployed OPCM contracts
- Adds a new CLI command to bootstrap a new OPCM deployment against an existing set of implementation contracts

Note on Solidity changes:

- Since the code for custom gas tokens is in the monorepo but isn't included in an official contracts release yet, we had to add interfaces for the pre-CGT contracts to the Solidity codebase.
- The `DeployImplementations` script looks at the release identifier to determine whether or not it should use the pre- or post-CGT interfaces.

* goimports

* lints

* fix tests

* revert tx manger changes

* Update packages/contracts-bedrock/src/L1/interfaces/IL1StandardBridgeV160.sol

Co-authored-by: Maurelian <john@oplabs.co>

* Update packages/contracts-bedrock/src/L1/interfaces/IL1CrossDomainMessengerV160.sol

Co-authored-by: Maurelian <john@oplabs.co>

* use new opcm

* fix test

* semver

* semver

* bump semver

* update manager deployment

* natspec

* SEMVER

---------

Co-authored-by: Maurelian <john@oplabs.co>
  • Loading branch information
mslipper and maurelian authored Sep 27, 2024
1 parent a96b228 commit 644dc2b
Show file tree
Hide file tree
Showing 28 changed files with 595 additions and 185 deletions.
7 changes: 7 additions & 0 deletions op-chain-ops/cmd/op-deployer/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"fmt"
"os"

"github.com/ethereum-optimism/optimism/op-chain-ops/deployer/bootstrap"

"github.com/ethereum-optimism/optimism/op-chain-ops/deployer/version"
opservice "github.com/ethereum-optimism/optimism/op-service"

Expand Down Expand Up @@ -41,6 +43,11 @@ func main() {
Flags: cliapp.ProtectFlags(deployer.ApplyFlags),
Action: deployer.ApplyCLI(),
},
{
Name: "bootstrap",
Usage: "bootstraps global contract instances",
Subcommands: bootstrap.Commands,
},
{
Name: "inspect",
Usage: "inspects the state of a deployment",
Expand Down
206 changes: 206 additions & 0 deletions op-chain-ops/deployer/bootstrap/bootstrap.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
package bootstrap

import (
"context"
"crypto/ecdsa"
"crypto/rand"
"fmt"
"math/big"
"strings"

"github.com/ethereum-optimism/optimism/op-chain-ops/deployer"
"github.com/ethereum-optimism/optimism/op-chain-ops/deployer/opcm"
"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/script"
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/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/log"
"github.com/urfave/cli/v2"
)

type OPCMConfig struct {
L1RPCUrl string
PrivateKey string
Logger log.Logger
ArtifactsURL *state.ArtifactsURL
ContractsRelease string

privateKeyECDSA *ecdsa.PrivateKey
}

func (c *OPCMConfig) 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.ArtifactsURL == nil {
return fmt.Errorf("artifacts URL must be specified")
}

if c.ContractsRelease == "" {
return fmt.Errorf("contracts release must be specified")
}

return nil
}

func OPCMCLI(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(ArtifactsURLFlagName)
artifactsURL := new(state.ArtifactsURL)
if err := artifactsURL.UnmarshalText([]byte(artifactsURLStr)); err != nil {
return fmt.Errorf("failed to parse artifacts URL: %w", err)
}
contractsRelease := cliCtx.String(ContractsReleaseFlagName)

ctx := ctxinterrupt.WithCancelOnInterrupt(cliCtx.Context)

return OPCM(ctx, OPCMConfig{
L1RPCUrl: l1RPCUrl,
PrivateKey: privateKey,
Logger: l,
ArtifactsURL: artifactsURL,
ContractsRelease: contractsRelease,
})
}

func OPCM(ctx context.Context, cfg OPCMConfig) error {
if err := cfg.Check(); err != nil {
return fmt.Errorf("invalid config for OPCM: %w", err)
}

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

artifactsFS, cleanup, err := pipeline.DownloadArtifacts(ctx, cfg.ArtifactsURL, progressor)
if err != nil {
return 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 fmt.Errorf("failed to connect to L1 RPC: %w", err)
}

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

superCfg, err := opcm.SuperchainFor(chainIDU64)
if err != nil {
return fmt.Errorf("error getting superchain config: %w", err)
}
standardVersionsTOML, err := opcm.StandardVersionsFor(chainIDU64)
if err != nil {
return fmt.Errorf("error getting standard versions TOML: %w", err)
}
opcmProxyOwnerAddr, err := opcm.ManagerOwnerAddrFor(chainIDU64)
if err != nil {
return fmt.Errorf("error getting superchain proxy admin: %w", err)
}

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

lgr.Info("deploying OPCM", "release", cfg.ContractsRelease)

var dio opcm.DeployImplementationsOutput
err = pipeline.CallScriptBroadcast(
ctx,
pipeline.CallScriptBroadcastOpts{
L1ChainID: chainID,
Logger: lgr,
ArtifactsFS: artifactsFS,
Deployer: chainDeployer,
Signer: signer,
Client: l1Client,
Broadcaster: pipeline.KeyedBroadcaster,
Handler: func(host *script.Host) error {
// We need to etch the Superchain addresses so that they have nonzero code
// and the checks in the OPCM constructor pass.
superchainConfigAddr := common.Address(*superCfg.Config.SuperchainConfigAddr)
protocolVersionsAddr := common.Address(*superCfg.Config.ProtocolVersionsAddr)
addresses := []common.Address{
superchainConfigAddr,
protocolVersionsAddr,
}
for _, addr := range addresses {
host.ImportAccount(addr, types.Account{
Code: []byte{0x00},
})
}

var salt common.Hash
_, err = rand.Read(salt[:])
if err != nil {
return fmt.Errorf("failed to generate CREATE2 salt: %w", err)
}

dio, err = opcm.DeployImplementations(
host,
opcm.DeployImplementationsInput{
Salt: salt,
WithdrawalDelaySeconds: big.NewInt(604800),
MinProposalSizeBytes: big.NewInt(126000),
ChallengePeriodSeconds: big.NewInt(86400),
ProofMaturityDelaySeconds: big.NewInt(604800),
DisputeGameFinalityDelaySeconds: big.NewInt(302400),
Release: cfg.ContractsRelease,
SuperchainConfigProxy: superchainConfigAddr,
ProtocolVersionsProxy: protocolVersionsAddr,
OpcmProxyOwner: opcmProxyOwnerAddr,
StandardVersionsToml: standardVersionsTOML,
UseInterop: false,
},
)
return err
},
},
)
if err != nil {
return fmt.Errorf("error deploying implementations: %w", err)
}

lgr.Info("deployed implementations")

if err := jsonutil.WriteJSON(dio, ioutil.ToStdOut()); err != nil {
return fmt.Errorf("failed to write output: %w", err)
}
return nil
}
41 changes: 41 additions & 0 deletions op-chain-ops/deployer/bootstrap/flags.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package bootstrap

import (
"github.com/ethereum-optimism/optimism/op-chain-ops/deployer"
"github.com/ethereum-optimism/optimism/op-service/cliapp"
"github.com/urfave/cli/v2"
)

const (
ArtifactsURLFlagName = "artifacts-url"
ContractsReleaseFlagName = "contracts-release"
)

var (
ArtifactsURLFlag = &cli.StringFlag{
Name: ArtifactsURLFlagName,
Usage: "URL to the artifacts directory.",
EnvVars: deployer.PrefixEnvVar("ARTIFACTS_URL"),
}
ContractsReleaseFlag = &cli.StringFlag{
Name: ContractsReleaseFlagName,
Usage: "Release of the contracts to deploy.",
EnvVars: deployer.PrefixEnvVar("CONTRACTS_RELEASE"),
}
)

var OPCMFlags = []cli.Flag{
deployer.L1RPCURLFlag,
deployer.PrivateKeyFlag,
ArtifactsURLFlag,
ContractsReleaseFlag,
}

var Commands = []*cli.Command{
{
Name: "opcm",
Usage: "Bootstrap an instance of OPCM.",
Flags: cliapp.ProtectFlags(OPCMFlags),
Action: OPCMCLI,
},
}
2 changes: 1 addition & 1 deletion op-chain-ops/deployer/broadcaster/keyed.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ func NewKeyedBroadcaster(cfg KeyedBroadcasterOpts) (*KeyedBroadcaster, error) {
mgrCfg.FeeLimitMultiplier.Store(5)
mgrCfg.FeeLimitThreshold.Store(big.NewInt(100))
mgrCfg.MinTipCap.Store(minTipCap)
mgrCfg.MinTipCap.Store(minBaseFee)
mgrCfg.MinBaseFee.Store(minBaseFee)

txmLogger := log.NewLogger(log.DiscardHandler())
if cfg.TXManagerLogger != nil {
Expand Down
11 changes: 5 additions & 6 deletions op-chain-ops/deployer/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,28 +30,27 @@ var (
L1ChainIDFlag = &cli.Uint64Flag{
Name: L1ChainIDFlagName,
Usage: "Chain ID of the L1 chain.",
EnvVars: prefixEnvVar("L1_CHAIN_ID"),
EnvVars: PrefixEnvVar("L1_CHAIN_ID"),
Value: 900,
}
L2ChainIDsFlag = &cli.StringFlag{
Name: L2ChainIDsFlagName,
Usage: "Comma-separated list of L2 chain IDs to deploy.",
EnvVars: prefixEnvVar("L2_CHAIN_IDS"),
EnvVars: PrefixEnvVar("L2_CHAIN_IDS"),
}
WorkdirFlag = &cli.StringFlag{
Name: WorkdirFlagName,
Usage: "Directory storing intent and stage. Defaults to the current directory.",
EnvVars: prefixEnvVar("WORKDIR"),
EnvVars: PrefixEnvVar("WORKDIR"),
Value: cwd(),
Aliases: []string{
OutdirFlagName,
},
}

PrivateKeyFlag = &cli.StringFlag{
Name: PrivateKeyFlagName,
Usage: "Private key of the deployer account.",
EnvVars: prefixEnvVar("PRIVATE_KEY"),
EnvVars: PrefixEnvVar("PRIVATE_KEY"),
}
)

Expand All @@ -69,7 +68,7 @@ var ApplyFlags = []cli.Flag{
PrivateKeyFlag,
}

func prefixEnvVar(name string) []string {
func PrefixEnvVar(name string) []string {
return op_service.PrefixEnvVar(EnvVarPrefix, name)
}

Expand Down
5 changes: 2 additions & 3 deletions op-chain-ops/deployer/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ func InitCLI() func(ctx *cli.Context) error {
outdir := ctx.String(OutdirFlagName)

l2ChainIDsRaw := ctx.String(L2ChainIDsFlagName)
l2ChainIDsStr := strings.Split(l2ChainIDsRaw, ",")
l2ChainIDs := make([]common.Hash, 0, len(l2ChainIDsStr))
l2ChainIDsStr := strings.Split(strings.TrimSpace(l2ChainIDsRaw), ",")
l2ChainIDs := make([]common.Hash, len(l2ChainIDsStr))
for _, idStr := range l2ChainIDsStr {
id, err := op_service.Parse256BitChainID(idStr)
if err != nil {
Expand All @@ -66,7 +66,6 @@ func Init(cfg InitConfig) error {

intent := &state.Intent{
L1ChainID: cfg.L1ChainID,
UseFaultProofs: true,
FundDevAccounts: true,
ContractsRelease: "dev",
}
Expand Down
2 changes: 0 additions & 2 deletions op-chain-ops/deployer/integration_test/apply_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,6 @@ func makeIntent(
ProtocolVersionsOwner: addrFor(devkeys.SuperchainDeployerKey.Key(l1ChainID)),
Guardian: addrFor(devkeys.SuperchainConfigGuardianKey.Key(l1ChainID)),
},
UseFaultProofs: true,
FundDevAccounts: true,
ContractArtifactsURL: (*state.ArtifactsURL)(artifactsURL),
ContractsRelease: "dev",
Expand Down Expand Up @@ -239,7 +238,6 @@ func validateOPChainDeployment(t *testing.T, ctx context.Context, l1Client *ethc
{"OptimismPortalProxyAddress", chainState.OptimismPortalProxyAddress},
{"DisputeGameFactoryProxyAddress", chainState.DisputeGameFactoryProxyAddress},
{"AnchorStateRegistryProxyAddress", chainState.AnchorStateRegistryProxyAddress},
{"AnchorStateRegistryImplAddress", chainState.AnchorStateRegistryImplAddress},
{"FaultDisputeGameAddress", chainState.FaultDisputeGameAddress},
{"PermissionedDisputeGameAddress", chainState.PermissionedDisputeGameAddress},
{"DelayedWETHPermissionedGameProxyAddress", chainState.DelayedWETHPermissionedGameProxyAddress},
Expand Down
2 changes: 1 addition & 1 deletion op-chain-ops/deployer/opcm/implementations.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ type DeployImplementationsInput struct {
ProtocolVersionsProxy common.Address
UseInterop bool // if true, deploy Interop implementations

SuperchainProxyAdmin common.Address
OpcmProxyOwner common.Address
StandardVersionsToml string // contents of 'standard-versions-mainnet.toml' or 'standard-versions-sepolia.toml' file
}

Expand Down
2 changes: 2 additions & 0 deletions op-chain-ops/deployer/opcm/opchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ type opcmDeployInput struct {
BlobBasefeeScalar uint32
L2ChainId *big.Int
StartingAnchorRoots []byte
SaltMixer string
}

// decodeOutputABIJSON defines an ABI for a fake method called "decodeOutput" that returns the
Expand Down Expand Up @@ -241,6 +242,7 @@ func DeployOPChainRaw(
BlobBasefeeScalar: input.BlobBaseFeeScalar,
L2ChainId: input.L2ChainId,
StartingAnchorRoots: input.StartingAnchorRoots(),
SaltMixer: input.SaltMixer,
})
if err != nil {
return out, fmt.Errorf("failed to pack deploy input: %w", err)
Expand Down
Loading

0 comments on commit 644dc2b

Please sign in to comment.