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 }