// Copyright 2015 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 utils contains internal helper functions for go-ethereum commands. package utils import ( "crypto/ecdsa" "fmt" "io/ioutil" "math/big" "os" "path/filepath" "runtime" "strconv" "strings" "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/accounts/keystore" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/eth/downloader" "github.com/ethereum/go-ethereum/eth/gasprice" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethstats" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/les" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p/discover" "github.com/ethereum/go-ethereum/p2p/discv5" "github.com/ethereum/go-ethereum/p2p/nat" "github.com/ethereum/go-ethereum/p2p/netutil" "github.com/ethereum/go-ethereum/params" whisper "github.com/ethereum/go-ethereum/whisper/whisperv2" "gopkg.in/urfave/cli.v1" ) func init() { cli.AppHelpTemplate = `{{.Name}} {{if .Flags}}[global options] {{end}}command{{if .Flags}} [command options]{{end}} [arguments...] VERSION: {{.Version}} COMMANDS: {{range .Commands}}{{.Name}}{{with .ShortName}}, {{.}}{{end}}{{ "\t" }}{{.Usage}} {{end}}{{if .Flags}} GLOBAL OPTIONS: {{range .Flags}}{{.}} {{end}}{{end}} ` cli.CommandHelpTemplate = `{{.Name}}{{if .Subcommands}} command{{end}}{{if .Flags}} [command options]{{end}} [arguments...] {{if .Description}}{{.Description}} {{end}}{{if .Subcommands}} SUBCOMMANDS: {{range .Subcommands}}{{.Name}}{{with .ShortName}}, {{.}}{{end}}{{ "\t" }}{{.Usage}} {{end}}{{end}}{{if .Flags}} OPTIONS: {{range .Flags}}{{.}} {{end}}{{end}} ` } // NewApp creates an app with sane defaults. func NewApp(gitCommit, usage string) *cli.App { app := cli.NewApp() app.Name = filepath.Base(os.Args[0]) app.Author = "" //app.Authors = nil app.Email = "" app.Version = params.Version if gitCommit != "" { app.Version += "-" + gitCommit[:8] } app.Usage = usage return app } // These are all the command line flags we support. // If you add to this list, please remember to include the // flag in the appropriate command definition. // // The flags are defined here so their names and help texts // are the same for all commands. var ( // General settings DataDirFlag = DirectoryFlag{ Name: "datadir", Usage: "Data directory for the databases and keystore", Value: DirectoryString{node.DefaultDataDir()}, } KeyStoreDirFlag = DirectoryFlag{ Name: "keystore", Usage: "Directory for the keystore (default = inside the datadir)", } EthashCacheDirFlag = DirectoryFlag{ Name: "ethash.cachedir", Usage: "Directory to store the ethash verification caches (default = inside the datadir)", } EthashCachesInMemoryFlag = cli.IntFlag{ Name: "ethash.cachesinmem", Usage: "Number of recent ethash caches to keep in memory (16MB each)", Value: eth.DefaultConfig.EthashCachesInMem, } EthashCachesOnDiskFlag = cli.IntFlag{ Name: "ethash.cachesondisk", Usage: "Number of recent ethash caches to keep on disk (16MB each)", Value: eth.DefaultConfig.EthashCachesOnDisk, } EthashDatasetDirFlag = DirectoryFlag{ Name: "ethash.dagdir", Usage: "Directory to store the ethash mining DAGs (default = inside home folder)", Value: DirectoryString{eth.DefaultConfig.EthashDatasetDir}, } EthashDatasetsInMemoryFlag = cli.IntFlag{ Name: "ethash.dagsinmem", Usage: "Number of recent ethash mining DAGs to keep in memory (1+GB each)", Value: eth.DefaultConfig.EthashDatasetsInMem, } EthashDatasetsOnDiskFlag = cli.IntFlag{ Name: "ethash.dagsondisk", Usage: "Number of recent ethash mining DAGs to keep on disk (1+GB each)", Value: eth.DefaultConfig.EthashDatasetsOnDisk, } NetworkIdFlag = cli.IntFlag{ Name: "networkid", Usage: "Network identifier (integer, 1=Frontier, 2=Morden (disused), 3=Ropsten)", Value: eth.DefaultConfig.NetworkId, } TestNetFlag = cli.BoolFlag{ Name: "testnet", Usage: "Ropsten network: pre-configured proof-of-work test network", } DevModeFlag = cli.BoolFlag{ Name: "dev", Usage: "Developer mode: pre-configured private network with several debugging flags", } IdentityFlag = cli.StringFlag{ Name: "identity", Usage: "Custom node name", } DocRootFlag = DirectoryFlag{ Name: "docroot", Usage: "Document Root for HTTPClient file scheme", Value: DirectoryString{homeDir()}, } FastSyncFlag = cli.BoolFlag{ Name: "fast", Usage: "Enable fast syncing through state downloads", } LightModeFlag = cli.BoolFlag{ Name: "light", Usage: "Enable light client mode", } defaultSyncMode = eth.DefaultConfig.SyncMode SyncModeFlag = TextMarshalerFlag{ Name: "syncmode", Usage: `Blockchain sync mode ("fast", "full", or "light")`, Value: &defaultSyncMode, } LightServFlag = cli.IntFlag{ Name: "lightserv", Usage: "Maximum percentage of time allowed for serving LES requests (0-90)", Value: 0, } LightPeersFlag = cli.IntFlag{ Name: "lightpeers", Usage: "Maximum number of LES client peers", Value: 20, } LightKDFFlag = cli.BoolFlag{ Name: "lightkdf", Usage: "Reduce key-derivation RAM & CPU usage at some expense of KDF strength", } // Performance tuning settings CacheFlag = cli.IntFlag{ Name: "cache", Usage: "Megabytes of memory allocated to internal caching (min 16MB / database forced)", Value: 128, } TrieCacheGenFlag = cli.IntFlag{ Name: "trie-cache-gens", Usage: "Number of trie node generations to keep in memory", Value: int(state.MaxTrieCacheGen), } // Miner settings MiningEnabledFlag = cli.BoolFlag{ Name: "mine", Usage: "Enable mining", } MinerThreadsFlag = cli.IntFlag{ Name: "minerthreads", Usage: "Number of CPU threads to use for mining", Value: runtime.NumCPU(), } TargetGasLimitFlag = cli.Uint64Flag{ Name: "targetgaslimit", Usage: "Target gas limit sets the artificial target gas floor for the blocks to mine", Value: params.GenesisGasLimit.Uint64(), } EtherbaseFlag = cli.StringFlag{ Name: "etherbase", Usage: "Public address for block mining rewards (default = first account created)", Value: "0", } GasPriceFlag = BigFlag{ Name: "gasprice", Usage: "Minimal gas price to accept for mining a transactions", Value: big.NewInt(20 * params.Shannon), } ExtraDataFlag = cli.StringFlag{ Name: "extradata", Usage: "Block extra data set by the miner (default = client version)", } // Account settings UnlockedAccountFlag = cli.StringFlag{ Name: "unlock", Usage: "Comma separated list of accounts to unlock", Value: "", } PasswordFileFlag = cli.StringFlag{ Name: "password", Usage: "Password file to use for non-inteactive password input", Value: "", } VMEnableDebugFlag = cli.BoolFlag{ Name: "vmdebug", Usage: "Record information useful for VM and contract debugging", } // Logging and debug settings EthStatsURLFlag = cli.StringFlag{ Name: "ethstats", Usage: "Reporting URL of a ethstats service (nodename:secret@host:port)", } MetricsEnabledFlag = cli.BoolFlag{ Name: metrics.MetricsEnabledFlag, Usage: "Enable metrics collection and reporting", } FakePoWFlag = cli.BoolFlag{ Name: "fakepow", Usage: "Disables proof-of-work verification", } NoCompactionFlag = cli.BoolFlag{ Name: "nocompaction", Usage: "Disables db compaction after import", } // RPC settings RPCEnabledFlag = cli.BoolFlag{ Name: "rpc", Usage: "Enable the HTTP-RPC server", } RPCListenAddrFlag = cli.StringFlag{ Name: "rpcaddr", Usage: "HTTP-RPC server listening interface", Value: node.DefaultHTTPHost, } RPCPortFlag = cli.IntFlag{ Name: "rpcport", Usage: "HTTP-RPC server listening port", Value: node.DefaultHTTPPort, } RPCCORSDomainFlag = cli.StringFlag{ Name: "rpccorsdomain", Usage: "Comma separated list of domains from which to accept cross origin requests (browser enforced)", Value: "", } RPCApiFlag = cli.StringFlag{ Name: "rpcapi", Usage: "API's offered over the HTTP-RPC interface", Value: "", } IPCDisabledFlag = cli.BoolFlag{ Name: "ipcdisable", Usage: "Disable the IPC-RPC server", } IPCPathFlag = DirectoryFlag{ Name: "ipcpath", Usage: "Filename for IPC socket/pipe within the datadir (explicit paths escape it)", } WSEnabledFlag = cli.BoolFlag{ Name: "ws", Usage: "Enable the WS-RPC server", } WSListenAddrFlag = cli.StringFlag{ Name: "wsaddr", Usage: "WS-RPC server listening interface", Value: node.DefaultWSHost, } WSPortFlag = cli.IntFlag{ Name: "wsport", Usage: "WS-RPC server listening port", Value: node.DefaultWSPort, } WSApiFlag = cli.StringFlag{ Name: "wsapi", Usage: "API's offered over the WS-RPC interface", Value: "", } WSAllowedOriginsFlag = cli.StringFlag{ Name: "wsorigins", Usage: "Origins from which to accept websockets requests", Value: "", } ExecFlag = cli.StringFlag{ Name: "exec", Usage: "Execute JavaScript statement (only in combination with console/attach)", } PreloadJSFlag = cli.StringFlag{ Name: "preload", Usage: "Comma separated list of JavaScript files to preload into the console", } // Network Settings MaxPeersFlag = cli.IntFlag{ Name: "maxpeers", Usage: "Maximum number of network peers (network disabled if set to 0)", Value: 25, } MaxPendingPeersFlag = cli.IntFlag{ Name: "maxpendpeers", Usage: "Maximum number of pending connection attempts (defaults used if set to 0)", Value: 0, } ListenPortFlag = cli.IntFlag{ Name: "port", Usage: "Network listening port", Value: 30303, } BootnodesFlag = cli.StringFlag{ Name: "bootnodes", Usage: "Comma separated enode URLs for P2P discovery bootstrap", Value: "", } NodeKeyFileFlag = cli.StringFlag{ Name: "nodekey", Usage: "P2P node key file", } NodeKeyHexFlag = cli.StringFlag{ Name: "nodekeyhex", Usage: "P2P node key as hex (for testing)", } NATFlag = cli.StringFlag{ Name: "nat", Usage: "NAT port mapping mechanism (any|none|upnp|pmp|extip:)", Value: "any", } NoDiscoverFlag = cli.BoolFlag{ Name: "nodiscover", Usage: "Disables the peer discovery mechanism (manual peer addition)", } DiscoveryV5Flag = cli.BoolFlag{ Name: "v5disc", Usage: "Enables the experimental RLPx V5 (Topic Discovery) mechanism", } NetrestrictFlag = cli.StringFlag{ Name: "netrestrict", Usage: "Restricts network communication to the given IP networks (CIDR masks)", } WhisperEnabledFlag = cli.BoolFlag{ Name: "shh", Usage: "Enable Whisper", } // ATM the url is left to the user and deployment to JSpathFlag = cli.StringFlag{ Name: "jspath", Usage: "JavaScript root path for `loadScript`", Value: ".", } SolcPathFlag = cli.StringFlag{ Name: "solc", Usage: "Solidity compiler command to be used", Value: "solc", } // Gas price oracle settings GpoBlocksFlag = cli.IntFlag{ Name: "gpoblocks", Usage: "Number of recent blocks to check for gas prices", Value: eth.DefaultConfig.GPO.Blocks, } GpoPercentileFlag = cli.IntFlag{ Name: "gpopercentile", Usage: "Suggested gas price is the given percentile of a set of recent transaction gas prices", Value: eth.DefaultConfig.GPO.Percentile, } ) // MakeDataDir retrieves the currently requested data directory, terminating // if none (or the empty string) is specified. If the node is starting a testnet, // the a subdirectory of the specified datadir will be used. func MakeDataDir(ctx *cli.Context) string { if path := ctx.GlobalString(DataDirFlag.Name); path != "" { // TODO: choose a different location outside of the regular datadir. if ctx.GlobalBool(TestNetFlag.Name) { return filepath.Join(path, "testnet") } return path } Fatalf("Cannot determine default data directory, please set manually (--datadir)") return "" } // setNodeKey creates a node key from set command line flags, either loading it // from a file or as a specified hex value. If neither flags were provided, this // method returns nil and an emphemeral key is to be generated. func setNodeKey(ctx *cli.Context, cfg *p2p.Config) { var ( hex = ctx.GlobalString(NodeKeyHexFlag.Name) file = ctx.GlobalString(NodeKeyFileFlag.Name) key *ecdsa.PrivateKey err error ) switch { case file != "" && hex != "": Fatalf("Options %q and %q are mutually exclusive", NodeKeyFileFlag.Name, NodeKeyHexFlag.Name) case file != "": if key, err = crypto.LoadECDSA(file); err != nil { Fatalf("Option %q: %v", NodeKeyFileFlag.Name, err) } cfg.PrivateKey = key case hex != "": if key, err = crypto.HexToECDSA(hex); err != nil { Fatalf("Option %q: %v", NodeKeyHexFlag.Name, err) } cfg.PrivateKey = key } } // setNodeUserIdent creates the user identifier from CLI flags. func setNodeUserIdent(ctx *cli.Context, cfg *node.Config) { if identity := ctx.GlobalString(IdentityFlag.Name); len(identity) > 0 { cfg.UserIdent = identity } } // setBootstrapNodes creates a list of bootstrap nodes from the command line // flags, reverting to pre-configured ones if none have been specified. func setBootstrapNodes(ctx *cli.Context, cfg *p2p.Config) { urls := params.MainnetBootnodes if ctx.GlobalIsSet(BootnodesFlag.Name) { urls = strings.Split(ctx.GlobalString(BootnodesFlag.Name), ",") } else if ctx.GlobalBool(TestNetFlag.Name) { urls = params.TestnetBootnodes } cfg.BootstrapNodes = make([]*discover.Node, 0, len(urls)) for _, url := range urls { node, err := discover.ParseNode(url) if err != nil { log.Error("Bootstrap URL invalid", "enode", url, "err", err) continue } cfg.BootstrapNodes = append(cfg.BootstrapNodes, node) } } // setBootstrapNodesV5 creates a list of bootstrap nodes from the command line // flags, reverting to pre-configured ones if none have been specified. func setBootstrapNodesV5(ctx *cli.Context, cfg *p2p.Config) { urls := params.DiscoveryV5Bootnodes if ctx.GlobalIsSet(BootnodesFlag.Name) { urls = strings.Split(ctx.GlobalString(BootnodesFlag.Name), ",") } else if cfg.BootstrapNodesV5 == nil { return // already set, don't apply defaults. } cfg.BootstrapNodesV5 = make([]*discv5.Node, 0, len(urls)) for _, url := range urls { node, err := discv5.ParseNode(url) if err != nil { log.Error("Bootstrap URL invalid", "enode", url, "err", err) continue } cfg.BootstrapNodesV5 = append(cfg.BootstrapNodesV5, node) } } // setListenAddress creates a TCP listening address string from set command // line flags. func setListenAddress(ctx *cli.Context, cfg *p2p.Config) { if ctx.GlobalIsSet(ListenPortFlag.Name) { cfg.ListenAddr = fmt.Sprintf(":%d", ctx.GlobalInt(ListenPortFlag.Name)) } } // setDiscoveryV5Address creates a UDP listening address string from set command // line flags for the V5 discovery protocol. func setDiscoveryV5Address(ctx *cli.Context, cfg *p2p.Config) { if ctx.GlobalIsSet(ListenPortFlag.Name) { cfg.DiscoveryV5Addr = fmt.Sprintf(":%d", ctx.GlobalInt(ListenPortFlag.Name)+1) } } // setNAT creates a port mapper from command line flags. func setNAT(ctx *cli.Context, cfg *p2p.Config) { if ctx.GlobalIsSet(NATFlag.Name) { natif, err := nat.Parse(ctx.GlobalString(NATFlag.Name)) if err != nil { Fatalf("Option %s: %v", NATFlag.Name, err) } cfg.NAT = natif } } // makeRPCModules splits input separated by a comma and trims excessive white // space from the substrings. func makeRPCModules(input string) []string { result := strings.Split(input, ",") for i, r := range result { result[i] = strings.TrimSpace(r) } return result } // setHTTP creates the HTTP RPC listener interface string from the set // command line flags, returning empty if the HTTP endpoint is disabled. func setHTTP(ctx *cli.Context, cfg *node.Config) { if ctx.GlobalBool(RPCEnabledFlag.Name) && cfg.HTTPHost == "" { cfg.HTTPHost = "127.0.0.1" if ctx.GlobalIsSet(RPCListenAddrFlag.Name) { cfg.HTTPHost = ctx.GlobalString(RPCListenAddrFlag.Name) } } if ctx.GlobalIsSet(RPCPortFlag.Name) { cfg.HTTPPort = ctx.GlobalInt(RPCPortFlag.Name) } if ctx.GlobalIsSet(RPCCORSDomainFlag.Name) { cfg.HTTPCors = ctx.GlobalString(RPCCORSDomainFlag.Name) } if ctx.GlobalIsSet(RPCApiFlag.Name) { cfg.HTTPModules = makeRPCModules(ctx.GlobalString(RPCApiFlag.Name)) } } // setWS creates the WebSocket RPC listener interface string from the set // command line flags, returning empty if the HTTP endpoint is disabled. func setWS(ctx *cli.Context, cfg *node.Config) { if ctx.GlobalBool(WSEnabledFlag.Name) && cfg.WSHost == "" { cfg.WSHost = "127.0.0.1" if ctx.GlobalIsSet(WSListenAddrFlag.Name) { cfg.WSHost = ctx.GlobalString(WSListenAddrFlag.Name) } } if ctx.GlobalIsSet(WSPortFlag.Name) { cfg.WSPort = ctx.GlobalInt(WSPortFlag.Name) } if ctx.GlobalIsSet(WSAllowedOriginsFlag.Name) { cfg.WSOrigins = ctx.GlobalString(WSAllowedOriginsFlag.Name) } if ctx.GlobalIsSet(WSApiFlag.Name) { cfg.WSModules = makeRPCModules(ctx.GlobalString(WSApiFlag.Name)) } } // setIPC creates an IPC path configuration from the set command line flags, // returning an empty string if IPC was explicitly disabled, or the set path. func setIPC(ctx *cli.Context, cfg *node.Config) { checkExclusive(ctx, IPCDisabledFlag, IPCPathFlag) switch { case ctx.GlobalBool(IPCDisabledFlag.Name): cfg.IPCPath = "" case ctx.GlobalIsSet(IPCPathFlag.Name): cfg.IPCPath = ctx.GlobalString(IPCPathFlag.Name) } } // makeDatabaseHandles raises out the number of allowed file handles per process // for Geth and returns half of the allowance to assign to the database. func makeDatabaseHandles() int { if err := raiseFdLimit(2048); err != nil { Fatalf("Failed to raise file descriptor allowance: %v", err) } limit, err := getFdLimit() if err != nil { Fatalf("Failed to retrieve file descriptor allowance: %v", err) } if limit > 2048 { // cap database file descriptors even if more is available limit = 2048 } return limit / 2 // Leave half for networking and other stuff } // MakeAddress converts an account specified directly as a hex encoded string or // a key index in the key store to an internal account representation. func MakeAddress(ks *keystore.KeyStore, account string) (accounts.Account, error) { // If the specified account is a valid address, return it if common.IsHexAddress(account) { return accounts.Account{Address: common.HexToAddress(account)}, nil } // Otherwise try to interpret the account as a keystore index index, err := strconv.Atoi(account) if err != nil || index < 0 { return accounts.Account{}, fmt.Errorf("invalid account address or index %q", account) } accs := ks.Accounts() if len(accs) <= index { return accounts.Account{}, fmt.Errorf("index %d higher than number of accounts %d", index, len(accs)) } return accs[index], nil } // setEtherbase retrieves the etherbase either from the directly specified // command line flags or from the keystore if CLI indexed. func setEtherbase(ctx *cli.Context, ks *keystore.KeyStore, cfg *eth.Config) { if ctx.GlobalIsSet(EtherbaseFlag.Name) { account, err := MakeAddress(ks, ctx.GlobalString(EtherbaseFlag.Name)) if err != nil { Fatalf("Option %q: %v", EtherbaseFlag.Name, err) } cfg.Etherbase = account.Address return } accounts := ks.Accounts() if (cfg.Etherbase == common.Address{}) { if len(accounts) > 0 { cfg.Etherbase = accounts[0].Address } else { log.Warn("No etherbase set and no accounts found as default") } } } // MakePasswordList reads password lines from the file specified by --password. func MakePasswordList(ctx *cli.Context) []string { path := ctx.GlobalString(PasswordFileFlag.Name) if path == "" { return nil } text, err := ioutil.ReadFile(path) if err != nil { Fatalf("Failed to read password file: %v", err) } lines := strings.Split(string(text), "\n") // Sanitise DOS line endings. for i := range lines { lines[i] = strings.TrimRight(lines[i], "\r") } return lines } func SetP2PConfig(ctx *cli.Context, cfg *p2p.Config) { setNodeKey(ctx, cfg) setNAT(ctx, cfg) setListenAddress(ctx, cfg) setDiscoveryV5Address(ctx, cfg) setBootstrapNodes(ctx, cfg) setBootstrapNodesV5(ctx, cfg) if ctx.GlobalIsSet(MaxPeersFlag.Name) { cfg.MaxPeers = ctx.GlobalInt(MaxPeersFlag.Name) } if ctx.GlobalIsSet(MaxPendingPeersFlag.Name) { cfg.MaxPendingPeers = ctx.GlobalInt(MaxPendingPeersFlag.Name) } if ctx.GlobalIsSet(NoDiscoverFlag.Name) || ctx.GlobalBool(LightModeFlag.Name) { cfg.NoDiscovery = true } // if we're running a light client or server, force enable the v5 peer discovery // unless it is explicitly disabled with --nodiscover note that explicitly specifying // --v5disc overrides --nodiscover, in which case the later only disables v4 discovery forceV5Discovery := (ctx.GlobalBool(LightModeFlag.Name) || ctx.GlobalInt(LightServFlag.Name) > 0) && !ctx.GlobalBool(NoDiscoverFlag.Name) if ctx.GlobalIsSet(DiscoveryV5Flag.Name) { cfg.DiscoveryV5 = ctx.GlobalBool(DiscoveryV5Flag.Name) } else if forceV5Discovery { cfg.DiscoveryV5 = true } if netrestrict := ctx.GlobalString(NetrestrictFlag.Name); netrestrict != "" { list, err := netutil.ParseNetlist(netrestrict) if err != nil { Fatalf("Option %q: %v", NetrestrictFlag.Name, err) } cfg.NetRestrict = list } if ctx.GlobalBool(DevModeFlag.Name) { // --dev mode can't use p2p networking. cfg.MaxPeers = 0 cfg.ListenAddr = ":0" cfg.NoDiscovery = true cfg.DiscoveryV5 = false } } // SetNodeConfig applies node-related command line flags to the config. func SetNodeConfig(ctx *cli.Context, cfg *node.Config) { SetP2PConfig(ctx, &cfg.P2P) setIPC(ctx, cfg) setHTTP(ctx, cfg) setWS(ctx, cfg) setNodeUserIdent(ctx, cfg) switch { case ctx.GlobalIsSet(DataDirFlag.Name): cfg.DataDir = ctx.GlobalString(DataDirFlag.Name) case ctx.GlobalBool(DevModeFlag.Name): cfg.DataDir = filepath.Join(os.TempDir(), "ethereum_dev_mode") case ctx.GlobalBool(TestNetFlag.Name): cfg.DataDir = filepath.Join(node.DefaultDataDir(), "testnet") } if ctx.GlobalIsSet(KeyStoreDirFlag.Name) { cfg.KeyStoreDir = ctx.GlobalString(KeyStoreDirFlag.Name) } if ctx.GlobalIsSet(LightKDFFlag.Name) { cfg.UseLightweightKDF = ctx.GlobalBool(LightKDFFlag.Name) } } func setGPO(ctx *cli.Context, cfg *gasprice.Config) { if ctx.GlobalIsSet(GpoBlocksFlag.Name) { cfg.Blocks = ctx.GlobalInt(GpoBlocksFlag.Name) } if ctx.GlobalIsSet(GpoPercentileFlag.Name) { cfg.Percentile = ctx.GlobalInt(GpoPercentileFlag.Name) } } func setEthash(ctx *cli.Context, cfg *eth.Config) { if ctx.GlobalIsSet(EthashCacheDirFlag.Name) { cfg.EthashCacheDir = ctx.GlobalString(EthashCacheDirFlag.Name) } if ctx.GlobalIsSet(EthashDatasetDirFlag.Name) { cfg.EthashDatasetDir = ctx.GlobalString(EthashDatasetDirFlag.Name) } if ctx.GlobalIsSet(EthashCachesInMemoryFlag.Name) { cfg.EthashCachesInMem = ctx.GlobalInt(EthashCachesInMemoryFlag.Name) } if ctx.GlobalIsSet(EthashCachesOnDiskFlag.Name) { cfg.EthashCachesOnDisk = ctx.GlobalInt(EthashCachesOnDiskFlag.Name) } if ctx.GlobalIsSet(EthashDatasetsInMemoryFlag.Name) { cfg.EthashDatasetsInMem = ctx.GlobalInt(EthashDatasetsInMemoryFlag.Name) } if ctx.GlobalIsSet(EthashDatasetsOnDiskFlag.Name) { cfg.EthashDatasetsOnDisk = ctx.GlobalInt(EthashDatasetsOnDiskFlag.Name) } } func checkExclusive(ctx *cli.Context, flags ...cli.Flag) { set := make([]string, 0, 1) for _, flag := range flags { if ctx.GlobalIsSet(flag.GetName()) { set = append(set, "--"+flag.GetName()) } } if len(set) > 1 { Fatalf("flags %v can't be used at the same time", strings.Join(set, ", ")) } } // SetEthConfig applies eth-related command line flags to the config. func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) { // Avoid conflicting network flags checkExclusive(ctx, DevModeFlag, TestNetFlag) checkExclusive(ctx, FastSyncFlag, LightModeFlag, SyncModeFlag) ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore) setEtherbase(ctx, ks, cfg) setGPO(ctx, &cfg.GPO) setEthash(ctx, cfg) switch { case ctx.GlobalIsSet(SyncModeFlag.Name): cfg.SyncMode = *GlobalTextMarshaler(ctx, SyncModeFlag.Name).(*downloader.SyncMode) case ctx.GlobalBool(FastSyncFlag.Name): cfg.SyncMode = downloader.FastSync case ctx.GlobalBool(LightModeFlag.Name): cfg.SyncMode = downloader.LightSync } if ctx.GlobalIsSet(LightServFlag.Name) { cfg.LightServ = ctx.GlobalInt(LightServFlag.Name) } if ctx.GlobalIsSet(LightPeersFlag.Name) { cfg.LightPeers = ctx.GlobalInt(LightPeersFlag.Name) } if ctx.GlobalIsSet(NetworkIdFlag.Name) { cfg.NetworkId = ctx.GlobalInt(NetworkIdFlag.Name) } // Ethereum needs to know maxPeers to calculate the light server peer ratio. // TODO(fjl): ensure Ethereum can get MaxPeers from node. cfg.MaxPeers = ctx.GlobalInt(MaxPeersFlag.Name) if ctx.GlobalIsSet(CacheFlag.Name) { cfg.DatabaseCache = ctx.GlobalInt(CacheFlag.Name) } cfg.DatabaseHandles = makeDatabaseHandles() if ctx.GlobalIsSet(MinerThreadsFlag.Name) { cfg.MinerThreads = ctx.GlobalInt(MinerThreadsFlag.Name) } if ctx.GlobalIsSet(DocRootFlag.Name) { cfg.DocRoot = ctx.GlobalString(DocRootFlag.Name) } if ctx.GlobalIsSet(ExtraDataFlag.Name) { cfg.ExtraData = []byte(ctx.GlobalString(ExtraDataFlag.Name)) } if ctx.GlobalIsSet(GasPriceFlag.Name) { cfg.GasPrice = GlobalBig(ctx, GasPriceFlag.Name) } if ctx.GlobalIsSet(SolcPathFlag.Name) { cfg.SolcPath = ctx.GlobalString(SolcPathFlag.Name) } if ctx.GlobalIsSet(VMEnableDebugFlag.Name) { // TODO(fjl): force-enable this in --dev mode cfg.EnablePreimageRecording = ctx.GlobalBool(VMEnableDebugFlag.Name) } // Override any default configs for --dev and --testnet. switch { case ctx.GlobalBool(TestNetFlag.Name): if !ctx.GlobalIsSet(NetworkIdFlag.Name) { cfg.NetworkId = 3 } cfg.Genesis = core.DefaultTestnetGenesisBlock() case ctx.GlobalBool(DevModeFlag.Name): cfg.Genesis = core.DevGenesisBlock() if !ctx.GlobalIsSet(GasPriceFlag.Name) { cfg.GasPrice = new(big.Int) } cfg.PowTest = true } // TODO(fjl): move trie cache generations into config if gen := ctx.GlobalInt(TrieCacheGenFlag.Name); gen > 0 { state.MaxTrieCacheGen = uint16(gen) } } // RegisterEthService adds an Ethereum client to the stack. func RegisterEthService(stack *node.Node, cfg *eth.Config) { var err error if cfg.SyncMode == downloader.LightSync { err = stack.Register(func(ctx *node.ServiceContext) (node.Service, error) { return les.New(ctx, cfg) }) } else { err = stack.Register(func(ctx *node.ServiceContext) (node.Service, error) { fullNode, err := eth.New(ctx, cfg) if fullNode != nil && cfg.LightServ > 0 { ls, _ := les.NewLesServer(fullNode, cfg) fullNode.AddLesServer(ls) } return fullNode, err }) } if err != nil { Fatalf("Failed to register the Ethereum service: %v", err) } } // RegisterShhService configures Whisper and adds it to the given node. func RegisterShhService(stack *node.Node) { if err := stack.Register(func(*node.ServiceContext) (node.Service, error) { return whisper.New(), nil }); err != nil { Fatalf("Failed to register the Whisper service: %v", err) } } // RegisterEthStatsService configures the Ethereum Stats daemon and adds it to // th egiven node. func RegisterEthStatsService(stack *node.Node, url string) { if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) { // Retrieve both eth and les services var ethServ *eth.Ethereum ctx.Service(ðServ) var lesServ *les.LightEthereum ctx.Service(&lesServ) return ethstats.New(url, ethServ, lesServ) }); err != nil { Fatalf("Failed to register the Ethereum Stats service: %v", err) } } // SetupNetwork configures the system for either the main net or some test network. func SetupNetwork(ctx *cli.Context) { // TODO(fjl): move target gas limit into config params.TargetGasLimit = new(big.Int).SetUint64(ctx.GlobalUint64(TargetGasLimitFlag.Name)) } func ChainDbName(ctx *cli.Context) string { if ctx.GlobalBool(LightModeFlag.Name) { return "lightchaindata" } else { return "chaindata" } } // MakeChainDatabase open an LevelDB using the flags passed to the client and will hard crash if it fails. func MakeChainDatabase(ctx *cli.Context, stack *node.Node) ethdb.Database { var ( cache = ctx.GlobalInt(CacheFlag.Name) handles = makeDatabaseHandles() name = ChainDbName(ctx) ) chainDb, err := stack.OpenDatabase(name, cache, handles) if err != nil { Fatalf("Could not open database: %v", err) } return chainDb } func MakeGenesis(ctx *cli.Context) *core.Genesis { var genesis *core.Genesis switch { case ctx.GlobalBool(TestNetFlag.Name): genesis = core.DefaultTestnetGenesisBlock() case ctx.GlobalBool(DevModeFlag.Name): genesis = core.DevGenesisBlock() } return genesis } // MakeChain creates a chain manager from set command line flags. func MakeChain(ctx *cli.Context, stack *node.Node) (chain *core.BlockChain, chainDb ethdb.Database) { var err error chainDb = MakeChainDatabase(ctx, stack) engine := ethash.NewFaker() if !ctx.GlobalBool(FakePoWFlag.Name) { engine = ethash.New("", 1, 0, "", 1, 0) } config, _, err := core.SetupGenesisBlock(chainDb, MakeGenesis(ctx)) if err != nil { Fatalf("%v", err) } vmcfg := vm.Config{EnablePreimageRecording: ctx.GlobalBool(VMEnableDebugFlag.Name)} chain, err = core.NewBlockChain(chainDb, config, engine, new(event.TypeMux), vmcfg) if err != nil { Fatalf("Can't create BlockChain: %v", err) } return chain, chainDb } // MakeConsolePreloads retrieves the absolute paths for the console JavaScript // scripts to preload before starting. func MakeConsolePreloads(ctx *cli.Context) []string { // Skip preloading if there's nothing to preload if ctx.GlobalString(PreloadJSFlag.Name) == "" { return nil } // Otherwise resolve absolute paths and return them preloads := []string{} assets := ctx.GlobalString(JSpathFlag.Name) for _, file := range strings.Split(ctx.GlobalString(PreloadJSFlag.Name), ",") { preloads = append(preloads, common.AbsolutePath(assets, strings.TrimSpace(file))) } return preloads }