Skip to content

Commit

Permalink
op-deployer: Separate L1 and L2 contract artifacts (#12480)
Browse files Browse the repository at this point in the history
* op-deployer: Separate L1 and L2 contract artifacts

Adds support for specifying separate artifact URLs for L1 and L2. There are two new fields in the intent - `L1ContractsLocator` and `L2ContractsLocator` - which together replace the `ContractArtifactsURL` and `ContractsRelease` fields. Specifying a file or HTTPS URL for either field will automatically enable dev mode for the deployment. Specifying a `tag://` URL will use the standard deployment for L1 and L2. The default have been set to the following:

- L1: `op-contracts/v1.6.0`
- L2: `op-contracts/v1.7.0-beta.1+l2-contracts`

Fixes ethereum-optimism/platforms-team#337.

* fix test

* fix another test
  • Loading branch information
mslipper authored Oct 16, 2024
1 parent dfdc28b commit 3f65402
Show file tree
Hide file tree
Showing 20 changed files with 390 additions and 179 deletions.
69 changes: 38 additions & 31 deletions op-chain-ops/deployer/apply.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ import (
"fmt"
"strings"

"github.com/ethereum-optimism/optimism/op-chain-ops/foundry"

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

"github.com/ethereum-optimism/optimism/op-chain-ops/deployer/pipeline"
Expand Down Expand Up @@ -94,6 +92,16 @@ func Apply(ctx context.Context, cfg ApplyConfig) error {
signer := opcrypto.SignerFnFromBind(opcrypto.PrivateKeySignerFn(cfg.privateKeyECDSA, chainID))
deployer := crypto.PubkeyToAddress(cfg.privateKeyECDSA.PublicKey)

intent, err := pipeline.ReadIntent(cfg.Workdir)
if err != nil {
return fmt.Errorf("failed to read intent: %w", err)
}

st, err := pipeline.ReadState(cfg.Workdir)
if err != nil {
return fmt.Errorf("failed to read state: %w", err)
}

env := &pipeline.Env{
Workdir: cfg.Workdir,
L1Client: l1Client,
Expand All @@ -102,29 +110,10 @@ func Apply(ctx context.Context, cfg ApplyConfig) error {
Deployer: deployer,
}

intent, err := env.ReadIntent()
if err != nil {
return err
}

if err := intent.Check(); err != nil {
return fmt.Errorf("invalid intent: %w", err)
}

st, err := env.ReadState()
if err != nil {
return err
}

if err := ApplyPipeline(ctx, env, intent, st); err != nil {
return err
}

st.AppliedIntent = intent
if err := env.WriteState(st); err != nil {
return err
}

return nil
}

Expand All @@ -143,16 +132,31 @@ func ApplyPipeline(
env.Logger.Info("artifacts download progress", "current", curr, "total", total)
}

artifactsFS, cleanup, err := pipeline.DownloadArtifacts(ctx, intent.ContractArtifactsURL, progressor)
l1ArtifactsFS, cleanupL1, err := pipeline.DownloadArtifacts(ctx, intent.L1ContractsLocator, progressor)
if err != nil {
return fmt.Errorf("failed to download artifacts: %w", err)
return fmt.Errorf("failed to download L1 artifacts: %w", err)
}
defer func() {
if err := cleanup(); err != nil {
env.Logger.Warn("failed to clean up artifacts", "err", err)
if err := cleanupL1(); err != nil {
env.Logger.Warn("failed to clean up L1 artifacts", "err", err)
}
}()

l2ArtifactsFS, cleanupL2, err := pipeline.DownloadArtifacts(ctx, intent.L2ContractsLocator, progressor)
if err != nil {
return fmt.Errorf("failed to download L2 artifacts: %w", err)
}
defer func() {
if err := cleanupL2(); err != nil {
env.Logger.Warn("failed to clean up L2 artifacts", "err", err)
}
}()

bundle := pipeline.ArtifactsBundle{
L1: l1ArtifactsFS,
L2: l2ArtifactsFS,
}

pline := []pipelineStage{
{"init", pipeline.Init},
{"deploy-superchain", pipeline.DeploySuperchain},
Expand All @@ -163,25 +167,28 @@ func ApplyPipeline(
chainID := chain.ID
pline = append(pline, pipelineStage{
fmt.Sprintf("deploy-opchain-%s", chainID.Hex()),
func(ctx context.Context, env *pipeline.Env, artifactsFS foundry.StatDirFs, intent *state.Intent, st *state.State) error {
return pipeline.DeployOPChain(ctx, env, artifactsFS, intent, st, chainID)
func(ctx context.Context, env *pipeline.Env, bundle pipeline.ArtifactsBundle, intent *state.Intent, st *state.State) error {
return pipeline.DeployOPChain(ctx, env, bundle, intent, st, chainID)
},
}, pipelineStage{
fmt.Sprintf("generate-l2-genesis-%s", chainID.Hex()),
func(ctx context.Context, env *pipeline.Env, artifactsFS foundry.StatDirFs, intent *state.Intent, st *state.State) error {
return pipeline.GenerateL2Genesis(ctx, env, artifactsFS, intent, st, chainID)
func(ctx context.Context, env *pipeline.Env, bundle pipeline.ArtifactsBundle, intent *state.Intent, st *state.State) error {
return pipeline.GenerateL2Genesis(ctx, env, bundle, intent, st, chainID)
},
})
}

for _, stage := range pline {
if err := stage.apply(ctx, env, artifactsFS, intent, st); err != nil {
if err := stage.apply(ctx, env, bundle, intent, st); err != nil {
return fmt.Errorf("error in pipeline stage apply: %w", err)
}
if err := pipeline.WriteState(env.Workdir, st); err != nil {
return fmt.Errorf("failed to write state: %w", err)
}
}

st.AppliedIntent = intent
if err := env.WriteState(st); err != nil {
if err := pipeline.WriteState(env.Workdir, st); err != nil {
return fmt.Errorf("failed to write state: %w", err)
}

Expand Down
17 changes: 8 additions & 9 deletions op-chain-ops/deployer/bootstrap/bootstrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import (
"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"
Expand All @@ -30,7 +29,7 @@ type OPCMConfig struct {
L1RPCUrl string
PrivateKey string
Logger log.Logger
ArtifactsURL *state.ArtifactsURL
ArtifactsLocator *opcm.ArtifactsLocator
ContractsRelease string

privateKeyECDSA *ecdsa.PrivateKey
Expand All @@ -55,8 +54,8 @@ func (c *OPCMConfig) Check() error {
return fmt.Errorf("logger must be specified")
}

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

if c.ContractsRelease == "" {
Expand All @@ -74,8 +73,8 @@ func OPCMCLI(cliCtx *cli.Context) error {
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 {
artifactsLocator := new(opcm.ArtifactsLocator)
if err := artifactsLocator.UnmarshalText([]byte(artifactsURLStr)); err != nil {
return fmt.Errorf("failed to parse artifacts URL: %w", err)
}
contractsRelease := cliCtx.String(ContractsReleaseFlagName)
Expand All @@ -86,7 +85,7 @@ func OPCMCLI(cliCtx *cli.Context) error {
L1RPCUrl: l1RPCUrl,
PrivateKey: privateKey,
Logger: l,
ArtifactsURL: artifactsURL,
ArtifactsLocator: artifactsLocator,
ContractsRelease: contractsRelease,
})
}
Expand All @@ -101,7 +100,7 @@ func OPCM(ctx context.Context, cfg OPCMConfig) error {
lgr.Info("artifacts download progress", "current", curr, "total", total)
}

artifactsFS, cleanup, err := pipeline.DownloadArtifacts(ctx, cfg.ArtifactsURL, progressor)
artifactsFS, cleanup, err := pipeline.DownloadArtifacts(ctx, cfg.ArtifactsLocator, progressor)
if err != nil {
return fmt.Errorf("failed to download artifacts: %w", err)
}
Expand All @@ -126,7 +125,7 @@ func OPCM(ctx context.Context, cfg OPCMConfig) error {
if err != nil {
return fmt.Errorf("error getting superchain config: %w", err)
}
standardVersionsTOML, err := opcm.StandardVersionsFor(chainIDU64)
standardVersionsTOML, err := opcm.StandardL1VersionsDataFor(chainIDU64)
if err != nil {
return fmt.Errorf("error getting standard versions TOML: %w", err)
}
Expand Down
12 changes: 6 additions & 6 deletions op-chain-ops/deployer/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import (
"path"
"strings"

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

op_service "github.com/ethereum-optimism/optimism/op-service"

"github.com/ethereum-optimism/optimism/op-chain-ops/deployer/state"
Expand All @@ -15,8 +17,6 @@ import (
"github.com/urfave/cli/v2"
)

var V160ArtifactsURL = state.MustParseArtifactsURL("https://storage.googleapis.com/oplabs-contract-artifacts/artifacts-v1-ee07c78c3d8d4cd8f7a933c050f5afeebaa281b57b226cc6f092b19de2a8d61f.tar.gz")

type InitConfig struct {
L1ChainID uint64
Outdir string
Expand Down Expand Up @@ -75,10 +75,10 @@ func Init(cfg InitConfig) error {
}

intent := &state.Intent{
L1ChainID: cfg.L1ChainID,
FundDevAccounts: true,
ContractsRelease: "op-contracts/v1.6.0",
ContractArtifactsURL: V160ArtifactsURL,
L1ChainID: cfg.L1ChainID,
FundDevAccounts: true,
L1ContractsLocator: opcm.DefaultL1ContractsLocator,
L2ContractsLocator: opcm.DefaultL2ContractsLocator,
}

l1ChainIDBig := intent.L1ChainIDBig()
Expand Down
3 changes: 1 addition & 2 deletions op-chain-ops/deployer/inspect/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,7 @@ func GenesisCLI(cliCtx *cli.Context) error {
return err
}

env := &pipeline.Env{Workdir: cfg.Workdir}
globalState, err := env.ReadState()
globalState, err := pipeline.ReadState(cfg.Workdir)
if err != nil {
return fmt.Errorf("failed to read intent: %w", err)
}
Expand Down
3 changes: 1 addition & 2 deletions op-chain-ops/deployer/inspect/l1.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,7 @@ func L1CLI(cliCtx *cli.Context) error {
return err
}

env := &pipeline.Env{Workdir: cfg.Workdir}
globalState, err := env.ReadState()
globalState, err := pipeline.ReadState(cfg.Workdir)
if err != nil {
return fmt.Errorf("failed to read intent: %w", err)
}
Expand Down
3 changes: 1 addition & 2 deletions op-chain-ops/deployer/inspect/rollup.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@ func RollupCLI(cliCtx *cli.Context) error {
return err
}

env := &pipeline.Env{Workdir: cfg.Workdir}
globalState, err := env.ReadState()
globalState, err := pipeline.ReadState(cfg.Workdir)
if err != nil {
return fmt.Errorf("failed to read intent: %w", err)
}
Expand Down
14 changes: 10 additions & 4 deletions op-chain-ops/deployer/integration_test/apply_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import (
"testing"
"time"

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

op_e2e "github.com/ethereum-optimism/optimism/op-e2e"

"github.com/ethereum-optimism/optimism/op-chain-ops/deployer"
Expand Down Expand Up @@ -194,6 +196,9 @@ func makeIntent(
artifactsDir := path.Join(monorepoDir, "packages", "contracts-bedrock", "forge-artifacts")
artifactsURL, err := url.Parse(fmt.Sprintf("file://%s", artifactsDir))
require.NoError(t, err)
artifactsLocator := &opcm.ArtifactsLocator{
URL: artifactsURL,
}

addrFor := func(key devkeys.Key) common.Address {
addr, err := dk.Address(key)
Expand All @@ -208,9 +213,9 @@ func makeIntent(
ProtocolVersionsOwner: addrFor(devkeys.SuperchainDeployerKey.Key(l1ChainID)),
Guardian: addrFor(devkeys.SuperchainConfigGuardianKey.Key(l1ChainID)),
},
FundDevAccounts: true,
ContractArtifactsURL: (*state.ArtifactsURL)(artifactsURL),
ContractsRelease: "dev",
FundDevAccounts: true,
L1ContractsLocator: artifactsLocator,
L2ContractsLocator: artifactsLocator,
Chains: []*state.ChainIntent{
{
ID: l2ChainID.Bytes32(),
Expand Down Expand Up @@ -353,7 +358,8 @@ func TestApplyExistingOPCM(t *testing.T) {
}

intent, st := makeIntent(t, l1ChainID, dk, l2ChainID)
intent.ContractsRelease = "op-contracts/v1.6.0"
intent.L1ContractsLocator = opcm.DefaultL1ContractsLocator
intent.L2ContractsLocator = opcm.DefaultL2ContractsLocator

require.NoError(t, deployer.ApplyPipeline(
ctx,
Expand Down
78 changes: 78 additions & 0 deletions op-chain-ops/deployer/opcm/artifacts_locator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package opcm

import (
"fmt"
"net/url"
"strings"
)

type schemeUnmarshaler func(string) (*ArtifactsLocator, error)

var schemeUnmarshalerDispatch = map[string]schemeUnmarshaler{
"tag": unmarshalTag,
"file": unmarshalURL,
"https": unmarshalURL,
}

type ArtifactsLocator struct {
URL *url.URL
Tag string
}

func (a *ArtifactsLocator) UnmarshalText(text []byte) error {
str := string(text)

for scheme, unmarshaler := range schemeUnmarshalerDispatch {
if !strings.HasPrefix(str, scheme+"://") {
continue
}

loc, err := unmarshaler(str)
if err != nil {
return err
}

*a = *loc
return nil
}

return fmt.Errorf("unsupported scheme")
}

func (a *ArtifactsLocator) MarshalText() ([]byte, error) {
if a.URL != nil {
return []byte(a.URL.String()), nil
}

if a.Tag != "" {
return []byte(a.Tag), nil
}

return nil, fmt.Errorf("no URL, path or tag set")
}

func (a *ArtifactsLocator) IsTag() bool {
return a.Tag != ""
}

func unmarshalTag(tag string) (*ArtifactsLocator, error) {
tag = strings.TrimPrefix(tag, "tag://")
if !strings.HasPrefix(tag, "op-contracts/") {
return nil, fmt.Errorf("invalid tag: %s", tag)
}

if _, err := StandardArtifactsURLForTag(tag); err != nil {
return nil, err
}

return &ArtifactsLocator{Tag: tag}, nil
}

func unmarshalURL(text string) (*ArtifactsLocator, error) {
u, err := url.Parse(text)
if err != nil {
return nil, err
}

return &ArtifactsLocator{URL: u}, nil
}
Loading

0 comments on commit 3f65402

Please sign in to comment.