Skip to content

Commit 213f762

Browse files
authored
Add inspection commands, fix deployer bugs (ethereum-optimism#11964)
Adds `op-deployer inspection *` commands to expose the generated genesis and rollup configs via the CLI or Golang libraries. Also fixes a bug in deployment - I accidentally deleted the code that set the deployer nonce, so live chain deployments were reverting.
1 parent 5fe7df2 commit 213f762

File tree

24 files changed

+583
-171
lines changed

24 files changed

+583
-171
lines changed

op-chain-ops/cmd/op-deployer/main.go

+7
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import (
44
"fmt"
55
"os"
66

7+
"github.com/ethereum-optimism/optimism/op-chain-ops/deployer/inspect"
8+
79
"github.com/ethereum-optimism/optimism/op-chain-ops/deployer"
810
"github.com/ethereum-optimism/optimism/op-service/cliapp"
911
"github.com/urfave/cli/v2"
@@ -27,6 +29,11 @@ func main() {
2729
Flags: cliapp.ProtectFlags(deployer.ApplyFlags),
2830
Action: deployer.ApplyCLI(),
2931
},
32+
{
33+
Name: "inspect",
34+
Usage: "inspects the state of a deployment",
35+
Subcommands: inspect.Commands,
36+
},
3037
}
3138
app.Writer = os.Stdout
3239
app.ErrWriter = os.Stderr

op-chain-ops/deployer/apply.go

+6
Original file line numberDiff line numberDiff line change
@@ -162,5 +162,11 @@ func ApplyPipeline(
162162
return fmt.Errorf("error in pipeline stage: %w", err)
163163
}
164164
}
165+
166+
st.AppliedIntent = intent
167+
if err := env.WriteState(st); err != nil {
168+
return fmt.Errorf("failed to write state: %w", err)
169+
}
170+
165171
return nil
166172
}
+117
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
package inspect
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/ethereum-optimism/optimism/op-chain-ops/deployer"
7+
"github.com/ethereum-optimism/optimism/op-chain-ops/deployer/pipeline"
8+
"github.com/ethereum-optimism/optimism/op-chain-ops/deployer/state"
9+
"github.com/ethereum/go-ethereum/common"
10+
"github.com/urfave/cli/v2"
11+
)
12+
13+
const (
14+
OutfileFlagName = "outfile"
15+
)
16+
17+
var (
18+
FlagOutfile = &cli.StringFlag{
19+
Name: OutfileFlagName,
20+
Usage: "output file. set to - to use stdout",
21+
Value: "-",
22+
}
23+
)
24+
25+
var Flags = []cli.Flag{
26+
deployer.WorkdirFlag,
27+
FlagOutfile,
28+
}
29+
30+
var Commands = []*cli.Command{
31+
{
32+
Name: "genesis",
33+
Usage: "outputs the genesis for an L2 chain",
34+
Args: true,
35+
ArgsUsage: "<chain-id>",
36+
Action: GenesisCLI,
37+
Flags: Flags,
38+
},
39+
{
40+
Name: "rollup",
41+
Usage: "outputs the rollup config for an L2 chain",
42+
Args: true,
43+
ArgsUsage: "<chain-id>",
44+
Action: RollupCLI,
45+
Flags: Flags,
46+
},
47+
}
48+
49+
type cliConfig struct {
50+
Workdir string
51+
Outfile string
52+
ChainID common.Hash
53+
}
54+
55+
func readConfig(cliCtx *cli.Context) (cliConfig, error) {
56+
var cfg cliConfig
57+
58+
outfile := cliCtx.String(OutfileFlagName)
59+
if outfile == "" {
60+
return cfg, fmt.Errorf("outfile flag is required")
61+
}
62+
63+
workdir := cliCtx.String(deployer.WorkdirFlagName)
64+
if workdir == "" {
65+
return cfg, fmt.Errorf("workdir flag is required")
66+
}
67+
68+
chainIDStr := cliCtx.Args().First()
69+
if chainIDStr == "" {
70+
return cfg, fmt.Errorf("chain-id argument is required")
71+
}
72+
73+
chainID, err := chainIDStrToHash(chainIDStr)
74+
if err != nil {
75+
return cfg, fmt.Errorf("failed to parse chain ID: %w", err)
76+
}
77+
78+
return cliConfig{
79+
Workdir: cliCtx.String(deployer.WorkdirFlagName),
80+
Outfile: cliCtx.String(OutfileFlagName),
81+
ChainID: chainID,
82+
}, nil
83+
}
84+
85+
type inspectState struct {
86+
GlobalState *state.State
87+
ChainIntent *state.ChainIntent
88+
ChainState *state.ChainState
89+
}
90+
91+
func bootstrapState(cfg cliConfig) (*inspectState, error) {
92+
env := &pipeline.Env{Workdir: cfg.Workdir}
93+
globalState, err := env.ReadState()
94+
if err != nil {
95+
return nil, fmt.Errorf("failed to read intent: %w", err)
96+
}
97+
98+
if globalState.AppliedIntent == nil {
99+
return nil, fmt.Errorf("chain state is not applied - run op-deployer apply")
100+
}
101+
102+
chainIntent, err := globalState.AppliedIntent.Chain(cfg.ChainID)
103+
if err != nil {
104+
return nil, fmt.Errorf("failed to get applied chain intent: %w", err)
105+
}
106+
107+
chainState, err := globalState.Chain(cfg.ChainID)
108+
if err != nil {
109+
return nil, fmt.Errorf("failed to get chain ID %s: %w", cfg.ChainID.String(), err)
110+
}
111+
112+
return &inspectState{
113+
GlobalState: globalState,
114+
ChainIntent: chainIntent,
115+
ChainState: chainState,
116+
}, nil
117+
}
+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package inspect
2+
3+
import (
4+
"fmt"
5+
"math/big"
6+
"strconv"
7+
"strings"
8+
9+
"github.com/ethereum-optimism/optimism/op-service/ioutil"
10+
"github.com/ethereum-optimism/optimism/op-service/jsonutil"
11+
"github.com/ethereum/go-ethereum/common"
12+
"github.com/urfave/cli/v2"
13+
)
14+
15+
func GenesisCLI(cliCtx *cli.Context) error {
16+
cfg, err := readConfig(cliCtx)
17+
if err != nil {
18+
return err
19+
}
20+
21+
st, err := bootstrapState(cfg)
22+
if err != nil {
23+
return err
24+
}
25+
26+
genesis, err := st.ChainState.UnmarshalGenesis()
27+
if err != nil {
28+
return fmt.Errorf("failed to unmarshal genesis: %w", err)
29+
}
30+
31+
if err := jsonutil.WriteJSON(genesis, ioutil.ToStdOutOrFileOrNoop(cfg.Outfile, 0o666)); err != nil {
32+
return fmt.Errorf("failed to write genesis: %w", err)
33+
}
34+
35+
return nil
36+
}
37+
38+
func chainIDStrToHash(in string) (common.Hash, error) {
39+
var chainIDBig *big.Int
40+
if strings.HasPrefix(in, "0x") {
41+
in = strings.TrimPrefix(in, "0x")
42+
var ok bool
43+
chainIDBig, ok = new(big.Int).SetString(in, 16)
44+
if !ok {
45+
return common.Hash{}, fmt.Errorf("failed to parse chain ID %s", in)
46+
}
47+
} else {
48+
inUint, err := strconv.ParseUint(in, 10, 64)
49+
if err != nil {
50+
return common.Hash{}, fmt.Errorf("failed to parse chain ID %s: %w", in, err)
51+
}
52+
53+
chainIDBig = new(big.Int).SetUint64(inUint)
54+
}
55+
56+
return common.BigToHash(chainIDBig), nil
57+
}
+90
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
package inspect
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/ethereum-optimism/optimism/op-chain-ops/deployer/pipeline"
7+
"github.com/ethereum-optimism/optimism/op-chain-ops/deployer/state"
8+
"github.com/ethereum-optimism/optimism/op-chain-ops/genesis"
9+
"github.com/ethereum-optimism/optimism/op-node/rollup"
10+
"github.com/ethereum-optimism/optimism/op-service/ioutil"
11+
"github.com/ethereum-optimism/optimism/op-service/jsonutil"
12+
"github.com/ethereum/go-ethereum/common"
13+
"github.com/urfave/cli/v2"
14+
)
15+
16+
func RollupCLI(cliCtx *cli.Context) error {
17+
cfg, err := readConfig(cliCtx)
18+
if err != nil {
19+
return err
20+
}
21+
22+
env := &pipeline.Env{Workdir: cfg.Workdir}
23+
globalState, err := env.ReadState()
24+
if err != nil {
25+
return fmt.Errorf("failed to read intent: %w", err)
26+
}
27+
28+
rollupConfig, err := Rollup(globalState, cfg.ChainID)
29+
if err != nil {
30+
return fmt.Errorf("failed to generate rollup config: %w", err)
31+
}
32+
33+
if err := jsonutil.WriteJSON(rollupConfig, ioutil.ToStdOutOrFileOrNoop(cfg.Outfile, 0o666)); err != nil {
34+
return fmt.Errorf("failed to write rollup config: %w", err)
35+
}
36+
37+
return nil
38+
}
39+
40+
func Rollup(globalState *state.State, chainID common.Hash) (*rollup.Config, error) {
41+
if globalState.AppliedIntent == nil {
42+
return nil, fmt.Errorf("chain state is not applied - run op-deployer apply")
43+
}
44+
45+
chainIntent, err := globalState.AppliedIntent.Chain(chainID)
46+
if err != nil {
47+
return nil, fmt.Errorf("failed to get applied chain intent: %w", err)
48+
}
49+
50+
chainState, err := globalState.Chain(chainID)
51+
if err != nil {
52+
return nil, fmt.Errorf("failed to get chain ID %s: %w", chainID.String(), err)
53+
}
54+
55+
l2Allocs, err := chainState.UnmarshalGenesis()
56+
if err != nil {
57+
return nil, fmt.Errorf("failed to unmarshal genesis: %w", err)
58+
}
59+
60+
config, err := state.CombineDeployConfig(
61+
globalState.AppliedIntent,
62+
chainIntent,
63+
globalState,
64+
chainState,
65+
)
66+
if err != nil {
67+
return nil, fmt.Errorf("failed to combine L2 init config: %w", err)
68+
}
69+
70+
l2GenesisBuilt, err := genesis.BuildL2Genesis(&config, l2Allocs, chainState.StartBlock)
71+
if err != nil {
72+
return nil, fmt.Errorf("failed to build L2 genesis: %w", err)
73+
}
74+
l2GenesisBlock := l2GenesisBuilt.ToBlock()
75+
76+
rollupConfig, err := config.RollupConfig(
77+
chainState.StartBlock,
78+
l2GenesisBlock.Hash(),
79+
l2GenesisBlock.Number().Uint64(),
80+
)
81+
if err != nil {
82+
return nil, fmt.Errorf("failed to build rollup config: %w", err)
83+
}
84+
85+
if err := rollupConfig.Check(); err != nil {
86+
return nil, fmt.Errorf("generated rollup config does not pass validation: %w", err)
87+
}
88+
89+
return rollupConfig, nil
90+
}

