cmd

package
v0.0.0-...-0e75302 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 9, 2024 License: MIT Imports: 35 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var AddNewChainCmd = cli.Command{
	Name:  "add-new-chain",
	Usage: "Add a new chain to the superchain-registry",
	Flags: []cli.Flag{
		flags.PublicRpcFlag,
		flags.SequencerRpcFlag,
		flags.ExplorerFlag,
		flags.SuperchainTargetFlag,
		flags.MonorepoDirFlag,
		flags.ChainNameFlag,
		flags.ChainShortNameFlag,
		flags.RollupConfigFlag,
		flags.GenesisFlag,
		flags.DeploymentsDirFlag,
		flags.StandardChainCandidateFlag,
		flags.GenesisCreationCommit,
		flags.DeployConfigFlag,
	},
	Action: func(c *cli.Context) error {
		standardChainCandidate := c.Bool(flags.StandardChainCandidateFlag.Name)

		superchainLevel := superchain.Frontier

		publicRPC := c.String(flags.PublicRpcFlag.Name)
		sequencerRPC := c.String(flags.SequencerRpcFlag.Name)
		explorer := c.String(flags.ExplorerFlag.Name)
		superchainTarget := c.String(flags.SuperchainTargetFlag.Name)
		monorepoDir := c.String(flags.MonorepoDirFlag.Name)

		chainName := c.String(flags.ChainNameFlag.Name)
		rollupConfigPath := c.String(flags.RollupConfigFlag.Name)
		genesisPath := c.String(flags.GenesisFlag.Name)
		deployConfigPath := c.String(flags.DeployConfigFlag.Name)
		genesisCreationCommit := c.String(flags.GenesisCreationCommit.Name)
		deploymentsDir := c.String(flags.DeploymentsDirFlag.Name)
		chainShortName := c.String(flags.ChainShortNameFlag.Name)

		_, thisFile, _, ok := runtime.Caller(0)
		if !ok {
			panic("error getting current filepath")
		}
		superchainRepoRoot := filepath.Dir(filepath.Dir(filepath.Dir(thisFile)))

		fmt.Printf("Chain Name:                     %s\n", chainName)
		fmt.Printf("Chain Short Name:               %s\n", chainShortName)
		fmt.Printf("Superchain target:              %s\n", superchainTarget)
		fmt.Printf("Superchain-registry repo dir:   %s\n", superchainRepoRoot)
		fmt.Printf("Monorepo dir:                   %s\n", monorepoDir)
		fmt.Printf("Deployments directory:          %s\n", deploymentsDir)
		fmt.Printf("Rollup config filepath:         %s\n", rollupConfigPath)
		fmt.Printf("Genesis filepath:               %s\n", genesisPath)
		fmt.Printf("Deploy config filepath:         %s\n", deployConfigPath)
		fmt.Printf("Genesis creation commit:        %s\n", genesisCreationCommit)
		fmt.Printf("Public RPC endpoint:            %s\n", publicRPC)
		fmt.Printf("Sequencer RPC endpoint:         %s\n", sequencerRPC)
		fmt.Printf("Block Explorer:                 %s\n", explorer)
		fmt.Println()

		targetDir := filepath.Join(superchainRepoRoot, "superchain", "configs", superchainTarget)
		if _, err := os.Stat(targetDir); os.IsNotExist(err) {
			return fmt.Errorf("superchain target directory not found. Please follow instructions to add a superchain target in CONTRIBUTING.md: %s", targetDir)
		}

		l1RpcUrl, err := config.GetL1RpcUrl(superchainTarget)
		if err != nil {
			return fmt.Errorf("failed to retrieve L1 rpc url: %w", err)
		}

		var addresses superchain.AddressList
		err = utils.ReadAddressesFromJSON(&addresses, deploymentsDir)
		if err != nil {
			return fmt.Errorf("failed to read addresses from JSON files: %w", err)
		}

		isFaultProofs, err := inferIsFaultProofs(addresses.SystemConfigProxy, addresses.OptimismPortalProxy, l1RpcUrl)
		if err != nil {
			return fmt.Errorf("failed to infer fault proofs status of chain: %w", err)
		}

		rollupConfig, err := config.ConstructChainConfig(rollupConfigPath, genesisPath, chainName, publicRPC, sequencerRPC, explorer, superchainLevel, standardChainCandidate)
		if err != nil {
			return fmt.Errorf("failed to construct rollup config: %w", err)
		}

		fmt.Printf("✅ Rollup config successfully constructed\n")

		err = utils.ReadAddressesFromChain(&addresses, l1RpcUrl, isFaultProofs)
		if err != nil {
			return fmt.Errorf("failed to read addresses from chain: %w", err)
		}

		fmt.Printf("✅ Addresses read from chain\n")

		if rollupConfig.AltDA != nil {
			addresses.DAChallengeAddress = *rollupConfig.AltDA.DAChallengeAddress
		}

		rollupConfig.Addresses = addresses

		l1RpcUrl, err = config.GetL1RpcUrl(superchainTarget)
		if err != nil {
			return fmt.Errorf("error getting l1RpcUrl: %w", err)
		}
		gpt, err := getGasPayingToken(l1RpcUrl, addresses.SystemConfigProxy)
		if err != nil {
			return fmt.Errorf("error inferring gas paying token: %w", err)
		}
		rollupConfig.GasPayingToken = gpt

		targetFilePath := filepath.Join(targetDir, chainShortName+".toml")
		err = config.WriteChainConfigTOML(rollupConfig, targetFilePath)
		if err != nil {
			return fmt.Errorf("error generating chain config %s.toml file: %w", chainShortName, err)
		}

		fmt.Printf("✅ Wrote config for new chain to %s\n", targetFilePath)

		folderName := fmt.Sprintf("%d", rollupConfig.ChainID)
		if runningTests := os.Getenv("SCR_RUN_TESTS"); runningTests == "true" {
			folderName = folderName + "-test"
		}
		genesisValidationInputsDir := filepath.Join(superchainRepoRoot, "validation", "genesis", "validation-inputs", folderName)
		err = os.MkdirAll(genesisValidationInputsDir, os.ModePerm)
		if err != nil {
			return err
		}
		err = copyDeployConfigFile(deployConfigPath, genesisValidationInputsDir)
		if err != nil {
			return fmt.Errorf("error copying deploy-config json file: %w", err)
		}
		fmt.Printf("✅ Copied deploy-config json file to validation module\n")

		err = writeGenesisValidationMetadata(genesisCreationCommit, genesisValidationInputsDir)
		if err != nil {
			return fmt.Errorf("error writing genesis validation metadata file: %w", err)
		}
		fmt.Printf("✅ Wrote genesis validation metadata file\n")

		return nil
	},
}
View Source
var CheckGenesisCmd = cli.Command{
	Name:  "check-genesis",
	Flags: []cli.Flag{flags.GenesisFlag},
	Usage: "Sanity check genesis (genesis.json) is reproducible",
	Action: func(ctx *cli.Context) error {
		genesisPath := ctx.String(flags.GenesisFlag.Name)
		fmt.Printf("Attempting to read from %s\n", genesisPath)
		file, err := os.ReadFile(genesisPath)
		if err != nil {
			return fmt.Errorf("failed to read from local genesis.json config file: %w", err)
		}
		var localGenesis *core.Genesis
		if err = json.Unmarshal(file, &localGenesis); err != nil {
			return fmt.Errorf("failed to unmarshal local genesis.json into core.Genesis struct: %w", err)
		}

		chainId := localGenesis.Config.ChainID.Uint64()

		gethGenesis, err := core.LoadOPStackGenesis(chainId)
		if err != nil {
			return fmt.Errorf("failed to load genesis via op-geth: ensure chainId has already been added to registry: %w", err)
		}

		opts := cmp.Options{cmpopts.IgnoreUnexported(big.Int{})}
		if diff := cmp.Diff(localGenesis, gethGenesis, opts...); diff != "" {
			return fmt.Errorf("local genesis.json (-) does not match config generated by op-geth (+): %s", diff)
		}

		fmt.Println("👌 Regenerated genesis config matches existing one")
		return nil
	},
}
View Source
var CheckRollupConfigCmd = cli.Command{
	Name:  "check-rollup-config",
	Flags: []cli.Flag{flags.RollupConfigFlag},
	Usage: "Sanity check rollup config (rollup.json) is reproducible",
	Action: func(ctx *cli.Context) error {

		rollupConfigPath := ctx.String(flags.RollupConfigFlag.Name)

		fmt.Printf("Attempting to read from %s\n", rollupConfigPath)
		file, err := os.ReadFile(rollupConfigPath)
		if err != nil {
			return fmt.Errorf("failed to read from local rollup.json config file: %w", err)
		}
		var localRollupConfig *rollup.Config
		if err = json.Unmarshal(file, &localRollupConfig); err != nil {
			return fmt.Errorf("failed to unmarshal local rollup.json into rollup.Config struct: %w", err)
		}

		opNodeRollupConfig, err := rollup.LoadOPStackRollupConfig(localRollupConfig.L2ChainID.Uint64())
		if err != nil {
			return fmt.Errorf("op-node failed to generate rollup config: %w", err)
		}

		localRollupConfig.ProtocolVersionsAddress = common.Address{}
		opNodeRollupConfig.ProtocolVersionsAddress = common.Address{}

		opts := cmp.Options{cmpopts.IgnoreUnexported(big.Int{})}
		if diff := cmp.Diff(localRollupConfig, opNodeRollupConfig, opts...); diff != "" {
			return fmt.Errorf("local rollup.json (-) does not match config generated by op-node (+): %s", diff)
		}

		fmt.Println("👌 Regenerated rollup config matches existing one")
		return nil
	},
}
View Source
var CompressGenesisCmd = cli.Command{
	Name: "compress-genesis",
	Flags: []cli.Flag{
		flags.GenesisFlag,
		flags.L2GenesisHeaderFlag,
		flags.ChainShortNameFlag,
		flags.SuperchainTargetFlag,
	},
	Usage: "Generate a single gzipped data file from a bytecode hex string",
	Action: func(ctx *cli.Context) error {

		_, thisFile, _, ok := runtime.Caller(0)
		if !ok {
			panic("error getting current filepath")
		}
		superchainRepoRoot := filepath.Dir(filepath.Dir(filepath.Dir(thisFile)))
		superchainTarget := ctx.String(flags.SuperchainTargetFlag.Name)
		chainShortName := ctx.String(flags.ChainShortNameFlag.Name)

		zipOutputDir := filepath.Join(superchainRepoRoot, "/superchain/extra/genesis", superchainTarget, chainShortName+".json.gz")
		genesisPath := ctx.Path(flags.GenesisFlag.Name)
		if genesisPath == "" {

			genesisHeaderPath := ctx.Path(flags.L2GenesisHeaderFlag.Name)
			genesisHeader, err := utils.LoadJSON[types.Header](genesisHeaderPath)
			if err != nil {
				return fmt.Errorf("genesis-header %q failed to load: %w", genesisHeaderPath, err)
			}
			if genesisHeader.TxHash != types.EmptyTxsHash {
				return errors.New("genesis-header based genesis must have no transactions")
			}
			if genesisHeader.ReceiptHash != types.EmptyReceiptsHash {
				return errors.New("genesis-header based genesis must have no receipts")
			}
			if genesisHeader.UncleHash != types.EmptyUncleHash {
				return errors.New("genesis-header based genesis must have no uncle hashes")
			}
			if genesisHeader.WithdrawalsHash != nil && *genesisHeader.WithdrawalsHash != types.EmptyWithdrawalsHash {
				return errors.New("genesis-header based genesis must have no withdrawals")
			}
			out := Genesis{
				Nonce:         genesisHeader.Nonce.Uint64(),
				Timestamp:     genesisHeader.Time,
				ExtraData:     genesisHeader.Extra,
				GasLimit:      genesisHeader.GasLimit,
				Difficulty:    (*hexutil.Big)(genesisHeader.Difficulty),
				Mixhash:       genesisHeader.MixDigest,
				Coinbase:      genesisHeader.Coinbase,
				Number:        genesisHeader.Number.Uint64(),
				GasUsed:       genesisHeader.GasUsed,
				ParentHash:    genesisHeader.ParentHash,
				BaseFee:       (*hexutil.Big)(genesisHeader.BaseFee),
				ExcessBlobGas: genesisHeader.ExcessBlobGas,
				BlobGasUsed:   genesisHeader.BlobGasUsed,
				Alloc:         make(jsonutil.LazySortedJsonMap[common.Address, GenesisAccount]),
				StateHash:     &genesisHeader.Root,
			}
			if err := writeGzipJSON(zipOutputDir, out); err != nil {
				return fmt.Errorf("failed to write output: %w", err)
			}
			return nil
		}

		genesis, err := utils.LoadJSON[core.Genesis](genesisPath)
		if err != nil {
			return fmt.Errorf("failed to load L2 genesis: %w", err)
		}

		bytecodesDir := filepath.Join(superchainRepoRoot, "/superchain/extra/bytecodes")
		fmt.Printf("using output bytecodes dir: %s\n", bytecodesDir)
		if err := os.MkdirAll(bytecodesDir, 0o755); err != nil {
			return fmt.Errorf("failed to make bytecodes dir: %w", err)
		}
		for addr, account := range genesis.Alloc {
			if len(account.Code) > 0 {
				err = writeBytecode(bytecodesDir, account.Code, addr)
				if err != nil {
					return err
				}
			}
		}

		fmt.Printf("✅ Inserted bytecodes to hash->code database %s\n", zipOutputDir)

		out := Genesis{
			Nonce:         genesis.Nonce,
			Timestamp:     genesis.Timestamp,
			ExtraData:     genesis.ExtraData,
			GasLimit:      genesis.GasLimit,
			Difficulty:    (*hexutil.Big)(genesis.Difficulty),
			Mixhash:       genesis.Mixhash,
			Coinbase:      genesis.Coinbase,
			Number:        genesis.Number,
			GasUsed:       genesis.GasUsed,
			ParentHash:    genesis.ParentHash,
			BaseFee:       (*hexutil.Big)(genesis.BaseFee),
			ExcessBlobGas: genesis.ExcessBlobGas,
			BlobGasUsed:   genesis.BlobGasUsed,
			Alloc:         make(jsonutil.LazySortedJsonMap[common.Address, GenesisAccount]),
		}

		for addr, account := range genesis.Alloc {
			var codeHash common.Hash
			if len(account.Code) > 0 {
				codeHash = crypto.Keccak256Hash(account.Code)
			}
			outAcc := GenesisAccount{
				CodeHash: codeHash,
				Nonce:    account.Nonce,
			}
			if account.Balance != nil && account.Balance.Cmp(common.Big0) != 0 {
				outAcc.Balance = (*hexutil.Big)(account.Balance)
			}
			if len(account.Storage) > 0 {
				outAcc.Storage = make(jsonutil.LazySortedJsonMap[common.Hash, common.Hash])
				for k, v := range account.Storage {
					outAcc.Storage[k] = v
				}
			}
			out.Alloc[addr] = outAcc
		}

		if err := writeGzipJSON(zipOutputDir, out); err != nil {
			return fmt.Errorf("failed to write output: %w", err)
		}

		fmt.Printf("✅ Wrote compressed genesis file to %s\n", zipOutputDir)
		return nil
	},
}
View Source
var PromoteToStandardCmd = cli.Command{
	Name:    "promote-to-standard",
	Flags:   []cli.Flag{flags.ChainIdFlag},
	Aliases: []string{"p"},
	Usage:   "Promote a chain to standard.",
	Action: func(ctx *cli.Context) error {
		chainId := flags.ChainIdFlag.Get(ctx)
		chain, ok := superchain.OPChains[chainId]
		if !ok {
			panic(fmt.Sprintf("No chain found with id %d", chainId))
		}

		copy, err := chain.PromoteToStandard()
		if err != nil {
			panic(err)
		}
		chain = copy

		_, thisFile, _, ok := runtime.Caller(0)
		if !ok {
			panic("Unable to get the current file path")
		}

		superchainRepoPath := filepath.Dir(filepath.Dir(filepath.Dir(thisFile)))
		targetDir := filepath.Join(superchainRepoPath, "superchain", "configs", chain.Superchain)
		targetFilePath := filepath.Join(targetDir, chain.Chain+".toml")
		err = config.WriteChainConfigTOML(*chain, targetFilePath)
		if err != nil {
			panic(err)
		}

		fmt.Println("Promoted chain to standard: ", chainId)
		return nil
	},
}
View Source
var UpdateConfigsCmd = cli.Command{
	Name:    "update-configs",
	Aliases: []string{"u"},
	Usage:   "Update all config toml files after superchain.ChainConfig struct is updated",
	Action: func(ctx *cli.Context) error {
		for _, chain := range superchain.OPChains {
			_, thisFile, _, ok := runtime.Caller(0)
			if !ok {
				panic("Unable to get the current file path")
			}

			err := chain.CheckDataAvailability()
			if err != nil {
				panic(err)
			}

			superchainRepoPath := filepath.Dir(filepath.Dir(filepath.Dir(thisFile)))
			targetDir := filepath.Join(superchainRepoPath, "superchain", "configs", chain.Superchain)
			targetFilePath := filepath.Join(targetDir, chain.Chain+".toml")
			err = config.WriteChainConfigTOML(*chain, targetFilePath)
			if err != nil {
				panic(err)
			}

		}
		return nil
	},
}

