github.com/ethereum/go-ethereum@v1.16.1/cmd/geth/config.go (about)

     1  // Copyright 2017 The go-ethereum Authors
     2  // This file is part of go-ethereum.
     3  //
     4  // go-ethereum is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // go-ethereum is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU General Public License
    15  // along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package main
    18  
    19  import (
    20  	"bufio"
    21  	"errors"
    22  	"fmt"
    23  	"os"
    24  	"reflect"
    25  	"runtime"
    26  	"slices"
    27  	"strings"
    28  	"unicode"
    29  
    30  	"github.com/ethereum/go-ethereum/accounts"
    31  	"github.com/ethereum/go-ethereum/accounts/external"
    32  	"github.com/ethereum/go-ethereum/accounts/keystore"
    33  	"github.com/ethereum/go-ethereum/accounts/scwallet"
    34  	"github.com/ethereum/go-ethereum/accounts/usbwallet"
    35  	"github.com/ethereum/go-ethereum/beacon/blsync"
    36  	"github.com/ethereum/go-ethereum/cmd/utils"
    37  	"github.com/ethereum/go-ethereum/common"
    38  	"github.com/ethereum/go-ethereum/common/hexutil"
    39  	"github.com/ethereum/go-ethereum/crypto"
    40  	"github.com/ethereum/go-ethereum/eth/catalyst"
    41  	"github.com/ethereum/go-ethereum/eth/ethconfig"
    42  	"github.com/ethereum/go-ethereum/internal/flags"
    43  	"github.com/ethereum/go-ethereum/internal/version"
    44  	"github.com/ethereum/go-ethereum/log"
    45  	"github.com/ethereum/go-ethereum/metrics"
    46  	"github.com/ethereum/go-ethereum/node"
    47  	"github.com/ethereum/go-ethereum/rpc"
    48  	"github.com/naoina/toml"
    49  	"github.com/urfave/cli/v2"
    50  )
    51  
    52  var (
    53  	dumpConfigCommand = &cli.Command{
    54  		Action:      dumpConfig,
    55  		Name:        "dumpconfig",
    56  		Usage:       "Export configuration values in a TOML format",
    57  		ArgsUsage:   "<dumpfile (optional)>",
    58  		Flags:       slices.Concat(nodeFlags, rpcFlags),
    59  		Description: `Export configuration values in TOML format (to stdout by default).`,
    60  	}
    61  
    62  	configFileFlag = &cli.StringFlag{
    63  		Name:     "config",
    64  		Usage:    "TOML configuration file",
    65  		Category: flags.EthCategory,
    66  	}
    67  )
    68  
    69  // These settings ensure that TOML keys use the same names as Go struct fields.
    70  var tomlSettings = toml.Config{
    71  	NormFieldName: func(rt reflect.Type, key string) string {
    72  		return key
    73  	},
    74  	FieldToKey: func(rt reflect.Type, field string) string {
    75  		return field
    76  	},
    77  	MissingField: func(rt reflect.Type, field string) error {
    78  		id := fmt.Sprintf("%s.%s", rt.String(), field)
    79  		if deprecatedConfigFields[id] {
    80  			log.Warn(fmt.Sprintf("Config field '%s' is deprecated and won't have any effect.", id))
    81  			return nil
    82  		}
    83  		var link string
    84  		if unicode.IsUpper(rune(rt.Name()[0])) && rt.PkgPath() != "main" {
    85  			link = fmt.Sprintf(", see https://godoc.org/%s#%s for available fields", rt.PkgPath(), rt.Name())
    86  		}
    87  		return fmt.Errorf("field '%s' is not defined in %s%s", field, rt.String(), link)
    88  	},
    89  }
    90  
    91  var deprecatedConfigFields = map[string]bool{
    92  	"ethconfig.Config.EVMInterpreter":          true,
    93  	"ethconfig.Config.EWASMInterpreter":        true,
    94  	"ethconfig.Config.TrieCleanCacheJournal":   true,
    95  	"ethconfig.Config.TrieCleanCacheRejournal": true,
    96  	"ethconfig.Config.LightServ":               true,
    97  	"ethconfig.Config.LightIngress":            true,
    98  	"ethconfig.Config.LightEgress":             true,
    99  	"ethconfig.Config.LightPeers":              true,
   100  	"ethconfig.Config.LightNoPrune":            true,
   101  	"ethconfig.Config.LightNoSyncServe":        true,
   102  }
   103  
   104  type ethstatsConfig struct {
   105  	URL string `toml:",omitempty"`
   106  }
   107  
   108  type gethConfig struct {
   109  	Eth      ethconfig.Config
   110  	Node     node.Config
   111  	Ethstats ethstatsConfig
   112  	Metrics  metrics.Config
   113  }
   114  
   115  func loadConfig(file string, cfg *gethConfig) error {
   116  	f, err := os.Open(file)
   117  	if err != nil {
   118  		return err
   119  	}
   120  	defer f.Close()
   121  
   122  	err = tomlSettings.NewDecoder(bufio.NewReader(f)).Decode(cfg)
   123  	// Add file name to errors that have a line number.
   124  	if _, ok := err.(*toml.LineError); ok {
   125  		err = errors.New(file + ", " + err.Error())
   126  	}
   127  	return err
   128  }
   129  
   130  func defaultNodeConfig() node.Config {
   131  	git, _ := version.VCS()
   132  	cfg := node.DefaultConfig
   133  	cfg.Name = clientIdentifier
   134  	cfg.Version = version.WithCommit(git.Commit, git.Date)
   135  	cfg.HTTPModules = append(cfg.HTTPModules, "eth")
   136  	cfg.WSModules = append(cfg.WSModules, "eth")
   137  	cfg.IPCPath = clientIdentifier + ".ipc"
   138  	return cfg
   139  }
   140  
   141  // loadBaseConfig loads the gethConfig based on the given command line
   142  // parameters and config file.
   143  func loadBaseConfig(ctx *cli.Context) gethConfig {
   144  	// Load defaults.
   145  	cfg := gethConfig{
   146  		Eth:     ethconfig.Defaults,
   147  		Node:    defaultNodeConfig(),
   148  		Metrics: metrics.DefaultConfig,
   149  	}
   150  
   151  	// Load config file.
   152  	if file := ctx.String(configFileFlag.Name); file != "" {
   153  		if err := loadConfig(file, &cfg); err != nil {
   154  			utils.Fatalf("%v", err)
   155  		}
   156  	}
   157  
   158  	// Apply flags.
   159  	utils.SetNodeConfig(ctx, &cfg.Node)
   160  	return cfg
   161  }
   162  
   163  // makeConfigNode loads geth configuration and creates a blank node instance.
   164  func makeConfigNode(ctx *cli.Context) (*node.Node, gethConfig) {
   165  	cfg := loadBaseConfig(ctx)
   166  	stack, err := node.New(&cfg.Node)
   167  	if err != nil {
   168  		utils.Fatalf("Failed to create the protocol stack: %v", err)
   169  	}
   170  	// Node doesn't by default populate account manager backends
   171  	if err := setAccountManagerBackends(stack.Config(), stack.AccountManager(), stack.KeyStoreDir()); err != nil {
   172  		utils.Fatalf("Failed to set account manager backends: %v", err)
   173  	}
   174  
   175  	utils.SetEthConfig(ctx, stack, &cfg.Eth)
   176  	if ctx.IsSet(utils.EthStatsURLFlag.Name) {
   177  		cfg.Ethstats.URL = ctx.String(utils.EthStatsURLFlag.Name)
   178  	}
   179  	applyMetricConfig(ctx, &cfg)
   180  
   181  	return stack, cfg
   182  }
   183  
   184  // constructs the disclaimer text block which will be printed in the logs upon
   185  // startup when Geth is running in dev mode.
   186  func constructDevModeBanner(ctx *cli.Context, cfg gethConfig) string {
   187  	devModeBanner := `You are running Geth in --dev mode. Please note the following:
   188  
   189    1. This mode is only intended for fast, iterative development without assumptions on
   190       security or persistence.
   191    2. The database is created in memory unless specified otherwise. Therefore, shutting down
   192       your computer or losing power will wipe your entire block data and chain state for
   193       your dev environment.
   194    3. A random, pre-allocated developer account will be available and unlocked as
   195       eth.coinbase, which can be used for testing. The random dev account is temporary,
   196       stored on a ramdisk, and will be lost if your machine is restarted.
   197    4. Mining is enabled by default. However, the client will only seal blocks if transactions
   198       are pending in the mempool. The miner's minimum accepted gas price is 1.
   199    5. Networking is disabled; there is no listen-address, the maximum number of peers is set
   200       to 0, and discovery is disabled.
   201  `
   202  	if !ctx.IsSet(utils.DataDirFlag.Name) {
   203  		devModeBanner += fmt.Sprintf(`
   204  
   205   Running in ephemeral mode.  The following account has been prefunded in the genesis:
   206  
   207         Account
   208         ------------------
   209         0x%x (10^49 ETH)
   210  `, cfg.Eth.Miner.PendingFeeRecipient)
   211  		if cfg.Eth.Miner.PendingFeeRecipient == utils.DeveloperAddr {
   212  			devModeBanner += fmt.Sprintf(` 
   213         Private Key
   214         ------------------
   215         0x%x
   216  `, crypto.FromECDSA(utils.DeveloperKey))
   217  		}
   218  	}
   219  
   220  	return devModeBanner
   221  }
   222  
   223  // makeFullNode loads geth configuration and creates the Ethereum backend.
   224  func makeFullNode(ctx *cli.Context) *node.Node {
   225  	stack, cfg := makeConfigNode(ctx)
   226  	if ctx.IsSet(utils.OverrideOsaka.Name) {
   227  		v := ctx.Uint64(utils.OverrideOsaka.Name)
   228  		cfg.Eth.OverrideOsaka = &v
   229  	}
   230  	if ctx.IsSet(utils.OverrideVerkle.Name) {
   231  		v := ctx.Uint64(utils.OverrideVerkle.Name)
   232  		cfg.Eth.OverrideVerkle = &v
   233  	}
   234  
   235  	// Start metrics export if enabled
   236  	utils.SetupMetrics(&cfg.Metrics)
   237  
   238  	backend, eth := utils.RegisterEthService(stack, &cfg.Eth)
   239  
   240  	// Create gauge with geth system and build information
   241  	if eth != nil { // The 'eth' backend may be nil in light mode
   242  		var protos []string
   243  		for _, p := range eth.Protocols() {
   244  			protos = append(protos, fmt.Sprintf("%v/%d", p.Name, p.Version))
   245  		}
   246  		metrics.NewRegisteredGaugeInfo("geth/info", nil).Update(metrics.GaugeInfoValue{
   247  			"arch":      runtime.GOARCH,
   248  			"os":        runtime.GOOS,
   249  			"version":   cfg.Node.Version,
   250  			"protocols": strings.Join(protos, ","),
   251  		})
   252  	}
   253  
   254  	// Configure log filter RPC API.
   255  	filterSystem := utils.RegisterFilterAPI(stack, backend, &cfg.Eth)
   256  
   257  	// Configure GraphQL if requested.
   258  	if ctx.IsSet(utils.GraphQLEnabledFlag.Name) {
   259  		utils.RegisterGraphQLService(stack, backend, filterSystem, &cfg.Node)
   260  	}
   261  	// Add the Ethereum Stats daemon if requested.
   262  	if cfg.Ethstats.URL != "" {
   263  		utils.RegisterEthStatsService(stack, backend, cfg.Ethstats.URL)
   264  	}
   265  	// Configure full-sync tester service if requested
   266  	if ctx.IsSet(utils.SyncTargetFlag.Name) {
   267  		hex := hexutil.MustDecode(ctx.String(utils.SyncTargetFlag.Name))
   268  		if len(hex) != common.HashLength {
   269  			utils.Fatalf("invalid sync target length: have %d, want %d", len(hex), common.HashLength)
   270  		}
   271  		utils.RegisterFullSyncTester(stack, eth, common.BytesToHash(hex))
   272  	}
   273  
   274  	if ctx.IsSet(utils.DeveloperFlag.Name) {
   275  		// Start dev mode.
   276  		simBeacon, err := catalyst.NewSimulatedBeacon(ctx.Uint64(utils.DeveloperPeriodFlag.Name), cfg.Eth.Miner.PendingFeeRecipient, eth)
   277  		if err != nil {
   278  			utils.Fatalf("failed to register dev mode catalyst service: %v", err)
   279  		}
   280  		catalyst.RegisterSimulatedBeaconAPIs(stack, simBeacon)
   281  		stack.RegisterLifecycle(simBeacon)
   282  
   283  		banner := constructDevModeBanner(ctx, cfg)
   284  		for _, line := range strings.Split(banner, "\n") {
   285  			log.Warn(line)
   286  		}
   287  	} else if ctx.IsSet(utils.BeaconApiFlag.Name) {
   288  		// Start blsync mode.
   289  		srv := rpc.NewServer()
   290  		srv.RegisterName("engine", catalyst.NewConsensusAPI(eth))
   291  		blsyncer := blsync.NewClient(utils.MakeBeaconLightConfig(ctx))
   292  		blsyncer.SetEngineRPC(rpc.DialInProc(srv))
   293  		stack.RegisterLifecycle(blsyncer)
   294  	} else {
   295  		// Launch the engine API for interacting with external consensus client.
   296  		err := catalyst.Register(stack, eth)
   297  		if err != nil {
   298  			utils.Fatalf("failed to register catalyst service: %v", err)
   299  		}
   300  	}
   301  	return stack
   302  }
   303  
   304  // dumpConfig is the dumpconfig command.
   305  func dumpConfig(ctx *cli.Context) error {
   306  	_, cfg := makeConfigNode(ctx)
   307  	comment := ""
   308  
   309  	if cfg.Eth.Genesis != nil {
   310  		cfg.Eth.Genesis = nil
   311  		comment += "# Note: this config doesn't contain the genesis block.\n\n"
   312  	}
   313  
   314  	out, err := tomlSettings.Marshal(&cfg)
   315  	if err != nil {
   316  		return err
   317  	}
   318  
   319  	dump := os.Stdout
   320  	if ctx.NArg() > 0 {
   321  		dump, err = os.OpenFile(ctx.Args().Get(0), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
   322  		if err != nil {
   323  			return err
   324  		}
   325  		defer dump.Close()
   326  	}
   327  	dump.WriteString(comment)
   328  	dump.Write(out)
   329  
   330  	return nil
   331  }
   332  
   333  func applyMetricConfig(ctx *cli.Context, cfg *gethConfig) {
   334  	if ctx.IsSet(utils.MetricsEnabledFlag.Name) {
   335  		cfg.Metrics.Enabled = ctx.Bool(utils.MetricsEnabledFlag.Name)
   336  	}
   337  	if ctx.IsSet(utils.MetricsEnabledExpensiveFlag.Name) {
   338  		log.Warn("Expensive metrics are collected by default, please remove this flag", "flag", utils.MetricsEnabledExpensiveFlag.Name)
   339  	}
   340  	if ctx.IsSet(utils.MetricsHTTPFlag.Name) {
   341  		cfg.Metrics.HTTP = ctx.String(utils.MetricsHTTPFlag.Name)
   342  	}
   343  	if ctx.IsSet(utils.MetricsPortFlag.Name) {
   344  		cfg.Metrics.Port = ctx.Int(utils.MetricsPortFlag.Name)
   345  	}
   346  	if ctx.IsSet(utils.MetricsEnableInfluxDBFlag.Name) {
   347  		cfg.Metrics.EnableInfluxDB = ctx.Bool(utils.MetricsEnableInfluxDBFlag.Name)
   348  	}
   349  	if ctx.IsSet(utils.MetricsInfluxDBEndpointFlag.Name) {
   350  		cfg.Metrics.InfluxDBEndpoint = ctx.String(utils.MetricsInfluxDBEndpointFlag.Name)
   351  	}
   352  	if ctx.IsSet(utils.MetricsInfluxDBDatabaseFlag.Name) {
   353  		cfg.Metrics.InfluxDBDatabase = ctx.String(utils.MetricsInfluxDBDatabaseFlag.Name)
   354  	}
   355  	if ctx.IsSet(utils.MetricsInfluxDBUsernameFlag.Name) {
   356  		cfg.Metrics.InfluxDBUsername = ctx.String(utils.MetricsInfluxDBUsernameFlag.Name)
   357  	}
   358  	if ctx.IsSet(utils.MetricsInfluxDBPasswordFlag.Name) {
   359  		cfg.Metrics.InfluxDBPassword = ctx.String(utils.MetricsInfluxDBPasswordFlag.Name)
   360  	}
   361  	if ctx.IsSet(utils.MetricsInfluxDBTagsFlag.Name) {
   362  		cfg.Metrics.InfluxDBTags = ctx.String(utils.MetricsInfluxDBTagsFlag.Name)
   363  	}
   364  	if ctx.IsSet(utils.MetricsEnableInfluxDBV2Flag.Name) {
   365  		cfg.Metrics.EnableInfluxDBV2 = ctx.Bool(utils.MetricsEnableInfluxDBV2Flag.Name)
   366  	}
   367  	if ctx.IsSet(utils.MetricsInfluxDBTokenFlag.Name) {
   368  		cfg.Metrics.InfluxDBToken = ctx.String(utils.MetricsInfluxDBTokenFlag.Name)
   369  	}
   370  	if ctx.IsSet(utils.MetricsInfluxDBBucketFlag.Name) {
   371  		cfg.Metrics.InfluxDBBucket = ctx.String(utils.MetricsInfluxDBBucketFlag.Name)
   372  	}
   373  	if ctx.IsSet(utils.MetricsInfluxDBOrganizationFlag.Name) {
   374  		cfg.Metrics.InfluxDBOrganization = ctx.String(utils.MetricsInfluxDBOrganizationFlag.Name)
   375  	}
   376  	// Sanity-check the commandline flags. It is fine if some unused fields is part
   377  	// of the toml-config, but we expect the commandline to only contain relevant
   378  	// arguments, otherwise it indicates an error.
   379  	var (
   380  		enableExport   = ctx.Bool(utils.MetricsEnableInfluxDBFlag.Name)
   381  		enableExportV2 = ctx.Bool(utils.MetricsEnableInfluxDBV2Flag.Name)
   382  	)
   383  	if enableExport || enableExportV2 {
   384  		v1FlagIsSet := ctx.IsSet(utils.MetricsInfluxDBUsernameFlag.Name) ||
   385  			ctx.IsSet(utils.MetricsInfluxDBPasswordFlag.Name)
   386  
   387  		v2FlagIsSet := ctx.IsSet(utils.MetricsInfluxDBTokenFlag.Name) ||
   388  			ctx.IsSet(utils.MetricsInfluxDBOrganizationFlag.Name) ||
   389  			ctx.IsSet(utils.MetricsInfluxDBBucketFlag.Name)
   390  
   391  		if enableExport && v2FlagIsSet {
   392  			utils.Fatalf("Flags --influxdb.metrics.organization, --influxdb.metrics.token, --influxdb.metrics.bucket are only available for influxdb-v2")
   393  		} else if enableExportV2 && v1FlagIsSet {
   394  			utils.Fatalf("Flags --influxdb.metrics.username, --influxdb.metrics.password are only available for influxdb-v1")
   395  		}
   396  	}
   397  }
   398  
   399  func setAccountManagerBackends(conf *node.Config, am *accounts.Manager, keydir string) error {
   400  	scryptN := keystore.StandardScryptN
   401  	scryptP := keystore.StandardScryptP
   402  	if conf.UseLightweightKDF {
   403  		scryptN = keystore.LightScryptN
   404  		scryptP = keystore.LightScryptP
   405  	}
   406  
   407  	// Assemble the supported backends
   408  	if len(conf.ExternalSigner) > 0 {
   409  		log.Info("Using external signer", "url", conf.ExternalSigner)
   410  		if extBackend, err := external.NewExternalBackend(conf.ExternalSigner); err == nil {
   411  			am.AddBackend(extBackend)
   412  			return nil
   413  		} else {
   414  			return fmt.Errorf("error connecting to external signer: %v", err)
   415  		}
   416  	}
   417  
   418  	// For now, we're using EITHER external signer OR local signers.
   419  	// If/when we implement some form of lockfile for USB and keystore wallets,
   420  	// we can have both, but it's very confusing for the user to see the same
   421  	// accounts in both externally and locally, plus very racey.
   422  	am.AddBackend(keystore.NewKeyStore(keydir, scryptN, scryptP))
   423  	if conf.USB {
   424  		// Start a USB hub for Ledger hardware wallets
   425  		if ledgerhub, err := usbwallet.NewLedgerHub(); err != nil {
   426  			log.Warn(fmt.Sprintf("Failed to start Ledger hub, disabling: %v", err))
   427  		} else {
   428  			am.AddBackend(ledgerhub)
   429  		}
   430  		// Start a USB hub for Trezor hardware wallets (HID version)
   431  		if trezorhub, err := usbwallet.NewTrezorHubWithHID(); err != nil {
   432  			log.Warn(fmt.Sprintf("Failed to start HID Trezor hub, disabling: %v", err))
   433  		} else {
   434  			am.AddBackend(trezorhub)
   435  		}
   436  		// Start a USB hub for Trezor hardware wallets (WebUSB version)
   437  		if trezorhub, err := usbwallet.NewTrezorHubWithWebUSB(); err != nil {
   438  			log.Warn(fmt.Sprintf("Failed to start WebUSB Trezor hub, disabling: %v", err))
   439  		} else {
   440  			am.AddBackend(trezorhub)
   441  		}
   442  	}
   443  	if len(conf.SmartCardDaemonPath) > 0 {
   444  		// Start a smart card hub
   445  		if schub, err := scwallet.NewHub(conf.SmartCardDaemonPath, scwallet.Scheme, keydir); err != nil {
   446  			log.Warn(fmt.Sprintf("Failed to start smart card hub, disabling: %v", err))
   447  		} else {
   448  			am.AddBackend(schub)
   449  		}
   450  	}
   451  
   452  	return nil
   453  }