op-chain-ops/deployer/pipeline/host.go

+6
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,12 @@ func CallScriptBroadcast(
6868
return fmt.Errorf("failed to enable cheats: %w", err)
6969
}
7070

71+
nonce, err := opts.Client.NonceAt(ctx, opts.Deployer, nil)
72+
if err != nil {
73+
return fmt.Errorf("failed to fetch nonce: %w", err)
74+
}
75+
h.SetNonce(opts.Deployer, nonce)
76+
7177
err = opts.Handler(h)
7278
if err != nil {
7379
return fmt.Errorf("failed to run handler: %w", err)

op-chain-ops/deployer/pipeline/l2genesis.go

+7-2
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ func GenerateL2Genesis(ctx context.Context, env *Env, intent *state.Intent, st *
4040
return fmt.Errorf("failed to get chain state: %w", err)
4141
}
4242

43-
initCfg, err := state.CombineL2InitConfig(intent, thisIntent)
43+
initCfg, err := state.CombineDeployConfig(intent, thisIntent, st, thisChainState)
4444
if err != nil {
4545
return fmt.Errorf("failed to combine L2 init config: %w", err)
4646
}
@@ -63,7 +63,7 @@ func GenerateL2Genesis(ctx context.Context, env *Env, intent *state.Intent, st *
6363
L1StandardBridgeProxy: thisChainState.L1StandardBridgeProxyAddress,
6464
L1ERC721BridgeProxy: thisChainState.L1ERC721BridgeProxyAddress,
6565
},
66-
L2Config: initCfg,
66+
L2Config: initCfg.L2InitializationConfig,
6767
})
6868
if err != nil {
6969
return fmt.Errorf("failed to call L2Genesis script: %w", err)
@@ -93,6 +93,11 @@ func GenerateL2Genesis(ctx context.Context, env *Env, intent *state.Intent, st *
9393
return fmt.Errorf("failed to close gzip writer: %w", err)
9494
}
9595
thisChainState.Genesis = buf.Bytes()
96+
startHeader, err := env.L1Client.HeaderByNumber(ctx, nil)
97+
if err != nil {
98+
return fmt.Errorf("failed to get start block: %w", err)
99+
}
100+
thisChainState.StartBlock = startHeader
96101

97102
if err := env.WriteState(st); err != nil {
98103
return fmt.Errorf("failed to write state: %w", err)

0 commit comments

Comments
 (0)