Functions

This section is empty.

Types

type Genesis

type Genesis struct {
	Nonce         uint64         `json:"nonce"`
	Timestamp     uint64         `json:"timestamp"`
	ExtraData     []byte         `json:"extraData"`
	GasLimit      uint64         `json:"gasLimit"`
	Difficulty    *hexutil.Big   `json:"difficulty"`
	Mixhash       common.Hash    `json:"mixHash"`
	Coinbase      common.Address `json:"coinbase"`
	Number        uint64         `json:"number"`
	GasUsed       uint64         `json:"gasUsed"`
	ParentHash    common.Hash    `json:"parentHash"`
	BaseFee       *hexutil.Big   `json:"baseFeePerGas"`
	ExcessBlobGas *uint64        `json:"excessBlobGas"` // EIP-4844
	BlobGasUsed   *uint64        `json:"blobGasUsed"`   // EIP-4844

	Alloc jsonutil.LazySortedJsonMap[common.Address, GenesisAccount] `json:"alloc"`
	// For genesis definitions without full state (OP-Mainnet, OP-Goerli)
	StateHash *common.Hash `json:"stateHash,omitempty"`
}

type GenesisAccount

type GenesisAccount struct {
	CodeHash common.Hash                                          `json:"codeHash,omitempty"`
	Storage  jsonutil.LazySortedJsonMap[common.Hash, common.Hash] `json:"storage,omitempty"`
	Balance  *hexutil.Big                                         `json:"balance,omitempty"`
	Nonce    uint64                                               `json:"nonce,omitempty"`
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL