// Copyright 2017 The go-ethereum Authors // This file is part of go-ethereum. // // go-ethereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // go-ethereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with go-ethereum. If not, see . package main import ( "bufio" "errors" "fmt" "math/big" "os" "reflect" "unicode" "gopkg.in/urfave/cli.v1" "github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/eth/ethconfig" "github.com/ethereum/go-ethereum/internal/ethapi" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/params" "github.com/naoina/toml" ) var ( dumpConfigCommand = cli.Command{ Action: utils.MigrateFlags(dumpConfig), Name: "dumpconfig", Usage: "Show configuration values", ArgsUsage: "", Flags: append(append(nodeFlags, rpcFlags...), whisperFlags...), Category: "MISCELLANEOUS COMMANDS", Description: `The dumpconfig command shows configuration values.`, } configFileFlag = cli.StringFlag{ Name: "config", Usage: "TOML configuration file", } ) // These settings ensure that TOML keys use the same names as Go struct fields. var tomlSettings = toml.Config{ NormFieldName: func(rt reflect.Type, key string) string { return key }, FieldToKey: func(rt reflect.Type, field string) string { return field }, MissingField: func(rt reflect.Type, field string) error { link := "" if unicode.IsUpper(rune(rt.Name()[0])) && rt.PkgPath() != "main" { link = fmt.Sprintf(", see https://godoc.org/%s#%s for available fields", rt.PkgPath(), rt.Name()) } return fmt.Errorf("field '%s' is not defined in %s%s", field, rt.String(), link) }, } type ethstatsConfig struct { URL string `toml:",omitempty"` } // whisper has been deprecated, but clients out there might still have [Shh] // in their config, which will crash. Cut them some slack by keeping the // config, and displaying a message that those config switches are ineffectual. // To be removed circa Q1 2021 -- @gballet. type whisperDeprecatedConfig struct { MaxMessageSize uint32 `toml:",omitempty"` MinimumAcceptedPOW float64 `toml:",omitempty"` RestrictConnectionBetweenLightClients bool `toml:",omitempty"` } type gethConfig struct { Eth ethconfig.Config Shh whisperDeprecatedConfig Node node.Config Ethstats ethstatsConfig Metrics metrics.Config } func loadConfig(file string, cfg *gethConfig) error { f, err := os.Open(file) if err != nil { return err } defer f.Close() err = tomlSettings.NewDecoder(bufio.NewReader(f)).Decode(cfg) // Add file name to errors that have a line number. if _, ok := err.(*toml.LineError); ok { err = errors.New(file + ", " + err.Error()) } return err } func defaultNodeConfig() node.Config { cfg := node.DefaultConfig cfg.Name = clientIdentifier cfg.Version = params.VersionWithCommit(gitCommit, gitDate) cfg.HTTPModules = append(cfg.HTTPModules, "eth") cfg.WSModules = append(cfg.WSModules, "eth") cfg.IPCPath = "geth.ipc" return cfg } // makeConfigNode loads geth configuration and creates a blank node instance. func makeConfigNode(ctx *cli.Context) (*node.Node, gethConfig) { // Load defaults. cfg := gethConfig{ Eth: ethconfig.Defaults, Node: defaultNodeConfig(), Metrics: metrics.DefaultConfig, } // Load config file. if file := ctx.GlobalString(configFileFlag.Name); file != "" { if err := loadConfig(file, &cfg); err != nil { utils.Fatalf("%v", err) } if cfg.Shh != (whisperDeprecatedConfig{}) { log.Warn("Deprecated whisper config detected. Whisper has been moved to github.com/ethereum/whisper") } } // Apply flags. utils.SetNodeConfig(ctx, &cfg.Node) stack, err := node.New(&cfg.Node) if err != nil { utils.Fatalf("Failed to create the protocol stack: %v", err) } utils.SetEthConfig(ctx, stack, &cfg.Eth) if ctx.GlobalIsSet(utils.EthStatsURLFlag.Name) { cfg.Ethstats.URL = ctx.GlobalString(utils.EthStatsURLFlag.Name) } utils.SetShhConfig(ctx, stack) applyMetricConfig(ctx, &cfg) return stack, cfg } // enableWhisper returns true in case one of the whisper flags is set. func checkWhisper(ctx *cli.Context) { for _, flag := range whisperFlags { if ctx.GlobalIsSet(flag.GetName()) { log.Warn("deprecated whisper flag detected. Whisper has been moved to github.com/ethereum/whisper") } } } // makeFullNode loads geth configuration and creates the Ethereum backend. func makeFullNode(ctx *cli.Context) (*node.Node, ethapi.Backend) { stack, cfg := makeConfigNode(ctx) if ctx.GlobalIsSet(utils.OverrideBerlinFlag.Name) { cfg.Eth.OverrideBerlin = new(big.Int).SetUint64(ctx.GlobalUint64(utils.OverrideBerlinFlag.Name)) } backend := utils.RegisterEthService(stack, &cfg.Eth) checkWhisper(ctx) // Configure GraphQL if requested if ctx.GlobalIsSet(utils.GraphQLEnabledFlag.Name) { utils.RegisterGraphQLService(stack, backend, cfg.Node) } // Add the Ethereum Stats daemon if requested. if cfg.Ethstats.URL != "" { utils.RegisterEthStatsService(stack, backend, cfg.Ethstats.URL) } return stack, backend } // dumpConfig is the dumpconfig command. func dumpConfig(ctx *cli.Context) error { _, cfg := makeConfigNode(ctx) comment := "" if cfg.Eth.Genesis != nil { cfg.Eth.Genesis = nil comment += "# Note: this config doesn't contain the genesis block.\n\n" } out, err := tomlSettings.Marshal(&cfg) if err != nil { return err } dump := os.Stdout if ctx.NArg() > 0 { dump, err = os.OpenFile(ctx.Args().Get(0), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644) if err != nil { return err } defer dump.Close() } dump.WriteString(comment) dump.Write(out) return nil } func applyMetricConfig(ctx *cli.Context, cfg *gethConfig) { if ctx.GlobalIsSet(utils.MetricsEnabledFlag.Name) { cfg.Metrics.Enabled = ctx.GlobalBool(utils.MetricsEnabledFlag.Name) } if ctx.GlobalIsSet(utils.MetricsEnabledExpensiveFlag.Name) { cfg.Metrics.EnabledExpensive = ctx.GlobalBool(utils.MetricsEnabledExpensiveFlag.Name) } if ctx.GlobalIsSet(utils.MetricsHTTPFlag.Name) { cfg.Metrics.HTTP = ctx.GlobalString(utils.MetricsHTTPFlag.Name) } if ctx.GlobalIsSet(utils.MetricsPortFlag.Name) { cfg.Metrics.Port = ctx.GlobalInt(utils.MetricsPortFlag.Name) } if ctx.GlobalIsSet(utils.MetricsEnableInfluxDBFlag.Name) { cfg.Metrics.EnableInfluxDB = ctx.GlobalBool(utils.MetricsEnableInfluxDBFlag.Name) } if ctx.GlobalIsSet(utils.MetricsInfluxDBEndpointFlag.Name) { cfg.Metrics.InfluxDBEndpoint = ctx.GlobalString(utils.MetricsInfluxDBEndpointFlag.Name) } if ctx.GlobalIsSet(utils.MetricsInfluxDBDatabaseFlag.Name) { cfg.Metrics.InfluxDBDatabase = ctx.GlobalString(utils.MetricsInfluxDBDatabaseFlag.Name) } if ctx.GlobalIsSet(utils.MetricsInfluxDBUsernameFlag.Name) { cfg.Metrics.InfluxDBUsername = ctx.GlobalString(utils.MetricsInfluxDBUsernameFlag.Name) } if ctx.GlobalIsSet(utils.MetricsInfluxDBPasswordFlag.Name) { cfg.Metrics.InfluxDBPassword = ctx.GlobalString(utils.MetricsInfluxDBPasswordFlag.Name) } if ctx.GlobalIsSet(utils.MetricsInfluxDBTagsFlag.Name) { cfg.Metrics.InfluxDBTags = ctx.GlobalString(utils.MetricsInfluxDBTagsFlag.Name) } }