diff --git a/cmd/geth/chaincmd.go b/cmd/geth/chaincmd.go index 6e19edeb46eb..e43287531ea3 100644 --- a/cmd/geth/chaincmd.go +++ b/cmd/geth/chaincmd.go @@ -64,7 +64,7 @@ It expects the genesis file as argument.`, Flags: utils.NetworkFlags, Category: "BLOCKCHAIN COMMANDS", Description: ` -The dumpgenesis command dumps the genesis block configuration in JSON format to stdout.`, +The dumpgenesis command prints a genesis block as JSON. What it outputs is determined in order of preference: testnet preset if it is set, preexisting datadir, mainnet.`, } importCommand = cli.Command{ Action: utils.MigrateFlags(importChain), @@ -207,13 +207,41 @@ func initGenesis(ctx *cli.Context) error { } func dumpGenesis(ctx *cli.Context) error { - // TODO(rjl493456442) support loading from the custom datadir - genesis := utils.MakeGenesis(ctx) - if genesis == nil { - genesis = core.DefaultGenesisBlock() + // if there is a testnet preset enabled, dump that + if utils.IsTestnetPreset(ctx) { + genesis := utils.MakeGenesis(ctx) + if err := json.NewEncoder(os.Stdout).Encode(genesis); err != nil { + utils.Fatalf("could not encode genesis: %s", err) + } + return nil + } + // dump whatever already exists in the datadir + stack, _ := makeConfigNode(ctx) + for _, name := range []string{"chaindata", "lightchaindata"} { + db, err := stack.OpenDatabase(name, 0, 0, "", true) + if err != nil { + if !os.IsNotExist(err) { + return err + } + continue + } + genesis, err := core.ReadGenesis(db) + if err != nil { + utils.Fatalf("failed to read genesis: %s", err) + } + db.Close() + + if err := json.NewEncoder(os.Stdout).Encode(*genesis); err != nil { + utils.Fatalf("could not encode stored genesis: %s", err) + } + return nil + } + if ctx.GlobalIsSet(utils.DataDirFlag.Name) { + utils.Fatalf("no existing datadir at %s", stack.Config().DataDir) } - if err := json.NewEncoder(os.Stdout).Encode(genesis); err != nil { - utils.Fatalf("could not encode genesis") + // no datadir exists or is specified, dump mainnet + if err := json.NewEncoder(os.Stdout).Encode(core.DefaultGenesisBlock()); err != nil { + utils.Fatalf("could not encode genesis: %s", err) } return nil } diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 907e3ce91677..a101c38c1652 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -1987,6 +1987,13 @@ func MakeChainDatabase(ctx *cli.Context, stack *node.Node, readonly bool) ethdb. return chainDb } +func IsTestnetPreset(ctx *cli.Context) bool { + if ctx.GlobalBool(RopstenFlag.Name) || ctx.GlobalBool(SepoliaFlag.Name) || ctx.GlobalBool(RinkebyFlag.Name) || ctx.GlobalBool(GoerliFlag.Name) || ctx.GlobalBool(KilnFlag.Name) || ctx.GlobalBool(DeveloperFlag.Name) { + return true + } + return false +} + func MakeGenesis(ctx *cli.Context) *core.Genesis { var genesis *core.Genesis switch { diff --git a/core/genesis.go b/core/genesis.go index aa7d704ea2c7..fb4d38aa4576 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -65,6 +65,41 @@ type Genesis struct { BaseFee *big.Int `json:"baseFeePerGas"` } +func ReadGenesis(db ethdb.Database) (*Genesis, error) { + var genesis Genesis + stored := rawdb.ReadCanonicalHash(db, 0) + if (stored == common.Hash{}) { + return nil, fmt.Errorf("invalid genesis hash in database: %x", stored) + } + blob := rawdb.ReadGenesisState(db, stored) + if blob == nil { + return nil, fmt.Errorf("genesis state missing from db") + } + if len(blob) != 0 { + if err := genesis.Alloc.UnmarshalJSON(blob); err != nil { + return nil, fmt.Errorf("could not unmartial genesis state json: %s", err) + } + } + genesis.Config = rawdb.ReadChainConfig(db, stored) + if genesis.Config == nil { + return nil, fmt.Errorf("genesis config missing from db") + } + genesisBlock := rawdb.ReadBlock(db, stored, 0) + if genesisBlock == nil { + return nil, fmt.Errorf("genesis block missing from db") + } + genesisHeader := genesisBlock.Header() + genesis.Nonce = genesisHeader.Nonce.Uint64() + genesis.Timestamp = genesisHeader.Time + genesis.ExtraData = genesisHeader.Extra + genesis.GasLimit = genesisHeader.GasLimit + genesis.Difficulty = genesisHeader.Difficulty + genesis.Mixhash = genesisHeader.MixDigest + genesis.Coinbase = genesisHeader.Coinbase + + return &genesis, nil +} + // GenesisAlloc specifies the initial state that is part of the genesis block. type GenesisAlloc map[common.Address]GenesisAccount