Commit 3f65402a authored by Matthew Slipper's avatar Matthew Slipper Committed by GitHub

op-deployer: Separate L1 and L2 contract artifacts (#12480)

* 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 https://github.com/ethereum-optimism/platforms-team/issues/337.

* fix test

* fix another test
parent dfdc28be
...@@ -6,8 +6,6 @@ import ( ...@@ -6,8 +6,6 @@ import (
"fmt" "fmt"
"strings" "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/state"
"github.com/ethereum-optimism/optimism/op-chain-ops/deployer/pipeline" "github.com/ethereum-optimism/optimism/op-chain-ops/deployer/pipeline"
...@@ -94,6 +92,16 @@ func Apply(ctx context.Context, cfg ApplyConfig) error { ...@@ -94,6 +92,16 @@ func Apply(ctx context.Context, cfg ApplyConfig) error {
signer := opcrypto.SignerFnFromBind(opcrypto.PrivateKeySignerFn(cfg.privateKeyECDSA, chainID)) signer := opcrypto.SignerFnFromBind(opcrypto.PrivateKeySignerFn(cfg.privateKeyECDSA, chainID))
deployer := crypto.PubkeyToAddress(cfg.privateKeyECDSA.PublicKey) 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{ env := &pipeline.Env{
Workdir: cfg.Workdir, Workdir: cfg.Workdir,
L1Client: l1Client, L1Client: l1Client,
...@@ -102,29 +110,10 @@ func Apply(ctx context.Context, cfg ApplyConfig) error { ...@@ -102,29 +110,10 @@ func Apply(ctx context.Context, cfg ApplyConfig) error {
Deployer: deployer, 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 { if err := ApplyPipeline(ctx, env, intent, st); err != nil {
return err return err
} }
st.AppliedIntent = intent
if err := env.WriteState(st); err != nil {
return err
}
return nil return nil
} }
...@@ -143,16 +132,31 @@ func ApplyPipeline( ...@@ -143,16 +132,31 @@ func ApplyPipeline(
env.Logger.Info("artifacts download progress", "current", curr, "total", total) 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 { if err != nil {
return fmt.Errorf("failed to download artifacts: %w", err) return fmt.Errorf("failed to download L1 artifacts: %w", err)
} }
defer func() { defer func() {
if err := cleanup(); err != nil { if err := cleanupL1(); err != nil {
env.Logger.Warn("failed to clean up artifacts", "err", err) 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{ pline := []pipelineStage{
{"init", pipeline.Init}, {"init", pipeline.Init},
{"deploy-superchain", pipeline.DeploySuperchain}, {"deploy-superchain", pipeline.DeploySuperchain},
...@@ -163,25 +167,28 @@ func ApplyPipeline( ...@@ -163,25 +167,28 @@ func ApplyPipeline(
chainID := chain.ID chainID := chain.ID
pline = append(pline, pipelineStage{ pline = append(pline, pipelineStage{
fmt.Sprintf("deploy-opchain-%s", chainID.Hex()), fmt.Sprintf("deploy-opchain-%s", chainID.Hex()),
func(ctx context.Context, env *pipeline.Env, artifactsFS foundry.StatDirFs, intent *state.Intent, st *state.State) error { func(ctx context.Context, env *pipeline.Env, bundle pipeline.ArtifactsBundle, intent *state.Intent, st *state.State) error {
return pipeline.DeployOPChain(ctx, env, artifactsFS, intent, st, chainID) return pipeline.DeployOPChain(ctx, env, bundle, intent, st, chainID)
}, },
}, pipelineStage{ }, pipelineStage{
fmt.Sprintf("generate-l2-genesis-%s", chainID.Hex()), 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 { func(ctx context.Context, env *pipeline.Env, bundle pipeline.ArtifactsBundle, intent *state.Intent, st *state.State) error {
return pipeline.GenerateL2Genesis(ctx, env, artifactsFS, intent, st, chainID) return pipeline.GenerateL2Genesis(ctx, env, bundle, intent, st, chainID)
}, },
}) })
} }
for _, stage := range pline { 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) 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 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) return fmt.Errorf("failed to write state: %w", err)
} }
......
...@@ -11,7 +11,6 @@ import ( ...@@ -11,7 +11,6 @@ import (
"github.com/ethereum-optimism/optimism/op-chain-ops/deployer" "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/opcm"
"github.com/ethereum-optimism/optimism/op-chain-ops/deployer/pipeline" "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" "github.com/ethereum-optimism/optimism/op-chain-ops/script"
opcrypto "github.com/ethereum-optimism/optimism/op-service/crypto" opcrypto "github.com/ethereum-optimism/optimism/op-service/crypto"
"github.com/ethereum-optimism/optimism/op-service/ctxinterrupt" "github.com/ethereum-optimism/optimism/op-service/ctxinterrupt"
...@@ -30,7 +29,7 @@ type OPCMConfig struct { ...@@ -30,7 +29,7 @@ type OPCMConfig struct {
L1RPCUrl string L1RPCUrl string
PrivateKey string PrivateKey string
Logger log.Logger Logger log.Logger
ArtifactsURL *state.ArtifactsURL ArtifactsLocator *opcm.ArtifactsLocator
ContractsRelease string ContractsRelease string
privateKeyECDSA *ecdsa.PrivateKey privateKeyECDSA *ecdsa.PrivateKey
...@@ -55,8 +54,8 @@ func (c *OPCMConfig) Check() error { ...@@ -55,8 +54,8 @@ func (c *OPCMConfig) Check() error {
return fmt.Errorf("logger must be specified") return fmt.Errorf("logger must be specified")
} }
if c.ArtifactsURL == nil { if c.ArtifactsLocator == nil {
return fmt.Errorf("artifacts URL must be specified") return fmt.Errorf("artifacts locator must be specified")
} }
if c.ContractsRelease == "" { if c.ContractsRelease == "" {
...@@ -74,8 +73,8 @@ func OPCMCLI(cliCtx *cli.Context) error { ...@@ -74,8 +73,8 @@ func OPCMCLI(cliCtx *cli.Context) error {
l1RPCUrl := cliCtx.String(deployer.L1RPCURLFlagName) l1RPCUrl := cliCtx.String(deployer.L1RPCURLFlagName)
privateKey := cliCtx.String(deployer.PrivateKeyFlagName) privateKey := cliCtx.String(deployer.PrivateKeyFlagName)
artifactsURLStr := cliCtx.String(ArtifactsURLFlagName) artifactsURLStr := cliCtx.String(ArtifactsURLFlagName)
artifactsURL := new(state.ArtifactsURL) artifactsLocator := new(opcm.ArtifactsLocator)
if err := artifactsURL.UnmarshalText([]byte(artifactsURLStr)); err != nil { if err := artifactsLocator.UnmarshalText([]byte(artifactsURLStr)); err != nil {
return fmt.Errorf("failed to parse artifacts URL: %w", err) return fmt.Errorf("failed to parse artifacts URL: %w", err)
} }
contractsRelease := cliCtx.String(ContractsReleaseFlagName) contractsRelease := cliCtx.String(ContractsReleaseFlagName)
...@@ -86,7 +85,7 @@ func OPCMCLI(cliCtx *cli.Context) error { ...@@ -86,7 +85,7 @@ func OPCMCLI(cliCtx *cli.Context) error {
L1RPCUrl: l1RPCUrl, L1RPCUrl: l1RPCUrl,
PrivateKey: privateKey, PrivateKey: privateKey,
Logger: l, Logger: l,
ArtifactsURL: artifactsURL, ArtifactsLocator: artifactsLocator,
ContractsRelease: contractsRelease, ContractsRelease: contractsRelease,
}) })
} }
...@@ -101,7 +100,7 @@ func OPCM(ctx context.Context, cfg OPCMConfig) error { ...@@ -101,7 +100,7 @@ func OPCM(ctx context.Context, cfg OPCMConfig) error {
lgr.Info("artifacts download progress", "current", curr, "total", total) 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 { if err != nil {
return fmt.Errorf("failed to download artifacts: %w", err) return fmt.Errorf("failed to download artifacts: %w", err)
} }
...@@ -126,7 +125,7 @@ func OPCM(ctx context.Context, cfg OPCMConfig) error { ...@@ -126,7 +125,7 @@ func OPCM(ctx context.Context, cfg OPCMConfig) error {
if err != nil { if err != nil {
return fmt.Errorf("error getting superchain config: %w", err) return fmt.Errorf("error getting superchain config: %w", err)
} }
standardVersionsTOML, err := opcm.StandardVersionsFor(chainIDU64) standardVersionsTOML, err := opcm.StandardL1VersionsDataFor(chainIDU64)
if err != nil { if err != nil {
return fmt.Errorf("error getting standard versions TOML: %w", err) return fmt.Errorf("error getting standard versions TOML: %w", err)
} }
......
...@@ -7,6 +7,8 @@ import ( ...@@ -7,6 +7,8 @@ import (
"path" "path"
"strings" "strings"
"github.com/ethereum-optimism/optimism/op-chain-ops/deployer/opcm"
op_service "github.com/ethereum-optimism/optimism/op-service" op_service "github.com/ethereum-optimism/optimism/op-service"
"github.com/ethereum-optimism/optimism/op-chain-ops/deployer/state" "github.com/ethereum-optimism/optimism/op-chain-ops/deployer/state"
...@@ -15,8 +17,6 @@ import ( ...@@ -15,8 +17,6 @@ import (
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
) )
var V160ArtifactsURL = state.MustParseArtifactsURL("https://storage.googleapis.com/oplabs-contract-artifacts/artifacts-v1-ee07c78c3d8d4cd8f7a933c050f5afeebaa281b57b226cc6f092b19de2a8d61f.tar.gz")
type InitConfig struct { type InitConfig struct {
L1ChainID uint64 L1ChainID uint64
Outdir string Outdir string
...@@ -75,10 +75,10 @@ func Init(cfg InitConfig) error { ...@@ -75,10 +75,10 @@ func Init(cfg InitConfig) error {
} }
intent := &state.Intent{ intent := &state.Intent{
L1ChainID: cfg.L1ChainID, L1ChainID: cfg.L1ChainID,
FundDevAccounts: true, FundDevAccounts: true,
ContractsRelease: "op-contracts/v1.6.0", L1ContractsLocator: opcm.DefaultL1ContractsLocator,
ContractArtifactsURL: V160ArtifactsURL, L2ContractsLocator: opcm.DefaultL2ContractsLocator,
} }
l1ChainIDBig := intent.L1ChainIDBig() l1ChainIDBig := intent.L1ChainIDBig()
......
...@@ -21,8 +21,7 @@ func GenesisCLI(cliCtx *cli.Context) error { ...@@ -21,8 +21,7 @@ func GenesisCLI(cliCtx *cli.Context) error {
return err return err
} }
env := &pipeline.Env{Workdir: cfg.Workdir} globalState, err := pipeline.ReadState(cfg.Workdir)
globalState, err := env.ReadState()
if err != nil { if err != nil {
return fmt.Errorf("failed to read intent: %w", err) return fmt.Errorf("failed to read intent: %w", err)
} }
......
...@@ -62,8 +62,7 @@ func L1CLI(cliCtx *cli.Context) error { ...@@ -62,8 +62,7 @@ func L1CLI(cliCtx *cli.Context) error {
return err return err
} }
env := &pipeline.Env{Workdir: cfg.Workdir} globalState, err := pipeline.ReadState(cfg.Workdir)
globalState, err := env.ReadState()
if err != nil { if err != nil {
return fmt.Errorf("failed to read intent: %w", err) return fmt.Errorf("failed to read intent: %w", err)
} }
......
...@@ -15,8 +15,7 @@ func RollupCLI(cliCtx *cli.Context) error { ...@@ -15,8 +15,7 @@ func RollupCLI(cliCtx *cli.Context) error {
return err return err
} }
env := &pipeline.Env{Workdir: cfg.Workdir} globalState, err := pipeline.ReadState(cfg.Workdir)
globalState, err := env.ReadState()
if err != nil { if err != nil {
return fmt.Errorf("failed to read intent: %w", err) return fmt.Errorf("failed to read intent: %w", err)
} }
......
...@@ -13,6 +13,8 @@ import ( ...@@ -13,6 +13,8 @@ import (
"testing" "testing"
"time" "time"
"github.com/ethereum-optimism/optimism/op-chain-ops/deployer/opcm"
op_e2e "github.com/ethereum-optimism/optimism/op-e2e" op_e2e "github.com/ethereum-optimism/optimism/op-e2e"
"github.com/ethereum-optimism/optimism/op-chain-ops/deployer" "github.com/ethereum-optimism/optimism/op-chain-ops/deployer"
...@@ -194,6 +196,9 @@ func makeIntent( ...@@ -194,6 +196,9 @@ func makeIntent(
artifactsDir := path.Join(monorepoDir, "packages", "contracts-bedrock", "forge-artifacts") artifactsDir := path.Join(monorepoDir, "packages", "contracts-bedrock", "forge-artifacts")
artifactsURL, err := url.Parse(fmt.Sprintf("file://%s", artifactsDir)) artifactsURL, err := url.Parse(fmt.Sprintf("file://%s", artifactsDir))
require.NoError(t, err) require.NoError(t, err)
artifactsLocator := &opcm.ArtifactsLocator{
URL: artifactsURL,
}
addrFor := func(key devkeys.Key) common.Address { addrFor := func(key devkeys.Key) common.Address {
addr, err := dk.Address(key) addr, err := dk.Address(key)
...@@ -208,9 +213,9 @@ func makeIntent( ...@@ -208,9 +213,9 @@ func makeIntent(
ProtocolVersionsOwner: addrFor(devkeys.SuperchainDeployerKey.Key(l1ChainID)), ProtocolVersionsOwner: addrFor(devkeys.SuperchainDeployerKey.Key(l1ChainID)),
Guardian: addrFor(devkeys.SuperchainConfigGuardianKey.Key(l1ChainID)), Guardian: addrFor(devkeys.SuperchainConfigGuardianKey.Key(l1ChainID)),
}, },
FundDevAccounts: true, FundDevAccounts: true,
ContractArtifactsURL: (*state.ArtifactsURL)(artifactsURL), L1ContractsLocator: artifactsLocator,
ContractsRelease: "dev", L2ContractsLocator: artifactsLocator,
Chains: []*state.ChainIntent{ Chains: []*state.ChainIntent{
{ {
ID: l2ChainID.Bytes32(), ID: l2ChainID.Bytes32(),
...@@ -353,7 +358,8 @@ func TestApplyExistingOPCM(t *testing.T) { ...@@ -353,7 +358,8 @@ func TestApplyExistingOPCM(t *testing.T) {
} }
intent, st := makeIntent(t, l1ChainID, dk, l2ChainID) 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( require.NoError(t, deployer.ApplyPipeline(
ctx, ctx,
......
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
}
package opcm
import (
"net/url"
"testing"
"github.com/stretchr/testify/require"
)
func TestArtifactsLocator_Marshaling(t *testing.T) {
tests := []struct {
name string
in string
out *ArtifactsLocator
err bool
}{
{
name: "valid tag",
in: "tag://op-contracts/v1.6.0",
out: &ArtifactsLocator{
Tag: "op-contracts/v1.6.0",
},
err: false,
},
{
name: "well-formed but nonexistent tag",
in: "tag://op-contracts/v1.5.0",
out: nil,
err: true,
},
{
name: "mal-formed tag",
in: "tag://honk",
out: nil,
err: true,
},
{
name: "valid HTTPS URL",
in: "https://example.com",
out: &ArtifactsLocator{
URL: parseUrl(t, "https://example.com"),
},
err: false,
},
{
name: "valid file URL",
in: "file:///tmp/artifacts",
out: &ArtifactsLocator{
URL: parseUrl(t, "file:///tmp/artifacts"),
},
err: false,
},
{
name: "empty",
in: "",
out: nil,
err: true,
},
{
name: "no scheme",
in: "example.com",
out: nil,
err: true,
},
{
name: "unsupported scheme",
in: "http://example.com",
out: nil,
err: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var a ArtifactsLocator
err := a.UnmarshalText([]byte(tt.in))
if tt.err {
require.Error(t, err)
return
}
require.NoError(t, err)
require.Equal(t, tt.out, &a)
})
}
}
func parseUrl(t *testing.T, u string) *url.URL {
parsed, err := url.Parse(u)
require.NoError(t, err)
return parsed
}
...@@ -3,6 +3,7 @@ package opcm ...@@ -3,6 +3,7 @@ package opcm
import ( import (
"embed" "embed"
"fmt" "fmt"
"net/url"
"github.com/BurntSushi/toml" "github.com/BurntSushi/toml"
...@@ -16,15 +17,23 @@ var StandardVersionsMainnetData string ...@@ -16,15 +17,23 @@ var StandardVersionsMainnetData string
//go:embed standard-versions-sepolia.toml //go:embed standard-versions-sepolia.toml
var StandardVersionsSepoliaData string var StandardVersionsSepoliaData string
var StandardVersionsSepolia StandardVersions var StandardL1VersionsSepolia StandardL1Versions
var StandardVersionsMainnet StandardVersions var StandardL1VersionsMainnet StandardL1Versions
type StandardVersions struct { var DefaultL1ContractsLocator = &ArtifactsLocator{
Releases map[string]StandardVersionsReleases `toml:"releases"` Tag: "op-contracts/v1.6.0",
} }
type StandardVersionsReleases struct { var DefaultL2ContractsLocator = &ArtifactsLocator{
Tag: "op-contracts/v1.7.0-beta.1+l2-contracts",
}
type StandardL1Versions struct {
Releases map[string]StandardL1VersionsReleases `toml:"releases"`
}
type StandardL1VersionsReleases struct {
OptimismPortal StandardVersionRelease `toml:"optimism_portal"` OptimismPortal StandardVersionRelease `toml:"optimism_portal"`
SystemConfig StandardVersionRelease `toml:"system_config"` SystemConfig StandardVersionRelease `toml:"system_config"`
AnchorStateRegistry StandardVersionRelease `toml:"anchor_state_registry"` AnchorStateRegistry StandardVersionRelease `toml:"anchor_state_registry"`
...@@ -48,7 +57,7 @@ type StandardVersionRelease struct { ...@@ -48,7 +57,7 @@ type StandardVersionRelease struct {
var _ embed.FS var _ embed.FS
func StandardVersionsFor(chainID uint64) (string, error) { func StandardL1VersionsDataFor(chainID uint64) (string, error) {
switch chainID { switch chainID {
case 1: case 1:
return StandardVersionsMainnetData, nil return StandardVersionsMainnetData, nil
...@@ -59,6 +68,17 @@ func StandardVersionsFor(chainID uint64) (string, error) { ...@@ -59,6 +68,17 @@ func StandardVersionsFor(chainID uint64) (string, error) {
} }
} }
func StandardL1VersionsFor(chainID uint64) (StandardL1Versions, error) {
switch chainID {
case 1:
return StandardL1VersionsMainnet, nil
case 11155111:
return StandardL1VersionsSepolia, nil
default:
return StandardL1Versions{}, fmt.Errorf("unsupported chain ID: %d", chainID)
}
}
func SuperchainFor(chainID uint64) (*superchain.Superchain, error) { func SuperchainFor(chainID uint64) (*superchain.Superchain, error) {
switch chainID { switch chainID {
case 1: case 1:
...@@ -93,14 +113,29 @@ func ManagerOwnerAddrFor(chainID uint64) (common.Address, error) { ...@@ -93,14 +113,29 @@ func ManagerOwnerAddrFor(chainID uint64) (common.Address, error) {
} }
} }
func StandardArtifactsURLForTag(tag string) (*url.URL, error) {
switch tag {
case "op-contracts/v1.6.0":
return url.Parse(standardArtifactsURL("ee07c78c3d8d4cd8f7a933c050f5afeebaa281b57b226cc6f092b19de2a8d61f"))
case "op-contracts/v1.7.0-beta.1+l2-contracts":
return url.Parse(standardArtifactsURL("40ca65dc738f0f5fbb05ec9ec953d9be94bc1c02a09fd871a36b152f6b36c1fe"))
default:
return nil, fmt.Errorf("unsupported tag: %s", tag)
}
}
func standardArtifactsURL(checksum string) string {
return fmt.Sprintf("https://storage.googleapis.com/oplabs-contract-artifacts/artifacts-v1-%s.tar.gz", checksum)
}
func init() { func init() {
StandardVersionsMainnet = StandardVersions{} StandardL1VersionsMainnet = StandardL1Versions{}
if err := toml.Unmarshal([]byte(StandardVersionsMainnetData), &StandardVersionsMainnet); err != nil { if err := toml.Unmarshal([]byte(StandardVersionsMainnetData), &StandardL1VersionsMainnet); err != nil {
panic(err) panic(err)
} }
StandardVersionsSepolia = StandardVersions{} StandardL1VersionsSepolia = StandardL1Versions{}
if err := toml.Unmarshal([]byte(StandardVersionsSepoliaData), &StandardVersionsSepolia); err != nil { if err := toml.Unmarshal([]byte(StandardVersionsSepoliaData), &StandardL1VersionsSepolia); err != nil {
panic(err) panic(err)
} }
} }
...@@ -15,7 +15,8 @@ import ( ...@@ -15,7 +15,8 @@ import (
"strings" "strings"
"time" "time"
"github.com/ethereum-optimism/optimism/op-chain-ops/deployer/state" "github.com/ethereum-optimism/optimism/op-chain-ops/deployer/opcm"
"github.com/ethereum-optimism/optimism/op-chain-ops/foundry" "github.com/ethereum-optimism/optimism/op-chain-ops/foundry"
) )
...@@ -27,10 +28,21 @@ type CleanupFunc func() error ...@@ -27,10 +28,21 @@ type CleanupFunc func() error
var noopCleanup = func() error { return nil } var noopCleanup = func() error { return nil }
func DownloadArtifacts(ctx context.Context, artifactsURL *state.ArtifactsURL, progress DownloadProgressor) (foundry.StatDirFs, CleanupFunc, error) { func DownloadArtifacts(ctx context.Context, loc *opcm.ArtifactsLocator, progress DownloadProgressor) (foundry.StatDirFs, CleanupFunc, error) {
switch artifactsURL.Scheme { var u *url.URL
var err error
if loc.IsTag() {
u, err = opcm.StandardArtifactsURLForTag(loc.Tag)
if err != nil {
return nil, nil, fmt.Errorf("failed to get standard artifacts URL for tag %s: %w", loc.Tag, err)
}
} else {
u = loc.URL
}
switch u.Scheme {
case "http", "https": case "http", "https":
req, err := http.NewRequestWithContext(ctx, http.MethodGet, (*url.URL)(artifactsURL).String(), nil) req, err := http.NewRequestWithContext(ctx, http.MethodGet, u.String(), nil)
if err != nil { if err != nil {
return nil, nil, fmt.Errorf("failed to create request: %w", err) return nil, nil, fmt.Errorf("failed to create request: %w", err)
} }
...@@ -73,7 +85,7 @@ func DownloadArtifacts(ctx context.Context, artifactsURL *state.ArtifactsURL, pr ...@@ -73,7 +85,7 @@ func DownloadArtifacts(ctx context.Context, artifactsURL *state.ArtifactsURL, pr
} }
return fs.(foundry.StatDirFs), cleanup, nil return fs.(foundry.StatDirFs), cleanup, nil
case "file": case "file":
fs := os.DirFS(artifactsURL.Path) fs := os.DirFS(u.Path)
return fs.(foundry.StatDirFs), noopCleanup, nil return fs.(foundry.StatDirFs), noopCleanup, nil
default: default:
return nil, nil, ErrUnsupportedArtifactsScheme return nil, nil, ErrUnsupportedArtifactsScheme
......
...@@ -9,7 +9,8 @@ import ( ...@@ -9,7 +9,8 @@ import (
"os" "os"
"testing" "testing"
"github.com/ethereum-optimism/optimism/op-chain-ops/deployer/state" "github.com/ethereum-optimism/optimism/op-chain-ops/deployer/opcm"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
...@@ -28,8 +29,11 @@ func TestDownloadArtifacts(t *testing.T) { ...@@ -28,8 +29,11 @@ func TestDownloadArtifacts(t *testing.T) {
ctx := context.Background() ctx := context.Background()
artifactsURL, err := url.Parse(ts.URL) artifactsURL, err := url.Parse(ts.URL)
require.NoError(t, err) require.NoError(t, err)
loc := &opcm.ArtifactsLocator{
URL: artifactsURL,
}
fs, cleanup, err := DownloadArtifacts(ctx, (*state.ArtifactsURL)(artifactsURL), nil) fs, cleanup, err := DownloadArtifacts(ctx, loc, nil)
require.NoError(t, err) require.NoError(t, err)
require.NotNil(t, fs) require.NotNil(t, fs)
defer func() { defer func() {
......
...@@ -23,8 +23,8 @@ type Env struct { ...@@ -23,8 +23,8 @@ type Env struct {
Logger log.Logger Logger log.Logger
} }
func (e *Env) ReadIntent() (*state.Intent, error) { func ReadIntent(workdir string) (*state.Intent, error) {
intentPath := path.Join(e.Workdir, "intent.toml") intentPath := path.Join(workdir, "intent.toml")
intent, err := jsonutil.LoadTOML[state.Intent](intentPath) intent, err := jsonutil.LoadTOML[state.Intent](intentPath)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to read intent file: %w", err) return nil, fmt.Errorf("failed to read intent file: %w", err)
...@@ -32,8 +32,8 @@ func (e *Env) ReadIntent() (*state.Intent, error) { ...@@ -32,8 +32,8 @@ func (e *Env) ReadIntent() (*state.Intent, error) {
return intent, nil return intent, nil
} }
func (e *Env) ReadState() (*state.State, error) { func ReadState(workdir string) (*state.State, error) {
statePath := path.Join(e.Workdir, "state.json") statePath := path.Join(workdir, "state.json")
st, err := jsonutil.LoadJSON[state.State](statePath) st, err := jsonutil.LoadJSON[state.State](statePath)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to read state file: %w", err) return nil, fmt.Errorf("failed to read state file: %w", err)
...@@ -41,9 +41,14 @@ func (e *Env) ReadState() (*state.State, error) { ...@@ -41,9 +41,14 @@ func (e *Env) ReadState() (*state.State, error) {
return st, nil return st, nil
} }
func (e *Env) WriteState(st *state.State) error { func WriteState(workdir string, st *state.State) error {
statePath := path.Join(e.Workdir, "state.json") statePath := path.Join(workdir, "state.json")
return st.WriteToFile(statePath) return st.WriteToFile(statePath)
} }
type Stage func(ctx context.Context, env *Env, artifactsFS foundry.StatDirFs, intent *state.Intent, state2 *state.State) error type ArtifactsBundle struct {
L1 foundry.StatDirFs
L2 foundry.StatDirFs
}
type Stage func(ctx context.Context, env *Env, bundle ArtifactsBundle, intent *state.Intent, st *state.State) error
...@@ -4,7 +4,6 @@ import ( ...@@ -4,7 +4,6 @@ import (
"context" "context"
"fmt" "fmt"
"math/big" "math/big"
"strings"
"github.com/ethereum-optimism/optimism/op-chain-ops/deployer/opcm" "github.com/ethereum-optimism/optimism/op-chain-ops/deployer/opcm"
"github.com/ethereum-optimism/optimism/op-chain-ops/deployer/state" "github.com/ethereum-optimism/optimism/op-chain-ops/deployer/state"
...@@ -12,7 +11,7 @@ import ( ...@@ -12,7 +11,7 @@ import (
"github.com/ethereum-optimism/optimism/op-chain-ops/script" "github.com/ethereum-optimism/optimism/op-chain-ops/script"
) )
func DeployImplementations(ctx context.Context, env *Env, artifactsFS foundry.StatDirFs, intent *state.Intent, st *state.State) error { func DeployImplementations(ctx context.Context, env *Env, bundle ArtifactsBundle, intent *state.Intent, st *state.State) error {
lgr := env.Logger.New("stage", "deploy-implementations") lgr := env.Logger.New("stage", "deploy-implementations")
if !shouldDeployImplementations(intent, st) { if !shouldDeployImplementations(intent, st) {
...@@ -23,12 +22,16 @@ func DeployImplementations(ctx context.Context, env *Env, artifactsFS foundry.St ...@@ -23,12 +22,16 @@ func DeployImplementations(ctx context.Context, env *Env, artifactsFS foundry.St
lgr.Info("deploying implementations") lgr.Info("deploying implementations")
var standardVersionsTOML string var standardVersionsTOML string
var contractsRelease string
var err error var err error
if strings.HasPrefix(intent.ContractsRelease, "op-contracts") { if intent.L1ContractsLocator.IsTag() {
standardVersionsTOML, err = opcm.StandardVersionsFor(intent.L1ChainID) standardVersionsTOML, err = opcm.StandardL1VersionsDataFor(intent.L1ChainID)
if err != nil { if err != nil {
return fmt.Errorf("error getting standard versions TOML: %w", err) return fmt.Errorf("error getting standard versions TOML: %w", err)
} }
contractsRelease = intent.L1ContractsLocator.Tag
} else {
contractsRelease = "dev"
} }
var dump *foundry.ForgeAllocs var dump *foundry.ForgeAllocs
...@@ -38,7 +41,7 @@ func DeployImplementations(ctx context.Context, env *Env, artifactsFS foundry.St ...@@ -38,7 +41,7 @@ func DeployImplementations(ctx context.Context, env *Env, artifactsFS foundry.St
CallScriptBroadcastOpts{ CallScriptBroadcastOpts{
L1ChainID: big.NewInt(int64(intent.L1ChainID)), L1ChainID: big.NewInt(int64(intent.L1ChainID)),
Logger: lgr, Logger: lgr,
ArtifactsFS: artifactsFS, ArtifactsFS: bundle.L1,
Deployer: env.Deployer, Deployer: env.Deployer,
Signer: env.Signer, Signer: env.Signer,
Client: env.L1Client, Client: env.L1Client,
...@@ -56,7 +59,7 @@ func DeployImplementations(ctx context.Context, env *Env, artifactsFS foundry.St ...@@ -56,7 +59,7 @@ func DeployImplementations(ctx context.Context, env *Env, artifactsFS foundry.St
ProofMaturityDelaySeconds: big.NewInt(604800), ProofMaturityDelaySeconds: big.NewInt(604800),
DisputeGameFinalityDelaySeconds: big.NewInt(302400), DisputeGameFinalityDelaySeconds: big.NewInt(302400),
MipsVersion: big.NewInt(1), MipsVersion: big.NewInt(1),
Release: intent.ContractsRelease, Release: contractsRelease,
SuperchainConfigProxy: st.SuperchainDeployment.SuperchainConfigProxyAddress, SuperchainConfigProxy: st.SuperchainDeployment.SuperchainConfigProxyAddress,
ProtocolVersionsProxy: st.SuperchainDeployment.ProtocolVersionsProxyAddress, ProtocolVersionsProxy: st.SuperchainDeployment.ProtocolVersionsProxyAddress,
OpcmProxyOwner: st.SuperchainDeployment.ProxyAdminAddress, OpcmProxyOwner: st.SuperchainDeployment.ProxyAdminAddress,
...@@ -93,9 +96,6 @@ func DeployImplementations(ctx context.Context, env *Env, artifactsFS foundry.St ...@@ -93,9 +96,6 @@ func DeployImplementations(ctx context.Context, env *Env, artifactsFS foundry.St
DisputeGameFactoryImplAddress: dio.DisputeGameFactoryImpl, DisputeGameFactoryImplAddress: dio.DisputeGameFactoryImpl,
StateDump: dump, StateDump: dump,
} }
if err := env.WriteState(st); err != nil {
return err
}
return nil return nil
} }
......
...@@ -4,11 +4,8 @@ import ( ...@@ -4,11 +4,8 @@ import (
"context" "context"
"crypto/rand" "crypto/rand"
"fmt" "fmt"
"strings"
"github.com/ethereum-optimism/optimism/op-chain-ops/deployer/opcm" "github.com/ethereum-optimism/optimism/op-chain-ops/deployer/opcm"
"github.com/ethereum-optimism/optimism/op-chain-ops/foundry"
"github.com/ethereum-optimism/optimism/op-chain-ops/script" "github.com/ethereum-optimism/optimism/op-chain-ops/script"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
...@@ -20,7 +17,7 @@ func IsSupportedStateVersion(version int) bool { ...@@ -20,7 +17,7 @@ func IsSupportedStateVersion(version int) bool {
return version == 1 return version == 1
} }
func Init(ctx context.Context, env *Env, _ foundry.StatDirFs, intent *state.Intent, st *state.State) error { func Init(ctx context.Context, env *Env, _ ArtifactsBundle, intent *state.Intent, st *state.State) error {
lgr := env.Logger.New("stage", "init") lgr := env.Logger.New("stage", "init")
lgr.Info("initializing pipeline") lgr.Info("initializing pipeline")
...@@ -36,7 +33,7 @@ func Init(ctx context.Context, env *Env, _ foundry.StatDirFs, intent *state.Inte ...@@ -36,7 +33,7 @@ func Init(ctx context.Context, env *Env, _ foundry.StatDirFs, intent *state.Inte
} }
} }
if strings.HasPrefix(intent.ContractsRelease, "op-contracts") { if intent.L1ContractsLocator.IsTag() {
superCfg, err := opcm.SuperchainFor(intent.L1ChainID) superCfg, err := opcm.SuperchainFor(intent.L1ChainID)
if err != nil { if err != nil {
return fmt.Errorf("error getting superchain config: %w", err) return fmt.Errorf("error getting superchain config: %w", err)
...@@ -64,29 +61,13 @@ func Init(ctx context.Context, env *Env, _ foundry.StatDirFs, intent *state.Inte ...@@ -64,29 +61,13 @@ func Init(ctx context.Context, env *Env, _ foundry.StatDirFs, intent *state.Inte
} }
} }
// If the state has never been applied, we don't need to perform
// any additional checks.
if st.AppliedIntent == nil {
return nil
}
// If the state has been applied, we need to check if any immutable
// fields have changed.
if st.AppliedIntent.L1ChainID != intent.L1ChainID {
return immutableErr("L1ChainID", st.AppliedIntent.L1ChainID, intent.L1ChainID)
}
if st.AppliedIntent.FundDevAccounts != intent.FundDevAccounts {
return immutableErr("fundDevAccounts", st.AppliedIntent.FundDevAccounts, intent.FundDevAccounts)
}
l1ChainID, err := env.L1Client.ChainID(ctx) l1ChainID, err := env.L1Client.ChainID(ctx)
if err != nil { if err != nil {
return fmt.Errorf("failed to get L1 chain ID: %w", err) return fmt.Errorf("failed to get L1 chain ID: %w", err)
} }
if l1ChainID.Cmp(intent.L1ChainIDBig()) != 0 { if l1ChainID.Cmp(intent.L1ChainIDBig()) != 0 {
return fmt.Errorf("L1 chain ID mismatch: got %d, expected %d", l1ChainID, intent.L1ChainID) return fmt.Errorf("l1 chain ID mismatch: got %d, expected %d", l1ChainID, intent.L1ChainID)
} }
deployerCode, err := env.L1Client.CodeAt(ctx, script.DeterministicDeployerAddress, nil) deployerCode, err := env.L1Client.CodeAt(ctx, script.DeterministicDeployerAddress, nil)
...@@ -97,6 +78,22 @@ func Init(ctx context.Context, env *Env, _ foundry.StatDirFs, intent *state.Inte ...@@ -97,6 +78,22 @@ func Init(ctx context.Context, env *Env, _ foundry.StatDirFs, intent *state.Inte
return fmt.Errorf("deterministic deployer is not deployed on this chain - please deploy it first") return fmt.Errorf("deterministic deployer is not deployed on this chain - please deploy it first")
} }
// If the state has never been applied, we don't need to perform
// any additional checks.
if st.AppliedIntent == nil {
return nil
}
// If the state has been applied, we need to check if any immutable
// fields have changed.
if st.AppliedIntent.L1ChainID != intent.L1ChainID {
return immutableErr("L1ChainID", st.AppliedIntent.L1ChainID, intent.L1ChainID)
}
if st.AppliedIntent.FundDevAccounts != intent.FundDevAccounts {
return immutableErr("fundDevAccounts", st.AppliedIntent.FundDevAccounts, intent.FundDevAccounts)
}
// TODO: validate individual // TODO: validate individual
return nil return nil
......
...@@ -15,7 +15,7 @@ import ( ...@@ -15,7 +15,7 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
) )
func GenerateL2Genesis(ctx context.Context, env *Env, artifactsFS foundry.StatDirFs, intent *state.Intent, st *state.State, chainID common.Hash) error { func GenerateL2Genesis(ctx context.Context, env *Env, bundle ArtifactsBundle, intent *state.Intent, st *state.State, chainID common.Hash) error {
lgr := env.Logger.New("stage", "generate-l2-genesis") lgr := env.Logger.New("stage", "generate-l2-genesis")
lgr.Info("generating L2 genesis", "id", chainID.Hex()) lgr.Info("generating L2 genesis", "id", chainID.Hex())
...@@ -41,7 +41,7 @@ func GenerateL2Genesis(ctx context.Context, env *Env, artifactsFS foundry.StatDi ...@@ -41,7 +41,7 @@ func GenerateL2Genesis(ctx context.Context, env *Env, artifactsFS foundry.StatDi
CallScriptBroadcastOpts{ CallScriptBroadcastOpts{
L1ChainID: big.NewInt(int64(intent.L1ChainID)), L1ChainID: big.NewInt(int64(intent.L1ChainID)),
Logger: lgr, Logger: lgr,
ArtifactsFS: artifactsFS, ArtifactsFS: bundle.L2,
Deployer: env.Deployer, Deployer: env.Deployer,
Signer: env.Signer, Signer: env.Signer,
Client: env.L1Client, Client: env.L1Client,
...@@ -89,9 +89,5 @@ func GenerateL2Genesis(ctx context.Context, env *Env, artifactsFS foundry.StatDi ...@@ -89,9 +89,5 @@ func GenerateL2Genesis(ctx context.Context, env *Env, artifactsFS foundry.StatDi
} }
thisChainState.StartBlock = startHeader thisChainState.StartBlock = startHeader
if err := env.WriteState(st); err != nil {
return fmt.Errorf("failed to write state: %w", err)
}
return nil return nil
} }
...@@ -9,11 +9,10 @@ import ( ...@@ -9,11 +9,10 @@ import (
"github.com/ethereum-optimism/optimism/op-chain-ops/deployer/opcm" "github.com/ethereum-optimism/optimism/op-chain-ops/deployer/opcm"
"github.com/ethereum-optimism/optimism/op-chain-ops/deployer/state" "github.com/ethereum-optimism/optimism/op-chain-ops/deployer/state"
"github.com/ethereum-optimism/optimism/op-chain-ops/foundry"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
) )
func DeployOPChain(ctx context.Context, env *Env, artifactsFS foundry.StatDirFs, intent *state.Intent, st *state.State, chainID common.Hash) error { func DeployOPChain(ctx context.Context, env *Env, bundle ArtifactsBundle, intent *state.Intent, st *state.State, chainID common.Hash) error {
lgr := env.Logger.New("stage", "deploy-opchain") lgr := env.Logger.New("stage", "deploy-opchain")
if !shouldDeployOPChain(intent, st, chainID) { if !shouldDeployOPChain(intent, st, chainID) {
...@@ -66,7 +65,7 @@ func DeployOPChain(ctx context.Context, env *Env, artifactsFS foundry.StatDirFs, ...@@ -66,7 +65,7 @@ func DeployOPChain(ctx context.Context, env *Env, artifactsFS foundry.StatDirFs,
env.L1Client, env.L1Client,
bcaster, bcaster,
env.Deployer, env.Deployer,
artifactsFS, bundle.L1,
input, input,
) )
if err != nil { if err != nil {
...@@ -91,9 +90,6 @@ func DeployOPChain(ctx context.Context, env *Env, artifactsFS foundry.StatDirFs, ...@@ -91,9 +90,6 @@ func DeployOPChain(ctx context.Context, env *Env, artifactsFS foundry.StatDirFs,
DelayedWETHPermissionedGameProxyAddress: dco.DelayedWETHPermissionedGameProxy, DelayedWETHPermissionedGameProxyAddress: dco.DelayedWETHPermissionedGameProxy,
DelayedWETHPermissionlessGameProxyAddress: dco.DelayedWETHPermissionlessGameProxy, DelayedWETHPermissionlessGameProxyAddress: dco.DelayedWETHPermissionlessGameProxy,
}) })
if err := env.WriteState(st); err != nil {
return err
}
return nil return nil
} }
......
...@@ -13,7 +13,7 @@ import ( ...@@ -13,7 +13,7 @@ import (
"github.com/ethereum-optimism/optimism/op-node/rollup" "github.com/ethereum-optimism/optimism/op-node/rollup"
) )
func DeploySuperchain(ctx context.Context, env *Env, artifactsFS foundry.StatDirFs, intent *state.Intent, st *state.State) error { func DeploySuperchain(ctx context.Context, env *Env, bundle ArtifactsBundle, intent *state.Intent, st *state.State) error {
lgr := env.Logger.New("stage", "deploy-superchain") lgr := env.Logger.New("stage", "deploy-superchain")
if !shouldDeploySuperchain(intent, st) { if !shouldDeploySuperchain(intent, st) {
...@@ -31,7 +31,7 @@ func DeploySuperchain(ctx context.Context, env *Env, artifactsFS foundry.StatDir ...@@ -31,7 +31,7 @@ func DeploySuperchain(ctx context.Context, env *Env, artifactsFS foundry.StatDir
CallScriptBroadcastOpts{ CallScriptBroadcastOpts{
L1ChainID: big.NewInt(int64(intent.L1ChainID)), L1ChainID: big.NewInt(int64(intent.L1ChainID)),
Logger: lgr, Logger: lgr,
ArtifactsFS: artifactsFS, ArtifactsFS: bundle.L1,
Deployer: env.Deployer, Deployer: env.Deployer,
Signer: env.Signer, Signer: env.Signer,
Client: env.L1Client, Client: env.L1Client,
...@@ -71,9 +71,6 @@ func DeploySuperchain(ctx context.Context, env *Env, artifactsFS foundry.StatDir ...@@ -71,9 +71,6 @@ func DeploySuperchain(ctx context.Context, env *Env, artifactsFS foundry.StatDir
ProtocolVersionsImplAddress: dso.ProtocolVersionsImpl, ProtocolVersionsImplAddress: dso.ProtocolVersionsImpl,
StateDump: dump, StateDump: dump,
} }
if err := env.WriteState(st); err != nil {
return err
}
return nil return nil
} }
......
package state
import "net/url"
type ArtifactsURL url.URL
func (a *ArtifactsURL) MarshalText() ([]byte, error) {
return []byte((*url.URL)(a).String()), nil
}
func (a *ArtifactsURL) UnmarshalText(text []byte) error {
u, err := url.Parse(string(text))
if err != nil {
return err
}
*a = ArtifactsURL(*u)
return nil
}
func ParseArtifactsURL(in string) (*ArtifactsURL, error) {
u, err := url.Parse(in)
if err != nil {
return nil, err
}
return (*ArtifactsURL)(u), nil
}
func MustParseArtifactsURL(in string) *ArtifactsURL {
u, err := ParseArtifactsURL(in)
if err != nil {
panic(err)
}
return u
}
...@@ -3,8 +3,8 @@ package state ...@@ -3,8 +3,8 @@ package state
import ( import (
"fmt" "fmt"
"math/big" "math/big"
"strings"
"github.com/ethereum-optimism/optimism/op-chain-ops/deployer/opcm"
"github.com/ethereum-optimism/optimism/op-service/ioutil" "github.com/ethereum-optimism/optimism/op-service/ioutil"
"github.com/ethereum-optimism/optimism/op-service/jsonutil" "github.com/ethereum-optimism/optimism/op-service/jsonutil"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
...@@ -19,9 +19,9 @@ type Intent struct { ...@@ -19,9 +19,9 @@ type Intent struct {
FundDevAccounts bool `json:"fundDevAccounts" toml:"fundDevAccounts"` FundDevAccounts bool `json:"fundDevAccounts" toml:"fundDevAccounts"`
ContractArtifactsURL *ArtifactsURL `json:"contractArtifactsURL" toml:"contractArtifactsURL"` L1ContractsLocator *opcm.ArtifactsLocator `json:"l1ContractsLocator" toml:"l1ContractsLocator"`
ContractsRelease string `json:"contractsRelease" toml:"contractsRelease"` L2ContractsLocator *opcm.ArtifactsLocator `json:"l2ContractsLocator" toml:"l2ContractsLocator"`
Chains []*ChainIntent `json:"chains" toml:"chains"` Chains []*ChainIntent `json:"chains" toml:"chains"`
...@@ -37,11 +37,31 @@ func (c *Intent) Check() error { ...@@ -37,11 +37,31 @@ func (c *Intent) Check() error {
return fmt.Errorf("l1ChainID must be set") return fmt.Errorf("l1ChainID must be set")
} }
if c.ContractsRelease == "dev" { if c.L1ContractsLocator == nil {
return c.checkDev() c.L1ContractsLocator = opcm.DefaultL1ContractsLocator
} }
return c.checkProd() if c.L2ContractsLocator == nil {
c.L2ContractsLocator = opcm.DefaultL2ContractsLocator
}
var err error
if c.L1ContractsLocator.IsTag() {
err = c.checkL1Prod()
} else {
err = c.checkL1Dev()
}
if err != nil {
return err
}
if c.L2ContractsLocator.IsTag() {
if err := c.checkL2Prod(); err != nil {
return err
}
}
return nil
} }
func (c *Intent) Chain(id common.Hash) (*ChainIntent, error) { func (c *Intent) Chain(id common.Hash) (*ChainIntent, error) {
...@@ -58,7 +78,20 @@ func (c *Intent) WriteToFile(path string) error { ...@@ -58,7 +78,20 @@ func (c *Intent) WriteToFile(path string) error {
return jsonutil.WriteTOML(c, ioutil.ToAtomicFile(path, 0o755)) return jsonutil.WriteTOML(c, ioutil.ToAtomicFile(path, 0o755))
} }
func (c *Intent) checkDev() error { func (c *Intent) checkL1Prod() error {
versions, err := opcm.StandardL1VersionsFor(c.L1ChainID)
if err != nil {
return err
}
if _, ok := versions.Releases[c.L1ContractsLocator.Tag]; !ok {
return fmt.Errorf("tag '%s' not found in standard versions", c.L1ContractsLocator.Tag)
}
return nil
}
func (c *Intent) checkL1Dev() error {
if c.SuperchainRoles.ProxyAdminOwner == emptyAddress { if c.SuperchainRoles.ProxyAdminOwner == emptyAddress {
return fmt.Errorf("proxyAdminOwner must be set") return fmt.Errorf("proxyAdminOwner must be set")
} }
...@@ -71,19 +104,12 @@ func (c *Intent) checkDev() error { ...@@ -71,19 +104,12 @@ func (c *Intent) checkDev() error {
c.SuperchainRoles.Guardian = c.SuperchainRoles.ProxyAdminOwner c.SuperchainRoles.Guardian = c.SuperchainRoles.ProxyAdminOwner
} }
if c.ContractArtifactsURL == nil {
return fmt.Errorf("contractArtifactsURL must be set in dev mode")
}
return nil return nil
} }
func (c *Intent) checkProd() error { func (c *Intent) checkL2Prod() error {
if !strings.HasPrefix(c.ContractsRelease, "op-contracts/") { _, err := opcm.StandardArtifactsURLForTag(c.L2ContractsLocator.Tag)
return fmt.Errorf("contractsVersion must be either the literal \"dev\" or start with \"op-contracts/\"") return err
}
return nil
} }
type SuperchainRoles struct { type SuperchainRoles struct {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment