Commit 50564d43 authored by Matthew Slipper's avatar Matthew Slipper Committed by GitHub

op-deployer: Support forking live chains (#12918)

parent ae78b73d
...@@ -53,15 +53,7 @@ func (c *CheatCodesPrecompile) CreateSelectFork_84d52b7a(urlOrAlias string, txHa ...@@ -53,15 +53,7 @@ func (c *CheatCodesPrecompile) CreateSelectFork_84d52b7a(urlOrAlias string, txHa
// createSelectFork implements vm.createSelectFork: // createSelectFork implements vm.createSelectFork:
// https://book.getfoundry.sh/cheatcodes/create-select-fork // https://book.getfoundry.sh/cheatcodes/create-select-fork
func (c *CheatCodesPrecompile) createSelectFork(opts ...ForkOption) (*big.Int, error) { func (c *CheatCodesPrecompile) createSelectFork(opts ...ForkOption) (*big.Int, error) {
src, err := c.h.onFork(opts...) return c.h.CreateSelectFork(opts...)
if err != nil {
return nil, fmt.Errorf("failed to setup fork source: %w", err)
}
id, err := c.h.state.CreateSelectFork(src)
if err != nil {
return nil, fmt.Errorf("failed to create-select fork: %w", err)
}
return id.U256().ToBig(), nil
} }
// ActiveFork implements vm.activeFork: // ActiveFork implements vm.activeFork:
......
...@@ -53,7 +53,9 @@ func NewForkDB(source ForkSource) *ForkDB { ...@@ -53,7 +53,9 @@ func NewForkDB(source ForkSource) *ForkDB {
// fakeRoot is just a marker; every account we load into the fork-db has this storage-root. // fakeRoot is just a marker; every account we load into the fork-db has this storage-root.
// When opening a storage-trie, we sanity-check we have this root, or an empty trie. // When opening a storage-trie, we sanity-check we have this root, or an empty trie.
// And then just return the same global trie view for storage reads/writes. // And then just return the same global trie view for storage reads/writes.
var fakeRoot = common.Hash{0: 42} // It needs to be set to EmptyRootHash to avoid contract collision errors when
// deploying contracts, since Geth checks the storage root prior to deployment.
var fakeRoot = types.EmptyRootHash
func (f *ForkDB) OpenTrie(root common.Hash) (state.Trie, error) { func (f *ForkDB) OpenTrie(root common.Hash) (state.Trie, error) {
if f.active.stateRoot != root { if f.active.stateRoot != root {
......
...@@ -54,8 +54,6 @@ func NewForkableState(base VMStateDB) *ForkableState { ...@@ -54,8 +54,6 @@ func NewForkableState(base VMStateDB) *ForkableState {
addresses.DefaultSenderAddr: ForkID{}, addresses.DefaultSenderAddr: ForkID{},
addresses.VMAddr: ForkID{}, addresses.VMAddr: ForkID{},
addresses.ConsoleAddr: ForkID{}, addresses.ConsoleAddr: ForkID{},
addresses.ScriptDeployer: ForkID{},
addresses.ForgeDeployer: ForkID{},
}, },
fallback: base, fallback: base,
idCounter: 0, idCounter: 0,
...@@ -194,11 +192,27 @@ func (fst *ForkableState) MakePersistent(addr common.Address) { ...@@ -194,11 +192,27 @@ func (fst *ForkableState) MakePersistent(addr common.Address) {
fst.persistent[addr] = fst.activeFork fst.persistent[addr] = fst.activeFork
} }
// MakeExcluded excludes an account from forking. This is useful for things like scripts, which
// should always use the fallback state.
func (fst *ForkableState) MakeExcluded(addr common.Address) {
fst.persistent[addr] = ForkID{}
}
// RevokePersistent is like vm.revokePersistent, it undoes a previous vm.makePersistent. // RevokePersistent is like vm.revokePersistent, it undoes a previous vm.makePersistent.
func (fst *ForkableState) RevokePersistent(addr common.Address) { func (fst *ForkableState) RevokePersistent(addr common.Address) {
delete(fst.persistent, addr) delete(fst.persistent, addr)
} }
// RevokeExcluded undoes MakeExcluded. It will panic if the account was marked as
// persistent in a different fork.
func (fst *ForkableState) RevokeExcluded(addr common.Address) {
forkID, ok := fst.persistent[addr]
if ok && forkID != (ForkID{}) {
panic(fmt.Sprintf("cannot revoke excluded account %s since it was made persistent in fork %q", addr, forkID))
}
delete(fst.persistent, addr)
}
// IsPersistent is like vm.isPersistent, it checks if an account persists across forks. // IsPersistent is like vm.isPersistent, it checks if an account persists across forks.
func (fst *ForkableState) IsPersistent(addr common.Address) bool { func (fst *ForkableState) IsPersistent(addr common.Address) bool {
_, ok := fst.persistent[addr] _, ok := fst.persistent[addr]
......
...@@ -9,7 +9,6 @@ import ( ...@@ -9,7 +9,6 @@ import (
"math/big" "math/big"
"github.com/ethereum-optimism/optimism/op-chain-ops/script/addresses" "github.com/ethereum-optimism/optimism/op-chain-ops/script/addresses"
"github.com/holiman/uint256" "github.com/holiman/uint256"
"github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/accounts/abi"
...@@ -850,3 +849,15 @@ func (h *Host) RememberOnLabel(label, srcFile, contract string) error { ...@@ -850,3 +849,15 @@ func (h *Host) RememberOnLabel(label, srcFile, contract string) error {
}) })
return nil return nil
} }
func (h *Host) CreateSelectFork(opts ...ForkOption) (*big.Int, error) {
src, err := h.onFork(opts...)
if err != nil {
return nil, fmt.Errorf("failed to setup fork source: %w", err)
}
id, err := h.state.CreateSelectFork(src)
if err != nil {
return nil, fmt.Errorf("failed to create-select fork: %w", err)
}
return id.U256().ToBig(), nil
}
...@@ -282,8 +282,8 @@ func TestForkingScript(t *testing.T) { ...@@ -282,8 +282,8 @@ func TestForkingScript(t *testing.T) {
addr, err := h.LoadContract("ScriptExample.s.sol", "ForkTester") addr, err := h.LoadContract("ScriptExample.s.sol", "ForkTester")
require.NoError(t, err) require.NoError(t, err)
h.AllowCheatcodes(addr) h.AllowCheatcodes(addr)
// Make this script persistent so it doesn't call the fork RPC. // Make this script excluded so it doesn't call the fork RPC.
h.state.MakePersistent(addr) h.state.MakeExcluded(addr)
t.Logf("allowing %s to access cheatcodes", addr) t.Logf("allowing %s to access cheatcodes", addr)
input := bytes4("run()") input := bytes4("run()")
......
...@@ -33,8 +33,8 @@ func WithScript[B any](h *Host, name string, contract string) (b *B, cleanup fun ...@@ -33,8 +33,8 @@ func WithScript[B any](h *Host, name string, contract string) (b *B, cleanup fun
// compute address of script contract to be deployed // compute address of script contract to be deployed
addr := crypto.CreateAddress(deployer, deployNonce) addr := crypto.CreateAddress(deployer, deployNonce)
h.Label(addr, contract) h.Label(addr, contract)
h.AllowCheatcodes(addr) // before constructor execution, give our script cheatcode access h.AllowCheatcodes(addr) // before constructor execution, give our script cheatcode access
h.state.MakePersistent(addr) // scripts are persistent across forks h.state.MakeExcluded(addr) // scripts are persistent across forks
// init bindings (with ABI check) // init bindings (with ABI check)
bindings, err := MakeBindings[B](h.ScriptBackendFn(addr), func(abiDef string) bool { bindings, err := MakeBindings[B](h.ScriptBackendFn(addr), func(abiDef string) bool {
......
...@@ -7,11 +7,15 @@ import ( ...@@ -7,11 +7,15 @@ import (
"math/big" "math/big"
"strings" "strings"
"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/forking"
"github.com/ethereum/go-ethereum/rpc"
"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/artifacts" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/artifacts"
"github.com/ethereum-optimism/optimism/op-deployer/pkg/env" "github.com/ethereum-optimism/optimism/op-deployer/pkg/env"
"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/broadcaster"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
...@@ -104,50 +108,43 @@ func Apply(ctx context.Context, cfg ApplyConfig) error { ...@@ -104,50 +108,43 @@ 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)
} }
var l1Client *ethclient.Client if err := ApplyPipeline(ctx, ApplyPipelineOpts{
var deployer common.Address L1RPCUrl: cfg.L1RPCUrl,
var bcaster broadcaster.Broadcaster DeployerPrivateKey: cfg.privateKeyECDSA,
var startingNonce uint64 Intent: intent,
if intent.DeploymentStrategy == state.DeploymentStrategyLive { State: st,
if err := cfg.CheckLive(); err != nil { Logger: cfg.Logger,
return fmt.Errorf("invalid config for apply: %w", err) StateWriter: pipeline.WorkdirStateWriter(cfg.Workdir),
} }); err != nil {
return 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) return nil
if err != nil { }
return fmt.Errorf("failed to get chain ID: %w", err)
}
signer := opcrypto.SignerFnFromBind(opcrypto.PrivateKeySignerFn(cfg.privateKeyECDSA, chainID)) type pipelineStage struct {
deployer = crypto.PubkeyToAddress(cfg.privateKeyECDSA.PublicKey) name string
apply func() error
}
bcaster, err = broadcaster.NewKeyedBroadcaster(broadcaster.KeyedBroadcasterOpts{ type ApplyPipelineOpts struct {
Logger: cfg.Logger, L1RPCUrl string
ChainID: new(big.Int).SetUint64(intent.L1ChainID), DeployerPrivateKey *ecdsa.PrivateKey
Client: l1Client, Intent *state.Intent
Signer: signer, State *state.State
From: deployer, Logger log.Logger
}) StateWriter pipeline.StateWriter
if err != nil { }
return fmt.Errorf("failed to create broadcaster: %w", err)
}
startingNonce, err = l1Client.NonceAt(ctx, deployer, nil) func ApplyPipeline(
if err != nil { ctx context.Context,
return fmt.Errorf("failed to get starting nonce: %w", err) opts ApplyPipelineOpts,
} ) error {
} else { intent := opts.Intent
deployer = common.Address{0x01} st := opts.State
bcaster = broadcaster.NoopBroadcaster()
}
progressor := func(curr, total int64) { progressor := func(curr, total int64) {
cfg.Logger.Info("artifacts download progress", "current", curr, "total", total) opts.Logger.Info("artifacts download progress", "current", curr, "total", total)
} }
l1ArtifactsFS, cleanupL1, err := artifacts.Download(ctx, intent.L1ContractsLocator, progressor) l1ArtifactsFS, cleanupL1, err := artifacts.Download(ctx, intent.L1ContractsLocator, progressor)
...@@ -156,7 +153,7 @@ func Apply(ctx context.Context, cfg ApplyConfig) error { ...@@ -156,7 +153,7 @@ func Apply(ctx context.Context, cfg ApplyConfig) error {
} }
defer func() { defer func() {
if err := cleanupL1(); err != nil { if err := cleanupL1(); err != nil {
cfg.Logger.Warn("failed to clean up L1 artifacts", "err", err) opts.Logger.Warn("failed to clean up L1 artifacts", "err", err)
} }
}() }()
...@@ -166,7 +163,7 @@ func Apply(ctx context.Context, cfg ApplyConfig) error { ...@@ -166,7 +163,7 @@ func Apply(ctx context.Context, cfg ApplyConfig) error {
} }
defer func() { defer func() {
if err := cleanupL2(); err != nil { if err := cleanupL2(); err != nil {
cfg.Logger.Warn("failed to clean up L2 artifacts", "err", err) opts.Logger.Warn("failed to clean up L2 artifacts", "err", err)
} }
}() }()
...@@ -175,52 +172,101 @@ func Apply(ctx context.Context, cfg ApplyConfig) error { ...@@ -175,52 +172,101 @@ func Apply(ctx context.Context, cfg ApplyConfig) error {
L2: l2ArtifactsFS, L2: l2ArtifactsFS,
} }
l1Host, err := env.DefaultScriptHost(bcaster, cfg.Logger, deployer, bundle.L1, startingNonce) var deployer common.Address
if err != nil { var bcaster broadcaster.Broadcaster
return fmt.Errorf("failed to create L1 script host: %w", err) var l1Client *ethclient.Client
var l1Host *script.Host
if intent.DeploymentStrategy == state.DeploymentStrategyLive {
l1RPC, err := rpc.Dial(opts.L1RPCUrl)
if err != nil {
return fmt.Errorf("failed to connect to L1 RPC: %w", err)
}
l1Client = ethclient.NewClient(l1RPC)
chainID, err := l1Client.ChainID(ctx)
if err != nil {
return fmt.Errorf("failed to get chain ID: %w", err)
}
signer := opcrypto.SignerFnFromBind(opcrypto.PrivateKeySignerFn(opts.DeployerPrivateKey, chainID))
deployer = crypto.PubkeyToAddress(opts.DeployerPrivateKey.PublicKey)
bcaster, err = broadcaster.NewKeyedBroadcaster(broadcaster.KeyedBroadcasterOpts{
Logger: opts.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)
}
l1Host, err = env.DefaultScriptHost(
bcaster,
opts.Logger,
deployer,
bundle.L1,
script.WithForkHook(func(cfg *script.ForkConfig) (forking.ForkSource, error) {
src, err := forking.RPCSourceByNumber(cfg.URLOrAlias, l1RPC, *cfg.BlockNumber)
if err != nil {
return nil, fmt.Errorf("failed to create RPC fork source: %w", err)
}
return forking.Cache(src), nil
}),
)
if err != nil {
return fmt.Errorf("failed to create L1 script host: %w", err)
}
latest, err := l1Client.HeaderByNumber(ctx, nil)
if err != nil {
return fmt.Errorf("failed to get latest block: %w", err)
}
if _, err := l1Host.CreateSelectFork(
script.ForkWithURLOrAlias("main"),
script.ForkWithBlockNumberU256(latest.Number),
); err != nil {
return fmt.Errorf("failed to select fork: %w", err)
}
} else {
deployer = common.Address{0x01}
bcaster = broadcaster.NoopBroadcaster()
l1Host, err = env.DefaultScriptHost(
bcaster,
opts.Logger,
deployer,
bundle.L1,
)
if err != nil {
return fmt.Errorf("failed to create L1 script host: %w", err)
}
} }
env := &pipeline.Env{ pEnv := &pipeline.Env{
StateWriter: pipeline.WorkdirStateWriter(cfg.Workdir), StateWriter: opts.StateWriter,
L1ScriptHost: l1Host, L1ScriptHost: l1Host,
L1Client: l1Client, L1Client: l1Client,
Logger: cfg.Logger, Logger: opts.Logger,
Broadcaster: bcaster, Broadcaster: bcaster,
Deployer: deployer, 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", func() error { {"init", func() error {
if intent.DeploymentStrategy == state.DeploymentStrategyLive { if intent.DeploymentStrategy == state.DeploymentStrategyLive {
return pipeline.InitLiveStrategy(ctx, env, intent, st) return pipeline.InitLiveStrategy(ctx, pEnv, intent, st)
} else { } else {
return pipeline.InitGenesisStrategy(env, intent, st) return pipeline.InitGenesisStrategy(pEnv, intent, st)
} }
}}, }},
{"deploy-superchain", func() error { {"deploy-superchain", func() error {
return pipeline.DeploySuperchain(env, intent, st) return pipeline.DeploySuperchain(pEnv, intent, st)
}}, }},
{"deploy-implementations", func() error { {"deploy-implementations", func() error {
return pipeline.DeployImplementations(env, intent, st) return pipeline.DeployImplementations(pEnv, intent, st)
}}, }},
} }
...@@ -230,21 +276,17 @@ func ApplyPipeline( ...@@ -230,21 +276,17 @@ func ApplyPipeline(
pline = append(pline, pipelineStage{ pline = append(pline, pipelineStage{
fmt.Sprintf("deploy-opchain-%s", chainID.Hex()), fmt.Sprintf("deploy-opchain-%s", chainID.Hex()),
func() error { func() error {
if intent.DeploymentStrategy == state.DeploymentStrategyLive { return pipeline.DeployOPChain(pEnv, intent, st, chainID)
return pipeline.DeployOPChainLiveStrategy(ctx, env, bundle, intent, st, chainID)
} else {
return pipeline.DeployOPChainGenesisStrategy(env, intent, st, chainID)
}
}, },
}, pipelineStage{ }, pipelineStage{
fmt.Sprintf("deploy-alt-da-%s", chainID.Hex()), fmt.Sprintf("deploy-alt-da-%s", chainID.Hex()),
func() error { func() error {
return pipeline.DeployAltDA(env, intent, st, chainID) return pipeline.DeployAltDA(pEnv, intent, st, chainID)
}, },
}, pipelineStage{ }, pipelineStage{
fmt.Sprintf("generate-l2-genesis-%s", chainID.Hex()), fmt.Sprintf("generate-l2-genesis-%s", chainID.Hex()),
func() error { func() error {
return pipeline.GenerateL2Genesis(env, intent, bundle, st, chainID) return pipeline.GenerateL2Genesis(pEnv, intent, bundle, st, chainID)
}, },
}) })
} }
...@@ -257,9 +299,9 @@ func ApplyPipeline( ...@@ -257,9 +299,9 @@ func ApplyPipeline(
fmt.Sprintf("set-start-block-%s", chainID.Hex()), fmt.Sprintf("set-start-block-%s", chainID.Hex()),
func() error { func() error {
if intent.DeploymentStrategy == state.DeploymentStrategyLive { if intent.DeploymentStrategy == state.DeploymentStrategyLive {
return pipeline.SetStartBlockLiveStrategy(ctx, env, st, chainID) return pipeline.SetStartBlockLiveStrategy(ctx, pEnv, st, chainID)
} else { } else {
return pipeline.SetStartBlockGenesisStrategy(env, st, chainID) return pipeline.SetStartBlockGenesisStrategy(pEnv, st, chainID)
} }
}, },
}) })
...@@ -271,23 +313,27 @@ func ApplyPipeline( ...@@ -271,23 +313,27 @@ func ApplyPipeline(
if err := stage.apply(); 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)
} }
dump, err := env.L1ScriptHost.StateDump()
if err != nil { if intent.DeploymentStrategy == state.DeploymentStrategyGenesis {
return fmt.Errorf("failed to dump state: %w", err) dump, err := pEnv.L1ScriptHost.StateDump()
} if err != nil {
st.L1StateDump = &state.GzipData[foundry.ForgeAllocs]{ return fmt.Errorf("failed to dump state: %w", err)
Data: dump, }
st.L1StateDump = &state.GzipData[foundry.ForgeAllocs]{
Data: dump,
}
} }
if _, err := env.Broadcaster.Broadcast(ctx); err != nil {
if _, err := pEnv.Broadcaster.Broadcast(ctx); err != nil {
return fmt.Errorf("failed to broadcast stage %s: %w", stage.name, err) return fmt.Errorf("failed to broadcast stage %s: %w", stage.name, err)
} }
if err := env.StateWriter.WriteState(st); err != nil { if err := pEnv.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 := env.StateWriter.WriteState(st); err != nil { if err := pEnv.StateWriter.WriteState(st); err != nil {
return fmt.Errorf("failed to write state: %w", err) return fmt.Errorf("failed to write state: %w", err)
} }
......
...@@ -159,11 +159,11 @@ func DelayedWETH(ctx context.Context, cfg DelayedWETHConfig) error { ...@@ -159,11 +159,11 @@ func DelayedWETH(ctx context.Context, cfg DelayedWETHConfig) error {
lgr, lgr,
chainDeployer, chainDeployer,
artifactsFS, artifactsFS,
nonce,
) )
if err != nil { if err != nil {
return fmt.Errorf("failed to create script host: %w", err) return fmt.Errorf("failed to create script host: %w", err)
} }
host.SetNonce(chainDeployer, nonce)
var release string var release string
if cfg.ArtifactsLocator.IsTag() { if cfg.ArtifactsLocator.IsTag() {
......
...@@ -162,11 +162,11 @@ func DisputeGame(ctx context.Context, cfg DisputeGameConfig) error { ...@@ -162,11 +162,11 @@ func DisputeGame(ctx context.Context, cfg DisputeGameConfig) error {
lgr, lgr,
chainDeployer, chainDeployer,
artifactsFS, artifactsFS,
nonce,
) )
if err != nil { if err != nil {
return fmt.Errorf("failed to create script host: %w", err) return fmt.Errorf("failed to create script host: %w", err)
} }
host.SetNonce(chainDeployer, nonce)
var release string var release string
if cfg.ArtifactsLocator.IsTag() { if cfg.ArtifactsLocator.IsTag() {
......
...@@ -157,11 +157,11 @@ func MIPS(ctx context.Context, cfg MIPSConfig) error { ...@@ -157,11 +157,11 @@ func MIPS(ctx context.Context, cfg MIPSConfig) error {
lgr, lgr,
chainDeployer, chainDeployer,
artifactsFS, artifactsFS,
nonce,
) )
if err != nil { if err != nil {
return fmt.Errorf("failed to create script host: %w", err) return fmt.Errorf("failed to create script host: %w", err)
} }
host.SetNonce(chainDeployer, nonce)
var release string var release string
if cfg.ArtifactsLocator.IsTag() { if cfg.ArtifactsLocator.IsTag() {
......
...@@ -193,11 +193,11 @@ func OPCM(ctx context.Context, cfg OPCMConfig) error { ...@@ -193,11 +193,11 @@ func OPCM(ctx context.Context, cfg OPCMConfig) error {
lgr, lgr,
chainDeployer, chainDeployer,
artifactsFS, artifactsFS,
nonce,
) )
if err != nil { if err != nil {
return fmt.Errorf("failed to create script host: %w", err) return fmt.Errorf("failed to create script host: %w", err)
} }
host.SetNonce(chainDeployer, nonce)
var release string var release string
if cfg.ArtifactsLocator.IsTag() { if cfg.ArtifactsLocator.IsTag() {
......
...@@ -72,7 +72,6 @@ func L2SemversCLI(cliCtx *cli.Context) error { ...@@ -72,7 +72,6 @@ func L2SemversCLI(cliCtx *cli.Context) error {
l, l,
common.Address{19: 0x01}, common.Address{19: 0x01},
artifactsFS, artifactsFS,
0,
) )
if err != nil { if err != nil {
return fmt.Errorf("failed to create script host: %w", err) return fmt.Errorf("failed to create script host: %w", err)
......
...@@ -3,6 +3,7 @@ package integration_test ...@@ -3,6 +3,7 @@ package integration_test
import ( import (
"bytes" "bytes"
"context" "context"
"crypto/rand"
"encoding/hex" "encoding/hex"
"fmt" "fmt"
"log/slog" "log/slog"
...@@ -17,19 +18,13 @@ import ( ...@@ -17,19 +18,13 @@ import (
"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/artifacts" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/artifacts"
"github.com/ethereum-optimism/optimism/op-chain-ops/script"
"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/testutil"
"github.com/ethereum-optimism/optimism/op-deployer/pkg/env"
"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/broadcaster"
"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/standard"
"github.com/ethereum-optimism/optimism/op-service/testutils/anvil"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/log"
"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/pipeline" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/pipeline"
"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/standard"
"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-deployer/pkg/deployer/testutil"
"github.com/ethereum-optimism/optimism/op-service/testutils/anvil"
"github.com/ethereum/go-ethereum/crypto"
op_e2e "github.com/ethereum-optimism/optimism/op-e2e" op_e2e "github.com/ethereum-optimism/optimism/op-e2e"
...@@ -37,7 +32,6 @@ import ( ...@@ -37,7 +32,6 @@ import (
"github.com/ethereum-optimism/optimism/op-chain-ops/devkeys" "github.com/ethereum-optimism/optimism/op-chain-ops/devkeys"
"github.com/ethereum-optimism/optimism/op-chain-ops/genesis" "github.com/ethereum-optimism/optimism/op-chain-ops/genesis"
opcrypto "github.com/ethereum-optimism/optimism/op-service/crypto"
"github.com/ethereum-optimism/optimism/op-service/predeploys" "github.com/ethereum-optimism/optimism/op-service/predeploys"
"github.com/ethereum-optimism/optimism/op-service/testlog" "github.com/ethereum-optimism/optimism/op-service/testlog"
"github.com/ethereum-optimism/optimism/op-service/testutils/kurtosisutil" "github.com/ethereum-optimism/optimism/op-service/testutils/kurtosisutil"
...@@ -106,36 +100,25 @@ func TestEndToEndApply(t *testing.T) { ...@@ -106,36 +100,25 @@ func TestEndToEndApply(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
pk, err := dk.Secret(depKey) pk, err := dk.Secret(depKey)
require.NoError(t, err) require.NoError(t, err)
signer := opcrypto.SignerFnFromBind(opcrypto.PrivateKeySignerFn(pk, l1ChainID))
l2ChainID1 := uint256.NewInt(1) l2ChainID1 := uint256.NewInt(1)
l2ChainID2 := uint256.NewInt(2) l2ChainID2 := uint256.NewInt(2)
deployerAddr, err := dk.Address(depKey)
require.NoError(t, err)
loc, _ := testutil.LocalArtifacts(t) loc, _ := testutil.LocalArtifacts(t)
bcaster, err := broadcaster.NewKeyedBroadcaster(broadcaster.KeyedBroadcasterOpts{
Logger: log.NewLogger(log.DiscardHandler()),
ChainID: l1ChainID,
Client: l1Client,
Signer: signer,
From: deployerAddr,
})
require.NoError(t, err)
env, bundle, _ := createEnv(t, lgr, l1Client, bcaster, deployerAddr)
intent, st := newIntent(t, l1ChainID, dk, l2ChainID1, loc, loc) intent, st := newIntent(t, l1ChainID, dk, l2ChainID1, loc, loc)
cg := ethClientCodeGetter(ctx, l1Client) cg := ethClientCodeGetter(ctx, l1Client)
t.Run("initial chain", func(t *testing.T) { t.Run("initial chain", func(t *testing.T) {
require.NoError(t, deployer.ApplyPipeline( require.NoError(t, deployer.ApplyPipeline(
ctx, ctx,
env, deployer.ApplyPipelineOpts{
bundle, L1RPCUrl: rpcURL,
intent, DeployerPrivateKey: pk,
st, Intent: intent,
State: st,
Logger: lgr,
StateWriter: pipeline.NoopStateWriter(),
},
)) ))
validateSuperchainDeployment(t, st, cg) validateSuperchainDeployment(t, st, cg)
...@@ -145,15 +128,18 @@ func TestEndToEndApply(t *testing.T) { ...@@ -145,15 +128,18 @@ func TestEndToEndApply(t *testing.T) {
t.Run("subsequent chain", func(t *testing.T) { t.Run("subsequent chain", func(t *testing.T) {
// create a new environment with wiped state to ensure we can continue using the // create a new environment with wiped state to ensure we can continue using the
// state from the previous deployment // state from the previous deployment
env, bundle, _ = createEnv(t, lgr, l1Client, bcaster, deployerAddr)
intent.Chains = append(intent.Chains, newChainIntent(t, dk, l1ChainID, l2ChainID2)) intent.Chains = append(intent.Chains, newChainIntent(t, dk, l1ChainID, l2ChainID2))
require.NoError(t, deployer.ApplyPipeline( require.NoError(t, deployer.ApplyPipeline(
ctx, ctx,
env, deployer.ApplyPipelineOpts{
bundle, L1RPCUrl: rpcURL,
intent, DeployerPrivateKey: pk,
st, Intent: intent,
State: st,
Logger: lgr,
StateWriter: pipeline.NoopStateWriter(),
},
)) ))
validateOPChainDeployment(t, cg, st, intent) validateOPChainDeployment(t, cg, st, intent)
...@@ -191,24 +177,11 @@ func TestApplyExistingOPCM(t *testing.T) { ...@@ -191,24 +177,11 @@ func TestApplyExistingOPCM(t *testing.T) {
dk, err := devkeys.NewMnemonicDevKeys(devkeys.TestMnemonic) dk, err := devkeys.NewMnemonicDevKeys(devkeys.TestMnemonic)
require.NoError(t, err) require.NoError(t, err)
// index 0 from Anvil's test set // index 0 from Anvil's test set
priv, err := crypto.HexToECDSA("ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80") pk, err := crypto.HexToECDSA("ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80")
require.NoError(t, err) require.NoError(t, err)
signer := opcrypto.SignerFnFromBind(opcrypto.PrivateKeySignerFn(priv, l1ChainID))
deployerAddr := common.HexToAddress("0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266")
l2ChainID := uint256.NewInt(1) l2ChainID := uint256.NewInt(1)
bcaster, err := broadcaster.NewKeyedBroadcaster(broadcaster.KeyedBroadcasterOpts{
Logger: lgr,
ChainID: l1ChainID,
Client: l1Client,
Signer: signer,
From: deployerAddr,
})
require.NoError(t, err)
env, bundle, _ := createEnv(t, lgr, l1Client, bcaster, deployerAddr)
intent, st := newIntent( intent, st := newIntent(
t, t,
l1ChainID, l1ChainID,
...@@ -217,13 +190,20 @@ func TestApplyExistingOPCM(t *testing.T) { ...@@ -217,13 +190,20 @@ func TestApplyExistingOPCM(t *testing.T) {
artifacts.DefaultL1ContractsLocator, artifacts.DefaultL1ContractsLocator,
artifacts.DefaultL2ContractsLocator, artifacts.DefaultL2ContractsLocator,
) )
// Define a new create2 salt to avoid contract address collisions
_, err = rand.Read(st.Create2Salt[:])
require.NoError(t, err)
require.NoError(t, deployer.ApplyPipeline( require.NoError(t, deployer.ApplyPipeline(
ctx, ctx,
env, deployer.ApplyPipelineOpts{
bundle, L1RPCUrl: runner.RPCUrl(),
intent, DeployerPrivateKey: pk,
st, Intent: intent,
State: st,
Logger: lgr,
StateWriter: pipeline.NoopStateWriter(),
},
)) ))
validateOPChainDeployment(t, ethClientCodeGetter(ctx, l1Client), st, intent) validateOPChainDeployment(t, ethClientCodeGetter(ctx, l1Client), st, intent)
...@@ -236,18 +216,12 @@ func TestL2BlockTimeOverride(t *testing.T) { ...@@ -236,18 +216,12 @@ func TestL2BlockTimeOverride(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
defer cancel() defer cancel()
env, bundle, intent, st := setupGenesisChain(t) opts, intent, st := setupGenesisChain(t)
intent.GlobalDeployOverrides = map[string]interface{}{ intent.GlobalDeployOverrides = map[string]interface{}{
"l2BlockTime": float64(3), "l2BlockTime": float64(3),
} }
require.NoError(t, deployer.ApplyPipeline( require.NoError(t, deployer.ApplyPipeline(ctx, opts))
ctx,
env,
bundle,
intent,
st,
))
cfg, err := state.CombineDeployConfig(intent, intent.Chains[0], st, st.Chains[0]) cfg, err := state.CombineDeployConfig(intent, intent.Chains[0], st, st.Chains[0])
require.NoError(t, err) require.NoError(t, err)
...@@ -260,16 +234,9 @@ func TestApplyGenesisStrategy(t *testing.T) { ...@@ -260,16 +234,9 @@ func TestApplyGenesisStrategy(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
defer cancel() defer cancel()
env, bundle, intent, st := setupGenesisChain(t) opts, intent, st := setupGenesisChain(t)
intent.DeploymentStrategy = state.DeploymentStrategyGenesis
require.NoError(t, deployer.ApplyPipeline( require.NoError(t, deployer.ApplyPipeline(ctx, opts))
ctx,
env,
bundle,
intent,
st,
))
cg := stateDumpCodeGetter(st) cg := stateDumpCodeGetter(st)
validateSuperchainDeployment(t, st, cg) validateSuperchainDeployment(t, st, cg)
...@@ -287,7 +254,7 @@ func TestProofParamOverrides(t *testing.T) { ...@@ -287,7 +254,7 @@ func TestProofParamOverrides(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
defer cancel() defer cancel()
env, bundle, intent, st := setupGenesisChain(t) opts, intent, st := setupGenesisChain(t)
intent.GlobalDeployOverrides = map[string]any{ intent.GlobalDeployOverrides = map[string]any{
"withdrawalDelaySeconds": standard.WithdrawalDelaySeconds + 1, "withdrawalDelaySeconds": standard.WithdrawalDelaySeconds + 1,
"minProposalSizeBytes": standard.MinProposalSizeBytes + 1, "minProposalSizeBytes": standard.MinProposalSizeBytes + 1,
...@@ -304,13 +271,7 @@ func TestProofParamOverrides(t *testing.T) { ...@@ -304,13 +271,7 @@ func TestProofParamOverrides(t *testing.T) {
"dangerouslyAllowCustomDisputeParameters": true, "dangerouslyAllowCustomDisputeParameters": true,
} }
require.NoError(t, deployer.ApplyPipeline( require.NoError(t, deployer.ApplyPipeline(ctx, opts))
ctx,
env,
bundle,
intent,
st,
))
allocs := st.L1StateDump.Data.Accounts allocs := st.L1StateDump.Data.Accounts
chainState := st.Chains[0] chainState := st.Chains[0]
...@@ -390,16 +351,10 @@ func TestInteropDeployment(t *testing.T) { ...@@ -390,16 +351,10 @@ func TestInteropDeployment(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
defer cancel() defer cancel()
env, bundle, intent, st := setupGenesisChain(t) opts, intent, st := setupGenesisChain(t)
intent.UseInterop = true intent.UseInterop = true
require.NoError(t, deployer.ApplyPipeline( require.NoError(t, deployer.ApplyPipeline(ctx, opts))
ctx,
env,
bundle,
intent,
st,
))
chainState := st.Chains[0] chainState := st.Chains[0]
depManagerSlot := common.HexToHash("0x1708e077affb93e89be2665fb0fb72581be66f84dc00d25fed755ae911905b1c") depManagerSlot := common.HexToHash("0x1708e077affb93e89be2665fb0fb72581be66f84dc00d25fed755ae911905b1c")
...@@ -414,7 +369,7 @@ func TestAltDADeployment(t *testing.T) { ...@@ -414,7 +369,7 @@ func TestAltDADeployment(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
defer cancel() defer cancel()
env, bundle, intent, st := setupGenesisChain(t) opts, intent, st := setupGenesisChain(t)
altDACfg := genesis.AltDADeployConfig{ altDACfg := genesis.AltDADeployConfig{
UseAltDA: true, UseAltDA: true,
DACommitmentType: altda.KeccakCommitmentString, DACommitmentType: altda.KeccakCommitmentString,
...@@ -425,13 +380,7 @@ func TestAltDADeployment(t *testing.T) { ...@@ -425,13 +380,7 @@ func TestAltDADeployment(t *testing.T) {
} }
intent.Chains[0].DangerousAltDAConfig = altDACfg intent.Chains[0].DangerousAltDAConfig = altDACfg
require.NoError(t, deployer.ApplyPipeline( require.NoError(t, deployer.ApplyPipeline(ctx, opts))
ctx,
env,
bundle,
intent,
st,
))
chainState := st.Chains[0] chainState := st.Chains[0]
require.NotEmpty(t, chainState.DataAvailabilityChallengeProxyAddress) require.NotEmpty(t, chainState.DataAvailabilityChallengeProxyAddress)
...@@ -450,23 +399,9 @@ func TestAltDADeployment(t *testing.T) { ...@@ -450,23 +399,9 @@ func TestAltDADeployment(t *testing.T) {
func TestInvalidL2Genesis(t *testing.T) { func TestInvalidL2Genesis(t *testing.T) {
op_e2e.InitParallel(t) op_e2e.InitParallel(t)
lgr := testlog.Logger(t, slog.LevelDebug)
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
defer cancel() defer cancel()
depKey := new(deployerKey)
l1ChainID := big.NewInt(77799777)
dk, err := devkeys.NewMnemonicDevKeys(devkeys.TestMnemonic)
require.NoError(t, err)
l2ChainID1 := uint256.NewInt(1)
deployerAddr, err := dk.Address(depKey)
require.NoError(t, err)
loc, _ := testutil.LocalArtifacts(t)
// these tests were generated by grepping all usages of the deploy // these tests were generated by grepping all usages of the deploy
// config in L2Genesis.s.sol. // config in L2Genesis.s.sol.
tests := []struct { tests := []struct {
...@@ -512,26 +447,18 @@ func TestInvalidL2Genesis(t *testing.T) { ...@@ -512,26 +447,18 @@ func TestInvalidL2Genesis(t *testing.T) {
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
env, bundle, _ := createEnv(t, lgr, nil, broadcaster.NoopBroadcaster(), deployerAddr) opts, intent, _ := setupGenesisChain(t)
intent, st := newIntent(t, l1ChainID, dk, l2ChainID1, loc, loc)
intent.Chains = append(intent.Chains, newChainIntent(t, dk, l1ChainID, l2ChainID1))
intent.DeploymentStrategy = state.DeploymentStrategyGenesis intent.DeploymentStrategy = state.DeploymentStrategyGenesis
intent.GlobalDeployOverrides = tt.overrides intent.GlobalDeployOverrides = tt.overrides
err := deployer.ApplyPipeline( err := deployer.ApplyPipeline(ctx, opts)
ctx,
env,
bundle,
intent,
st,
)
require.Error(t, err) require.Error(t, err)
require.ErrorContains(t, err, "failed to combine L2 init config") require.ErrorContains(t, err, "failed to combine L2 init config")
}) })
} }
} }
func setupGenesisChain(t *testing.T) (*pipeline.Env, pipeline.ArtifactsBundle, *state.Intent, *state.State) { func setupGenesisChain(t *testing.T) (deployer.ApplyPipelineOpts, *state.Intent, *state.State) {
lgr := testlog.Logger(t, slog.LevelDebug) lgr := testlog.Logger(t, slog.LevelDebug)
depKey := new(deployerKey) depKey := new(deployerKey)
...@@ -541,51 +468,24 @@ func setupGenesisChain(t *testing.T) (*pipeline.Env, pipeline.ArtifactsBundle, * ...@@ -541,51 +468,24 @@ func setupGenesisChain(t *testing.T) (*pipeline.Env, pipeline.ArtifactsBundle, *
l2ChainID1 := uint256.NewInt(1) l2ChainID1 := uint256.NewInt(1)
deployerAddr, err := dk.Address(depKey) priv, err := dk.Secret(depKey)
require.NoError(t, err) require.NoError(t, err)
loc, _ := testutil.LocalArtifacts(t) loc, _ := testutil.LocalArtifacts(t)
env, bundle, _ := createEnv(t, lgr, nil, broadcaster.NoopBroadcaster(), deployerAddr)
intent, st := newIntent(t, l1ChainID, dk, l2ChainID1, loc, loc) intent, st := newIntent(t, l1ChainID, dk, l2ChainID1, loc, loc)
intent.Chains = append(intent.Chains, newChainIntent(t, dk, l1ChainID, l2ChainID1)) intent.Chains = append(intent.Chains, newChainIntent(t, dk, l1ChainID, l2ChainID1))
intent.DeploymentStrategy = state.DeploymentStrategyGenesis intent.DeploymentStrategy = state.DeploymentStrategyGenesis
return env, bundle, intent, st
}
func createEnv(
t *testing.T,
lgr log.Logger,
l1Client *ethclient.Client,
bcaster broadcaster.Broadcaster,
deployerAddr common.Address,
) (*pipeline.Env, pipeline.ArtifactsBundle, *script.Host) {
_, artifactsFS := testutil.LocalArtifacts(t)
host, err := env.DefaultScriptHost(
bcaster,
lgr,
deployerAddr,
artifactsFS,
0,
)
require.NoError(t, err)
env := &pipeline.Env{
StateWriter: pipeline.NoopStateWriter(),
L1ScriptHost: host,
L1Client: l1Client,
Broadcaster: bcaster,
Deployer: deployerAddr,
Logger: lgr,
}
bundle := pipeline.ArtifactsBundle{ opts := deployer.ApplyPipelineOpts{
L1: artifactsFS, DeployerPrivateKey: priv,
L2: artifactsFS, Intent: intent,
State: st,
Logger: lgr,
StateWriter: pipeline.NoopStateWriter(),
} }
return env, bundle, host return opts, intent, st
} }
func addrFor(t *testing.T, dk *devkeys.MnemonicDevKeys, key devkeys.Key) common.Address { func addrFor(t *testing.T, dk *devkeys.MnemonicDevKeys, key devkeys.Key) common.Address {
......
...@@ -21,7 +21,6 @@ func TestDeployAltDA(t *testing.T) { ...@@ -21,7 +21,6 @@ func TestDeployAltDA(t *testing.T) {
testlog.Logger(t, log.LevelInfo), testlog.Logger(t, log.LevelInfo),
common.Address{'D'}, common.Address{'D'},
artifacts, artifacts,
0,
) )
require.NoError(t, err) require.NoError(t, err)
......
...@@ -22,7 +22,6 @@ func TestDeployDelayedWETH(t *testing.T) { ...@@ -22,7 +22,6 @@ func TestDeployDelayedWETH(t *testing.T) {
testlog.Logger(t, log.LevelInfo), testlog.Logger(t, log.LevelInfo),
common.Address{'D'}, common.Address{'D'},
artifacts, artifacts,
0,
) )
require.NoError(t, err) require.NoError(t, err)
......
[
{
"type": "function",
"name": "decodeOutput",
"inputs": [],
"outputs": [
{
"name": "output",
"indexed": false,
"type": "tuple",
"components": [
{
"name": "opChainProxyAdmin",
"type": "address"
},
{
"name": "addressManager",
"type": "address"
},
{
"name": "l1ERC721BridgeProxy",
"type": "address"
},
{
"name": "systemConfigProxy",
"type": "address"
},
{
"name": "optimismMintableERC20FactoryProxy",
"type": "address"
},
{
"name": "l1StandardBridgeProxy",
"type": "address"
},
{
"name": "l1CrossDomainMessengerProxy",
"type": "address"
},
{
"name": "optimismPortalProxy",
"type": "address"
},
{
"name": "disputeGameFactoryProxy",
"type": "address"
},
{
"name": "anchorStateRegistryProxy",
"type": "address"
},
{
"name": "anchorStateRegistryImpl",
"type": "address"
},
{
"name": "faultDisputeGame",
"type": "address",
"internalType": "contract FaultDisputeGame"
},
{
"name": "permissionedDisputeGame",
"type": "address"
},
{
"name": "delayedWETHPermissionedGameProxy",
"type": "address"
},
{
"name": "delayedWETHPermissionlessGameProxy",
"type": "address"
}
]
}
]
}
]
\ No newline at end of file
...@@ -21,7 +21,6 @@ func TestDeployDisputeGame(t *testing.T) { ...@@ -21,7 +21,6 @@ func TestDeployDisputeGame(t *testing.T) {
testlog.Logger(t, log.LevelInfo), testlog.Logger(t, log.LevelInfo),
common.Address{'D'}, common.Address{'D'},
artifacts, artifacts,
0,
) )
require.NoError(t, err) require.NoError(t, err)
......
...@@ -20,7 +20,6 @@ func TestDeployMIPS(t *testing.T) { ...@@ -20,7 +20,6 @@ func TestDeployMIPS(t *testing.T) {
testlog.Logger(t, log.LevelInfo), testlog.Logger(t, log.LevelInfo),
common.Address{'D'}, common.Address{'D'},
artifacts, artifacts,
0,
) )
require.NoError(t, err) require.NoError(t, err)
......
package opcm package opcm
import ( import (
"context" _ "embed"
"fmt" "fmt"
"math/big" "math/big"
"strings"
_ "embed"
"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/script" "github.com/ethereum-optimism/optimism/op-chain-ops/script"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/holiman/uint256"
) )
// PermissionedGameStartingAnchorRoots is a root of bytes32(hex"dead") for the permissioned game at block 0, // PermissionedGameStartingAnchorRoots is a root of bytes32(hex"dead") for the permissioned game at block 0,
...@@ -25,48 +15,6 @@ var PermissionedGameStartingAnchorRoots = []byte{ ...@@ -25,48 +15,6 @@ var PermissionedGameStartingAnchorRoots = []byte{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xde, 0xad, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xde, 0xad, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
} }
// opcmRolesBase is an internal struct used to pass the roles to OPCM. See opcmDeployInputV160 for more info.
type opcmRolesBase struct {
OpChainProxyAdminOwner common.Address
SystemConfigOwner common.Address
Batcher common.Address
UnsafeBlockSigner common.Address
Proposer common.Address
Challenger common.Address
}
type opcmDeployInputBase struct {
BasefeeScalar uint32
BlobBasefeeScalar uint32
L2ChainId *big.Int
StartingAnchorRoots []byte
SaltMixer string
GasLimit uint64
DisputeGameType uint32
DisputeAbsolutePrestate common.Hash
DisputeMaxGameDepth *big.Int
DisputeSplitDepth *big.Int
DisputeClockExtension uint64
DisputeMaxClockDuration uint64
}
// opcmDeployInputV160 is the input struct for the deploy method of the OPStackManager contract. We
// define a separate struct here to match what the OPSM contract expects.
type opcmDeployInputV160 struct {
opcmDeployInputBase
Roles opcmRolesBase
}
type opcmRolesIsthmus struct {
opcmRolesBase
FeeAdmin common.Address
}
type opcmDeployInputIsthmus struct {
opcmDeployInputBase
Roles opcmRolesIsthmus
}
type DeployOPChainInputV160 struct { type DeployOPChainInputV160 struct {
OpChainProxyAdminOwner common.Address OpChainProxyAdminOwner common.Address
SystemConfigOwner common.Address SystemConfigOwner common.Address
...@@ -99,49 +47,11 @@ func (input *DeployOPChainInputV160) StartingAnchorRoots() []byte { ...@@ -99,49 +47,11 @@ func (input *DeployOPChainInputV160) StartingAnchorRoots() []byte {
return PermissionedGameStartingAnchorRoots return PermissionedGameStartingAnchorRoots
} }
func DeployOPChainInputV160DeployCalldata(input DeployOPChainInputV160) any {
return opcmDeployInputV160{
Roles: opcmRolesBase{
OpChainProxyAdminOwner: input.OpChainProxyAdminOwner,
SystemConfigOwner: input.SystemConfigOwner,
Batcher: input.Batcher,
UnsafeBlockSigner: input.UnsafeBlockSigner,
Proposer: input.Proposer,
Challenger: input.Challenger,
},
opcmDeployInputBase: opcmDeployInputBase{
BasefeeScalar: input.BasefeeScalar,
BlobBasefeeScalar: input.BlobBaseFeeScalar,
L2ChainId: input.L2ChainId,
StartingAnchorRoots: input.StartingAnchorRoots(),
SaltMixer: input.SaltMixer,
GasLimit: input.GasLimit,
DisputeGameType: input.DisputeGameType,
DisputeAbsolutePrestate: input.DisputeAbsolutePrestate,
DisputeMaxGameDepth: new(big.Int).SetUint64(input.DisputeMaxGameDepth),
DisputeSplitDepth: new(big.Int).SetUint64(input.DisputeSplitDepth),
DisputeClockExtension: input.DisputeClockExtension,
DisputeMaxClockDuration: input.DisputeMaxClockDuration,
},
}
}
type DeployOPChainInputIsthmus struct { type DeployOPChainInputIsthmus struct {
DeployOPChainInputV160 DeployOPChainInputV160
SystemConfigFeeAdmin common.Address SystemConfigFeeAdmin common.Address
} }
func DeployOPChainInputIsthmusDeployCalldata(input DeployOPChainInputIsthmus) any {
v160Data := DeployOPChainInputV160DeployCalldata(input.DeployOPChainInputV160).(opcmDeployInputV160)
return opcmDeployInputIsthmus{
Roles: opcmRolesIsthmus{
opcmRolesBase: v160Data.Roles,
FeeAdmin: input.SystemConfigFeeAdmin,
},
opcmDeployInputBase: v160Data.opcmDeployInputBase,
}
}
type DeployOPChainOutput struct { type DeployOPChainOutput struct {
OpChainProxyAdmin common.Address OpChainProxyAdmin common.Address
AddressManager common.Address AddressManager common.Address
...@@ -210,116 +120,58 @@ func deployOPChain[T any](host *script.Host, input T) (DeployOPChainOutput, erro ...@@ -210,116 +120,58 @@ func deployOPChain[T any](host *script.Host, input T) (DeployOPChainOutput, erro
return dco, nil return dco, nil
} }
// decodeOutputABIJSONV160 defines an ABI for a fake method called "decodeOutput" that returns the type ReadImplementationAddressesInput struct {
// DeployOutput struct. This allows the code in the deployer to decode directly into a struct DeployOPChainOutput
// using Geth's ABI library. OpcmProxy common.Address
// Release string
//go:embed deployOutput_v160.json
var decodeOutputABIJSONV160 string
var decodeOutputABIV160 abi.ABI
func DeployOPChainRawV160(
ctx context.Context,
l1 *ethclient.Client,
bcast broadcaster.Broadcaster,
deployer common.Address,
artifacts foundry.StatDirFs,
input DeployOPChainInputV160,
) (DeployOPChainOutput, error) {
return deployOPChainRaw(ctx, l1, bcast, deployer, artifacts, input.OpcmProxy, DeployOPChainInputV160DeployCalldata(input))
} }
func DeployOPChainRawIsthmus( type ReadImplementationAddressesOutput struct {
ctx context.Context, DelayedWETH common.Address
l1 *ethclient.Client, OptimismPortal common.Address
bcast broadcaster.Broadcaster, SystemConfig common.Address
deployer common.Address, L1CrossDomainMessenger common.Address
artifacts foundry.StatDirFs, L1ERC721Bridge common.Address
input DeployOPChainInputIsthmus, L1StandardBridge common.Address
) (DeployOPChainOutput, error) { OptimismMintableERC20Factory common.Address
return deployOPChainRaw(ctx, l1, bcast, deployer, artifacts, input.OpcmProxy, DeployOPChainInputIsthmusDeployCalldata(input)) DisputeGameFactory common.Address
MipsSingleton common.Address
PreimageOracleSingleton common.Address
} }
// DeployOPChainRaw deploys an OP Chain using a raw call to a pre-deployed OPSM contract. type ReadImplementationAddressesScript struct {
func deployOPChainRaw( Run func(input, output common.Address) error
ctx context.Context, }
l1 *ethclient.Client,
bcast broadcaster.Broadcaster,
deployer common.Address,
artifacts foundry.StatDirFs,
opcmProxyAddress common.Address,
input any,
) (DeployOPChainOutput, error) {
var out DeployOPChainOutput
artifactsFS := &foundry.ArtifactsFS{FS: artifacts} func ReadImplementationAddresses(host *script.Host, input ReadImplementationAddressesInput) (ReadImplementationAddressesOutput, error) {
opcmArtifacts, err := artifactsFS.ReadArtifact("OPContractsManager.sol", "OPContractsManager") var rio ReadImplementationAddressesOutput
if err != nil { inputAddr := host.NewScriptAddress()
return out, fmt.Errorf("failed to read OPStackManager artifact: %w", err) outputAddr := host.NewScriptAddress()
}
opcmABI := opcmArtifacts.ABI cleanupInput, err := script.WithPrecompileAtAddress[*ReadImplementationAddressesInput](host, inputAddr, &input)
calldata, err := opcmABI.Pack("deploy", input)
if err != nil { if err != nil {
return out, fmt.Errorf("failed to pack deploy input: %w", err) return rio, fmt.Errorf("failed to insert ReadImplementationAddressesInput precompile: %w", err)
} }
defer cleanupInput()
host.Label(inputAddr, "ReadImplementationAddressesInput")
nonce, err := l1.NonceAt(ctx, deployer, nil) cleanupOutput, err := script.WithPrecompileAtAddress[*ReadImplementationAddressesOutput](host, outputAddr, &rio,
script.WithFieldSetter[*ReadImplementationAddressesOutput])
if err != nil { if err != nil {
return out, fmt.Errorf("failed to read nonce: %w", err) return rio, fmt.Errorf("failed to insert ReadImplementationAddressesOutput precompile: %w", err)
} }
defer cleanupOutput()
host.Label(outputAddr, "ReadImplementationAddressesOutput")
bcast.Hook(script.Broadcast{ deployScript, cleanupDeploy, err := script.WithScript[ReadImplementationAddressesScript](host, "ReadImplementationAddresses.s.sol", "ReadImplementationAddresses")
From: deployer,
To: opcmProxyAddress,
Input: calldata,
Value: (*hexutil.U256)(uint256.NewInt(0)),
// use hardcoded 19MM gas for now since this is roughly what we've seen this deployment cost.
GasUsed: 19_000_000,
Type: script.BroadcastCall,
Nonce: nonce,
})
results, err := bcast.Broadcast(ctx)
if err != nil { if err != nil {
return out, fmt.Errorf("failed to broadcast OP chain deployment: %w", err) return rio, fmt.Errorf("failed to load ReadImplementationAddresses script: %w", err)
} }
defer cleanupDeploy()
deployedEvent := opcmABI.Events["Deployed"] if err := deployScript.Run(inputAddr, outputAddr); err != nil {
res := results[0] return rio, fmt.Errorf("failed to run ReadImplementationAddresses script: %w", err)
for _, log := range res.Receipt.Logs {
if log.Topics[0] != deployedEvent.ID {
continue
}
type EventData struct {
DeployOutput []byte
}
var data EventData
if err := opcmABI.UnpackIntoInterface(&data, "Deployed", log.Data); err != nil {
return out, fmt.Errorf("failed to unpack Deployed event: %w", err)
}
type OutputData struct {
Output DeployOPChainOutput
}
var outData OutputData
if err := decodeOutputABIV160.UnpackIntoInterface(&outData, "decodeOutput", data.DeployOutput); err != nil {
return out, fmt.Errorf("failed to unpack DeployOutput: %w", err)
}
return outData.Output, nil
} }
return out, fmt.Errorf("failed to find Deployed event") return rio, nil
}
func init() {
var err error
decodeOutputABIV160, err = abi.JSON(strings.NewReader(decodeOutputABIJSONV160))
if err != nil {
panic(fmt.Sprintf("failed to parse decodeOutput ABI: %v", err))
}
} }
...@@ -23,7 +23,6 @@ type Env struct { ...@@ -23,7 +23,6 @@ type Env struct {
L1ScriptHost *script.Host L1ScriptHost *script.Host
L1Client *ethclient.Client L1Client *ethclient.Client
Broadcaster broadcaster.Broadcaster Broadcaster broadcaster.Broadcaster
Host *script.Host
Deployer common.Address Deployer common.Address
Logger log.Logger Logger log.Logger
} }
......
...@@ -58,8 +58,6 @@ func DeployImplementations(env *Env, intent *state.Intent, st *state.State) erro ...@@ -58,8 +58,6 @@ func DeployImplementations(env *Env, intent *state.Intent, st *state.State) erro
return fmt.Errorf("error merging proof params from overrides: %w", err) return fmt.Errorf("error merging proof params from overrides: %w", err)
} }
env.L1ScriptHost.ImportState(st.L1StateDump.Data)
dio, err := opcm.DeployImplementations( dio, err := opcm.DeployImplementations(
env.L1ScriptHost, env.L1ScriptHost,
opcm.DeployImplementationsInput{ opcm.DeployImplementationsInput{
......
...@@ -3,7 +3,7 @@ package pipeline ...@@ -3,7 +3,7 @@ package pipeline
import ( import (
"fmt" "fmt"
env2 "github.com/ethereum-optimism/optimism/op-deployer/pkg/env" "github.com/ethereum-optimism/optimism/op-deployer/pkg/env"
"github.com/ethereum-optimism/optimism/op-chain-ops/foundry" "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/broadcaster"
...@@ -13,8 +13,8 @@ import ( ...@@ -13,8 +13,8 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
) )
func GenerateL2Genesis(env *Env, intent *state.Intent, bundle ArtifactsBundle, st *state.State, chainID common.Hash) error { func GenerateL2Genesis(pEnv *Env, intent *state.Intent, bundle ArtifactsBundle, st *state.State, chainID common.Hash) error {
lgr := env.Logger.New("stage", "generate-l2-genesis") lgr := pEnv.Logger.New("stage", "generate-l2-genesis")
thisIntent, err := intent.Chain(chainID) thisIntent, err := intent.Chain(chainID)
if err != nil { if err != nil {
...@@ -38,12 +38,11 @@ func GenerateL2Genesis(env *Env, intent *state.Intent, bundle ArtifactsBundle, s ...@@ -38,12 +38,11 @@ func GenerateL2Genesis(env *Env, intent *state.Intent, bundle ArtifactsBundle, s
return fmt.Errorf("failed to combine L2 init config: %w", err) return fmt.Errorf("failed to combine L2 init config: %w", err)
} }
host, err := env2.DefaultScriptHost( host, err := env.DefaultScriptHost(
broadcaster.NoopBroadcaster(), broadcaster.NoopBroadcaster(),
env.Logger, pEnv.Logger,
env.Deployer, pEnv.Deployer,
bundle.L2, bundle.L2,
0,
) )
if err != nil { if err != nil {
return fmt.Errorf("failed to create L2 script host: %w", err) return fmt.Errorf("failed to create L2 script host: %w", err)
...@@ -60,7 +59,7 @@ func GenerateL2Genesis(env *Env, intent *state.Intent, bundle ArtifactsBundle, s ...@@ -60,7 +59,7 @@ func GenerateL2Genesis(env *Env, intent *state.Intent, bundle ArtifactsBundle, s
return fmt.Errorf("failed to call L2Genesis script: %w", err) return fmt.Errorf("failed to call L2Genesis script: %w", err)
} }
host.Wipe(env.Deployer) host.Wipe(pEnv.Deployer)
dump, err := host.StateDump() dump, err := host.StateDump()
if err != nil { if err != nil {
......
package pipeline package pipeline
import ( import (
"context"
"encoding/hex"
"errors"
"fmt" "fmt"
"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/artifacts"
"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/standard" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/standard"
"github.com/ethereum-optimism/optimism/op-service/jsonutil" "github.com/ethereum-optimism/optimism/op-service/jsonutil"
"github.com/ethereum-optimism/optimism/op-chain-ops/genesis"
"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/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethclient"
) )
func DeployOPChainLiveStrategy(ctx context.Context, env *Env, bundle ArtifactsBundle, intent *state.Intent, st *state.State, chainID common.Hash) error { func DeployOPChain(env *Env, intent *state.Intent, st *state.State, chainID common.Hash) error {
lgr := env.Logger.New("stage", "deploy-opchain", "strategy", "live") lgr := env.Logger.New("stage", "deploy-opchain")
if !shouldDeployOPChain(st, chainID) { if !shouldDeployOPChain(st, chainID) {
lgr.Info("opchain deployment not needed") lgr.Info("opchain deployment not needed")
...@@ -31,6 +24,7 @@ func DeployOPChainLiveStrategy(ctx context.Context, env *Env, bundle ArtifactsBu ...@@ -31,6 +24,7 @@ func DeployOPChainLiveStrategy(ctx context.Context, env *Env, bundle ArtifactsBu
return fmt.Errorf("failed to get chain intent: %w", err) return fmt.Errorf("failed to get chain intent: %w", err)
} }
var opcmAddr common.Address
var deployFunc func() (opcm.DeployOPChainOutput, error) var deployFunc func() (opcm.DeployOPChainOutput, error)
switch intent.L1ContractsLocator.Tag { switch intent.L1ContractsLocator.Tag {
case standard.ContractsV160Tag, standard.ContractsV170Beta1L2Tag: case standard.ContractsV160Tag, standard.ContractsV170Beta1L2Tag:
...@@ -40,13 +34,8 @@ func DeployOPChainLiveStrategy(ctx context.Context, env *Env, bundle ArtifactsBu ...@@ -40,13 +34,8 @@ func DeployOPChainLiveStrategy(ctx context.Context, env *Env, bundle ArtifactsBu
return opcm.DeployOPChainOutput{}, fmt.Errorf("error making deploy OP chain input: %w", err) return opcm.DeployOPChainOutput{}, fmt.Errorf("error making deploy OP chain input: %w", err)
} }
return opcm.DeployOPChainRawV160(ctx, opcmAddr = input.OpcmProxy
env.L1Client, return opcm.DeployOPChainV160(env.L1ScriptHost, input)
env.Broadcaster,
env.Deployer,
bundle.L1,
input,
)
} }
default: default:
deployFunc = func() (opcm.DeployOPChainOutput, error) { deployFunc = func() (opcm.DeployOPChainOutput, error) {
...@@ -55,165 +44,47 @@ func DeployOPChainLiveStrategy(ctx context.Context, env *Env, bundle ArtifactsBu ...@@ -55,165 +44,47 @@ func DeployOPChainLiveStrategy(ctx context.Context, env *Env, bundle ArtifactsBu
return opcm.DeployOPChainOutput{}, fmt.Errorf("error making deploy OP chain input: %w", err) return opcm.DeployOPChainOutput{}, fmt.Errorf("error making deploy OP chain input: %w", err)
} }
return opcm.DeployOPChainRawIsthmus(ctx, opcmAddr = input.OpcmProxy
env.L1Client, return opcm.DeployOPChainIsthmus(env.L1ScriptHost, input)
env.Broadcaster,
env.Deployer,
bundle.L1,
input,
)
} }
} }
var dco opcm.DeployOPChainOutput var dco opcm.DeployOPChainOutput
lgr.Info("deploying OP chain using existing OPCM", "id", chainID.Hex(), "opcmAddress", st.ImplementationsDeployment.OpcmProxyAddress.Hex()) lgr.Info("deploying OP chain using local allocs", "id", chainID.Hex())
dco, err = deployFunc() dco, err = deployFunc()
if err != nil { if err != nil {
return fmt.Errorf("error deploying OP chain: %w", err) return fmt.Errorf("error deploying OP chain: %w", err)
} }
st.Chains = append(st.Chains, makeChainState(chainID, dco)) st.Chains = append(st.Chains, makeChainState(chainID, dco))
opcmProxyAddress := st.ImplementationsDeployment.OpcmProxyAddress
err = conditionallySetImplementationAddresses(ctx, env.L1Client, intent, st, dco, opcmProxyAddress)
if err != nil {
return fmt.Errorf("failed to set implementation addresses: %w", err)
}
return nil
}
// 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.
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() {
return nil
}
block, err := client.BlockByNumber(ctx, nil)
if err != nil {
return fmt.Errorf("failed to get latest block by number: %w", err)
}
currentBlockHash := block.Hash()
errCh := make(chan error, 8)
setImplementationAddressTasks := []func(){
func() {
setEIP1967ImplementationAddress(ctx, client, errCh, dco.DelayedWETHPermissionedGameProxy, currentBlockHash, &st.ImplementationsDeployment.DelayedWETHImplAddress)
},
func() {
setEIP1967ImplementationAddress(ctx, client, errCh, dco.OptimismPortalProxy, currentBlockHash, &st.ImplementationsDeployment.OptimismPortalImplAddress)
},
func() {
setEIP1967ImplementationAddress(ctx, client, errCh, dco.SystemConfigProxy, currentBlockHash, &st.ImplementationsDeployment.SystemConfigImplAddress)
},
func() {
setRDPImplementationAddress(ctx, client, errCh, dco.AddressManager, &st.ImplementationsDeployment.L1CrossDomainMessengerImplAddress, "OVM_L1CrossDomainMessenger")
},
func() {
setEIP1967ImplementationAddress(ctx, client, errCh, dco.L1ERC721BridgeProxy, currentBlockHash, &st.ImplementationsDeployment.L1ERC721BridgeImplAddress)
},
func() {
setEIP1967ImplementationAddress(ctx, client, errCh, dco.L1StandardBridgeProxy, currentBlockHash, &st.ImplementationsDeployment.L1StandardBridgeImplAddress)
},
func() {
setEIP1967ImplementationAddress(ctx, client, errCh, dco.OptimismMintableERC20FactoryProxy, currentBlockHash, &st.ImplementationsDeployment.OptimismMintableERC20FactoryImplAddress)
},
func() {
setEIP1967ImplementationAddress(ctx, client, errCh, dco.DisputeGameFactoryProxy, currentBlockHash, &st.ImplementationsDeployment.DisputeGameFactoryImplAddress)
},
func() {
setMipsSingletonAddress(ctx, client, intent.L1ContractsLocator, errCh, opcmProxyAddress, &st.ImplementationsDeployment.MipsSingletonAddress)
setPreimageOracleAddress(ctx, client, errCh, st.ImplementationsDeployment.MipsSingletonAddress, &st.ImplementationsDeployment.PreimageOracleSingletonAddress)
},
}
for _, task := range setImplementationAddressTasks {
go task()
}
var lastTaskErr error
for i := 0; i < len(setImplementationAddressTasks); i++ {
taskErr := <-errCh
if taskErr != nil {
lastTaskErr = taskErr
}
}
if lastTaskErr != nil {
return fmt.Errorf("failed to set implementation addresses: %w", lastTaskErr)
}
return nil
}
func setMipsSingletonAddress(ctx context.Context, client *ethclient.Client, l1ArtifactsLocator *artifacts.Locator, errCh chan error, opcmProxyAddress common.Address, singletonAddress *common.Address) {
if !l1ArtifactsLocator.IsTag() {
errCh <- errors.New("L1 contracts locator is not a tag, cannot set MIPS singleton address")
return
}
opcmContract := opcm.NewContract(opcmProxyAddress, client)
mipsSingletonAddress, err := opcmContract.GetOPCMImplementationAddress(ctx, l1ArtifactsLocator.Tag, "MIPS")
if err == nil {
*singletonAddress = mipsSingletonAddress
}
errCh <- err
}
func setPreimageOracleAddress(ctx context.Context, client *ethclient.Client, errCh chan error, mipsSingletonAddress common.Address, preimageOracleAddress *common.Address) {
opcmContract := opcm.NewContract(mipsSingletonAddress, client)
preimageOracle, err := opcmContract.GenericAddressGetter(ctx, "oracle")
if err == nil {
*preimageOracleAddress = preimageOracle
}
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) { var release string
lgr.Info("opchain deployment not needed") if intent.L1ContractsLocator.IsTag() {
return nil release = intent.L1ContractsLocator.Tag
} else {
release = "dev"
} }
thisIntent, err := intent.Chain(chainID) readInput := opcm.ReadImplementationAddressesInput{
if err != nil { DeployOPChainOutput: dco,
return fmt.Errorf("failed to get chain intent: %w", err) OpcmProxy: opcmAddr,
Release: release,
} }
impls, err := opcm.ReadImplementationAddresses(env.L1ScriptHost, readInput)
var deployFunc func() (opcm.DeployOPChainOutput, error)
switch intent.L1ContractsLocator.Tag {
case standard.ContractsV160Tag, standard.ContractsV170Beta1L2Tag:
deployFunc = func() (opcm.DeployOPChainOutput, error) {
input, err := makeDCIV160(intent, thisIntent, chainID, st)
if err != nil {
return opcm.DeployOPChainOutput{}, fmt.Errorf("error making deploy OP chain input: %w", err)
}
return opcm.DeployOPChainV160(env.L1ScriptHost, input)
}
default:
deployFunc = func() (opcm.DeployOPChainOutput, error) {
input, err := makeDCIIsthmus(intent, thisIntent, chainID, st)
if err != nil {
return opcm.DeployOPChainOutput{}, fmt.Errorf("error making deploy OP chain input: %w", err)
}
return opcm.DeployOPChainIsthmus(env.L1ScriptHost, input)
}
}
env.L1ScriptHost.ImportState(st.L1StateDump.Data)
var dco opcm.DeployOPChainOutput
lgr.Info("deploying OP chain using local allocs", "id", chainID.Hex())
dco, err = deployFunc()
if err != nil { if err != nil {
return fmt.Errorf("error deploying OP chain: %w", err) return fmt.Errorf("failed to read implementation addresses: %w", err)
} }
st.Chains = append(st.Chains, makeChainState(chainID, dco)) st.ImplementationsDeployment.DelayedWETHImplAddress = impls.DelayedWETH
st.ImplementationsDeployment.OptimismPortalImplAddress = impls.OptimismPortal
st.ImplementationsDeployment.SystemConfigImplAddress = impls.SystemConfig
st.ImplementationsDeployment.L1CrossDomainMessengerImplAddress = impls.L1CrossDomainMessenger
st.ImplementationsDeployment.L1ERC721BridgeImplAddress = impls.L1ERC721Bridge
st.ImplementationsDeployment.L1StandardBridgeImplAddress = impls.L1StandardBridge
st.ImplementationsDeployment.OptimismMintableERC20FactoryImplAddress = impls.OptimismMintableERC20Factory
st.ImplementationsDeployment.DisputeGameFactoryImplAddress = impls.DisputeGameFactory
st.ImplementationsDeployment.MipsSingletonAddress = impls.MipsSingleton
st.ImplementationsDeployment.PreimageOracleSingletonAddress = impls.PreimageOracleSingleton
return nil return nil
} }
...@@ -301,33 +172,6 @@ func makeChainState(chainID common.Hash, dco opcm.DeployOPChainOutput) *state.Ch ...@@ -301,33 +172,6 @@ func makeChainState(chainID common.Hash, dco opcm.DeployOPChainOutput) *state.Ch
} }
} }
func setRDPImplementationAddress(ctx context.Context, client *ethclient.Client, errCh chan error, addressManager common.Address, implAddress *common.Address, getNameArg string) {
if *implAddress != (common.Address{}) {
errCh <- nil
return
}
addressManagerContract := opcm.NewContract(addressManager, client)
address, err := addressManagerContract.GetAddressByNameViaAddressManager(ctx, getNameArg)
if err == nil {
*implAddress = address
}
errCh <- err
}
func setEIP1967ImplementationAddress(ctx context.Context, client *ethclient.Client, errCh chan error, proxy common.Address, currentBlockHash common.Hash, implAddress *common.Address) {
if *implAddress != (common.Address{}) {
errCh <- nil
return
}
storageValue, err := client.StorageAtHash(ctx, proxy, genesis.ImplementationSlot, currentBlockHash)
if err == nil {
*implAddress = common.HexToAddress(hex.EncodeToString(storageValue))
}
errCh <- err
}
func shouldDeployOPChain(st *state.State, chainID common.Hash) bool { func shouldDeployOPChain(st *state.State, chainID common.Hash) bool {
for _, chain := range st.Chains { for _, chain := range st.Chains {
if chain.ID == chainID { if chain.ID == chainID {
......
...@@ -172,7 +172,7 @@ func SystemOwnerAddrFor(chainID uint64) (common.Address, error) { ...@@ -172,7 +172,7 @@ func SystemOwnerAddrFor(chainID uint64) (common.Address, error) {
func ArtifactsURLForTag(tag string) (*url.URL, error) { func ArtifactsURLForTag(tag string) (*url.URL, error) {
switch tag { switch tag {
case "op-contracts/v1.6.0": case "op-contracts/v1.6.0":
return url.Parse(standardArtifactsURL("ee07c78c3d8d4cd8f7a933c050f5afeebaa281b57b226cc6f092b19de2a8d61f")) return url.Parse(standardArtifactsURL("3a27c6dc0cb61b36feaac26def98c64b4a48ec8f5c5ba6965e8ae3157606043c"))
case "op-contracts/v1.7.0-beta.1+l2-contracts": case "op-contracts/v1.7.0-beta.1+l2-contracts":
return url.Parse(standardArtifactsURL("b0fb1f6f674519d637cff39a22187a5993d7f81a6d7b7be6507a0b50a5e38597")) return url.Parse(standardArtifactsURL("b0fb1f6f674519d637cff39a22187a5993d7f81a6d7b7be6507a0b50a5e38597"))
default: default:
......
...@@ -16,7 +16,7 @@ func DefaultScriptHost( ...@@ -16,7 +16,7 @@ func DefaultScriptHost(
lgr log.Logger, lgr log.Logger,
deployer common.Address, deployer common.Address,
artifacts foundry.StatDirFs, artifacts foundry.StatDirFs,
startingNonce uint64, additionalOpts ...script.HostOption,
) (*script.Host, error) { ) (*script.Host, error) {
scriptCtx := script.DefaultContext scriptCtx := script.DefaultContext
scriptCtx.Sender = deployer scriptCtx.Sender = deployer
...@@ -26,16 +26,16 @@ func DefaultScriptHost( ...@@ -26,16 +26,16 @@ func DefaultScriptHost(
&foundry.ArtifactsFS{FS: artifacts}, &foundry.ArtifactsFS{FS: artifacts},
nil, nil,
scriptCtx, scriptCtx,
script.WithBroadcastHook(bcaster.Hook), append([]script.HostOption{
script.WithIsolatedBroadcasts(), script.WithBroadcastHook(bcaster.Hook),
script.WithCreate2Deployer(), script.WithIsolatedBroadcasts(),
script.WithCreate2Deployer(),
}, additionalOpts...)...,
) )
if err := h.EnableCheats(); err != nil { if err := h.EnableCheats(); err != nil {
return nil, fmt.Errorf("failed to enable cheats: %w", err) return nil, fmt.Errorf("failed to enable cheats: %w", err)
} }
h.SetNonce(deployer, startingNonce)
return h, nil return h, nil
} }
...@@ -235,7 +235,7 @@ contract DeployOPChainOutput is BaseDeployIO { ...@@ -235,7 +235,7 @@ contract DeployOPChainOutput is BaseDeployIO {
IDelayedWETH internal _delayedWETHPermissionedGameProxy; IDelayedWETH internal _delayedWETHPermissionedGameProxy;
IDelayedWETH internal _delayedWETHPermissionlessGameProxy; IDelayedWETH internal _delayedWETHPermissionlessGameProxy;
function set(bytes4 _sel, address _addr) public { function set(bytes4 _sel, address _addr) public virtual {
require(_addr != address(0), "DeployOPChainOutput: cannot set zero address"); require(_addr != address(0), "DeployOPChainOutput: cannot set zero address");
// forgefmt: disable-start // forgefmt: disable-start
if (_sel == this.opChainProxyAdmin.selector) _opChainProxyAdmin = IProxyAdmin(_addr) ; if (_sel == this.opChainProxyAdmin.selector) _opChainProxyAdmin = IProxyAdmin(_addr) ;
......
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;
import { BaseDeployIO } from "scripts/deploy/BaseDeployIO.sol";
import { IProxy } from "src/universal/interfaces/IProxy.sol";
import { Script } from "forge-std/Script.sol";
import { DeployUtils } from "scripts/libraries/DeployUtils.sol";
import { DeployOPChainOutput } from "scripts/deploy/DeployOPChain.s.sol";
import { IMIPS } from "src/cannon/interfaces/IMIPS.sol";
import { OPContractsManager } from "src/L1/OPContractsManager.sol";
import { IAddressManager } from "src/legacy/interfaces/IAddressManager.sol";
import { IStaticL1ChugSplashProxy } from "src/legacy/interfaces/IL1ChugSplashProxy.sol";
contract ReadImplementationAddressesInput is DeployOPChainOutput {
OPContractsManager internal _opcmProxy;
string internal _release;
function set(bytes4 _sel, address _addr) public override {
require(_addr != address(0), "ReadImplementationAddressesInput: cannot set zero address");
if (_sel == this.opcmProxy.selector) _opcmProxy = OPContractsManager(_addr);
else if (_sel == this.addressManager.selector) _addressManager = IAddressManager(_addr);
else super.set(_sel, _addr);
}
function set(bytes4 _sel, string memory _val) public {
if (_sel == this.release.selector) _release = _val;
else revert("ReadImplementationAddressesInput: unknown selector");
}
function opcmProxy() public view returns (OPContractsManager) {
DeployUtils.assertValidContractAddress(address(_opcmProxy));
return _opcmProxy;
}
function release() public view returns (string memory) {
require(bytes(_release).length != 0, "ReadImplementationAddressesInput: release not set");
return _release;
}
}
contract ReadImplementationAddressesOutput is BaseDeployIO {
address internal _delayedWETH;
address internal _optimismPortal;
address internal _systemConfig;
address internal _l1CrossDomainMessenger;
address internal _l1ERC721Bridge;
address internal _l1StandardBridge;
address internal _optimismMintableERC20Factory;
address internal _disputeGameFactory;
address internal _mipsSingleton;
address internal _preimageOracleSingleton;
function set(bytes4 _sel, address _addr) public {
require(_addr != address(0), "ReadImplementationAddressesOutput: cannot set zero address");
if (_sel == this.delayedWETH.selector) _delayedWETH = _addr;
else if (_sel == this.optimismPortal.selector) _optimismPortal = _addr;
else if (_sel == this.systemConfig.selector) _systemConfig = _addr;
else if (_sel == this.l1CrossDomainMessenger.selector) _l1CrossDomainMessenger = _addr;
else if (_sel == this.l1ERC721Bridge.selector) _l1ERC721Bridge = _addr;
else if (_sel == this.l1StandardBridge.selector) _l1StandardBridge = _addr;
else if (_sel == this.optimismMintableERC20Factory.selector) _optimismMintableERC20Factory = _addr;
else if (_sel == this.disputeGameFactory.selector) _disputeGameFactory = _addr;
else if (_sel == this.mipsSingleton.selector) _mipsSingleton = _addr;
else if (_sel == this.preimageOracleSingleton.selector) _preimageOracleSingleton = _addr;
else revert("ReadImplementationAddressesOutput: unknown selector");
}
function delayedWETH() public view returns (address) {
require(_delayedWETH != address(0), "ReadImplementationAddressesOutput: delayedWETH not set");
return _delayedWETH;
}
function optimismPortal() public view returns (address) {
require(_optimismPortal != address(0), "ReadImplementationAddressesOutput: optimismPortal not set");
return _optimismPortal;
}
function systemConfig() public view returns (address) {
require(_systemConfig != address(0), "ReadImplementationAddressesOutput: systemConfig not set");
return _systemConfig;
}
function l1CrossDomainMessenger() public view returns (address) {
require(
_l1CrossDomainMessenger != address(0), "ReadImplementationAddressesOutput: l1CrossDomainMessenger not set"
);
return _l1CrossDomainMessenger;
}
function l1ERC721Bridge() public view returns (address) {
require(_l1ERC721Bridge != address(0), "ReadImplementationAddressesOutput: l1ERC721Bridge not set");
return _l1ERC721Bridge;
}
function l1StandardBridge() public view returns (address) {
require(_l1StandardBridge != address(0), "ReadImplementationAddressesOutput: l1StandardBridge not set");
return _l1StandardBridge;
}
function optimismMintableERC20Factory() public view returns (address) {
require(
_optimismMintableERC20Factory != address(0),
"ReadImplementationAddressesOutput: optimismMintableERC20Factory not set"
);
return _optimismMintableERC20Factory;
}
function disputeGameFactory() public view returns (address) {
require(_disputeGameFactory != address(0), "ReadImplementationAddressesOutput: disputeGameFactory not set");
return _disputeGameFactory;
}
function mipsSingleton() public view returns (address) {
require(_mipsSingleton != address(0), "ReadImplementationAddressesOutput: mipsSingleton not set");
return _mipsSingleton;
}
function preimageOracleSingleton() public view returns (address) {
require(
_preimageOracleSingleton != address(0), "ReadImplementationAddressesOutput: preimageOracleSingleton not set"
);
return _preimageOracleSingleton;
}
}
contract ReadImplementationAddresses is Script {
function run(ReadImplementationAddressesInput _rii, ReadImplementationAddressesOutput _rio) public {
address[6] memory eip1967Proxies = [
address(_rii.delayedWETHPermissionedGameProxy()),
address(_rii.optimismPortalProxy()),
address(_rii.systemConfigProxy()),
address(_rii.l1ERC721BridgeProxy()),
address(_rii.optimismMintableERC20FactoryProxy()),
address(_rii.disputeGameFactoryProxy())
];
bytes4[6] memory sels = [
_rio.delayedWETH.selector,
_rio.optimismPortal.selector,
_rio.systemConfig.selector,
_rio.l1ERC721Bridge.selector,
_rio.optimismMintableERC20Factory.selector,
_rio.disputeGameFactory.selector
];
for (uint256 i = 0; i < eip1967Proxies.length; i++) {
IProxy proxy = IProxy(payable(eip1967Proxies[i]));
vm.prank(address(0));
_rio.set(sels[i], proxy.implementation());
}
vm.prank(address(0));
address l1SBImpl = IStaticL1ChugSplashProxy(address(_rii.l1StandardBridgeProxy())).getImplementation();
vm.prank(address(0));
_rio.set(_rio.l1StandardBridge.selector, l1SBImpl);
(address mipsLogic,) = _rii.opcmProxy().implementations(_rii.release(), "MIPS");
_rio.set(_rio.mipsSingleton.selector, mipsLogic);
IAddressManager am = _rii.addressManager();
_rio.set(_rio.l1CrossDomainMessenger.selector, am.getAddress("OVM_L1CrossDomainMessenger"));
address preimageOracle = address(IMIPS(mipsLogic).oracle());
_rio.set(_rio.preimageOracleSingleton.selector, preimageOracle);
}
}
...@@ -44,7 +44,11 @@ else ...@@ -44,7 +44,11 @@ else
tar="tar" tar="tar"
fi fi
"$tar" -czf "$archive_name" artifacts forge-artifacts cache rm -f COMMIT
commit=$(git rev-parse HEAD)
echo "$commit" > COMMIT
"$tar" -czf "$archive_name" artifacts forge-artifacts cache COMMIT
du -sh "$archive_name" | awk '{$1=$1};1' # trim leading whitespace du -sh "$archive_name" | awk '{$1=$1};1' # trim leading whitespace
echoerr "> Done." echoerr "> Done."
...@@ -52,4 +56,5 @@ echoerr "> Uploading artifacts to GCS..." ...@@ -52,4 +56,5 @@ echoerr "> Uploading artifacts to GCS..."
gcloud storage cp "$archive_name" "gs://$DEPLOY_BUCKET/$archive_name" gcloud storage cp "$archive_name" "gs://$DEPLOY_BUCKET/$archive_name"
echoerr "> Done." echoerr "> Done."
rm "$archive_name" rm "$archive_name"
\ No newline at end of file rm COMMIT
\ No newline at end of file
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