Commit c6c9c4b5 authored by Matthew Slipper's avatar Matthew Slipper Committed by GitHub

op-deployer: Support L1 alloc deployments (#12517)

* op-deployer: Support L1 alloc deployments

Adds support for deploying to L1 allocs file. When deploying to an allocs file, all contracts necessary for the L2 will be included in the L1's genesis state. This is useful for devnet deployments and test automation.

To use an L1 allocs file, set the field `deploymentStrategy` in the intent to `allocs`. The default is `live`, for live chains.

* make apply command work, fix test

* merge artifacts

* fix merge artifacts

* Update op-deployer/pkg/deployer/state/state.go
Co-authored-by: default avatarBlaine Malone <blainemalone01@gmail.com>

* fix: extra check for singleton contracts in tests.

* Update op-deployer/pkg/deployer/flags.go
Co-authored-by: default avatarBlaine Malone <blainemalone01@gmail.com>

* remove unused param

* fix merge artifact

---------
Co-authored-by: default avatarBlaine Malone <blainemalone01@gmail.com>
parent 1e59d083
bin
.deployer
\ No newline at end of file
...@@ -4,8 +4,13 @@ import ( ...@@ -4,8 +4,13 @@ import (
"context" "context"
"crypto/ecdsa" "crypto/ecdsa"
"fmt" "fmt"
"math/big"
"strings" "strings"
"github.com/ethereum-optimism/optimism/op-chain-ops/foundry"
"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/broadcaster"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/pipeline" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/pipeline"
"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/state" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/state"
...@@ -28,23 +33,17 @@ type ApplyConfig struct { ...@@ -28,23 +33,17 @@ type ApplyConfig struct {
} }
func (a *ApplyConfig) Check() error { func (a *ApplyConfig) Check() error {
if a.L1RPCUrl == "" {
return fmt.Errorf("l1RPCUrl must be specified")
}
if a.Workdir == "" { if a.Workdir == "" {
return fmt.Errorf("workdir must be specified") return fmt.Errorf("workdir must be specified")
} }
if a.PrivateKey == "" { if a.PrivateKey != "" {
return fmt.Errorf("private key must be specified") privECDSA, err := crypto.HexToECDSA(strings.TrimPrefix(a.PrivateKey, "0x"))
} if err != nil {
return fmt.Errorf("failed to parse private key: %w", err)
privECDSA, err := crypto.HexToECDSA(strings.TrimPrefix(a.PrivateKey, "0x")) }
if err != nil { a.privateKeyECDSA = privECDSA
return fmt.Errorf("failed to parse private key: %w", err)
} }
a.privateKeyECDSA = privECDSA
if a.Logger == nil { if a.Logger == nil {
return fmt.Errorf("logger must be specified") return fmt.Errorf("logger must be specified")
...@@ -53,6 +52,18 @@ func (a *ApplyConfig) Check() error { ...@@ -53,6 +52,18 @@ func (a *ApplyConfig) Check() error {
return nil return nil
} }
func (a *ApplyConfig) CheckLive() error {
if a.privateKeyECDSA == nil {
return fmt.Errorf("private key must be specified")
}
if a.L1RPCUrl == "" {
return fmt.Errorf("l1RPCUrl must be specified")
}
return nil
}
func ApplyCLI() func(cliCtx *cli.Context) error { func ApplyCLI() func(cliCtx *cli.Context) error {
return func(cliCtx *cli.Context) error { return func(cliCtx *cli.Context) error {
logCfg := oplog.ReadCLIConfig(cliCtx) logCfg := oplog.ReadCLIConfig(cliCtx)
...@@ -79,19 +90,6 @@ func Apply(ctx context.Context, cfg ApplyConfig) error { ...@@ -79,19 +90,6 @@ func Apply(ctx context.Context, cfg ApplyConfig) error {
return fmt.Errorf("invalid config for apply: %w", err) return fmt.Errorf("invalid config for apply: %w", 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)
}
signer := opcrypto.SignerFnFromBind(opcrypto.PrivateKeySignerFn(cfg.privateKeyECDSA, chainID))
deployer := crypto.PubkeyToAddress(cfg.privateKeyECDSA.PublicKey)
intent, err := pipeline.ReadIntent(cfg.Workdir) intent, err := pipeline.ReadIntent(cfg.Workdir)
if err != nil { if err != nil {
return fmt.Errorf("failed to read intent: %w", err) return fmt.Errorf("failed to read intent: %w", err)
...@@ -102,34 +100,50 @@ func Apply(ctx context.Context, cfg ApplyConfig) error { ...@@ -102,34 +100,50 @@ func Apply(ctx context.Context, cfg ApplyConfig) error {
return fmt.Errorf("failed to read state: %w", err) return fmt.Errorf("failed to read state: %w", err)
} }
env := &pipeline.Env{ var l1Client *ethclient.Client
Workdir: cfg.Workdir, var deployer common.Address
L1Client: l1Client, var bcaster broadcaster.Broadcaster
Logger: cfg.Logger, var startingNonce uint64
Signer: signer, if intent.DeploymentStrategy == state.DeploymentStrategyLive {
Deployer: deployer, if err := cfg.CheckLive(); err != nil {
} return fmt.Errorf("invalid config for apply: %w", err)
}
if err := ApplyPipeline(ctx, env, intent, st); err != nil { l1Client, err = ethclient.Dial(cfg.L1RPCUrl)
return err if err != nil {
} return fmt.Errorf("failed to connect to L1 RPC: %w", err)
}
return nil chainID, err := l1Client.ChainID(ctx)
} if err != nil {
return fmt.Errorf("failed to get chain ID: %w", err)
}
type pipelineStage struct { signer := opcrypto.SignerFnFromBind(opcrypto.PrivateKeySignerFn(cfg.privateKeyECDSA, chainID))
name string deployer = crypto.PubkeyToAddress(cfg.privateKeyECDSA.PublicKey)
apply pipeline.Stage
} bcaster, err = broadcaster.NewKeyedBroadcaster(broadcaster.KeyedBroadcasterOpts{
Logger: cfg.Logger,
ChainID: new(big.Int).SetUint64(intent.L1ChainID),
Client: l1Client,
Signer: signer,
From: deployer,
})
if err != nil {
return fmt.Errorf("failed to create broadcaster: %w", err)
}
startingNonce, err = l1Client.NonceAt(ctx, deployer, nil)
if err != nil {
return fmt.Errorf("failed to get starting nonce: %w", err)
}
} else {
deployer = common.Address{0x01}
bcaster = broadcaster.NoopBroadcaster()
}
func ApplyPipeline(
ctx context.Context,
env *pipeline.Env,
intent *state.Intent,
st *state.State,
) error {
progressor := func(curr, total int64) { progressor := func(curr, total int64) {
env.Logger.Info("artifacts download progress", "current", curr, "total", total) cfg.Logger.Info("artifacts download progress", "current", curr, "total", total)
} }
l1ArtifactsFS, cleanupL1, err := pipeline.DownloadArtifacts(ctx, intent.L1ContractsLocator, progressor) l1ArtifactsFS, cleanupL1, err := pipeline.DownloadArtifacts(ctx, intent.L1ContractsLocator, progressor)
...@@ -138,7 +152,7 @@ func ApplyPipeline( ...@@ -138,7 +152,7 @@ func ApplyPipeline(
} }
defer func() { defer func() {
if err := cleanupL1(); err != nil { if err := cleanupL1(); err != nil {
env.Logger.Warn("failed to clean up L1 artifacts", "err", err) cfg.Logger.Warn("failed to clean up L1 artifacts", "err", err)
} }
}() }()
...@@ -148,7 +162,7 @@ func ApplyPipeline( ...@@ -148,7 +162,7 @@ func ApplyPipeline(
} }
defer func() { defer func() {
if err := cleanupL2(); err != nil { if err := cleanupL2(); err != nil {
env.Logger.Warn("failed to clean up L2 artifacts", "err", err) cfg.Logger.Warn("failed to clean up L2 artifacts", "err", err)
} }
}() }()
...@@ -157,38 +171,114 @@ func ApplyPipeline( ...@@ -157,38 +171,114 @@ func ApplyPipeline(
L2: l2ArtifactsFS, L2: l2ArtifactsFS,
} }
l1Host, err := pipeline.DefaultScriptHost(bcaster, cfg.Logger, deployer, bundle.L1, startingNonce)
if err != nil {
return fmt.Errorf("failed to create L1 script host: %w", err)
}
env := &pipeline.Env{
StateWriter: pipeline.WorkdirStateWriter(cfg.Workdir),
L1ScriptHost: l1Host,
L1Client: l1Client,
Logger: cfg.Logger,
Broadcaster: bcaster,
Deployer: deployer,
}
if err := ApplyPipeline(ctx, env, bundle, intent, st); err != nil {
return err
}
return nil
}
type pipelineStage struct {
name string
apply func() error
}
func ApplyPipeline(
ctx context.Context,
env *pipeline.Env,
bundle pipeline.ArtifactsBundle,
intent *state.Intent,
st *state.State,
) error {
pline := []pipelineStage{ pline := []pipelineStage{
{"init", pipeline.Init}, {"init", func() error {
{"deploy-superchain", pipeline.DeploySuperchain}, if intent.DeploymentStrategy == state.DeploymentStrategyLive {
{"deploy-implementations", pipeline.DeployImplementations}, return pipeline.InitLiveStrategy(ctx, env, intent, st)
} else {
return pipeline.InitGenesisStrategy(env, intent, st)
}
}},
{"deploy-superchain", func() error {
return pipeline.DeploySuperchain(env, intent, st)
}},
{"deploy-implementations", func() error {
return pipeline.DeployImplementations(env, intent, st)
}},
} }
// Deploy all OP Chains first.
for _, chain := range intent.Chains { for _, chain := range intent.Chains {
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, bundle pipeline.ArtifactsBundle, intent *state.Intent, st *state.State) error { func() error {
return pipeline.DeployOPChain(ctx, env, bundle, intent, st, chainID) if intent.DeploymentStrategy == state.DeploymentStrategyLive {
return pipeline.DeployOPChainLiveStrategy(ctx, env, bundle, intent, st, chainID)
} else {
return pipeline.DeployOPChainGenesisStrategy(env, 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, bundle pipeline.ArtifactsBundle, intent *state.Intent, st *state.State) error { func() error {
return pipeline.GenerateL2Genesis(ctx, env, bundle, intent, st, chainID) return pipeline.GenerateL2Genesis(env, intent, bundle, st, chainID)
}, },
}) })
} }
// Set start block after all OP chains have been deployed, since the
// genesis strategy requires all the OP chains to exist in genesis.
for _, chain := range intent.Chains {
chainID := chain.ID
pline = append(pline, pipelineStage{
fmt.Sprintf("set-start-block-%s", chainID.Hex()),
func() error {
if intent.DeploymentStrategy == state.DeploymentStrategyLive {
return pipeline.SetStartBlockLiveStrategy(ctx, env, st, chainID)
} else {
return pipeline.SetStartBlockGenesisStrategy(env, st, chainID)
}
},
})
}
// Run through the pipeline. The state dump is captured between
// every step.
for _, stage := range pline { for _, stage := range pline {
if err := stage.apply(ctx, env, bundle, intent, st); err != nil { if err := stage.apply(); 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 { dump, err := env.L1ScriptHost.StateDump()
if err != nil {
return fmt.Errorf("failed to dump state: %w", err)
}
st.L1StateDump = &state.GzipData[foundry.ForgeAllocs]{
Data: dump,
}
if _, err := env.Broadcaster.Broadcast(ctx); err != nil {
return fmt.Errorf("failed to broadcast stage %s: %w", stage.name, err)
}
if err := env.StateWriter.WriteState(st); err != nil {
return fmt.Errorf("failed to write state: %w", err) return fmt.Errorf("failed to write state: %w", err)
} }
} }
st.AppliedIntent = intent st.AppliedIntent = intent
if err := pipeline.WriteState(env.Workdir, st); err != nil { if err := env.StateWriter.WriteState(st); err != nil {
return fmt.Errorf("failed to write state: %w", err) return fmt.Errorf("failed to write state: %w", err)
} }
......
...@@ -8,11 +8,12 @@ import ( ...@@ -8,11 +8,12 @@ import (
"math/big" "math/big"
"strings" "strings"
"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/broadcaster"
"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer"
"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/opcm" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/opcm"
"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/pipeline" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/pipeline"
"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"
"github.com/ethereum-optimism/optimism/op-service/ioutil" "github.com/ethereum-optimism/optimism/op-service/ioutil"
...@@ -131,6 +132,33 @@ func OPCM(ctx context.Context, cfg OPCMConfig) error { ...@@ -131,6 +132,33 @@ func OPCM(ctx context.Context, cfg OPCMConfig) error {
signer := opcrypto.SignerFnFromBind(opcrypto.PrivateKeySignerFn(cfg.privateKeyECDSA, chainID)) signer := opcrypto.SignerFnFromBind(opcrypto.PrivateKeySignerFn(cfg.privateKeyECDSA, chainID))
chainDeployer := crypto.PubkeyToAddress(cfg.privateKeyECDSA.PublicKey) 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 fmt.Errorf("failed to create broadcaster: %w", err)
}
nonce, err := l1Client.NonceAt(ctx, chainDeployer, nil)
if err != nil {
return fmt.Errorf("failed to get starting nonce: %w", err)
}
host, err := pipeline.DefaultScriptHost(
bcaster,
lgr,
chainDeployer,
artifactsFS,
nonce,
)
if err != nil {
return fmt.Errorf("failed to create script host: %w", err)
}
var release string var release string
if cfg.ArtifactsLocator.IsTag() { if cfg.ArtifactsLocator.IsTag() {
release = cfg.ArtifactsLocator.Tag release = cfg.ArtifactsLocator.Tag
...@@ -140,63 +168,51 @@ func OPCM(ctx context.Context, cfg OPCMConfig) error { ...@@ -140,63 +168,51 @@ func OPCM(ctx context.Context, cfg OPCMConfig) error {
lgr.Info("deploying OPCM", "release", release) lgr.Info("deploying OPCM", "release", release)
var dio opcm.DeployImplementationsOutput // We need to etch the Superchain addresses so that they have nonzero code
err = pipeline.CallScriptBroadcast( // and the checks in the OPCM constructor pass.
ctx, superchainConfigAddr := common.Address(*superCfg.Config.SuperchainConfigAddr)
pipeline.CallScriptBroadcastOpts{ protocolVersionsAddr := common.Address(*superCfg.Config.ProtocolVersionsAddr)
L1ChainID: chainID, addresses := []common.Address{
Logger: lgr, superchainConfigAddr,
ArtifactsFS: artifactsFS, protocolVersionsAddr,
Deployer: chainDeployer, }
Signer: signer, for _, addr := range addresses {
Client: l1Client, host.ImportAccount(addr, types.Account{
Broadcaster: pipeline.KeyedBroadcaster, Code: []byte{0x00},
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) var salt common.Hash
protocolVersionsAddr := common.Address(*superCfg.Config.ProtocolVersionsAddr) _, err = rand.Read(salt[:])
addresses := []common.Address{ if err != nil {
superchainConfigAddr, return fmt.Errorf("failed to generate CREATE2 salt: %w", err)
protocolVersionsAddr, }
}
for _, addr := range addresses { dio, err := opcm.DeployImplementations(
host.ImportAccount(addr, types.Account{ host,
Code: []byte{0x00}, opcm.DeployImplementationsInput{
}) Salt: salt,
} WithdrawalDelaySeconds: big.NewInt(604800),
MinProposalSizeBytes: big.NewInt(126000),
var salt common.Hash ChallengePeriodSeconds: big.NewInt(86400),
_, err = rand.Read(salt[:]) ProofMaturityDelaySeconds: big.NewInt(604800),
if err != nil { DisputeGameFinalityDelaySeconds: big.NewInt(302400),
return fmt.Errorf("failed to generate CREATE2 salt: %w", err) Release: release,
} SuperchainConfigProxy: superchainConfigAddr,
ProtocolVersionsProxy: protocolVersionsAddr,
dio, err = opcm.DeployImplementations( OpcmProxyOwner: opcmProxyOwnerAddr,
host, StandardVersionsToml: standardVersionsTOML,
opcm.DeployImplementationsInput{ UseInterop: false,
Salt: salt,
WithdrawalDelaySeconds: big.NewInt(604800),
MinProposalSizeBytes: big.NewInt(126000),
ChallengePeriodSeconds: big.NewInt(86400),
ProofMaturityDelaySeconds: big.NewInt(604800),
DisputeGameFinalityDelaySeconds: big.NewInt(302400),
Release: release,
SuperchainConfigProxy: superchainConfigAddr,
ProtocolVersionsProxy: protocolVersionsAddr,
OpcmProxyOwner: opcmProxyOwnerAddr,
StandardVersionsToml: standardVersionsTOML,
UseInterop: false,
},
)
return err
},
}, },
) )
if err != nil { if err != nil {
return fmt.Errorf("error deploying implementations: %w", err) return fmt.Errorf("error deploying implementations: %w", err)
} }
if _, err := bcaster.Broadcast(ctx); err != nil {
return fmt.Errorf("failed to broadcast: %w", err)
}
lgr.Info("deployed implementations") lgr.Info("deployed implementations")
if err := jsonutil.WriteJSON(dio, ioutil.ToStdOut()); err != nil { if err := jsonutil.WriteJSON(dio, ioutil.ToStdOut()); err != nil {
......
...@@ -9,7 +9,7 @@ import ( ...@@ -9,7 +9,7 @@ import (
type discardBroadcaster struct { type discardBroadcaster struct {
} }
func DiscardBroadcaster() Broadcaster { func NoopBroadcaster() Broadcaster {
return &discardBroadcaster{} return &discardBroadcaster{}
} }
......
...@@ -4,6 +4,7 @@ import ( ...@@ -4,6 +4,7 @@ import (
"context" "context"
"fmt" "fmt"
"math/big" "math/big"
"sync"
"time" "time"
"github.com/ethereum-optimism/optimism/op-service/eth" "github.com/ethereum-optimism/optimism/op-service/eth"
...@@ -29,6 +30,7 @@ type KeyedBroadcaster struct { ...@@ -29,6 +30,7 @@ type KeyedBroadcaster struct {
mgr txmgr.TxManager mgr txmgr.TxManager
bcasts []script.Broadcast bcasts []script.Broadcast
client *ethclient.Client client *ethclient.Client
mtx sync.Mutex
} }
type KeyedBroadcasterOpts struct { type KeyedBroadcasterOpts struct {
...@@ -88,20 +90,32 @@ func NewKeyedBroadcaster(cfg KeyedBroadcasterOpts) (*KeyedBroadcaster, error) { ...@@ -88,20 +90,32 @@ func NewKeyedBroadcaster(cfg KeyedBroadcasterOpts) (*KeyedBroadcaster, error) {
} }
func (t *KeyedBroadcaster) Hook(bcast script.Broadcast) { func (t *KeyedBroadcaster) Hook(bcast script.Broadcast) {
t.mtx.Lock()
t.bcasts = append(t.bcasts, bcast) t.bcasts = append(t.bcasts, bcast)
t.mtx.Unlock()
} }
func (t *KeyedBroadcaster) Broadcast(ctx context.Context) ([]BroadcastResult, error) { func (t *KeyedBroadcaster) Broadcast(ctx context.Context) ([]BroadcastResult, error) {
results := make([]BroadcastResult, len(t.bcasts)) // Empty the internal broadcast buffer as soon as this method is called.
futures := make([]<-chan txmgr.SendResponse, len(t.bcasts)) t.mtx.Lock()
ids := make([]common.Hash, len(t.bcasts)) bcasts := t.bcasts
t.bcasts = nil
t.mtx.Unlock()
if len(bcasts) == 0 {
return nil, nil
}
results := make([]BroadcastResult, len(bcasts))
futures := make([]<-chan txmgr.SendResponse, len(bcasts))
ids := make([]common.Hash, len(bcasts))
latestBlock, err := t.client.BlockByNumber(ctx, nil) latestBlock, err := t.client.BlockByNumber(ctx, nil)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to get latest block: %w", err) return nil, fmt.Errorf("failed to get latest block: %w", err)
} }
for i, bcast := range t.bcasts { for i, bcast := range bcasts {
futures[i], ids[i] = t.broadcast(ctx, bcast, latestBlock.GasLimit()) futures[i], ids[i] = t.broadcast(ctx, bcast, latestBlock.GasLimit())
t.lgr.Info( t.lgr.Info(
"transaction broadcasted", "transaction broadcasted",
...@@ -116,7 +130,7 @@ func (t *KeyedBroadcaster) Broadcast(ctx context.Context) ([]BroadcastResult, er ...@@ -116,7 +130,7 @@ func (t *KeyedBroadcaster) Broadcast(ctx context.Context) ([]BroadcastResult, er
bcastRes := <-fut bcastRes := <-fut
completed++ completed++
outRes := BroadcastResult{ outRes := BroadcastResult{
Broadcast: t.bcasts[i], Broadcast: bcasts[i],
} }
if bcastRes.Err == nil { if bcastRes.Err == nil {
...@@ -131,7 +145,7 @@ func (t *KeyedBroadcaster) Broadcast(ctx context.Context) ([]BroadcastResult, er ...@@ -131,7 +145,7 @@ func (t *KeyedBroadcaster) Broadcast(ctx context.Context) ([]BroadcastResult, er
"transaction failed on chain", "transaction failed on chain",
"id", ids[i], "id", ids[i],
"completed", completed, "completed", completed,
"total", len(t.bcasts), "total", len(bcasts),
"hash", outRes.Receipt.TxHash.String(), "hash", outRes.Receipt.TxHash.String(),
"nonce", outRes.Broadcast.Nonce, "nonce", outRes.Broadcast.Nonce,
) )
...@@ -140,7 +154,7 @@ func (t *KeyedBroadcaster) Broadcast(ctx context.Context) ([]BroadcastResult, er ...@@ -140,7 +154,7 @@ func (t *KeyedBroadcaster) Broadcast(ctx context.Context) ([]BroadcastResult, er
"transaction confirmed", "transaction confirmed",
"id", ids[i], "id", ids[i],
"completed", completed, "completed", completed,
"total", len(t.bcasts), "total", len(bcasts),
"hash", outRes.Receipt.TxHash.String(), "hash", outRes.Receipt.TxHash.String(),
"nonce", outRes.Broadcast.Nonce, "nonce", outRes.Broadcast.Nonce,
"creation", outRes.Receipt.ContractAddress, "creation", outRes.Receipt.ContractAddress,
...@@ -153,7 +167,7 @@ func (t *KeyedBroadcaster) Broadcast(ctx context.Context) ([]BroadcastResult, er ...@@ -153,7 +167,7 @@ func (t *KeyedBroadcaster) Broadcast(ctx context.Context) ([]BroadcastResult, er
"transaction failed", "transaction failed",
"id", ids[i], "id", ids[i],
"completed", completed, "completed", completed,
"total", len(t.bcasts), "total", len(bcasts),
"err", bcastRes.Err, "err", bcastRes.Err,
) )
} }
......
package deployer package deployer
import ( import (
"fmt"
"os" "os"
"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/state"
op_service "github.com/ethereum-optimism/optimism/op-service" op_service "github.com/ethereum-optimism/optimism/op-service"
oplog "github.com/ethereum-optimism/optimism/op-service/log" oplog "github.com/ethereum-optimism/optimism/op-service/log"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
) )
const ( const (
EnvVarPrefix = "DEPLOYER" EnvVarPrefix = "DEPLOYER"
L1RPCURLFlagName = "l1-rpc-url" L1RPCURLFlagName = "l1-rpc-url"
L1ChainIDFlagName = "l1-chain-id" L1ChainIDFlagName = "l1-chain-id"
L2ChainIDsFlagName = "l2-chain-ids" L2ChainIDsFlagName = "l2-chain-ids"
WorkdirFlagName = "workdir" WorkdirFlagName = "workdir"
OutdirFlagName = "outdir" OutdirFlagName = "outdir"
PrivateKeyFlagName = "private-key" PrivateKeyFlagName = "private-key"
DeploymentStrategyFlagName = "deployment-strategy"
) )
var ( var (
L1RPCURLFlag = &cli.StringFlag{ L1RPCURLFlag = &cli.StringFlag{
Name: L1RPCURLFlagName, Name: L1RPCURLFlagName,
Usage: "RPC URL for the L1 chain. Can be set to 'genesis' for deployments " + Usage: "RPC URL for the L1 chain. Must be set for live chains. " +
"that will be deployed at the launch of the L1.", "Can be blank for chains deploying to local allocs files.",
EnvVars: []string{ EnvVars: []string{
"L1_RPC_URL", "L1_RPC_URL",
}, },
...@@ -52,6 +56,12 @@ var ( ...@@ -52,6 +56,12 @@ var (
Usage: "Private key of the deployer account.", Usage: "Private key of the deployer account.",
EnvVars: PrefixEnvVar("PRIVATE_KEY"), EnvVars: PrefixEnvVar("PRIVATE_KEY"),
} }
DeploymentStrategyFlag = &cli.StringFlag{
Name: DeploymentStrategyFlagName,
Usage: fmt.Sprintf("Deployment strategy to use. Options: %s, %s", state.DeploymentStrategyLive, state.DeploymentStrategyGenesis),
EnvVars: PrefixEnvVar("DEPLOYMENT_STRATEGY"),
Value: string(state.DeploymentStrategyLive),
}
) )
var GlobalFlags = append([]cli.Flag{}, oplog.CLIFlags(EnvVarPrefix)...) var GlobalFlags = append([]cli.Flag{}, oplog.CLIFlags(EnvVarPrefix)...)
...@@ -60,6 +70,7 @@ var InitFlags = []cli.Flag{ ...@@ -60,6 +70,7 @@ var InitFlags = []cli.Flag{
L1ChainIDFlag, L1ChainIDFlag,
L2ChainIDsFlag, L2ChainIDsFlag,
WorkdirFlag, WorkdirFlag,
DeploymentStrategyFlag,
} }
var ApplyFlags = []cli.Flag{ var ApplyFlags = []cli.Flag{
......
...@@ -8,7 +8,7 @@ import ( ...@@ -8,7 +8,7 @@ import (
"strings" "strings"
"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/opcm" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/opcm"
state2 "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/state" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/state"
op_service "github.com/ethereum-optimism/optimism/op-service" op_service "github.com/ethereum-optimism/optimism/op-service"
...@@ -18,12 +18,17 @@ import ( ...@@ -18,12 +18,17 @@ import (
) )
type InitConfig struct { type InitConfig struct {
L1ChainID uint64 DeploymentStrategy state.DeploymentStrategy
Outdir string L1ChainID uint64
L2ChainIDs []common.Hash Outdir string
L2ChainIDs []common.Hash
} }
func (c *InitConfig) Check() error { func (c *InitConfig) Check() error {
if err := c.DeploymentStrategy.Check(); err != nil {
return err
}
if c.L1ChainID == 0 { if c.L1ChainID == 0 {
return fmt.Errorf("l1ChainID must be specified") return fmt.Errorf("l1ChainID must be specified")
} }
...@@ -41,24 +46,30 @@ func (c *InitConfig) Check() error { ...@@ -41,24 +46,30 @@ func (c *InitConfig) Check() error {
func InitCLI() func(ctx *cli.Context) error { func InitCLI() func(ctx *cli.Context) error {
return func(ctx *cli.Context) error { return func(ctx *cli.Context) error {
deploymentStrategy := ctx.String(DeploymentStrategyFlagName)
l1ChainID := ctx.Uint64(L1ChainIDFlagName) l1ChainID := ctx.Uint64(L1ChainIDFlagName)
outdir := ctx.String(OutdirFlagName) outdir := ctx.String(OutdirFlagName)
l2ChainIDsRaw := ctx.String(L2ChainIDsFlagName) l2ChainIDsRaw := ctx.String(L2ChainIDsFlagName)
if len(l2ChainIDsRaw) == 0 {
return fmt.Errorf("must specify at least one L2 chain ID")
}
l2ChainIDsStr := strings.Split(strings.TrimSpace(l2ChainIDsRaw), ",") l2ChainIDsStr := strings.Split(strings.TrimSpace(l2ChainIDsRaw), ",")
l2ChainIDs := make([]common.Hash, len(l2ChainIDsStr)) l2ChainIDs := make([]common.Hash, len(l2ChainIDsStr))
for i, idStr := range l2ChainIDsStr { for i, idStr := range l2ChainIDsStr {
id, err := op_service.Parse256BitChainID(idStr) id, err := op_service.Parse256BitChainID(idStr)
if err != nil { if err != nil {
return fmt.Errorf("invalid chain ID: %w", err) return fmt.Errorf("invalid L2 chain ID '%s': %w", idStr, err)
} }
l2ChainIDs[i] = id l2ChainIDs[i] = id
} }
err := Init(InitConfig{ err := Init(InitConfig{
L1ChainID: l1ChainID, DeploymentStrategy: state.DeploymentStrategy(deploymentStrategy),
Outdir: outdir, L1ChainID: l1ChainID,
L2ChainIDs: l2ChainIDs, Outdir: outdir,
L2ChainIDs: l2ChainIDs,
}) })
if err != nil { if err != nil {
return err return err
...@@ -74,7 +85,8 @@ func Init(cfg InitConfig) error { ...@@ -74,7 +85,8 @@ func Init(cfg InitConfig) error {
return fmt.Errorf("invalid config for init: %w", err) return fmt.Errorf("invalid config for init: %w", err)
} }
intent := &state2.Intent{ intent := &state.Intent{
DeploymentStrategy: cfg.DeploymentStrategy,
L1ChainID: cfg.L1ChainID, L1ChainID: cfg.L1ChainID,
FundDevAccounts: true, FundDevAccounts: true,
L1ContractsLocator: opcm.DefaultL1ContractsLocator, L1ContractsLocator: opcm.DefaultL1ContractsLocator,
...@@ -96,7 +108,7 @@ func Init(cfg InitConfig) error { ...@@ -96,7 +108,7 @@ func Init(cfg InitConfig) error {
} }
return addr return addr
} }
intent.SuperchainRoles = state2.SuperchainRoles{ intent.SuperchainRoles = &state.SuperchainRoles{
ProxyAdminOwner: addrFor(devkeys.L1ProxyAdminOwnerRole.Key(l1ChainIDBig)), ProxyAdminOwner: addrFor(devkeys.L1ProxyAdminOwnerRole.Key(l1ChainIDBig)),
ProtocolVersionsOwner: addrFor(devkeys.SuperchainProtocolVersionsOwner.Key(l1ChainIDBig)), ProtocolVersionsOwner: addrFor(devkeys.SuperchainProtocolVersionsOwner.Key(l1ChainIDBig)),
Guardian: addrFor(devkeys.SuperchainConfigGuardianKey.Key(l1ChainIDBig)), Guardian: addrFor(devkeys.SuperchainConfigGuardianKey.Key(l1ChainIDBig)),
...@@ -104,14 +116,14 @@ func Init(cfg InitConfig) error { ...@@ -104,14 +116,14 @@ func Init(cfg InitConfig) error {
for _, l2ChainID := range cfg.L2ChainIDs { for _, l2ChainID := range cfg.L2ChainIDs {
l2ChainIDBig := l2ChainID.Big() l2ChainIDBig := l2ChainID.Big()
intent.Chains = append(intent.Chains, &state2.ChainIntent{ intent.Chains = append(intent.Chains, &state.ChainIntent{
ID: l2ChainID, ID: l2ChainID,
BaseFeeVaultRecipient: common.Address{}, BaseFeeVaultRecipient: common.Address{},
L1FeeVaultRecipient: common.Address{}, L1FeeVaultRecipient: common.Address{},
SequencerFeeVaultRecipient: common.Address{}, SequencerFeeVaultRecipient: common.Address{},
Eip1559Denominator: 50, Eip1559Denominator: 50,
Eip1559Elasticity: 6, Eip1559Elasticity: 6,
Roles: state2.ChainRoles{ Roles: state.ChainRoles{
ProxyAdminOwner: addrFor(devkeys.L2ProxyAdminOwnerRole.Key(l2ChainIDBig)), ProxyAdminOwner: addrFor(devkeys.L2ProxyAdminOwnerRole.Key(l2ChainIDBig)),
SystemConfigOwner: addrFor(devkeys.SystemConfigOwner.Key(l2ChainIDBig)), SystemConfigOwner: addrFor(devkeys.SystemConfigOwner.Key(l2ChainIDBig)),
GovernanceTokenOwner: addrFor(devkeys.L2ProxyAdminOwnerRole.Key(l2ChainIDBig)), GovernanceTokenOwner: addrFor(devkeys.L2ProxyAdminOwnerRole.Key(l2ChainIDBig)),
...@@ -123,7 +135,7 @@ func Init(cfg InitConfig) error { ...@@ -123,7 +135,7 @@ func Init(cfg InitConfig) error {
}) })
} }
st := &state2.State{ st := &state.State{
Version: 1, Version: 1,
} }
......
...@@ -4,7 +4,7 @@ import ( ...@@ -4,7 +4,7 @@ import (
"fmt" "fmt"
"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/pipeline" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/pipeline"
state2 "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/state" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/state"
"github.com/ethereum-optimism/optimism/op-chain-ops/genesis" "github.com/ethereum-optimism/optimism/op-chain-ops/genesis"
"github.com/ethereum-optimism/optimism/op-node/rollup" "github.com/ethereum-optimism/optimism/op-node/rollup"
...@@ -39,7 +39,7 @@ func GenesisCLI(cliCtx *cli.Context) error { ...@@ -39,7 +39,7 @@ func GenesisCLI(cliCtx *cli.Context) error {
return nil return nil
} }
func GenesisAndRollup(globalState *state2.State, chainID common.Hash) (*core.Genesis, *rollup.Config, error) { func GenesisAndRollup(globalState *state.State, chainID common.Hash) (*core.Genesis, *rollup.Config, error) {
if globalState.AppliedIntent == nil { if globalState.AppliedIntent == nil {
return nil, nil, fmt.Errorf("chain state is not applied - run op-deployer apply") return nil, nil, fmt.Errorf("chain state is not applied - run op-deployer apply")
} }
...@@ -54,12 +54,8 @@ func GenesisAndRollup(globalState *state2.State, chainID common.Hash) (*core.Gen ...@@ -54,12 +54,8 @@ func GenesisAndRollup(globalState *state2.State, chainID common.Hash) (*core.Gen
return nil, nil, fmt.Errorf("failed to get chain ID %s: %w", chainID.String(), err) return nil, nil, fmt.Errorf("failed to get chain ID %s: %w", chainID.String(), err)
} }
l2Allocs, err := chainState.UnmarshalAllocs() l2Allocs := chainState.Allocs.Data
if err != nil { config, err := state.CombineDeployConfig(
return nil, nil, fmt.Errorf("failed to unmarshal genesis: %w", err)
}
config, err := state2.CombineDeployConfig(
globalState.AppliedIntent, globalState.AppliedIntent,
chainIntent, chainIntent,
globalState, globalState,
......
...@@ -24,6 +24,8 @@ var ErrUnsupportedArtifactsScheme = errors.New("unsupported artifacts URL scheme ...@@ -24,6 +24,8 @@ var ErrUnsupportedArtifactsScheme = errors.New("unsupported artifacts URL scheme
type DownloadProgressor func(current, total int64) type DownloadProgressor func(current, total int64)
func NoopDownloadProgressor(current, total int64) {}
type CleanupFunc func() error type CleanupFunc func() error
var noopCleanup = func() error { return nil } var noopCleanup = func() error { return nil }
......
...@@ -5,11 +5,13 @@ import ( ...@@ -5,11 +5,13 @@ import (
"fmt" "fmt"
"path" "path"
state2 "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/state" "github.com/ethereum-optimism/optimism/op-chain-ops/script"
"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/broadcaster"
"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/state"
"github.com/ethereum-optimism/optimism/op-chain-ops/foundry" "github.com/ethereum-optimism/optimism/op-chain-ops/foundry"
opcrypto "github.com/ethereum-optimism/optimism/op-service/crypto"
"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"
"github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/ethclient"
...@@ -17,32 +19,56 @@ import ( ...@@ -17,32 +19,56 @@ import (
) )
type Env struct { type Env struct {
Workdir string StateWriter StateWriter
L1Client *ethclient.Client L1ScriptHost *script.Host
Signer opcrypto.SignerFn L1Client *ethclient.Client
Deployer common.Address Broadcaster broadcaster.Broadcaster
Logger log.Logger Host *script.Host
Deployer common.Address
Logger log.Logger
}
type StateWriter interface {
WriteState(st *state.State) error
}
type stateWriterFunc func(st *state.State) error
func (f stateWriterFunc) WriteState(st *state.State) error {
return f(st)
}
func WorkdirStateWriter(workdir string) StateWriter {
return stateWriterFunc(func(st *state.State) error {
return WriteState(workdir, st)
})
}
func NoopStateWriter() StateWriter {
return stateWriterFunc(func(st *state.State) error {
return nil
})
} }
func ReadIntent(workdir string) (*state2.Intent, error) { func ReadIntent(workdir string) (*state.Intent, error) {
intentPath := path.Join(workdir, "intent.toml") intentPath := path.Join(workdir, "intent.toml")
intent, err := jsonutil.LoadTOML[state2.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)
} }
return intent, nil return intent, nil
} }
func ReadState(workdir string) (*state2.State, error) { func ReadState(workdir string) (*state.State, error) {
statePath := path.Join(workdir, "state.json") statePath := path.Join(workdir, "state.json")
st, err := jsonutil.LoadJSON[state2.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)
} }
return st, nil return st, nil
} }
func WriteState(workdir string, st *state2.State) error { func WriteState(workdir string, st *state.State) error {
statePath := path.Join(workdir, "state.json") statePath := path.Join(workdir, "state.json")
return st.WriteToFile(statePath) return st.WriteToFile(statePath)
} }
...@@ -52,4 +78,4 @@ type ArtifactsBundle struct { ...@@ -52,4 +78,4 @@ type ArtifactsBundle struct {
L2 foundry.StatDirFs L2 foundry.StatDirFs
} }
type Stage func(ctx context.Context, env *Env, bundle ArtifactsBundle, intent *state2.Intent, st *state2.State) error type Stage func(ctx context.Context, env *Env, bundle ArtifactsBundle, intent *state.Intent, st *state.State) error
package pipeline package pipeline
import ( import (
"context"
"fmt" "fmt"
"math/big"
broadcaster2 "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/broadcaster" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/broadcaster"
"github.com/ethereum-optimism/optimism/op-chain-ops/foundry" "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"
opcrypto "github.com/ethereum-optimism/optimism/op-service/crypto"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
) )
type BroadcasterFactory func(opts CallScriptBroadcastOpts) (broadcaster2.Broadcaster, error) func DefaultScriptHost(
bcaster broadcaster.Broadcaster,
func KeyedBroadcaster(opts CallScriptBroadcastOpts) (broadcaster2.Broadcaster, error) { lgr log.Logger,
return broadcaster2.NewKeyedBroadcaster(broadcaster2.KeyedBroadcasterOpts{ deployer common.Address,
Logger: opts.Logger, artifacts foundry.StatDirFs,
ChainID: opts.L1ChainID, startingNonce uint64,
Client: opts.Client, ) (*script.Host, error) {
Signer: opts.Signer,
From: opts.Deployer,
})
}
func DiscardBroadcaster(opts CallScriptBroadcastOpts) (broadcaster2.Broadcaster, error) {
return broadcaster2.DiscardBroadcaster(), nil
}
type CallScriptBroadcastOpts struct {
L1ChainID *big.Int
Logger log.Logger
ArtifactsFS foundry.StatDirFs
Deployer common.Address
Signer opcrypto.SignerFn
Client *ethclient.Client
Handler func(host *script.Host) error
Broadcaster BroadcasterFactory
}
func CallScriptBroadcast(
ctx context.Context,
opts CallScriptBroadcastOpts,
) error {
bcaster, err := opts.Broadcaster(opts)
if err != nil {
return fmt.Errorf("failed to create broadcaster: %w", err)
}
scriptCtx := script.DefaultContext scriptCtx := script.DefaultContext
scriptCtx.Sender = opts.Deployer scriptCtx.Sender = deployer
scriptCtx.Origin = opts.Deployer scriptCtx.Origin = deployer
artifacts := &foundry.ArtifactsFS{FS: opts.ArtifactsFS}
h := script.NewHost( h := script.NewHost(
opts.Logger, lgr,
artifacts, &foundry.ArtifactsFS{FS: artifacts},
nil, nil,
scriptCtx, scriptCtx,
script.WithBroadcastHook(bcaster.Hook), script.WithBroadcastHook(bcaster.Hook),
...@@ -66,23 +32,10 @@ func CallScriptBroadcast( ...@@ -66,23 +32,10 @@ func CallScriptBroadcast(
) )
if err := h.EnableCheats(); err != nil { if err := h.EnableCheats(); err != nil {
return fmt.Errorf("failed to enable cheats: %w", err) return nil, fmt.Errorf("failed to enable cheats: %w", err)
}
nonce, err := opts.Client.NonceAt(ctx, opts.Deployer, nil)
if err != nil {
return fmt.Errorf("failed to fetch nonce: %w", err)
} }
h.SetNonce(opts.Deployer, nonce)
err = opts.Handler(h) h.SetNonce(deployer, startingNonce)
if err != nil {
return fmt.Errorf("failed to run handler: %w", err)
}
if _, err := bcaster.Broadcast(ctx); err != nil {
return fmt.Errorf("failed to broadcast: %w", err)
}
return nil return h, nil
} }
package pipeline package pipeline
import ( import (
"context"
"fmt" "fmt"
"math/big" "math/big"
"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/opcm" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/opcm"
"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/state" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/state"
"github.com/ethereum-optimism/optimism/op-chain-ops/foundry"
"github.com/ethereum-optimism/optimism/op-chain-ops/script"
) )
func DeployImplementations(ctx context.Context, env *Env, bundle ArtifactsBundle, intent *state.Intent, st *state.State) error { func DeployImplementations(env *Env, 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) {
...@@ -25,7 +21,7 @@ func DeployImplementations(ctx context.Context, env *Env, bundle ArtifactsBundle ...@@ -25,7 +21,7 @@ func DeployImplementations(ctx context.Context, env *Env, bundle ArtifactsBundle
var standardVersionsTOML string var standardVersionsTOML string
var contractsRelease string var contractsRelease string
var err error var err error
if intent.L1ContractsLocator.IsTag() { if intent.L1ContractsLocator.IsTag() && intent.DeploymentStrategy == state.DeploymentStrategyLive {
standardVersionsTOML, err = opcm.StandardL1VersionsDataFor(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)
...@@ -35,48 +31,24 @@ func DeployImplementations(ctx context.Context, env *Env, bundle ArtifactsBundle ...@@ -35,48 +31,24 @@ func DeployImplementations(ctx context.Context, env *Env, bundle ArtifactsBundle
contractsRelease = "dev" contractsRelease = "dev"
} }
var dump *foundry.ForgeAllocs env.L1ScriptHost.ImportState(st.L1StateDump.Data)
var dio opcm.DeployImplementationsOutput
err = CallScriptBroadcast(
ctx,
CallScriptBroadcastOpts{
L1ChainID: big.NewInt(int64(intent.L1ChainID)),
Logger: lgr,
ArtifactsFS: bundle.L1,
Deployer: env.Deployer,
Signer: env.Signer,
Client: env.L1Client,
Broadcaster: KeyedBroadcaster,
Handler: func(host *script.Host) error {
host.ImportState(st.SuperchainDeployment.StateDump)
dio, err = opcm.DeployImplementations( dio, err := opcm.DeployImplementations(
host, env.L1ScriptHost,
opcm.DeployImplementationsInput{ opcm.DeployImplementationsInput{
Salt: st.Create2Salt, Salt: st.Create2Salt,
WithdrawalDelaySeconds: big.NewInt(604800), WithdrawalDelaySeconds: big.NewInt(604800),
MinProposalSizeBytes: big.NewInt(126000), MinProposalSizeBytes: big.NewInt(126000),
ChallengePeriodSeconds: big.NewInt(86400), ChallengePeriodSeconds: big.NewInt(86400),
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: 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,
StandardVersionsToml: standardVersionsTOML, StandardVersionsToml: standardVersionsTOML,
UseInterop: false, UseInterop: false,
},
)
if err != nil {
return fmt.Errorf("error deploying implementations: %w", err)
}
dump, err = host.StateDump()
if err != nil {
return fmt.Errorf("error dumping state: %w", err)
}
return nil
},
}, },
) )
if err != nil { if err != nil {
...@@ -95,7 +67,6 @@ func DeployImplementations(ctx context.Context, env *Env, bundle ArtifactsBundle ...@@ -95,7 +67,6 @@ func DeployImplementations(ctx context.Context, env *Env, bundle ArtifactsBundle
L1StandardBridgeImplAddress: dio.L1StandardBridgeImpl, L1StandardBridgeImplAddress: dio.L1StandardBridgeImpl,
OptimismMintableERC20FactoryImplAddress: dio.OptimismMintableERC20FactoryImpl, OptimismMintableERC20FactoryImplAddress: dio.OptimismMintableERC20FactoryImpl,
DisputeGameFactoryImplAddress: dio.DisputeGameFactoryImpl, DisputeGameFactoryImplAddress: dio.DisputeGameFactoryImpl,
StateDump: dump,
} }
return nil return nil
......
...@@ -6,7 +6,7 @@ import ( ...@@ -6,7 +6,7 @@ import (
"fmt" "fmt"
"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/opcm" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/opcm"
state2 "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/state" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/state"
"github.com/ethereum-optimism/optimism/op-chain-ops/script" "github.com/ethereum-optimism/optimism/op-chain-ops/script"
...@@ -17,20 +17,12 @@ func IsSupportedStateVersion(version int) bool { ...@@ -17,20 +17,12 @@ func IsSupportedStateVersion(version int) bool {
return version == 1 return version == 1
} }
func Init(ctx context.Context, env *Env, _ ArtifactsBundle, intent *state2.Intent, st *state2.State) error { func InitLiveStrategy(ctx context.Context, env *Env, intent *state.Intent, st *state.State) error {
lgr := env.Logger.New("stage", "init") lgr := env.Logger.New("stage", "init", "strategy", "live")
lgr.Info("initializing pipeline") lgr.Info("initializing pipeline")
// Ensure the state version is supported. if err := initCommonChecks(st); err != nil {
if !IsSupportedStateVersion(st.Version) { return err
return fmt.Errorf("unsupported state version: %d", st.Version)
}
if st.Create2Salt == (common.Hash{}) {
_, err := rand.Read(st.Create2Salt[:])
if err != nil {
return fmt.Errorf("failed to generate CREATE2 salt: %w", err)
}
} }
if intent.L1ContractsLocator.IsTag() { if intent.L1ContractsLocator.IsTag() {
...@@ -46,7 +38,7 @@ func Init(ctx context.Context, env *Env, _ ArtifactsBundle, intent *state2.Inten ...@@ -46,7 +38,7 @@ func Init(ctx context.Context, env *Env, _ ArtifactsBundle, intent *state2.Inten
// Have to do this weird pointer thing below because the Superchain Registry defines its // Have to do this weird pointer thing below because the Superchain Registry defines its
// own Address type. // own Address type.
st.SuperchainDeployment = &state2.SuperchainDeployment{ st.SuperchainDeployment = &state.SuperchainDeployment{
ProxyAdminAddress: proxyAdmin, ProxyAdminAddress: proxyAdmin,
ProtocolVersionsProxyAddress: common.Address(*superCfg.Config.ProtocolVersionsAddr), ProtocolVersionsProxyAddress: common.Address(*superCfg.Config.ProtocolVersionsAddr),
SuperchainConfigProxyAddress: common.Address(*superCfg.Config.SuperchainConfigAddr), SuperchainConfigProxyAddress: common.Address(*superCfg.Config.SuperchainConfigAddr),
...@@ -56,7 +48,7 @@ func Init(ctx context.Context, env *Env, _ ArtifactsBundle, intent *state2.Inten ...@@ -56,7 +48,7 @@ func Init(ctx context.Context, env *Env, _ ArtifactsBundle, intent *state2.Inten
if err != nil { if err != nil {
return fmt.Errorf("error getting OPCM proxy address: %w", err) return fmt.Errorf("error getting OPCM proxy address: %w", err)
} }
st.ImplementationsDeployment = &state2.ImplementationsDeployment{ st.ImplementationsDeployment = &state.ImplementationsDeployment{
OpcmProxyAddress: opcmProxy, OpcmProxyAddress: opcmProxy,
} }
} }
...@@ -99,6 +91,38 @@ func Init(ctx context.Context, env *Env, _ ArtifactsBundle, intent *state2.Inten ...@@ -99,6 +91,38 @@ func Init(ctx context.Context, env *Env, _ ArtifactsBundle, intent *state2.Inten
return nil return nil
} }
func initCommonChecks(st *state.State) error {
// Ensure the state version is supported.
if !IsSupportedStateVersion(st.Version) {
return fmt.Errorf("unsupported state version: %d", st.Version)
}
if st.Create2Salt == (common.Hash{}) {
_, err := rand.Read(st.Create2Salt[:])
if err != nil {
return fmt.Errorf("failed to generate CREATE2 salt: %w", err)
}
}
return nil
}
func InitGenesisStrategy(env *Env, intent *state.Intent, st *state.State) error {
lgr := env.Logger.New("stage", "init", "strategy", "genesis")
lgr.Info("initializing pipeline")
if err := initCommonChecks(st); err != nil {
return err
}
if intent.SuperchainRoles == nil {
return fmt.Errorf("superchain roles must be set for genesis strategy")
}
// Mostly a stub for now.
return nil
}
func immutableErr(field string, was, is any) error { func immutableErr(field string, was, is any) error {
return fmt.Errorf("%s is immutable: was %v, is %v", field, was, is) return fmt.Errorf("%s is immutable: was %v, is %v", field, was, is)
} }
package pipeline package pipeline
import ( import (
"bytes"
"compress/gzip"
"context"
"encoding/json"
"fmt" "fmt"
"math/big"
"github.com/ethereum-optimism/optimism/op-chain-ops/foundry"
"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/deployer/opcm"
state2 "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/state" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/state"
"github.com/ethereum-optimism/optimism/op-chain-ops/foundry"
"github.com/ethereum-optimism/optimism/op-chain-ops/script"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
) )
func GenerateL2Genesis(ctx context.Context, env *Env, bundle ArtifactsBundle, intent *state2.Intent, st *state2.State, chainID common.Hash) error { func GenerateL2Genesis(env *Env, intent *state.Intent, bundle ArtifactsBundle, 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())
thisIntent, err := intent.Chain(chainID) thisIntent, err := intent.Chain(chainID)
if err != nil { if err != nil {
return fmt.Errorf("failed to get chain intent: %w", err) return fmt.Errorf("failed to get chain intent: %w", err)
...@@ -31,64 +24,54 @@ func GenerateL2Genesis(ctx context.Context, env *Env, bundle ArtifactsBundle, in ...@@ -31,64 +24,54 @@ func GenerateL2Genesis(ctx context.Context, env *Env, bundle ArtifactsBundle, in
return fmt.Errorf("failed to get chain state: %w", err) return fmt.Errorf("failed to get chain state: %w", err)
} }
initCfg, err := state2.CombineDeployConfig(intent, thisIntent, st, thisChainState) if !shouldGenerateL2Genesis(thisChainState) {
lgr.Info("L2 genesis generation not needed")
return nil
}
lgr.Info("generating L2 genesis", "id", chainID.Hex())
initCfg, err := state.CombineDeployConfig(intent, thisIntent, st, thisChainState)
if err != nil { if err != nil {
return fmt.Errorf("failed to combine L2 init config: %w", err) return fmt.Errorf("failed to combine L2 init config: %w", err)
} }
var dump *foundry.ForgeAllocs host, err := DefaultScriptHost(
err = CallScriptBroadcast( broadcaster.NoopBroadcaster(),
ctx, env.Logger,
CallScriptBroadcastOpts{ env.Deployer,
L1ChainID: big.NewInt(int64(intent.L1ChainID)), bundle.L2,
Logger: lgr, 0,
ArtifactsFS: bundle.L2,
Deployer: env.Deployer,
Signer: env.Signer,
Client: env.L1Client,
Broadcaster: DiscardBroadcaster,
Handler: func(host *script.Host) error {
err := opcm.L2Genesis(host, &opcm.L2GenesisInput{
L1Deployments: opcm.L1Deployments{
L1CrossDomainMessengerProxy: thisChainState.L1CrossDomainMessengerProxyAddress,
L1StandardBridgeProxy: thisChainState.L1StandardBridgeProxyAddress,
L1ERC721BridgeProxy: thisChainState.L1ERC721BridgeProxyAddress,
},
L2Config: initCfg.L2InitializationConfig,
})
if err != nil {
return fmt.Errorf("failed to call L2Genesis script: %w", err)
}
host.Wipe(env.Deployer)
dump, err = host.StateDump()
if err != nil {
return fmt.Errorf("failed to dump state: %w", err)
}
return nil
},
},
) )
if err != nil { if err != nil {
return fmt.Errorf("failed to call L2Genesis script: %w", err) return fmt.Errorf("failed to create L2 script host: %w", err)
} }
var buf bytes.Buffer if err := opcm.L2Genesis(host, &opcm.L2GenesisInput{
gw := gzip.NewWriter(&buf) L1Deployments: opcm.L1Deployments{
if err := json.NewEncoder(gw).Encode(dump); err != nil { L1CrossDomainMessengerProxy: thisChainState.L1CrossDomainMessengerProxyAddress,
return fmt.Errorf("failed to encode state dump: %w", err) L1StandardBridgeProxy: thisChainState.L1StandardBridgeProxyAddress,
} L1ERC721BridgeProxy: thisChainState.L1ERC721BridgeProxyAddress,
if err := gw.Close(); err != nil { },
return fmt.Errorf("failed to close gzip writer: %w", err) L2Config: initCfg.L2InitializationConfig,
}); err != nil {
return fmt.Errorf("failed to call L2Genesis script: %w", err)
} }
thisChainState.Allocs = buf.Bytes()
startHeader, err := env.L1Client.HeaderByNumber(ctx, nil) host.Wipe(env.Deployer)
dump, err := host.StateDump()
if err != nil { if err != nil {
return fmt.Errorf("failed to get start block: %w", err) return fmt.Errorf("failed to dump state: %w", err)
}
thisChainState.Allocs = &state.GzipData[foundry.ForgeAllocs]{
Data: dump,
} }
thisChainState.StartBlock = startHeader
return nil return nil
} }
func shouldGenerateL2Genesis(thisChainState *state.ChainState) bool {
return thisChainState.Allocs == nil
}
...@@ -5,71 +5,35 @@ import ( ...@@ -5,71 +5,35 @@ import (
"encoding/hex" "encoding/hex"
"errors" "errors"
"fmt" "fmt"
"math/big"
"github.com/ethereum-optimism/optimism/op-chain-ops/genesis" "github.com/ethereum-optimism/optimism/op-chain-ops/genesis"
"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/deployer/opcm"
"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/state" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/state"
state2 "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/state"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/ethclient"
) )
func DeployOPChain(ctx context.Context, env *Env, bundle ArtifactsBundle, intent *state2.Intent, st *state2.State, chainID common.Hash) error { func DeployOPChainLiveStrategy(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", "strategy", "live")
if !shouldDeployOPChain(st, chainID) { if !shouldDeployOPChain(st, chainID) {
lgr.Info("opchain deployment not needed") lgr.Info("opchain deployment not needed")
return nil return nil
} }
lgr.Info("deploying OP chain", "id", chainID.Hex())
thisIntent, err := intent.Chain(chainID) thisIntent, err := intent.Chain(chainID)
if err != nil { if err != nil {
return fmt.Errorf("failed to get chain intent: %w", err) return fmt.Errorf("failed to get chain intent: %w", err)
} }
opcmProxyAddress := st.ImplementationsDeployment.OpcmProxyAddress input := makeDCI(thisIntent, chainID, st)
input := opcm.DeployOPChainInput{
OpChainProxyAdminOwner: thisIntent.Roles.ProxyAdminOwner,
SystemConfigOwner: thisIntent.Roles.SystemConfigOwner,
Batcher: thisIntent.Roles.Batcher,
UnsafeBlockSigner: thisIntent.Roles.UnsafeBlockSigner,
Proposer: thisIntent.Roles.Proposer,
Challenger: thisIntent.Roles.Challenger,
BasefeeScalar: 1368,
BlobBaseFeeScalar: 801949,
L2ChainId: chainID.Big(),
OpcmProxy: opcmProxyAddress,
SaltMixer: st.Create2Salt.String(), // passing through salt generated at state initialization
GasLimit: 60_000_000,
DisputeGameType: 1, // PERMISSIONED_CANNON Game Type
DisputeAbsolutePrestate: common.HexToHash("0x038512e02c4c3f7bdaec27d00edf55b7155e0905301e1a88083e4e0a6764d54c"),
DisputeMaxGameDepth: 73,
DisputeSplitDepth: 30,
DisputeClockExtension: 10800, // 3 hours (input in seconds)
DisputeMaxClockDuration: 302400, // 3.5 days (input in seconds)
}
var dco opcm.DeployOPChainOutput var dco opcm.DeployOPChainOutput
lgr.Info("deploying using existing OPCM", "address", opcmProxyAddress.Hex()) lgr.Info("deploying OP chain using existing OPCM", "id", chainID.Hex(), "opcmAddress", st.ImplementationsDeployment.OpcmProxyAddress.Hex())
bcaster, err := broadcaster.NewKeyedBroadcaster(broadcaster.KeyedBroadcasterOpts{
Logger: lgr,
ChainID: big.NewInt(int64(intent.L1ChainID)),
Client: env.L1Client,
Signer: env.Signer,
From: env.Deployer,
})
if err != nil {
return fmt.Errorf("failed to create broadcaster: %w", err)
}
dco, err = opcm.DeployOPChainRaw( dco, err = opcm.DeployOPChainRaw(
ctx, ctx,
env.L1Client, env.L1Client,
bcaster, env.Broadcaster,
env.Deployer, env.Deployer,
bundle.L1, bundle.L1,
input, input,
...@@ -78,25 +42,8 @@ func DeployOPChain(ctx context.Context, env *Env, bundle ArtifactsBundle, intent ...@@ -78,25 +42,8 @@ func DeployOPChain(ctx context.Context, env *Env, bundle ArtifactsBundle, intent
return fmt.Errorf("error deploying OP chain: %w", err) return fmt.Errorf("error deploying OP chain: %w", err)
} }
st.Chains = append(st.Chains, &state2.ChainState{ st.Chains = append(st.Chains, makeChainState(chainID, dco))
ID: chainID, opcmProxyAddress := st.ImplementationsDeployment.OpcmProxyAddress
ProxyAdminAddress: dco.OpChainProxyAdmin,
AddressManagerAddress: dco.AddressManager,
L1ERC721BridgeProxyAddress: dco.L1ERC721BridgeProxy,
SystemConfigProxyAddress: dco.SystemConfigProxy,
OptimismMintableERC20FactoryProxyAddress: dco.OptimismMintableERC20FactoryProxy,
L1StandardBridgeProxyAddress: dco.L1StandardBridgeProxy,
L1CrossDomainMessengerProxyAddress: dco.L1CrossDomainMessengerProxy,
OptimismPortalProxyAddress: dco.OptimismPortalProxy,
DisputeGameFactoryProxyAddress: dco.DisputeGameFactoryProxy,
AnchorStateRegistryProxyAddress: dco.AnchorStateRegistryProxy,
AnchorStateRegistryImplAddress: dco.AnchorStateRegistryImpl,
FaultDisputeGameAddress: dco.FaultDisputeGame,
PermissionedDisputeGameAddress: dco.PermissionedDisputeGame,
DelayedWETHPermissionedGameProxyAddress: dco.DelayedWETHPermissionedGameProxy,
DelayedWETHPermissionlessGameProxyAddress: dco.DelayedWETHPermissionlessGameProxy,
})
err = conditionallySetImplementationAddresses(ctx, env.L1Client, intent, st, dco, opcmProxyAddress) err = conditionallySetImplementationAddresses(ctx, env.L1Client, intent, st, dco, opcmProxyAddress)
if err != nil { if err != nil {
return fmt.Errorf("failed to set implementation addresses: %w", err) return fmt.Errorf("failed to set implementation addresses: %w", err)
...@@ -107,7 +54,7 @@ func DeployOPChain(ctx context.Context, env *Env, bundle ArtifactsBundle, intent ...@@ -107,7 +54,7 @@ func DeployOPChain(ctx context.Context, env *Env, bundle ArtifactsBundle, intent
// Only try to set the implementation addresses if we reused existing implementations from a release tag. // Only try to set the implementation addresses if we reused existing implementations from a release tag.
// The reason why these addresses could be empty is because only DeployOPChain.s.sol is invoked as part of the pipeline. // The reason why these addresses could be empty is because only DeployOPChain.s.sol is invoked as part of the pipeline.
func conditionallySetImplementationAddresses(ctx context.Context, client *ethclient.Client, intent *state2.Intent, st *state.State, dco opcm.DeployOPChainOutput, opcmProxyAddress common.Address) error { func conditionallySetImplementationAddresses(ctx context.Context, client *ethclient.Client, intent *state.Intent, st *state.State, dco opcm.DeployOPChainOutput, opcmProxyAddress common.Address) error {
if !intent.L1ContractsLocator.IsTag() { if !intent.L1ContractsLocator.IsTag() {
return nil return nil
} }
...@@ -166,8 +113,6 @@ func conditionallySetImplementationAddresses(ctx context.Context, client *ethcli ...@@ -166,8 +113,6 @@ func conditionallySetImplementationAddresses(ctx context.Context, client *ethcli
return fmt.Errorf("failed to set implementation addresses: %w", lastTaskErr) return fmt.Errorf("failed to set implementation addresses: %w", lastTaskErr)
} }
fmt.Printf("st.ImplementationsDeployment: %+v\n", st.ImplementationsDeployment)
return nil return nil
} }
...@@ -194,6 +139,82 @@ func setPreimageOracleAddress(ctx context.Context, client *ethclient.Client, err ...@@ -194,6 +139,82 @@ func setPreimageOracleAddress(ctx context.Context, client *ethclient.Client, err
errCh <- err errCh <- err
} }
func DeployOPChainGenesisStrategy(env *Env, intent *state.Intent, st *state.State, chainID common.Hash) error {
lgr := env.Logger.New("stage", "deploy-opchain", "strategy", "genesis")
if !shouldDeployOPChain(st, chainID) {
lgr.Info("opchain deployment not needed")
return nil
}
thisIntent, err := intent.Chain(chainID)
if err != nil {
return fmt.Errorf("failed to get chain intent: %w", err)
}
input := makeDCI(thisIntent, chainID, st)
env.L1ScriptHost.ImportState(st.L1StateDump.Data)
var dco opcm.DeployOPChainOutput
lgr.Info("deploying OP chain using local allocs", "id", chainID.Hex())
dco, err = opcm.DeployOPChain(
env.L1ScriptHost,
input,
)
if err != nil {
return fmt.Errorf("error deploying OP chain: %w", err)
}
st.Chains = append(st.Chains, makeChainState(chainID, dco))
return nil
}
func makeDCI(thisIntent *state.ChainIntent, chainID common.Hash, st *state.State) opcm.DeployOPChainInput {
return opcm.DeployOPChainInput{
OpChainProxyAdminOwner: thisIntent.Roles.ProxyAdminOwner,
SystemConfigOwner: thisIntent.Roles.SystemConfigOwner,
Batcher: thisIntent.Roles.Batcher,
UnsafeBlockSigner: thisIntent.Roles.UnsafeBlockSigner,
Proposer: thisIntent.Roles.Proposer,
Challenger: thisIntent.Roles.Challenger,
BasefeeScalar: 1368,
BlobBaseFeeScalar: 801949,
L2ChainId: chainID.Big(),
OpcmProxy: st.ImplementationsDeployment.OpcmProxyAddress,
SaltMixer: st.Create2Salt.String(), // passing through salt generated at state initialization
GasLimit: 60_000_000,
DisputeGameType: 1, // PERMISSIONED_CANNON Game Type
DisputeAbsolutePrestate: common.HexToHash("0x038512e02c4c3f7bdaec27d00edf55b7155e0905301e1a88083e4e0a6764d54c"),
DisputeMaxGameDepth: 73,
DisputeSplitDepth: 30,
DisputeClockExtension: 10800, // 3 hours (input in seconds)
DisputeMaxClockDuration: 302400, // 3.5 days (input in seconds)
}
}
func makeChainState(chainID common.Hash, dco opcm.DeployOPChainOutput) *state.ChainState {
return &state.ChainState{
ID: chainID,
ProxyAdminAddress: dco.OpChainProxyAdmin,
AddressManagerAddress: dco.AddressManager,
L1ERC721BridgeProxyAddress: dco.L1ERC721BridgeProxy,
SystemConfigProxyAddress: dco.SystemConfigProxy,
OptimismMintableERC20FactoryProxyAddress: dco.OptimismMintableERC20FactoryProxy,
L1StandardBridgeProxyAddress: dco.L1StandardBridgeProxy,
L1CrossDomainMessengerProxyAddress: dco.L1CrossDomainMessengerProxy,
OptimismPortalProxyAddress: dco.OptimismPortalProxy,
DisputeGameFactoryProxyAddress: dco.DisputeGameFactoryProxy,
AnchorStateRegistryProxyAddress: dco.AnchorStateRegistryProxy,
AnchorStateRegistryImplAddress: dco.AnchorStateRegistryImpl,
FaultDisputeGameAddress: dco.FaultDisputeGame,
PermissionedDisputeGameAddress: dco.PermissionedDisputeGame,
DelayedWETHPermissionedGameProxyAddress: dco.DelayedWETHPermissionedGameProxy,
DelayedWETHPermissionlessGameProxyAddress: dco.DelayedWETHPermissionlessGameProxy,
}
}
func setRDPImplementationAddress(ctx context.Context, client *ethclient.Client, errCh chan error, addressManager common.Address, implAddress *common.Address, getNameArg string) { func setRDPImplementationAddress(ctx context.Context, client *ethclient.Client, errCh chan error, addressManager common.Address, implAddress *common.Address, getNameArg string) {
if *implAddress != (common.Address{}) { if *implAddress != (common.Address{}) {
errCh <- nil errCh <- nil
......
package pipeline
import (
"context"
"fmt"
"time"
"github.com/ethereum-optimism/optimism/op-chain-ops/genesis"
"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/state"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
)
func SetStartBlockLiveStrategy(ctx context.Context, env *Env, st *state.State, chainID common.Hash) error {
lgr := env.Logger.New("stage", "set-start-block", "strategy", "live")
lgr.Info("setting start block", "id", chainID.Hex())
thisChainState, err := st.Chain(chainID)
if err != nil {
return fmt.Errorf("failed to get chain state: %w", err)
}
startHeader, err := env.L1Client.HeaderByNumber(ctx, nil)
if err != nil {
return fmt.Errorf("failed to get start block: %w", err)
}
thisChainState.StartBlock = startHeader
return nil
}
func SetStartBlockGenesisStrategy(env *Env, st *state.State, chainID common.Hash) error {
lgr := env.Logger.New("stage", "set-start-block", "strategy", "genesis")
lgr.Info("setting start block", "id", chainID.Hex())
thisChainState, err := st.Chain(chainID)
if err != nil {
return fmt.Errorf("failed to get chain state: %w", err)
}
deployConfig := &genesis.DeployConfig{
DevL1DeployConfig: genesis.DevL1DeployConfig{
L1BlockTime: 12,
L1GenesisBlockTimestamp: hexutil.Uint64(time.Now().Unix()),
},
L2InitializationConfig: genesis.L2InitializationConfig{
L2CoreDeployConfig: genesis.L2CoreDeployConfig{
L1ChainID: 900,
},
DevDeployConfig: genesis.DevDeployConfig{
FundDevAccounts: true,
},
},
}
devGenesis, err := genesis.BuildL1DeveloperGenesis(deployConfig, st.L1StateDump.Data, &genesis.L1Deployments{})
if err != nil {
return fmt.Errorf("failed to build L1 developer genesis: %w", err)
}
thisChainState.StartBlock = devGenesis.ToBlock().Header()
return nil
}
package pipeline package pipeline
import ( import (
"context"
"fmt" "fmt"
"math/big"
"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/opcm" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/opcm"
state2 "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/state" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/state"
"github.com/ethereum-optimism/optimism/op-chain-ops/script"
"github.com/ethereum-optimism/optimism/op-chain-ops/foundry"
"github.com/ethereum-optimism/optimism/op-node/rollup" "github.com/ethereum-optimism/optimism/op-node/rollup"
) )
func DeploySuperchain(ctx context.Context, env *Env, bundle ArtifactsBundle, intent *state2.Intent, st *state2.State) error { func DeploySuperchain(env *Env, 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) {
...@@ -24,58 +19,32 @@ func DeploySuperchain(ctx context.Context, env *Env, bundle ArtifactsBundle, int ...@@ -24,58 +19,32 @@ func DeploySuperchain(ctx context.Context, env *Env, bundle ArtifactsBundle, int
lgr.Info("deploying superchain") lgr.Info("deploying superchain")
var dump *foundry.ForgeAllocs dso, err := opcm.DeploySuperchain(
var dso opcm.DeploySuperchainOutput env.L1ScriptHost,
var err error opcm.DeploySuperchainInput{
err = CallScriptBroadcast( SuperchainProxyAdminOwner: intent.SuperchainRoles.ProxyAdminOwner,
ctx, ProtocolVersionsOwner: intent.SuperchainRoles.ProtocolVersionsOwner,
CallScriptBroadcastOpts{ Guardian: intent.SuperchainRoles.Guardian,
L1ChainID: big.NewInt(int64(intent.L1ChainID)), Paused: false,
Logger: lgr, RequiredProtocolVersion: rollup.OPStackSupport,
ArtifactsFS: bundle.L1, RecommendedProtocolVersion: rollup.OPStackSupport,
Deployer: env.Deployer,
Signer: env.Signer,
Client: env.L1Client,
Broadcaster: KeyedBroadcaster,
Handler: func(host *script.Host) error {
dso, err = opcm.DeploySuperchain(
host,
opcm.DeploySuperchainInput{
SuperchainProxyAdminOwner: intent.SuperchainRoles.ProxyAdminOwner,
ProtocolVersionsOwner: intent.SuperchainRoles.ProtocolVersionsOwner,
Guardian: intent.SuperchainRoles.Guardian,
Paused: false,
RequiredProtocolVersion: rollup.OPStackSupport,
RecommendedProtocolVersion: rollup.OPStackSupport,
},
)
if err != nil {
return fmt.Errorf("failed to deploy superchain: %w", err)
}
dump, err = host.StateDump()
if err != nil {
return fmt.Errorf("error dumping state: %w", err)
}
return nil
},
}, },
) )
if err != nil { if err != nil {
return fmt.Errorf("error deploying superchain: %w", err) return fmt.Errorf("failed to deploy superchain: %w", err)
} }
st.SuperchainDeployment = &state2.SuperchainDeployment{ st.SuperchainDeployment = &state.SuperchainDeployment{
ProxyAdminAddress: dso.SuperchainProxyAdmin, ProxyAdminAddress: dso.SuperchainProxyAdmin,
SuperchainConfigProxyAddress: dso.SuperchainConfigProxy, SuperchainConfigProxyAddress: dso.SuperchainConfigProxy,
SuperchainConfigImplAddress: dso.SuperchainConfigImpl, SuperchainConfigImplAddress: dso.SuperchainConfigImpl,
ProtocolVersionsProxyAddress: dso.ProtocolVersionsProxy, ProtocolVersionsProxyAddress: dso.ProtocolVersionsProxy,
ProtocolVersionsImplAddress: dso.ProtocolVersionsImpl, ProtocolVersionsImplAddress: dso.ProtocolVersionsImpl,
StateDump: dump,
} }
return nil return nil
} }
func shouldDeploySuperchain(intent *state2.Intent, st *state2.State) bool { func shouldDeploySuperchain(intent *state.Intent, st *state.State) bool {
return st.SuperchainDeployment == nil return st.SuperchainDeployment == nil
} }
package state
import (
"bytes"
"compress/gzip"
"encoding/json"
"fmt"
)
type GzipData[T any] struct {
Data *T
}
func (g *GzipData[T]) MarshalJSON() ([]byte, error) {
jsonData, err := json.Marshal(g.Data)
if err != nil {
return nil, fmt.Errorf("failed to encode json: %w", err)
}
var buf bytes.Buffer
gw := gzip.NewWriter(&buf)
if _, err := gw.Write(jsonData); err != nil {
return nil, fmt.Errorf("failed to write gzip data: %w", err)
}
if err := gw.Close(); err != nil {
return nil, fmt.Errorf("failed to close gzip writer: %w", err)
}
return json.Marshal(Base64Bytes(buf.Bytes()))
}
func (g *GzipData[T]) UnmarshalJSON(b []byte) error {
var b64B Base64Bytes
if err := json.Unmarshal(b, &b64B); err != nil {
return fmt.Errorf("failed to decode gzip data: %w", err)
}
gr, err := gzip.NewReader(bytes.NewReader(b64B))
if err != nil {
return fmt.Errorf("failed to create gzip reader: %w", err)
}
defer gr.Close()
var data T
if err := json.NewDecoder(gr).Decode(&data); err != nil {
return fmt.Errorf("failed to decode gzip data: %w", err)
}
g.Data = &data
return nil
}
package state
import (
"encoding/json"
"fmt"
"math/big"
"testing"
"github.com/ethereum-optimism/optimism/op-chain-ops/foundry"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/stretchr/testify/require"
)
func TestGzipData_Marshaling(t *testing.T) {
type ts struct {
Field *GzipData[foundry.ForgeAllocs]
}
tests := []struct {
name string
in ts
out string
}{
{
name: "empty",
in: ts{},
out: "null",
},
{
name: "contains some data",
in: ts{
Field: &GzipData[foundry.ForgeAllocs]{
Data: &foundry.ForgeAllocs{
Accounts: map[common.Address]types.Account{
common.HexToAddress("0x1"): {
Balance: big.NewInt(1),
},
},
},
},
},
out: `"H4sIAAAAAAAA/6pWMqgwIA4YKllVKyUl5iTmJacqWSkZVBgq1dYCAgAA//9hulF0QAAAAA=="`,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
data, err := json.Marshal(tt.in)
require.NoError(t, err)
require.Equal(t, fmt.Sprintf(`{"Field":%s}`, tt.out), string(data))
var unmarshalled ts
err = json.Unmarshal(data, &unmarshalled)
require.NoError(t, err)
require.EqualValues(t, tt.in, unmarshalled)
})
}
}
...@@ -11,12 +11,30 @@ import ( ...@@ -11,12 +11,30 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
) )
type DeploymentStrategy string
const (
DeploymentStrategyLive DeploymentStrategy = "live"
DeploymentStrategyGenesis DeploymentStrategy = "genesis"
)
func (d DeploymentStrategy) Check() error {
switch d {
case DeploymentStrategyLive, DeploymentStrategyGenesis:
return nil
default:
return fmt.Errorf("deployment strategy must be 'live' or 'genesis'")
}
}
var emptyAddress common.Address var emptyAddress common.Address
type Intent struct { type Intent struct {
DeploymentStrategy DeploymentStrategy `json:"deploymentStrategy" toml:"deploymentStrategy"`
L1ChainID uint64 `json:"l1ChainID" toml:"l1ChainID"` L1ChainID uint64 `json:"l1ChainID" toml:"l1ChainID"`
SuperchainRoles SuperchainRoles `json:"superchainRoles" toml:"-"` SuperchainRoles *SuperchainRoles `json:"superchainRoles" toml:"superchainRoles,omitempty"`
FundDevAccounts bool `json:"fundDevAccounts" toml:"fundDevAccounts"` FundDevAccounts bool `json:"fundDevAccounts" toml:"fundDevAccounts"`
...@@ -34,6 +52,10 @@ func (c *Intent) L1ChainIDBig() *big.Int { ...@@ -34,6 +52,10 @@ func (c *Intent) L1ChainIDBig() *big.Int {
} }
func (c *Intent) Check() error { func (c *Intent) Check() error {
if c.DeploymentStrategy != DeploymentStrategyLive && c.DeploymentStrategy != DeploymentStrategyGenesis {
return fmt.Errorf("deploymentStrategy must be 'live' or 'local'")
}
if c.L1ChainID == 0 { if c.L1ChainID == 0 {
return fmt.Errorf("l1ChainID must be set") return fmt.Errorf("l1ChainID must be set")
} }
......
package state package state
import ( import (
"bytes"
"compress/gzip"
"encoding/json"
"fmt" "fmt"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
...@@ -40,6 +37,9 @@ type State struct { ...@@ -40,6 +37,9 @@ type State struct {
// Chains contains data about L2 chain deployments. // Chains contains data about L2 chain deployments.
Chains []*ChainState `json:"opChainDeployments"` Chains []*ChainState `json:"opChainDeployments"`
// L1StateDump contains the complete L1 state dump of the deployment.
L1StateDump *GzipData[foundry.ForgeAllocs] `json:"l1StateDump"`
} }
func (s *State) WriteToFile(path string) error { func (s *State) WriteToFile(path string) error {
...@@ -56,27 +56,25 @@ func (s *State) Chain(id common.Hash) (*ChainState, error) { ...@@ -56,27 +56,25 @@ func (s *State) Chain(id common.Hash) (*ChainState, error) {
} }
type SuperchainDeployment struct { type SuperchainDeployment struct {
ProxyAdminAddress common.Address `json:"proxyAdminAddress"` ProxyAdminAddress common.Address `json:"proxyAdminAddress"`
SuperchainConfigProxyAddress common.Address `json:"superchainConfigProxyAddress"` SuperchainConfigProxyAddress common.Address `json:"superchainConfigProxyAddress"`
SuperchainConfigImplAddress common.Address `json:"superchainConfigImplAddress"` SuperchainConfigImplAddress common.Address `json:"superchainConfigImplAddress"`
ProtocolVersionsProxyAddress common.Address `json:"protocolVersionsProxyAddress"` ProtocolVersionsProxyAddress common.Address `json:"protocolVersionsProxyAddress"`
ProtocolVersionsImplAddress common.Address `json:"protocolVersionsImplAddress"` ProtocolVersionsImplAddress common.Address `json:"protocolVersionsImplAddress"`
StateDump *foundry.ForgeAllocs `json:"-"`
} }
type ImplementationsDeployment struct { type ImplementationsDeployment struct {
OpcmProxyAddress common.Address `json:"opcmProxyAddress"` OpcmProxyAddress common.Address `json:"opcmProxyAddress"`
DelayedWETHImplAddress common.Address `json:"delayedWETHImplAddress"` DelayedWETHImplAddress common.Address `json:"delayedWETHImplAddress"`
OptimismPortalImplAddress common.Address `json:"optimismPortalImplAddress"` OptimismPortalImplAddress common.Address `json:"optimismPortalImplAddress"`
PreimageOracleSingletonAddress common.Address `json:"preimageOracleSingletonAddress"` PreimageOracleSingletonAddress common.Address `json:"preimageOracleSingletonAddress"`
MipsSingletonAddress common.Address `json:"mipsSingletonAddress"` MipsSingletonAddress common.Address `json:"mipsSingletonAddress"`
SystemConfigImplAddress common.Address `json:"systemConfigImplAddress"` SystemConfigImplAddress common.Address `json:"systemConfigImplAddress"`
L1CrossDomainMessengerImplAddress common.Address `json:"l1CrossDomainMessengerImplAddress"` L1CrossDomainMessengerImplAddress common.Address `json:"l1CrossDomainMessengerImplAddress"`
L1ERC721BridgeImplAddress common.Address `json:"l1ERC721BridgeImplAddress"` L1ERC721BridgeImplAddress common.Address `json:"l1ERC721BridgeImplAddress"`
L1StandardBridgeImplAddress common.Address `json:"l1StandardBridgeImplAddress"` L1StandardBridgeImplAddress common.Address `json:"l1StandardBridgeImplAddress"`
OptimismMintableERC20FactoryImplAddress common.Address `json:"optimismMintableERC20FactoryImplAddress"` OptimismMintableERC20FactoryImplAddress common.Address `json:"optimismMintableERC20FactoryImplAddress"`
DisputeGameFactoryImplAddress common.Address `json:"disputeGameFactoryImplAddress"` DisputeGameFactoryImplAddress common.Address `json:"disputeGameFactoryImplAddress"`
StateDump *foundry.ForgeAllocs `json:"-"`
} }
type ChainState struct { type ChainState struct {
...@@ -98,22 +96,7 @@ type ChainState struct { ...@@ -98,22 +96,7 @@ type ChainState struct {
DelayedWETHPermissionedGameProxyAddress common.Address `json:"delayedWETHPermissionedGameProxyAddress"` DelayedWETHPermissionedGameProxyAddress common.Address `json:"delayedWETHPermissionedGameProxyAddress"`
DelayedWETHPermissionlessGameProxyAddress common.Address `json:"delayedWETHPermissionlessGameProxyAddress"` DelayedWETHPermissionlessGameProxyAddress common.Address `json:"delayedWETHPermissionlessGameProxyAddress"`
Allocs Base64Bytes `json:"allocs"` Allocs *GzipData[foundry.ForgeAllocs] `json:"allocs"`
StartBlock *types.Header `json:"startBlock"` StartBlock *types.Header `json:"startBlock"`
} }
func (c *ChainState) UnmarshalAllocs() (*foundry.ForgeAllocs, error) {
gr, err := gzip.NewReader(bytes.NewReader(c.Allocs))
if err != nil {
return nil, fmt.Errorf("failed to create gzip reader: %w", err)
}
defer gr.Close()
var allocs foundry.ForgeAllocs
if err := json.NewDecoder(gr).Decode(&allocs); err != nil {
return nil, fmt.Errorf("failed to decode allocs: %w", err)
}
return &allocs, nil
}
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