cmd.go 4.7 KB
Newer Older
1 2 3
package genesis

import (
4
	"context"
5
	"encoding/json"
6
	"fmt"
7
	"math/big"
8
	"os"
9 10
	"path/filepath"

11 12
	"github.com/urfave/cli"

13 14 15
	"github.com/ethereum/go-ethereum/core/types"
	"github.com/ethereum/go-ethereum/ethclient"

16
	"github.com/ethereum-optimism/optimism/op-bindings/hardhat"
17
	"github.com/ethereum-optimism/optimism/op-chain-ops/genesis"
18 19 20 21
)

var Subcommands = cli.Commands{
	{
22 23
		Name:  "devnet",
		Usage: "Initialize new L1 and L2 genesis files and rollup config suitable for a local devnet",
24 25
		Flags: []cli.Flag{
			cli.StringFlag{
26 27
				Name:  "deploy-config",
				Usage: "Path to hardhat deploy config file",
28 29
			},
			cli.StringFlag{
30 31
				Name:  "outfile.l1",
				Usage: "Path to L1 genesis output file",
32 33
			},
			cli.StringFlag{
34 35
				Name:  "outfile.l2",
				Usage: "Path to L2 genesis output file",
36 37
			},
			cli.StringFlag{
38 39
				Name:  "outfile.rollup",
				Usage: "Path to rollup output file",
40 41 42
			},
		},
		Action: func(ctx *cli.Context) error {
43
			deployConfig := ctx.String("deploy-config")
44
			config, err := genesis.NewDeployConfig(deployConfig)
45 46 47 48
			if err != nil {
				return err
			}

49
			// Add the developer L1 addresses to the config
50
			if err := config.InitDeveloperDeployedAddresses(); err != nil {
51 52 53
				return err
			}

54 55 56 57
			if err := config.Check(); err != nil {
				return err
			}

58
			l1Genesis, err := genesis.BuildL1DeveloperGenesis(config)
59 60 61 62
			if err != nil {
				return err
			}

63
			l1StartBlock := l1Genesis.ToBlock()
64
			l2Genesis, err := genesis.BuildL2DeveloperGenesis(config, l1StartBlock)
65 66 67 68
			if err != nil {
				return err
			}

69 70
			l2GenesisBlock := l2Genesis.ToBlock()
			rollupConfig, err := config.RollupConfig(l1StartBlock, l2GenesisBlock.Hash(), l2GenesisBlock.Number().Uint64())
71 72 73
			if err != nil {
				return err
			}
74

75
			if err := writeGenesisFile(ctx.String("outfile.l1"), l1Genesis); err != nil {
76 77
				return err
			}
78
			if err := writeGenesisFile(ctx.String("outfile.l2"), l2Genesis); err != nil {
79 80
				return err
			}
81
			return writeGenesisFile(ctx.String("outfile.rollup"), rollupConfig)
82 83
		},
	},
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
	{
		Name:  "l2",
		Usage: "Generates an L2 genesis file and rollup config suitable for a deployed network",
		Flags: []cli.Flag{
			cli.StringFlag{
				Name:  "l1-rpc",
				Usage: "L1 RPC URL",
			},
			cli.StringFlag{
				Name:  "deploy-config",
				Usage: "Path to hardhat deploy config file",
			},
			cli.StringFlag{
				Name:  "deployment-dir",
				Usage: "Path to deployment directory",
			},
			cli.StringFlag{
				Name:  "outfile.l2",
				Usage: "Path to L2 genesis output file",
			},
			cli.StringFlag{
				Name:  "outfile.rollup",
				Usage: "Path to rollup output file",
			},
		},
		Action: func(ctx *cli.Context) error {
			deployConfig := ctx.String("deploy-config")
			config, err := genesis.NewDeployConfig(deployConfig)
			if err != nil {
				return err
			}

116 117 118 119 120 121 122 123 124
			depPath, network := filepath.Split(ctx.String("deployment-dir"))
			hh, err := hardhat.New(network, nil, []string{depPath})
			if err != nil {
				return err
			}

			// Read the appropriate deployment addresses from disk
			if err := config.GetDeployedAddresses(hh); err != nil {
				return err
125
			}
126 127 128
			// Sanity check the config
			if err := config.Check(); err != nil {
				return err
129
			}
130 131 132

			client, err := ethclient.Dial(ctx.String("l1-rpc"))
			if err != nil {
133
				return fmt.Errorf("cannot dial %s: %w", ctx.String("l1-rpc"), err)
134 135 136 137 138 139 140 141 142
			}

			var l1StartBlock *types.Block
			if config.L1StartingBlockTag.BlockHash != nil {
				l1StartBlock, err = client.BlockByHash(context.Background(), *config.L1StartingBlockTag.BlockHash)
			} else if config.L1StartingBlockTag.BlockNumber != nil {
				l1StartBlock, err = client.BlockByNumber(context.Background(), big.NewInt(config.L1StartingBlockTag.BlockNumber.Int64()))
			}
			if err != nil {
143
				return fmt.Errorf("error getting l1 start block: %w", err)
144 145
			}

146 147
			// Build the developer L2 genesis block
			l2Genesis, err := genesis.BuildL2DeveloperGenesis(config, l1StartBlock)
148
			if err != nil {
149
				return fmt.Errorf("error creating l2 developer genesis: %w", err)
150 151
			}

152 153
			l2GenesisBlock := l2Genesis.ToBlock()
			rollupConfig, err := config.RollupConfig(l1StartBlock, l2GenesisBlock.Hash(), l2GenesisBlock.Number().Uint64())
154 155 156
			if err != nil {
				return err
			}
157 158 159
			if err := rollupConfig.Check(); err != nil {
				return fmt.Errorf("generated rollup config does not pass validation: %w", err)
			}
160 161 162 163 164 165 166 167 168

			if err := writeGenesisFile(ctx.String("outfile.l2"), l2Genesis); err != nil {
				return err
			}
			return writeGenesisFile(ctx.String("outfile.rollup"), rollupConfig)
		},
	},
}

169
func writeGenesisFile(outfile string, input any) error {
170 171 172 173 174 175 176 177 178 179
	f, err := os.OpenFile(outfile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o755)
	if err != nil {
		return err
	}
	defer f.Close()

	enc := json.NewEncoder(f)
	enc.SetIndent("", "  ")
	return enc.Encode(input)
}