github.com/myafeier/go-ethereum@v1.6.8-0.20170719123245-3e0dbe0eaa72/cmd/utils/flags.go (about) 1 // Copyright 2015 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 utils contains internal helper functions for go-ethereum commands. 18 package utils 19 20 import ( 21 "crypto/ecdsa" 22 "fmt" 23 "io/ioutil" 24 "math/big" 25 "os" 26 "path/filepath" 27 "runtime" 28 "strconv" 29 "strings" 30 31 "github.com/ethereum/go-ethereum/accounts" 32 "github.com/ethereum/go-ethereum/accounts/keystore" 33 "github.com/ethereum/go-ethereum/common" 34 "github.com/ethereum/go-ethereum/consensus/ethash" 35 "github.com/ethereum/go-ethereum/core" 36 "github.com/ethereum/go-ethereum/core/state" 37 "github.com/ethereum/go-ethereum/core/vm" 38 "github.com/ethereum/go-ethereum/crypto" 39 "github.com/ethereum/go-ethereum/eth" 40 "github.com/ethereum/go-ethereum/eth/downloader" 41 "github.com/ethereum/go-ethereum/eth/gasprice" 42 "github.com/ethereum/go-ethereum/ethdb" 43 "github.com/ethereum/go-ethereum/ethstats" 44 "github.com/ethereum/go-ethereum/event" 45 "github.com/ethereum/go-ethereum/les" 46 "github.com/ethereum/go-ethereum/log" 47 "github.com/ethereum/go-ethereum/metrics" 48 "github.com/ethereum/go-ethereum/node" 49 "github.com/ethereum/go-ethereum/p2p" 50 "github.com/ethereum/go-ethereum/p2p/discover" 51 "github.com/ethereum/go-ethereum/p2p/discv5" 52 "github.com/ethereum/go-ethereum/p2p/nat" 53 "github.com/ethereum/go-ethereum/p2p/netutil" 54 "github.com/ethereum/go-ethereum/params" 55 whisper "github.com/ethereum/go-ethereum/whisper/whisperv5" 56 "gopkg.in/urfave/cli.v1" 57 ) 58 59 var ( 60 CommandHelpTemplate = `{{.cmd.Name}}{{if .cmd.Subcommands}} command{{end}}{{if .cmd.Flags}} [command options]{{end}} [arguments...] 61 {{if .cmd.Description}}{{.cmd.Description}} 62 {{end}}{{if .cmd.Subcommands}} 63 SUBCOMMANDS: 64 {{range .cmd.Subcommands}}{{.cmd.Name}}{{with .cmd.ShortName}}, {{.cmd}}{{end}}{{ "\t" }}{{.cmd.Usage}} 65 {{end}}{{end}}{{if .categorizedFlags}} 66 {{range $idx, $categorized := .categorizedFlags}}{{$categorized.Name}} OPTIONS: 67 {{range $categorized.Flags}}{{"\t"}}{{.}} 68 {{end}} 69 {{end}}{{end}}` 70 ) 71 72 func init() { 73 cli.AppHelpTemplate = `{{.Name}} {{if .Flags}}[global options] {{end}}command{{if .Flags}} [command options]{{end}} [arguments...] 74 75 VERSION: 76 {{.Version}} 77 78 COMMANDS: 79 {{range .Commands}}{{.Name}}{{with .ShortName}}, {{.}}{{end}}{{ "\t" }}{{.Usage}} 80 {{end}}{{if .Flags}} 81 GLOBAL OPTIONS: 82 {{range .Flags}}{{.}} 83 {{end}}{{end}} 84 ` 85 86 cli.CommandHelpTemplate = CommandHelpTemplate 87 } 88 89 // NewApp creates an app with sane defaults. 90 func NewApp(gitCommit, usage string) *cli.App { 91 app := cli.NewApp() 92 app.Name = filepath.Base(os.Args[0]) 93 app.Author = "" 94 //app.Authors = nil 95 app.Email = "" 96 app.Version = params.Version 97 if gitCommit != "" { 98 app.Version += "-" + gitCommit[:8] 99 } 100 app.Usage = usage 101 return app 102 } 103 104 // These are all the command line flags we support. 105 // If you add to this list, please remember to include the 106 // flag in the appropriate command definition. 107 // 108 // The flags are defined here so their names and help texts 109 // are the same for all commands. 110 111 var ( 112 // General settings 113 DataDirFlag = DirectoryFlag{ 114 Name: "datadir", 115 Usage: "Data directory for the databases and keystore", 116 Value: DirectoryString{node.DefaultDataDir()}, 117 } 118 KeyStoreDirFlag = DirectoryFlag{ 119 Name: "keystore", 120 Usage: "Directory for the keystore (default = inside the datadir)", 121 } 122 NoUSBFlag = cli.BoolFlag{ 123 Name: "nousb", 124 Usage: "Disables monitoring for and managine USB hardware wallets", 125 } 126 NetworkIdFlag = cli.Uint64Flag{ 127 Name: "networkid", 128 Usage: "Network identifier (integer, 1=Frontier, 2=Morden (disused), 3=Ropsten, 4=Rinkeby)", 129 Value: eth.DefaultConfig.NetworkId, 130 } 131 TestnetFlag = cli.BoolFlag{ 132 Name: "testnet", 133 Usage: "Ropsten network: pre-configured proof-of-work test network", 134 } 135 RinkebyFlag = cli.BoolFlag{ 136 Name: "rinkeby", 137 Usage: "Rinkeby network: pre-configured proof-of-authority test network", 138 } 139 DevModeFlag = cli.BoolFlag{ 140 Name: "dev", 141 Usage: "Developer mode: pre-configured private network with several debugging flags", 142 } 143 IdentityFlag = cli.StringFlag{ 144 Name: "identity", 145 Usage: "Custom node name", 146 } 147 DocRootFlag = DirectoryFlag{ 148 Name: "docroot", 149 Usage: "Document Root for HTTPClient file scheme", 150 Value: DirectoryString{homeDir()}, 151 } 152 FastSyncFlag = cli.BoolFlag{ 153 Name: "fast", 154 Usage: "Enable fast syncing through state downloads", 155 } 156 LightModeFlag = cli.BoolFlag{ 157 Name: "light", 158 Usage: "Enable light client mode", 159 } 160 defaultSyncMode = eth.DefaultConfig.SyncMode 161 SyncModeFlag = TextMarshalerFlag{ 162 Name: "syncmode", 163 Usage: `Blockchain sync mode ("fast", "full", or "light")`, 164 Value: &defaultSyncMode, 165 } 166 167 LightServFlag = cli.IntFlag{ 168 Name: "lightserv", 169 Usage: "Maximum percentage of time allowed for serving LES requests (0-90)", 170 Value: 0, 171 } 172 LightPeersFlag = cli.IntFlag{ 173 Name: "lightpeers", 174 Usage: "Maximum number of LES client peers", 175 Value: 20, 176 } 177 LightKDFFlag = cli.BoolFlag{ 178 Name: "lightkdf", 179 Usage: "Reduce key-derivation RAM & CPU usage at some expense of KDF strength", 180 } 181 // Ethash settings 182 EthashCacheDirFlag = DirectoryFlag{ 183 Name: "ethash.cachedir", 184 Usage: "Directory to store the ethash verification caches (default = inside the datadir)", 185 } 186 EthashCachesInMemoryFlag = cli.IntFlag{ 187 Name: "ethash.cachesinmem", 188 Usage: "Number of recent ethash caches to keep in memory (16MB each)", 189 Value: eth.DefaultConfig.EthashCachesInMem, 190 } 191 EthashCachesOnDiskFlag = cli.IntFlag{ 192 Name: "ethash.cachesondisk", 193 Usage: "Number of recent ethash caches to keep on disk (16MB each)", 194 Value: eth.DefaultConfig.EthashCachesOnDisk, 195 } 196 EthashDatasetDirFlag = DirectoryFlag{ 197 Name: "ethash.dagdir", 198 Usage: "Directory to store the ethash mining DAGs (default = inside home folder)", 199 Value: DirectoryString{eth.DefaultConfig.EthashDatasetDir}, 200 } 201 EthashDatasetsInMemoryFlag = cli.IntFlag{ 202 Name: "ethash.dagsinmem", 203 Usage: "Number of recent ethash mining DAGs to keep in memory (1+GB each)", 204 Value: eth.DefaultConfig.EthashDatasetsInMem, 205 } 206 EthashDatasetsOnDiskFlag = cli.IntFlag{ 207 Name: "ethash.dagsondisk", 208 Usage: "Number of recent ethash mining DAGs to keep on disk (1+GB each)", 209 Value: eth.DefaultConfig.EthashDatasetsOnDisk, 210 } 211 // Transaction pool settings 212 TxPoolNoLocalsFlag = cli.BoolFlag{ 213 Name: "txpool.nolocals", 214 Usage: "Disables price exemptions for locally submitted transactions", 215 } 216 TxPoolPriceLimitFlag = cli.Uint64Flag{ 217 Name: "txpool.pricelimit", 218 Usage: "Minimum gas price limit to enforce for acceptance into the pool", 219 Value: eth.DefaultConfig.TxPool.PriceLimit, 220 } 221 TxPoolPriceBumpFlag = cli.Uint64Flag{ 222 Name: "txpool.pricebump", 223 Usage: "Price bump percentage to replace an already existing transaction", 224 Value: eth.DefaultConfig.TxPool.PriceBump, 225 } 226 TxPoolAccountSlotsFlag = cli.Uint64Flag{ 227 Name: "txpool.accountslots", 228 Usage: "Minimum number of executable transaction slots guaranteed per account", 229 Value: eth.DefaultConfig.TxPool.AccountSlots, 230 } 231 TxPoolGlobalSlotsFlag = cli.Uint64Flag{ 232 Name: "txpool.globalslots", 233 Usage: "Maximum number of executable transaction slots for all accounts", 234 Value: eth.DefaultConfig.TxPool.GlobalSlots, 235 } 236 TxPoolAccountQueueFlag = cli.Uint64Flag{ 237 Name: "txpool.accountqueue", 238 Usage: "Maximum number of non-executable transaction slots permitted per account", 239 Value: eth.DefaultConfig.TxPool.AccountQueue, 240 } 241 TxPoolGlobalQueueFlag = cli.Uint64Flag{ 242 Name: "txpool.globalqueue", 243 Usage: "Maximum number of non-executable transaction slots for all accounts", 244 Value: eth.DefaultConfig.TxPool.GlobalQueue, 245 } 246 TxPoolLifetimeFlag = cli.DurationFlag{ 247 Name: "txpool.lifetime", 248 Usage: "Maximum amount of time non-executable transaction are queued", 249 Value: eth.DefaultConfig.TxPool.Lifetime, 250 } 251 // Performance tuning settings 252 CacheFlag = cli.IntFlag{ 253 Name: "cache", 254 Usage: "Megabytes of memory allocated to internal caching (min 16MB / database forced)", 255 Value: 128, 256 } 257 TrieCacheGenFlag = cli.IntFlag{ 258 Name: "trie-cache-gens", 259 Usage: "Number of trie node generations to keep in memory", 260 Value: int(state.MaxTrieCacheGen), 261 } 262 // Miner settings 263 MiningEnabledFlag = cli.BoolFlag{ 264 Name: "mine", 265 Usage: "Enable mining", 266 } 267 MinerThreadsFlag = cli.IntFlag{ 268 Name: "minerthreads", 269 Usage: "Number of CPU threads to use for mining", 270 Value: runtime.NumCPU(), 271 } 272 TargetGasLimitFlag = cli.Uint64Flag{ 273 Name: "targetgaslimit", 274 Usage: "Target gas limit sets the artificial target gas floor for the blocks to mine", 275 Value: params.GenesisGasLimit.Uint64(), 276 } 277 EtherbaseFlag = cli.StringFlag{ 278 Name: "etherbase", 279 Usage: "Public address for block mining rewards (default = first account created)", 280 Value: "0", 281 } 282 GasPriceFlag = BigFlag{ 283 Name: "gasprice", 284 Usage: "Minimal gas price to accept for mining a transactions", 285 Value: eth.DefaultConfig.GasPrice, 286 } 287 ExtraDataFlag = cli.StringFlag{ 288 Name: "extradata", 289 Usage: "Block extra data set by the miner (default = client version)", 290 } 291 // Account settings 292 UnlockedAccountFlag = cli.StringFlag{ 293 Name: "unlock", 294 Usage: "Comma separated list of accounts to unlock", 295 Value: "", 296 } 297 PasswordFileFlag = cli.StringFlag{ 298 Name: "password", 299 Usage: "Password file to use for non-inteactive password input", 300 Value: "", 301 } 302 303 VMEnableDebugFlag = cli.BoolFlag{ 304 Name: "vmdebug", 305 Usage: "Record information useful for VM and contract debugging", 306 } 307 // Logging and debug settings 308 EthStatsURLFlag = cli.StringFlag{ 309 Name: "ethstats", 310 Usage: "Reporting URL of a ethstats service (nodename:secret@host:port)", 311 } 312 MetricsEnabledFlag = cli.BoolFlag{ 313 Name: metrics.MetricsEnabledFlag, 314 Usage: "Enable metrics collection and reporting", 315 } 316 FakePoWFlag = cli.BoolFlag{ 317 Name: "fakepow", 318 Usage: "Disables proof-of-work verification", 319 } 320 NoCompactionFlag = cli.BoolFlag{ 321 Name: "nocompaction", 322 Usage: "Disables db compaction after import", 323 } 324 // RPC settings 325 RPCEnabledFlag = cli.BoolFlag{ 326 Name: "rpc", 327 Usage: "Enable the HTTP-RPC server", 328 } 329 RPCListenAddrFlag = cli.StringFlag{ 330 Name: "rpcaddr", 331 Usage: "HTTP-RPC server listening interface", 332 Value: node.DefaultHTTPHost, 333 } 334 RPCPortFlag = cli.IntFlag{ 335 Name: "rpcport", 336 Usage: "HTTP-RPC server listening port", 337 Value: node.DefaultHTTPPort, 338 } 339 RPCCORSDomainFlag = cli.StringFlag{ 340 Name: "rpccorsdomain", 341 Usage: "Comma separated list of domains from which to accept cross origin requests (browser enforced)", 342 Value: "", 343 } 344 RPCApiFlag = cli.StringFlag{ 345 Name: "rpcapi", 346 Usage: "API's offered over the HTTP-RPC interface", 347 Value: "", 348 } 349 IPCDisabledFlag = cli.BoolFlag{ 350 Name: "ipcdisable", 351 Usage: "Disable the IPC-RPC server", 352 } 353 IPCPathFlag = DirectoryFlag{ 354 Name: "ipcpath", 355 Usage: "Filename for IPC socket/pipe within the datadir (explicit paths escape it)", 356 } 357 WSEnabledFlag = cli.BoolFlag{ 358 Name: "ws", 359 Usage: "Enable the WS-RPC server", 360 } 361 WSListenAddrFlag = cli.StringFlag{ 362 Name: "wsaddr", 363 Usage: "WS-RPC server listening interface", 364 Value: node.DefaultWSHost, 365 } 366 WSPortFlag = cli.IntFlag{ 367 Name: "wsport", 368 Usage: "WS-RPC server listening port", 369 Value: node.DefaultWSPort, 370 } 371 WSApiFlag = cli.StringFlag{ 372 Name: "wsapi", 373 Usage: "API's offered over the WS-RPC interface", 374 Value: "", 375 } 376 WSAllowedOriginsFlag = cli.StringFlag{ 377 Name: "wsorigins", 378 Usage: "Origins from which to accept websockets requests", 379 Value: "", 380 } 381 ExecFlag = cli.StringFlag{ 382 Name: "exec", 383 Usage: "Execute JavaScript statement", 384 } 385 PreloadJSFlag = cli.StringFlag{ 386 Name: "preload", 387 Usage: "Comma separated list of JavaScript files to preload into the console", 388 } 389 390 // Network Settings 391 MaxPeersFlag = cli.IntFlag{ 392 Name: "maxpeers", 393 Usage: "Maximum number of network peers (network disabled if set to 0)", 394 Value: 25, 395 } 396 MaxPendingPeersFlag = cli.IntFlag{ 397 Name: "maxpendpeers", 398 Usage: "Maximum number of pending connection attempts (defaults used if set to 0)", 399 Value: 0, 400 } 401 ListenPortFlag = cli.IntFlag{ 402 Name: "port", 403 Usage: "Network listening port", 404 Value: 30303, 405 } 406 BootnodesFlag = cli.StringFlag{ 407 Name: "bootnodes", 408 Usage: "Comma separated enode URLs for P2P discovery bootstrap (set v4+v5 instead for light servers)", 409 Value: "", 410 } 411 BootnodesV4Flag = cli.StringFlag{ 412 Name: "bootnodesv4", 413 Usage: "Comma separated enode URLs for P2P v4 discovery bootstrap (light server, full nodes)", 414 Value: "", 415 } 416 BootnodesV5Flag = cli.StringFlag{ 417 Name: "bootnodesv5", 418 Usage: "Comma separated enode URLs for P2P v5 discovery bootstrap (light server, light nodes)", 419 Value: "", 420 } 421 NodeKeyFileFlag = cli.StringFlag{ 422 Name: "nodekey", 423 Usage: "P2P node key file", 424 } 425 NodeKeyHexFlag = cli.StringFlag{ 426 Name: "nodekeyhex", 427 Usage: "P2P node key as hex (for testing)", 428 } 429 NATFlag = cli.StringFlag{ 430 Name: "nat", 431 Usage: "NAT port mapping mechanism (any|none|upnp|pmp|extip:<IP>)", 432 Value: "any", 433 } 434 NoDiscoverFlag = cli.BoolFlag{ 435 Name: "nodiscover", 436 Usage: "Disables the peer discovery mechanism (manual peer addition)", 437 } 438 DiscoveryV5Flag = cli.BoolFlag{ 439 Name: "v5disc", 440 Usage: "Enables the experimental RLPx V5 (Topic Discovery) mechanism", 441 } 442 NetrestrictFlag = cli.StringFlag{ 443 Name: "netrestrict", 444 Usage: "Restricts network communication to the given IP networks (CIDR masks)", 445 } 446 447 // ATM the url is left to the user and deployment to 448 JSpathFlag = cli.StringFlag{ 449 Name: "jspath", 450 Usage: "JavaScript root path for `loadScript`", 451 Value: ".", 452 } 453 454 // Gas price oracle settings 455 GpoBlocksFlag = cli.IntFlag{ 456 Name: "gpoblocks", 457 Usage: "Number of recent blocks to check for gas prices", 458 Value: eth.DefaultConfig.GPO.Blocks, 459 } 460 GpoPercentileFlag = cli.IntFlag{ 461 Name: "gpopercentile", 462 Usage: "Suggested gas price is the given percentile of a set of recent transaction gas prices", 463 Value: eth.DefaultConfig.GPO.Percentile, 464 } 465 WhisperEnabledFlag = cli.BoolFlag{ 466 Name: "shh", 467 Usage: "Enable Whisper", 468 } 469 WhisperMaxMessageSizeFlag = cli.IntFlag{ 470 Name: "shh.maxmessagesize", 471 Usage: "Max message size accepted", 472 Value: int(whisper.DefaultMaxMessageSize), 473 } 474 WhisperMinPOWFlag = cli.Float64Flag{ 475 Name: "shh.pow", 476 Usage: "Minimum POW accepted", 477 Value: whisper.DefaultMinimumPoW, 478 } 479 ) 480 481 // MakeDataDir retrieves the currently requested data directory, terminating 482 // if none (or the empty string) is specified. If the node is starting a testnet, 483 // the a subdirectory of the specified datadir will be used. 484 func MakeDataDir(ctx *cli.Context) string { 485 if path := ctx.GlobalString(DataDirFlag.Name); path != "" { 486 if ctx.GlobalBool(TestnetFlag.Name) { 487 return filepath.Join(path, "testnet") 488 } 489 if ctx.GlobalBool(RinkebyFlag.Name) { 490 return filepath.Join(path, "rinkeby") 491 } 492 return path 493 } 494 Fatalf("Cannot determine default data directory, please set manually (--datadir)") 495 return "" 496 } 497 498 // setNodeKey creates a node key from set command line flags, either loading it 499 // from a file or as a specified hex value. If neither flags were provided, this 500 // method returns nil and an emphemeral key is to be generated. 501 func setNodeKey(ctx *cli.Context, cfg *p2p.Config) { 502 var ( 503 hex = ctx.GlobalString(NodeKeyHexFlag.Name) 504 file = ctx.GlobalString(NodeKeyFileFlag.Name) 505 key *ecdsa.PrivateKey 506 err error 507 ) 508 switch { 509 case file != "" && hex != "": 510 Fatalf("Options %q and %q are mutually exclusive", NodeKeyFileFlag.Name, NodeKeyHexFlag.Name) 511 case file != "": 512 if key, err = crypto.LoadECDSA(file); err != nil { 513 Fatalf("Option %q: %v", NodeKeyFileFlag.Name, err) 514 } 515 cfg.PrivateKey = key 516 case hex != "": 517 if key, err = crypto.HexToECDSA(hex); err != nil { 518 Fatalf("Option %q: %v", NodeKeyHexFlag.Name, err) 519 } 520 cfg.PrivateKey = key 521 } 522 } 523 524 // setNodeUserIdent creates the user identifier from CLI flags. 525 func setNodeUserIdent(ctx *cli.Context, cfg *node.Config) { 526 if identity := ctx.GlobalString(IdentityFlag.Name); len(identity) > 0 { 527 cfg.UserIdent = identity 528 } 529 } 530 531 // setBootstrapNodes creates a list of bootstrap nodes from the command line 532 // flags, reverting to pre-configured ones if none have been specified. 533 func setBootstrapNodes(ctx *cli.Context, cfg *p2p.Config) { 534 urls := params.MainnetBootnodes 535 switch { 536 case ctx.GlobalIsSet(BootnodesFlag.Name) || ctx.GlobalIsSet(BootnodesV4Flag.Name): 537 if ctx.GlobalIsSet(BootnodesV4Flag.Name) { 538 urls = strings.Split(ctx.GlobalString(BootnodesV4Flag.Name), ",") 539 } else { 540 urls = strings.Split(ctx.GlobalString(BootnodesFlag.Name), ",") 541 } 542 case ctx.GlobalBool(TestnetFlag.Name): 543 urls = params.TestnetBootnodes 544 case ctx.GlobalBool(RinkebyFlag.Name): 545 urls = params.RinkebyBootnodes 546 } 547 548 cfg.BootstrapNodes = make([]*discover.Node, 0, len(urls)) 549 for _, url := range urls { 550 node, err := discover.ParseNode(url) 551 if err != nil { 552 log.Error("Bootstrap URL invalid", "enode", url, "err", err) 553 continue 554 } 555 cfg.BootstrapNodes = append(cfg.BootstrapNodes, node) 556 } 557 } 558 559 // setBootstrapNodesV5 creates a list of bootstrap nodes from the command line 560 // flags, reverting to pre-configured ones if none have been specified. 561 func setBootstrapNodesV5(ctx *cli.Context, cfg *p2p.Config) { 562 urls := params.DiscoveryV5Bootnodes 563 switch { 564 case ctx.GlobalIsSet(BootnodesFlag.Name) || ctx.GlobalIsSet(BootnodesV5Flag.Name): 565 if ctx.GlobalIsSet(BootnodesV5Flag.Name) { 566 urls = strings.Split(ctx.GlobalString(BootnodesV5Flag.Name), ",") 567 } else { 568 urls = strings.Split(ctx.GlobalString(BootnodesFlag.Name), ",") 569 } 570 case ctx.GlobalBool(RinkebyFlag.Name): 571 urls = params.RinkebyV5Bootnodes 572 case cfg.BootstrapNodesV5 != nil: 573 return // already set, don't apply defaults. 574 } 575 576 cfg.BootstrapNodesV5 = make([]*discv5.Node, 0, len(urls)) 577 for _, url := range urls { 578 node, err := discv5.ParseNode(url) 579 if err != nil { 580 log.Error("Bootstrap URL invalid", "enode", url, "err", err) 581 continue 582 } 583 cfg.BootstrapNodesV5 = append(cfg.BootstrapNodesV5, node) 584 } 585 } 586 587 // setListenAddress creates a TCP listening address string from set command 588 // line flags. 589 func setListenAddress(ctx *cli.Context, cfg *p2p.Config) { 590 if ctx.GlobalIsSet(ListenPortFlag.Name) { 591 cfg.ListenAddr = fmt.Sprintf(":%d", ctx.GlobalInt(ListenPortFlag.Name)) 592 } 593 } 594 595 // setDiscoveryV5Address creates a UDP listening address string from set command 596 // line flags for the V5 discovery protocol. 597 func setDiscoveryV5Address(ctx *cli.Context, cfg *p2p.Config) { 598 if ctx.GlobalIsSet(ListenPortFlag.Name) { 599 cfg.DiscoveryV5Addr = fmt.Sprintf(":%d", ctx.GlobalInt(ListenPortFlag.Name)+1) 600 } 601 } 602 603 // setNAT creates a port mapper from command line flags. 604 func setNAT(ctx *cli.Context, cfg *p2p.Config) { 605 if ctx.GlobalIsSet(NATFlag.Name) { 606 natif, err := nat.Parse(ctx.GlobalString(NATFlag.Name)) 607 if err != nil { 608 Fatalf("Option %s: %v", NATFlag.Name, err) 609 } 610 cfg.NAT = natif 611 } 612 } 613 614 // splitAndTrim splits input separated by a comma 615 // and trims excessive white space from the substrings. 616 func splitAndTrim(input string) []string { 617 result := strings.Split(input, ",") 618 for i, r := range result { 619 result[i] = strings.TrimSpace(r) 620 } 621 return result 622 } 623 624 // setHTTP creates the HTTP RPC listener interface string from the set 625 // command line flags, returning empty if the HTTP endpoint is disabled. 626 func setHTTP(ctx *cli.Context, cfg *node.Config) { 627 if ctx.GlobalBool(RPCEnabledFlag.Name) && cfg.HTTPHost == "" { 628 cfg.HTTPHost = "127.0.0.1" 629 if ctx.GlobalIsSet(RPCListenAddrFlag.Name) { 630 cfg.HTTPHost = ctx.GlobalString(RPCListenAddrFlag.Name) 631 } 632 } 633 634 if ctx.GlobalIsSet(RPCPortFlag.Name) { 635 cfg.HTTPPort = ctx.GlobalInt(RPCPortFlag.Name) 636 } 637 if ctx.GlobalIsSet(RPCCORSDomainFlag.Name) { 638 cfg.HTTPCors = splitAndTrim(ctx.GlobalString(RPCCORSDomainFlag.Name)) 639 } 640 if ctx.GlobalIsSet(RPCApiFlag.Name) { 641 cfg.HTTPModules = splitAndTrim(ctx.GlobalString(RPCApiFlag.Name)) 642 } 643 } 644 645 // setWS creates the WebSocket RPC listener interface string from the set 646 // command line flags, returning empty if the HTTP endpoint is disabled. 647 func setWS(ctx *cli.Context, cfg *node.Config) { 648 if ctx.GlobalBool(WSEnabledFlag.Name) && cfg.WSHost == "" { 649 cfg.WSHost = "127.0.0.1" 650 if ctx.GlobalIsSet(WSListenAddrFlag.Name) { 651 cfg.WSHost = ctx.GlobalString(WSListenAddrFlag.Name) 652 } 653 } 654 655 if ctx.GlobalIsSet(WSPortFlag.Name) { 656 cfg.WSPort = ctx.GlobalInt(WSPortFlag.Name) 657 } 658 if ctx.GlobalIsSet(WSAllowedOriginsFlag.Name) { 659 cfg.WSOrigins = splitAndTrim(ctx.GlobalString(WSAllowedOriginsFlag.Name)) 660 } 661 if ctx.GlobalIsSet(WSApiFlag.Name) { 662 cfg.WSModules = splitAndTrim(ctx.GlobalString(WSApiFlag.Name)) 663 } 664 } 665 666 // setIPC creates an IPC path configuration from the set command line flags, 667 // returning an empty string if IPC was explicitly disabled, or the set path. 668 func setIPC(ctx *cli.Context, cfg *node.Config) { 669 checkExclusive(ctx, IPCDisabledFlag, IPCPathFlag) 670 switch { 671 case ctx.GlobalBool(IPCDisabledFlag.Name): 672 cfg.IPCPath = "" 673 case ctx.GlobalIsSet(IPCPathFlag.Name): 674 cfg.IPCPath = ctx.GlobalString(IPCPathFlag.Name) 675 } 676 } 677 678 // makeDatabaseHandles raises out the number of allowed file handles per process 679 // for Geth and returns half of the allowance to assign to the database. 680 func makeDatabaseHandles() int { 681 if err := raiseFdLimit(2048); err != nil { 682 Fatalf("Failed to raise file descriptor allowance: %v", err) 683 } 684 limit, err := getFdLimit() 685 if err != nil { 686 Fatalf("Failed to retrieve file descriptor allowance: %v", err) 687 } 688 if limit > 2048 { // cap database file descriptors even if more is available 689 limit = 2048 690 } 691 return limit / 2 // Leave half for networking and other stuff 692 } 693 694 // MakeAddress converts an account specified directly as a hex encoded string or 695 // a key index in the key store to an internal account representation. 696 func MakeAddress(ks *keystore.KeyStore, account string) (accounts.Account, error) { 697 // If the specified account is a valid address, return it 698 if common.IsHexAddress(account) { 699 return accounts.Account{Address: common.HexToAddress(account)}, nil 700 } 701 // Otherwise try to interpret the account as a keystore index 702 index, err := strconv.Atoi(account) 703 if err != nil || index < 0 { 704 return accounts.Account{}, fmt.Errorf("invalid account address or index %q", account) 705 } 706 accs := ks.Accounts() 707 if len(accs) <= index { 708 return accounts.Account{}, fmt.Errorf("index %d higher than number of accounts %d", index, len(accs)) 709 } 710 return accs[index], nil 711 } 712 713 // setEtherbase retrieves the etherbase either from the directly specified 714 // command line flags or from the keystore if CLI indexed. 715 func setEtherbase(ctx *cli.Context, ks *keystore.KeyStore, cfg *eth.Config) { 716 if ctx.GlobalIsSet(EtherbaseFlag.Name) { 717 account, err := MakeAddress(ks, ctx.GlobalString(EtherbaseFlag.Name)) 718 if err != nil { 719 Fatalf("Option %q: %v", EtherbaseFlag.Name, err) 720 } 721 cfg.Etherbase = account.Address 722 return 723 } 724 accounts := ks.Accounts() 725 if (cfg.Etherbase == common.Address{}) { 726 if len(accounts) > 0 { 727 cfg.Etherbase = accounts[0].Address 728 } else { 729 log.Warn("No etherbase set and no accounts found as default") 730 } 731 } 732 } 733 734 // MakePasswordList reads password lines from the file specified by the global --password flag. 735 func MakePasswordList(ctx *cli.Context) []string { 736 path := ctx.GlobalString(PasswordFileFlag.Name) 737 if path == "" { 738 return nil 739 } 740 text, err := ioutil.ReadFile(path) 741 if err != nil { 742 Fatalf("Failed to read password file: %v", err) 743 } 744 lines := strings.Split(string(text), "\n") 745 // Sanitise DOS line endings. 746 for i := range lines { 747 lines[i] = strings.TrimRight(lines[i], "\r") 748 } 749 return lines 750 } 751 752 func SetP2PConfig(ctx *cli.Context, cfg *p2p.Config) { 753 setNodeKey(ctx, cfg) 754 setNAT(ctx, cfg) 755 setListenAddress(ctx, cfg) 756 setDiscoveryV5Address(ctx, cfg) 757 setBootstrapNodes(ctx, cfg) 758 setBootstrapNodesV5(ctx, cfg) 759 760 if ctx.GlobalIsSet(MaxPeersFlag.Name) { 761 cfg.MaxPeers = ctx.GlobalInt(MaxPeersFlag.Name) 762 } 763 if ctx.GlobalIsSet(MaxPendingPeersFlag.Name) { 764 cfg.MaxPendingPeers = ctx.GlobalInt(MaxPendingPeersFlag.Name) 765 } 766 if ctx.GlobalIsSet(NoDiscoverFlag.Name) || ctx.GlobalBool(LightModeFlag.Name) { 767 cfg.NoDiscovery = true 768 } 769 770 // if we're running a light client or server, force enable the v5 peer discovery 771 // unless it is explicitly disabled with --nodiscover note that explicitly specifying 772 // --v5disc overrides --nodiscover, in which case the later only disables v4 discovery 773 forceV5Discovery := (ctx.GlobalBool(LightModeFlag.Name) || ctx.GlobalInt(LightServFlag.Name) > 0) && !ctx.GlobalBool(NoDiscoverFlag.Name) 774 if ctx.GlobalIsSet(DiscoveryV5Flag.Name) { 775 cfg.DiscoveryV5 = ctx.GlobalBool(DiscoveryV5Flag.Name) 776 } else if forceV5Discovery { 777 cfg.DiscoveryV5 = true 778 } 779 780 if netrestrict := ctx.GlobalString(NetrestrictFlag.Name); netrestrict != "" { 781 list, err := netutil.ParseNetlist(netrestrict) 782 if err != nil { 783 Fatalf("Option %q: %v", NetrestrictFlag.Name, err) 784 } 785 cfg.NetRestrict = list 786 } 787 788 if ctx.GlobalBool(DevModeFlag.Name) { 789 // --dev mode can't use p2p networking. 790 cfg.MaxPeers = 0 791 cfg.ListenAddr = ":0" 792 cfg.DiscoveryV5Addr = ":0" 793 cfg.NoDiscovery = true 794 cfg.DiscoveryV5 = false 795 } 796 } 797 798 // SetNodeConfig applies node-related command line flags to the config. 799 func SetNodeConfig(ctx *cli.Context, cfg *node.Config) { 800 SetP2PConfig(ctx, &cfg.P2P) 801 setIPC(ctx, cfg) 802 setHTTP(ctx, cfg) 803 setWS(ctx, cfg) 804 setNodeUserIdent(ctx, cfg) 805 806 switch { 807 case ctx.GlobalIsSet(DataDirFlag.Name): 808 cfg.DataDir = ctx.GlobalString(DataDirFlag.Name) 809 case ctx.GlobalBool(DevModeFlag.Name): 810 cfg.DataDir = filepath.Join(os.TempDir(), "ethereum_dev_mode") 811 case ctx.GlobalBool(TestnetFlag.Name): 812 cfg.DataDir = filepath.Join(node.DefaultDataDir(), "testnet") 813 case ctx.GlobalBool(RinkebyFlag.Name): 814 cfg.DataDir = filepath.Join(node.DefaultDataDir(), "rinkeby") 815 } 816 817 if ctx.GlobalIsSet(KeyStoreDirFlag.Name) { 818 cfg.KeyStoreDir = ctx.GlobalString(KeyStoreDirFlag.Name) 819 } 820 if ctx.GlobalIsSet(LightKDFFlag.Name) { 821 cfg.UseLightweightKDF = ctx.GlobalBool(LightKDFFlag.Name) 822 } 823 if ctx.GlobalIsSet(NoUSBFlag.Name) { 824 cfg.NoUSB = ctx.GlobalBool(NoUSBFlag.Name) 825 } 826 } 827 828 func setGPO(ctx *cli.Context, cfg *gasprice.Config) { 829 if ctx.GlobalIsSet(GpoBlocksFlag.Name) { 830 cfg.Blocks = ctx.GlobalInt(GpoBlocksFlag.Name) 831 } 832 if ctx.GlobalIsSet(GpoPercentileFlag.Name) { 833 cfg.Percentile = ctx.GlobalInt(GpoPercentileFlag.Name) 834 } 835 } 836 837 func setTxPool(ctx *cli.Context, cfg *core.TxPoolConfig) { 838 if ctx.GlobalIsSet(TxPoolNoLocalsFlag.Name) { 839 cfg.NoLocals = ctx.GlobalBool(TxPoolNoLocalsFlag.Name) 840 } 841 if ctx.GlobalIsSet(TxPoolPriceLimitFlag.Name) { 842 cfg.PriceLimit = ctx.GlobalUint64(TxPoolPriceLimitFlag.Name) 843 } 844 if ctx.GlobalIsSet(TxPoolPriceBumpFlag.Name) { 845 cfg.PriceBump = ctx.GlobalUint64(TxPoolPriceBumpFlag.Name) 846 } 847 if ctx.GlobalIsSet(TxPoolAccountSlotsFlag.Name) { 848 cfg.AccountSlots = ctx.GlobalUint64(TxPoolAccountSlotsFlag.Name) 849 } 850 if ctx.GlobalIsSet(TxPoolGlobalSlotsFlag.Name) { 851 cfg.GlobalSlots = ctx.GlobalUint64(TxPoolGlobalSlotsFlag.Name) 852 } 853 if ctx.GlobalIsSet(TxPoolAccountQueueFlag.Name) { 854 cfg.AccountQueue = ctx.GlobalUint64(TxPoolAccountQueueFlag.Name) 855 } 856 if ctx.GlobalIsSet(TxPoolGlobalQueueFlag.Name) { 857 cfg.GlobalQueue = ctx.GlobalUint64(TxPoolGlobalQueueFlag.Name) 858 } 859 if ctx.GlobalIsSet(TxPoolLifetimeFlag.Name) { 860 cfg.Lifetime = ctx.GlobalDuration(TxPoolLifetimeFlag.Name) 861 } 862 } 863 864 func setEthash(ctx *cli.Context, cfg *eth.Config) { 865 if ctx.GlobalIsSet(EthashCacheDirFlag.Name) { 866 cfg.EthashCacheDir = ctx.GlobalString(EthashCacheDirFlag.Name) 867 } 868 if ctx.GlobalIsSet(EthashDatasetDirFlag.Name) { 869 cfg.EthashDatasetDir = ctx.GlobalString(EthashDatasetDirFlag.Name) 870 } 871 if ctx.GlobalIsSet(EthashCachesInMemoryFlag.Name) { 872 cfg.EthashCachesInMem = ctx.GlobalInt(EthashCachesInMemoryFlag.Name) 873 } 874 if ctx.GlobalIsSet(EthashCachesOnDiskFlag.Name) { 875 cfg.EthashCachesOnDisk = ctx.GlobalInt(EthashCachesOnDiskFlag.Name) 876 } 877 if ctx.GlobalIsSet(EthashDatasetsInMemoryFlag.Name) { 878 cfg.EthashDatasetsInMem = ctx.GlobalInt(EthashDatasetsInMemoryFlag.Name) 879 } 880 if ctx.GlobalIsSet(EthashDatasetsOnDiskFlag.Name) { 881 cfg.EthashDatasetsOnDisk = ctx.GlobalInt(EthashDatasetsOnDiskFlag.Name) 882 } 883 } 884 885 func checkExclusive(ctx *cli.Context, flags ...cli.Flag) { 886 set := make([]string, 0, 1) 887 for _, flag := range flags { 888 if ctx.GlobalIsSet(flag.GetName()) { 889 set = append(set, "--"+flag.GetName()) 890 } 891 } 892 if len(set) > 1 { 893 Fatalf("flags %v can't be used at the same time", strings.Join(set, ", ")) 894 } 895 } 896 897 // SetShhConfig applies shh-related command line flags to the config. 898 func SetShhConfig(ctx *cli.Context, stack *node.Node, cfg *whisper.Config) { 899 if ctx.GlobalIsSet(WhisperMaxMessageSizeFlag.Name) { 900 cfg.MaxMessageSize = uint32(ctx.GlobalUint(WhisperMaxMessageSizeFlag.Name)) 901 } 902 if ctx.GlobalIsSet(WhisperMinPOWFlag.Name) { 903 cfg.MinimumAcceptedPOW = ctx.GlobalFloat64(WhisperMinPOWFlag.Name) 904 } 905 } 906 907 // SetEthConfig applies eth-related command line flags to the config. 908 func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) { 909 // Avoid conflicting network flags 910 checkExclusive(ctx, DevModeFlag, TestnetFlag, RinkebyFlag) 911 checkExclusive(ctx, FastSyncFlag, LightModeFlag, SyncModeFlag) 912 913 ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore) 914 setEtherbase(ctx, ks, cfg) 915 setGPO(ctx, &cfg.GPO) 916 setTxPool(ctx, &cfg.TxPool) 917 setEthash(ctx, cfg) 918 919 switch { 920 case ctx.GlobalIsSet(SyncModeFlag.Name): 921 cfg.SyncMode = *GlobalTextMarshaler(ctx, SyncModeFlag.Name).(*downloader.SyncMode) 922 case ctx.GlobalBool(FastSyncFlag.Name): 923 cfg.SyncMode = downloader.FastSync 924 case ctx.GlobalBool(LightModeFlag.Name): 925 cfg.SyncMode = downloader.LightSync 926 } 927 if ctx.GlobalIsSet(LightServFlag.Name) { 928 cfg.LightServ = ctx.GlobalInt(LightServFlag.Name) 929 } 930 if ctx.GlobalIsSet(LightPeersFlag.Name) { 931 cfg.LightPeers = ctx.GlobalInt(LightPeersFlag.Name) 932 } 933 if ctx.GlobalIsSet(NetworkIdFlag.Name) { 934 cfg.NetworkId = ctx.GlobalUint64(NetworkIdFlag.Name) 935 } 936 937 // Ethereum needs to know maxPeers to calculate the light server peer ratio. 938 // TODO(fjl): ensure Ethereum can get MaxPeers from node. 939 cfg.MaxPeers = ctx.GlobalInt(MaxPeersFlag.Name) 940 941 if ctx.GlobalIsSet(CacheFlag.Name) { 942 cfg.DatabaseCache = ctx.GlobalInt(CacheFlag.Name) 943 } 944 cfg.DatabaseHandles = makeDatabaseHandles() 945 946 if ctx.GlobalIsSet(MinerThreadsFlag.Name) { 947 cfg.MinerThreads = ctx.GlobalInt(MinerThreadsFlag.Name) 948 } 949 if ctx.GlobalIsSet(DocRootFlag.Name) { 950 cfg.DocRoot = ctx.GlobalString(DocRootFlag.Name) 951 } 952 if ctx.GlobalIsSet(ExtraDataFlag.Name) { 953 cfg.ExtraData = []byte(ctx.GlobalString(ExtraDataFlag.Name)) 954 } 955 if ctx.GlobalIsSet(GasPriceFlag.Name) { 956 cfg.GasPrice = GlobalBig(ctx, GasPriceFlag.Name) 957 } 958 if ctx.GlobalIsSet(VMEnableDebugFlag.Name) { 959 // TODO(fjl): force-enable this in --dev mode 960 cfg.EnablePreimageRecording = ctx.GlobalBool(VMEnableDebugFlag.Name) 961 } 962 963 // Override any default configs for hard coded networks. 964 switch { 965 case ctx.GlobalBool(TestnetFlag.Name): 966 if !ctx.GlobalIsSet(NetworkIdFlag.Name) { 967 cfg.NetworkId = 3 968 } 969 cfg.Genesis = core.DefaultTestnetGenesisBlock() 970 case ctx.GlobalBool(RinkebyFlag.Name): 971 if !ctx.GlobalIsSet(NetworkIdFlag.Name) { 972 cfg.NetworkId = 4 973 } 974 cfg.Genesis = core.DefaultRinkebyGenesisBlock() 975 case ctx.GlobalBool(DevModeFlag.Name): 976 cfg.Genesis = core.DevGenesisBlock() 977 if !ctx.GlobalIsSet(GasPriceFlag.Name) { 978 cfg.GasPrice = new(big.Int) 979 } 980 cfg.PowTest = true 981 } 982 983 // TODO(fjl): move trie cache generations into config 984 if gen := ctx.GlobalInt(TrieCacheGenFlag.Name); gen > 0 { 985 state.MaxTrieCacheGen = uint16(gen) 986 } 987 } 988 989 // RegisterEthService adds an Ethereum client to the stack. 990 func RegisterEthService(stack *node.Node, cfg *eth.Config) { 991 var err error 992 if cfg.SyncMode == downloader.LightSync { 993 err = stack.Register(func(ctx *node.ServiceContext) (node.Service, error) { 994 return les.New(ctx, cfg) 995 }) 996 } else { 997 err = stack.Register(func(ctx *node.ServiceContext) (node.Service, error) { 998 fullNode, err := eth.New(ctx, cfg) 999 if fullNode != nil && cfg.LightServ > 0 { 1000 ls, _ := les.NewLesServer(fullNode, cfg) 1001 fullNode.AddLesServer(ls) 1002 } 1003 return fullNode, err 1004 }) 1005 } 1006 if err != nil { 1007 Fatalf("Failed to register the Ethereum service: %v", err) 1008 } 1009 } 1010 1011 // RegisterShhService configures Whisper and adds it to the given node. 1012 func RegisterShhService(stack *node.Node, cfg *whisper.Config) { 1013 if err := stack.Register(func(n *node.ServiceContext) (node.Service, error) { 1014 return whisper.New(cfg), nil 1015 }); err != nil { 1016 Fatalf("Failed to register the Whisper service: %v", err) 1017 } 1018 } 1019 1020 // RegisterEthStatsService configures the Ethereum Stats daemon and adds it to 1021 // th egiven node. 1022 func RegisterEthStatsService(stack *node.Node, url string) { 1023 if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) { 1024 // Retrieve both eth and les services 1025 var ethServ *eth.Ethereum 1026 ctx.Service(ðServ) 1027 1028 var lesServ *les.LightEthereum 1029 ctx.Service(&lesServ) 1030 1031 return ethstats.New(url, ethServ, lesServ) 1032 }); err != nil { 1033 Fatalf("Failed to register the Ethereum Stats service: %v", err) 1034 } 1035 } 1036 1037 // SetupNetwork configures the system for either the main net or some test network. 1038 func SetupNetwork(ctx *cli.Context) { 1039 // TODO(fjl): move target gas limit into config 1040 params.TargetGasLimit = new(big.Int).SetUint64(ctx.GlobalUint64(TargetGasLimitFlag.Name)) 1041 } 1042 1043 // MakeChainDatabase open an LevelDB using the flags passed to the client and will hard crash if it fails. 1044 func MakeChainDatabase(ctx *cli.Context, stack *node.Node) ethdb.Database { 1045 var ( 1046 cache = ctx.GlobalInt(CacheFlag.Name) 1047 handles = makeDatabaseHandles() 1048 ) 1049 name := "chaindata" 1050 if ctx.GlobalBool(LightModeFlag.Name) { 1051 name = "lightchaindata" 1052 } 1053 chainDb, err := stack.OpenDatabase(name, cache, handles) 1054 if err != nil { 1055 Fatalf("Could not open database: %v", err) 1056 } 1057 return chainDb 1058 } 1059 1060 func MakeGenesis(ctx *cli.Context) *core.Genesis { 1061 var genesis *core.Genesis 1062 switch { 1063 case ctx.GlobalBool(TestnetFlag.Name): 1064 genesis = core.DefaultTestnetGenesisBlock() 1065 case ctx.GlobalBool(RinkebyFlag.Name): 1066 genesis = core.DefaultRinkebyGenesisBlock() 1067 case ctx.GlobalBool(DevModeFlag.Name): 1068 genesis = core.DevGenesisBlock() 1069 } 1070 return genesis 1071 } 1072 1073 // MakeChain creates a chain manager from set command line flags. 1074 func MakeChain(ctx *cli.Context, stack *node.Node) (chain *core.BlockChain, chainDb ethdb.Database) { 1075 var err error 1076 chainDb = MakeChainDatabase(ctx, stack) 1077 1078 engine := ethash.NewFaker() 1079 if !ctx.GlobalBool(FakePoWFlag.Name) { 1080 engine = ethash.New("", 1, 0, "", 1, 0) 1081 } 1082 config, _, err := core.SetupGenesisBlock(chainDb, MakeGenesis(ctx)) 1083 if err != nil { 1084 Fatalf("%v", err) 1085 } 1086 vmcfg := vm.Config{EnablePreimageRecording: ctx.GlobalBool(VMEnableDebugFlag.Name)} 1087 chain, err = core.NewBlockChain(chainDb, config, engine, new(event.TypeMux), vmcfg) 1088 if err != nil { 1089 Fatalf("Can't create BlockChain: %v", err) 1090 } 1091 return chain, chainDb 1092 } 1093 1094 // MakeConsolePreloads retrieves the absolute paths for the console JavaScript 1095 // scripts to preload before starting. 1096 func MakeConsolePreloads(ctx *cli.Context) []string { 1097 // Skip preloading if there's nothing to preload 1098 if ctx.GlobalString(PreloadJSFlag.Name) == "" { 1099 return nil 1100 } 1101 // Otherwise resolve absolute paths and return them 1102 preloads := []string{} 1103 1104 assets := ctx.GlobalString(JSpathFlag.Name) 1105 for _, file := range strings.Split(ctx.GlobalString(PreloadJSFlag.Name), ",") { 1106 preloads = append(preloads, common.AbsolutePath(assets, strings.TrimSpace(file))) 1107 } 1108 return preloads 1109 } 1110 1111 // MigrateFlags sets the global flag from a local flag when it's set. 1112 // This is a temporary function used for migrating old command/flags to the 1113 // new format. 1114 // 1115 // e.g. geth account new --keystore /tmp/mykeystore --lightkdf 1116 // 1117 // is equivalent after calling this method with: 1118 // 1119 // geth --keystore /tmp/mykeystore --lightkdf account new 1120 // 1121 // This allows the use of the existing configuration functionality. 1122 // When all flags are migrated this function can be removed and the existing 1123 // configuration functionality must be changed that is uses local flags 1124 func MigrateFlags(action func(ctx *cli.Context) error) func(*cli.Context) error { 1125 return func(ctx *cli.Context) error { 1126 for _, name := range ctx.FlagNames() { 1127 if ctx.IsSet(name) { 1128 ctx.GlobalSet(name, ctx.String(name)) 1129 } 1130 } 1131 return action(ctx) 1132 } 1133 }