diff --git a/command/flagset/flagset.go b/command/flagset/flagset.go index 696765e8c6543d4cc91d530903399199ddeba7ca..c272bfbcad4e4174b8b012dbfcfcbb9620e9c757 100644 --- a/command/flagset/flagset.go +++ b/command/flagset/flagset.go @@ -3,6 +3,7 @@ package flagset import ( "flag" "fmt" + "math/big" "strings" "time" ) @@ -46,10 +47,6 @@ func (f *Flagset) Args() []string { return f.set.Args() } -func (f *Flagset) BoolVar() { - -} - type BoolFlag struct { Name string Usage string @@ -95,6 +92,56 @@ func (f *Flagset) IntFlag(i *IntFlag) { f.set.IntVar(i.Value, i.Name, i.Default, i.Usage) } +type Uint64Flag struct { + Name string + Usage string + Default uint64 + Value *uint64 +} + +func (f *Flagset) Uint64Flag(i *Uint64Flag) { + f.addFlag(&FlagVar{ + Name: i.Name, + Usage: i.Usage, + }) + f.set.Uint64Var(i.Value, i.Name, i.Default, i.Usage) +} + +type BigIntFlag struct { + Name string + Usage string + Default *big.Int + Value *big.Int +} + +func (b *BigIntFlag) String() string { + return b.Value.String() +} + +func (b *BigIntFlag) Set(value string) error { + num := new(big.Int) + + var ok bool + if strings.HasPrefix(value, "0x") { + num, ok = num.SetString(value[2:], 16) + } else { + num, ok = num.SetString(value, 10) + } + if !ok { + return fmt.Errorf("failed to set big int") + } + b.Value = num + return nil +} + +func (f *Flagset) BigIntFlag(b *BigIntFlag) { + f.addFlag(&FlagVar{ + Name: b.Name, + Usage: b.Usage, + }) + f.set.Var(b, b.Name, b.Usage) +} + type SliceStringFlag struct { Name string Usage string @@ -102,7 +149,7 @@ type SliceStringFlag struct { } func (i *SliceStringFlag) String() string { - return "" + return strings.Join(i.Value, ",") } func (i *SliceStringFlag) Set(value string) error { @@ -124,9 +171,75 @@ type DurationFlag struct { Value *time.Duration } +func (d *DurationFlag) String() string { + return d.Value.String() +} + +func (d *DurationFlag) Set(value string) error { + v, err := time.ParseDuration(value) + if err != nil { + return err + } + d.Value = &v + return nil +} + func (f *Flagset) DurationFlag(d *DurationFlag) { f.addFlag(&FlagVar{ Name: d.Name, Usage: d.Usage, }) + f.set.Var(d, d.Name, d.Usage) +} + +type MapStringFlag struct { + Name string + Usage string + Value *map[string]string +} + +func (m *MapStringFlag) String() string { + ls := []string{} + for k, v := range *m.Value { + ls = append(ls, k+"="+v) + } + return strings.Join(ls, ",") +} + +func (m *MapStringFlag) Set(value string) error { + if m.Value == nil { + m.Value = &map[string]string{} + } + for _, t := range strings.Split(value, ",") { + if t != "" { + kv := strings.Split(t, "=") + + if len(kv) == 2 { + (*m.Value)[kv[0]] = kv[1] + } + } + } + return nil +} + +func (f *Flagset) MapStringFlag(m *MapStringFlag) { + f.addFlag(&FlagVar{ + Name: m.Name, + Usage: m.Usage, + }) + f.set.Var(m, m.Name, m.Usage) +} + +type Float64Flag struct { + Name string + Usage string + Value *float64 +} + +func (f *Flagset) Float64Flag(i *Float64Flag) { + f.addFlag(&FlagVar{ + Name: i.Name, + Usage: i.Usage, + }) + f.set.Float64Var(i.Value, i.Name, *i.Value, "") } diff --git a/command/server/command.go b/command/server/command.go index 59c106fea21bb11546e462b19796b33663cbd090..56b1e1f1053b607f7993fb3eb7336ab0cdb8df0b 100644 --- a/command/server/command.go +++ b/command/server/command.go @@ -11,9 +11,12 @@ import ( "github.com/ethereum/go-ethereum/accounts/keystore" "github.com/ethereum/go-ethereum/eth" + "github.com/ethereum/go-ethereum/eth/tracers" "github.com/ethereum/go-ethereum/ethstats" + "github.com/ethereum/go-ethereum/graphql" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/metrics" + "github.com/ethereum/go-ethereum/metrics/influxdb" "github.com/ethereum/go-ethereum/node" "github.com/mattn/go-colorable" "github.com/mattn/go-isatty" @@ -57,89 +60,136 @@ func (c *Command) Run(args []string) int { // read config file config := DefaultConfig() if c.configFile != "" { - c, err := readConfigFile(c.configFile) + cfg, err := readConfigFile(c.configFile) if err != nil { - panic(err) + c.UI.Error(err.Error()) + return 1 + } + if err := config.Merge(cfg); err != nil { + c.UI.Error(err.Error()) + return 1 } - config.Merge(c) } - config.Merge(c.cliConfig) + if err := config.Merge(c.cliConfig); err != nil { + c.UI.Error(err.Error()) + return 1 + } // start the logger setupLogger(*config.LogLevel) // load the chain genesis if err := config.loadChain(); err != nil { - panic(err) + c.UI.Error(err.Error()) + return 1 } // create the node/stack nodeCfg, err := config.buildNode() if err != nil { - panic(err) + c.UI.Error(err.Error()) + return 1 } stack, err := node.New(nodeCfg) if err != nil { - panic(err) + c.UI.Error(err.Error()) + return 1 } c.node = stack - // TODO: MakeChain? - // TODO: Metrics - // TODO: apis - // TODO: Graphql - // register the ethereum backend ethCfg, err := config.buildEth() if err != nil { - panic(err) + c.UI.Error(err.Error()) + return 1 } backend, err := eth.New(stack, ethCfg) if err != nil { - panic(err) + c.UI.Error(err.Error()) + return 1 } - // This is the tracers api, not sure if this should be here, i do not think so. - // c.node.RegisterAPIs(tracers.APIs(backend.APIBackend)) + // debug tracing is enabled by default + stack.RegisterAPIs(tracers.APIs(backend.APIBackend)) + + // graphql is started from another place + if *config.JsonRPC.Graphql.Enabled { + if err := graphql.New(stack, backend.APIBackend, config.JsonRPC.Cors, config.JsonRPC.Modules); err != nil { + c.UI.Error(fmt.Sprintf("Failed to register the GraphQL service: %v", err)) + return 1 + } + } // register ethash service if config.EthStats != nil { if err := ethstats.New(stack, backend.APIBackend, backend.Engine(), *config.EthStats); err != nil { - panic(err) + c.UI.Error(err.Error()) + return 1 } } // setup account manager (only keystore) { keydir := stack.KeyStoreDir() - scryptN := keystore.StandardScryptN - scryptP := keystore.StandardScryptP - - stack.AccountManager().AddBackend(keystore.NewKeyStore(keydir, scryptN, scryptP)) + n, p := keystore.StandardScryptN, keystore.StandardScryptP + if *config.UseLightweightKDF { + n, p = keystore.LightScryptN, keystore.LightScryptP + } + stack.AccountManager().AddBackend(keystore.NewKeyStore(keydir, n, p)) } // sealing (if enabled) if *config.Sealer.Enabled { if err := backend.StartMining(1); err != nil { - panic(err) + c.UI.Error(err.Error()) + return 1 } } - c.setupMetrics() + if err := c.setupMetrics(config.Metrics); err != nil { + c.UI.Error(err.Error()) + return 1 + } // start the node if err := c.node.Start(); err != nil { - panic(err) + c.UI.Error(err.Error()) + return 1 } return c.handleSignals() } -func (c *Command) setupMetrics() error { +func (c *Command) setupMetrics(config *MetricsConfig) error { + metrics.Enabled = *config.Enabled + metrics.EnabledExpensive = *config.Expensive + if !metrics.Enabled { - // metrics are globally disabled + // metrics are disabled, do not set up any sink return nil } + log.Info("Enabling metrics collection") + + // influxdb + if v1Enabled, v2Enabled := (*config.InfluxDB.V1Enabled), (*config.InfluxDB.V2Enabled); v1Enabled || v2Enabled { + if v1Enabled && v2Enabled { + return fmt.Errorf("both influx v1 and influx v2 cannot be enabled") + } + + cfg := config.InfluxDB + tags := *cfg.Tags + endpoint := *cfg.Endpoint + + if v1Enabled { + log.Info("Enabling metrics export to InfluxDB (v1)") + go influxdb.InfluxDBWithTags(metrics.DefaultRegistry, 10*time.Second, endpoint, *cfg.Database, *cfg.Username, *cfg.Password, "geth.", tags) + } + if v2Enabled { + log.Info("Enabling metrics export to InfluxDB (v2)") + go influxdb.InfluxDBV2WithTags(metrics.DefaultRegistry, 10*time.Second, endpoint, *cfg.Token, *cfg.Bucket, *cfg.Organization, "geth.", tags) + } + } + // Start system runtime metrics collection go metrics.CollectProcessMetrics(3 * time.Second) diff --git a/command/server/config.go b/command/server/config.go index c8dd5c27c5df0f3e2d6d89d753f6da4ab1604f02..5236d20c7240e66da196694ae990a4a3b63bc04b 100644 --- a/command/server/config.go +++ b/command/server/config.go @@ -4,29 +4,44 @@ import ( "crypto/ecdsa" "fmt" "io/ioutil" + "math" "math/big" "os" "path/filepath" "strconv" "time" + godebug "runtime/debug" + "github.com/ethereum/go-ethereum/command/server/chains" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/fdlimit" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/eth/downloader" "github.com/ethereum/go-ethereum/eth/ethconfig" + "github.com/ethereum/go-ethereum/eth/gasprice" + "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p/enode" + "github.com/ethereum/go-ethereum/p2p/nat" "github.com/ethereum/go-ethereum/params" "github.com/imdario/mergo" + gopsutil "github.com/shirou/gopsutil/mem" ) +func mapStrPtr(m map[string]string) *map[string]string { + return &m +} + func stringPtr(s string) *string { return &s } +func float64Ptr(f float64) *float64 { + return &f +} + func uint64Ptr(i uint64) *uint64 { return &i } @@ -42,28 +57,39 @@ func durPtr(d time.Duration) *time.Duration { type Config struct { chain *chains.Chain - Chain *string - Debug *bool - LogLevel *string - DataDir *string - P2P *P2PConfig - SyncMode *string - EthStats *string - TxPool *TxPoolConfig - Sealer *SealerConfig - JsonRPC *JsonRPCConfig + Chain *string + Debug *bool + Whitelist *map[string]string + UseLightweightKDF *bool + LogLevel *string + DataDir *string + P2P *P2PConfig + SyncMode *string + GcMode *string + Snapshot *bool + EthStats *string + Heimdall *HeimdallConfig + TxPool *TxPoolConfig + Sealer *SealerConfig + JsonRPC *JsonRPCConfig + Gpo *GpoConfig + Ethstats *string + Metrics *MetricsConfig + Cache *CacheConfig } type P2PConfig struct { - MaxPeers *uint64 - Bind *string - Port *uint64 - NoDiscover *bool - V5Disc *bool - Discovery *P2PDiscovery + MaxPeers *uint64 + MaxPendPeers *uint64 + Bind *string + Port *uint64 + NoDiscover *bool + NAT *string + Discovery *P2PDiscovery } type P2PDiscovery struct { + V5Enabled *bool Bootnodes []string BootnodesV4 []string BootnodesV5 []string @@ -72,6 +98,11 @@ type P2PDiscovery struct { DNS []string } +type HeimdallConfig struct { + URL *string + Without *bool +} + type TxPoolConfig struct { Locals []string NoLocals *bool @@ -95,21 +126,84 @@ type SealerConfig struct { } type JsonRPCConfig struct { + IPCDisable *bool + IPCPath *string + + Modules []string + VHost []string + Cors []string + Bind *string + + GasCap *uint64 + TxFeeCap *float64 + + Http *APIConfig + Ws *APIConfig + Graphql *APIConfig +} + +type APIConfig struct { + Enabled *bool + Port *uint64 + Prefix *string +} + +type GpoConfig struct { + Blocks *uint64 + Percentile *uint64 + MaxPrice *big.Int + IgnorePrice *big.Int +} + +type MetricsConfig struct { + Enabled *bool + Expensive *bool + InfluxDB *InfluxDBConfig +} + +type InfluxDBConfig struct { + V1Enabled *bool + Endpoint *string + Database *string + Username *string + Password *string + Tags *map[string]string + V2Enabled *bool + Token *string + Bucket *string + Organization *string +} + +type CacheConfig struct { + Cache *uint64 + PercGc *uint64 + PercSnapshot *uint64 + PercDatabase *uint64 + PercTrie *uint64 + Journal *string + Rejournal *time.Duration + NoPrefetch *bool + Preimages *bool + TxLookupLimit *uint64 } func DefaultConfig() *Config { return &Config{ - Chain: stringPtr("mainnet"), - Debug: boolPtr(false), - LogLevel: stringPtr("INFO"), - DataDir: stringPtr(""), + Chain: stringPtr("mainnet"), + Debug: boolPtr(false), + UseLightweightKDF: boolPtr(false), + Whitelist: mapStrPtr(map[string]string{}), + LogLevel: stringPtr("INFO"), + DataDir: stringPtr(node.DefaultDataDir()), P2P: &P2PConfig{ - MaxPeers: uint64Ptr(30), - Bind: stringPtr("0.0.0.0"), - Port: uint64Ptr(30303), - NoDiscover: boolPtr(false), - V5Disc: boolPtr(false), + MaxPeers: uint64Ptr(30), + MaxPendPeers: uint64Ptr(50), + Bind: stringPtr("0.0.0.0"), + Port: uint64Ptr(30303), + NoDiscover: boolPtr(false), + NAT: stringPtr("any"), Discovery: &P2PDiscovery{ + V5Enabled: boolPtr(false), Bootnodes: []string{}, BootnodesV4: []string{}, BootnodesV5: []string{}, @@ -118,7 +212,13 @@ func DefaultConfig() *Config { DNS: []string{}, }, }, + Heimdall: &HeimdallConfig{ + URL: stringPtr("http://localhost:1317"), + Without: boolPtr(false), + }, SyncMode: stringPtr("fast"), + GcMode: stringPtr("full"), + Snapshot: boolPtr(true), EthStats: stringPtr(""), TxPool: &TxPoolConfig{ Locals: []string{}, @@ -134,9 +234,66 @@ func DefaultConfig() *Config { LifeTime: durPtr(3 * time.Hour), }, Sealer: &SealerConfig{ - Enabled: boolPtr(false), - GasCeil: uint64Ptr(8000000), - GasPrice: big.NewInt(params.GWei), + Enabled: boolPtr(false), + GasCeil: uint64Ptr(8000000), + GasPrice: big.NewInt(params.GWei), + ExtraData: stringPtr(""), + }, + Gpo: &GpoConfig{ + Blocks: uint64Ptr(20), + Percentile: uint64Ptr(60), + MaxPrice: gasprice.DefaultMaxPrice, + IgnorePrice: gasprice.DefaultIgnorePrice, + }, + JsonRPC: &JsonRPCConfig{ + IPCDisable: boolPtr(false), + Modules: []string{"web3", "net"}, + Cors: []string{"*"}, + VHost: []string{"*"}, + GasCap: uint64Ptr(ethconfig.Defaults.RPCGasCap), + TxFeeCap: float64Ptr(ethconfig.Defaults.RPCTxFeeCap), + Http: &APIConfig{ + Enabled: boolPtr(false), + Port: uint64Ptr(8545), + Prefix: stringPtr(""), + }, + Ws: &APIConfig{ + Enabled: boolPtr(false), + Port: uint64Ptr(8546), + Prefix: stringPtr(""), + }, + Graphql: &APIConfig{ + Enabled: boolPtr(false), + }, + }, + Ethstats: stringPtr(""), + Metrics: &MetricsConfig{ + Enabled: boolPtr(false), + Expensive: boolPtr(false), + InfluxDB: &InfluxDBConfig{ + V1Enabled: boolPtr(false), + Endpoint: stringPtr(""), + Database: stringPtr(""), + Username: stringPtr(""), + Password: stringPtr(""), + Tags: mapStrPtr(map[string]string{}), + V2Enabled: boolPtr(false), + Token: stringPtr(""), + Bucket: stringPtr(""), + Organization: stringPtr(""), + }, + }, + Cache: &CacheConfig{ + Cache: uint64Ptr(1024), + PercDatabase: uint64Ptr(50), + PercTrie: uint64Ptr(15), + PercGc: uint64Ptr(25), + PercSnapshot: uint64Ptr(10), + Journal: stringPtr("triecache"), + Rejournal: durPtr(60 * time.Minute), + NoPrefetch: boolPtr(false), + Preimages: boolPtr(false), + TxLookupLimit: uint64Ptr(2350000), }, } } @@ -163,13 +320,22 @@ func (c *Config) loadChain() error { } c.chain = chain - // preload some default values that are on the chain file + // preload some default values that depend on the chain file + + // the genesis files defines default bootnodes if c.P2P.Discovery.Bootnodes == nil { c.P2P.Discovery.Bootnodes = c.chain.Bootnodes } if c.P2P.Discovery.DNS == nil { c.P2P.Discovery.DNS = c.chain.DNS } + + // depending on the chain we have different cache values + if *c.Chain != "mainnet" { + c.Cache.Cache = uint64Ptr(4096) + } else { + c.Cache.Cache = uint64Ptr(1024) + } return nil } @@ -184,25 +350,30 @@ func (c *Config) buildEth() (*ethconfig.Config, error) { // txpool options { - cfg := n.TxPool - cfg.NoLocals = *c.TxPool.NoLocals - cfg.Journal = *c.TxPool.Journal - cfg.Rejournal = *c.TxPool.Rejournal - cfg.PriceLimit = *c.TxPool.PriceLimit - cfg.PriceBump = *c.TxPool.PriceBump - cfg.AccountSlots = *c.TxPool.AccountSlots - cfg.GlobalSlots = *c.TxPool.GlobalSlots - cfg.AccountQueue = *c.TxPool.AccountQueue - cfg.GlobalQueue = *c.TxPool.GlobalQueue - cfg.Lifetime = *c.TxPool.LifeTime + n.TxPool.NoLocals = *c.TxPool.NoLocals + n.TxPool.Journal = *c.TxPool.Journal + n.TxPool.Rejournal = *c.TxPool.Rejournal + n.TxPool.PriceLimit = *c.TxPool.PriceLimit + n.TxPool.PriceBump = *c.TxPool.PriceBump + n.TxPool.AccountSlots = *c.TxPool.AccountSlots + n.TxPool.GlobalSlots = *c.TxPool.GlobalSlots + n.TxPool.AccountQueue = *c.TxPool.AccountQueue + n.TxPool.GlobalQueue = *c.TxPool.GlobalQueue + n.TxPool.Lifetime = *c.TxPool.LifeTime } // miner options { - cfg := n.Miner - cfg.Etherbase = common.HexToAddress(*c.Sealer.Etherbase) - cfg.GasPrice = c.Sealer.GasPrice - cfg.GasCeil = *c.Sealer.GasCeil + n.Miner.GasPrice = c.Sealer.GasPrice + n.Miner.GasCeil = *c.Sealer.GasCeil + n.Miner.ExtraData = []byte(*c.Sealer.ExtraData) + + if etherbase := c.Sealer.Etherbase; etherbase != nil { + if !common.IsHexAddress(*etherbase) { + return nil, fmt.Errorf("etherbase is not an address: %s", *etherbase) + } + n.Miner.Etherbase = common.HexToAddress(*etherbase) + } } // discovery (this params should be in node.Config) @@ -211,16 +382,106 @@ func (c *Config) buildEth() (*ethconfig.Config, error) { n.SnapDiscoveryURLs = c.P2P.Discovery.DNS } - var syncMode downloader.SyncMode + // whitelist + { + n.Whitelist = map[uint64]common.Hash{} + for k, v := range *c.Whitelist { + number, err := strconv.ParseUint(k, 0, 64) + if err != nil { + return nil, fmt.Errorf("invalid whitelist block number %s: %v", k, err) + } + var hash common.Hash + if err = hash.UnmarshalText([]byte(v)); err != nil { + return nil, fmt.Errorf("invalid whitelist hash %s: %v", v, err) + } + n.Whitelist[number] = hash + } + } + + // cache + { + cache := *c.Cache.Cache + calcPerc := func(val *uint64) int { + return int(cache * (*val) / 100) + } + + // Cap the cache allowance + mem, err := gopsutil.VirtualMemory() + if err == nil { + if 32<<(^uintptr(0)>>63) == 32 && mem.Total > 2*1024*1024*1024 { + log.Warn("Lowering memory allowance on 32bit arch", "available", mem.Total/1024/1024, "addressable", 2*1024) + mem.Total = 2 * 1024 * 1024 * 1024 + } + allowance := uint64(mem.Total / 1024 / 1024 / 3) + if cache > allowance { + log.Warn("Sanitizing cache to Go's GC limits", "provided", cache, "updated", allowance) + cache = allowance + } + } + // Tune the garbage collector + gogc := math.Max(20, math.Min(100, 100/(float64(cache)/1024))) + + log.Debug("Sanitizing Go's GC trigger", "percent", int(gogc)) + godebug.SetGCPercent(int(gogc)) + + n.TrieCleanCacheJournal = *c.Cache.Journal + n.TrieCleanCacheRejournal = *c.Cache.Rejournal + n.DatabaseCache = calcPerc(c.Cache.PercDatabase) + n.SnapshotCache = calcPerc(c.Cache.PercSnapshot) + n.TrieCleanCache = calcPerc(c.Cache.PercTrie) + n.TrieDirtyCache = calcPerc(c.Cache.PercGc) + n.NoPrefetch = *c.Cache.NoPrefetch + n.Preimages = *c.Cache.Preimages + n.TxLookupLimit = *c.Cache.TxLookupLimit + } + + n.RPCGasCap = *c.JsonRPC.GasCap + if n.RPCGasCap != 0 { + log.Info("Set global gas cap", "cap", n.RPCGasCap) + } else { + log.Info("Global gas cap disabled") + } + n.RPCTxFeeCap = *c.JsonRPC.TxFeeCap + + // sync mode. It can either be "fast", "full" or "snap". We disable + // for now the "light" mode. switch *c.SyncMode { case "fast": - syncMode = downloader.FastSync + n.SyncMode = downloader.FastSync + case "full": + n.SyncMode = downloader.FullSync + case "snap": + n.SyncMode = downloader.SnapSync default: - return nil, fmt.Errorf("sync mode '%s' not found", syncMode) + return nil, fmt.Errorf("sync mode '%s' not found", *c.SyncMode) + } + + // archive mode. It can either be "archive" or "full". + switch *c.GcMode { + case "full": + n.NoPruning = false + case "archive": + n.NoPruning = true + if !n.Preimages { + n.Preimages = true + log.Info("Enabling recording of key preimages since archive mode is used") + } + default: + return nil, fmt.Errorf("gcmode '%s' not found", *c.GcMode) } - n.SyncMode = syncMode - n.DatabaseHandles = dbHandles + // snapshot disable check + if *c.Snapshot { + if n.SyncMode == downloader.SnapSync { + log.Info("Snap sync requested, enabling --snapshot") + } else { + // disable snapshot + n.TrieCleanCache += n.SnapshotCache + n.SnapshotCache = 0 + } + } + + n.DatabaseHandles = dbHandles return &n, nil } @@ -231,23 +492,54 @@ var ( ) func (c *Config) buildNode() (*node.Config, error) { + ipcPath := "" + if !*c.JsonRPC.IPCDisable { + ipcPath = clientIdentifier + ".ipc" + if c.JsonRPC.IPCPath != nil { + ipcPath = *c.JsonRPC.IPCPath + } + } + cfg := &node.Config{ - Name: clientIdentifier, - DataDir: *c.DataDir, - Version: params.VersionWithCommit(gitCommit, gitDate), - IPCPath: clientIdentifier + ".ipc", + Name: clientIdentifier, + DataDir: *c.DataDir, + UseLightweightKDF: *c.UseLightweightKDF, + Version: params.VersionWithCommit(gitCommit, gitDate), + IPCPath: ipcPath, P2P: p2p.Config{ - MaxPeers: int(*c.P2P.MaxPeers), - ListenAddr: *c.P2P.Bind + ":" + strconv.Itoa(int(*c.P2P.Port)), + MaxPeers: int(*c.P2P.MaxPeers), + MaxPendingPeers: int(*c.P2P.MaxPendPeers), + ListenAddr: *c.P2P.Bind + ":" + strconv.Itoa(int(*c.P2P.Port)), + DiscoveryV5: *c.P2P.Discovery.V5Enabled, }, - /* - HTTPHost: *c.BindAddr, - HTTPPort: int(*c.Ports.HTTP), - HTTPVirtualHosts: []string{"*"}, - WSHost: *c.BindAddr, - WSPort: int(*c.Ports.Websocket), - */ + HTTPModules: c.JsonRPC.Modules, + HTTPCors: c.JsonRPC.Cors, + HTTPVirtualHosts: c.JsonRPC.VHost, + HTTPPathPrefix: *c.JsonRPC.Http.Prefix, + WSModules: c.JsonRPC.Modules, + WSOrigins: c.JsonRPC.Cors, + WSPathPrefix: *c.JsonRPC.Ws.Prefix, + GraphQLCors: c.JsonRPC.Cors, + GraphQLVirtualHosts: c.JsonRPC.VHost, + } + + // enable jsonrpc endpoints + { + if *c.JsonRPC.Http.Enabled { + cfg.HTTPHost = *c.JsonRPC.Bind + cfg.HTTPPort = int(*c.JsonRPC.Http.Port) + } + if *c.JsonRPC.Ws.Enabled { + cfg.WSHost = *c.JsonRPC.Bind + cfg.WSPort = int(*c.JsonRPC.Ws.Port) + } + } + + natif, err := nat.Parse(*c.P2P.NAT) + if err != nil { + return nil, fmt.Errorf("wrong 'nat' flag: %v", err) } + cfg.P2P.NAT = natif // setup private key for DevP2P if not found devP2PPrivKey, err := readDevP2PKey(*c.DataDir) diff --git a/command/server/flags.go b/command/server/flags.go index 66d426279878fd0c88472a4aee1dc8f6fb4c51aa..b0359d525f74fa2e9438ea85a65ee3a5d8466efc 100644 --- a/command/server/flags.go +++ b/command/server/flags.go @@ -1,65 +1,415 @@ package server -import "github.com/ethereum/go-ethereum/command/flagset" +import ( + "github.com/ethereum/go-ethereum/command/flagset" +) func (c *Command) Flags() *flagset.Flagset { c.cliConfig = DefaultConfig() - f := flagset.NewFlagSet("") + f := flagset.NewFlagSet("server") f.BoolFlag(&flagset.BoolFlag{ Name: "debug", - Value: c.cliConfig.Debug, Usage: "Path of the file to apply", + Value: c.cliConfig.Debug, }) f.StringFlag(&flagset.StringFlag{ Name: "chain", - Value: c.cliConfig.Chain, Usage: "Name of the chain to sync", + Value: c.cliConfig.Chain, }) f.StringFlag(&flagset.StringFlag{ Name: "log-level", - Value: c.cliConfig.LogLevel, Usage: "Set log level for the server", + Value: c.cliConfig.LogLevel, }) f.StringFlag(&flagset.StringFlag{ - Name: "data-dir", - Value: c.cliConfig.DataDir, + Name: "datadir", Usage: "Path of the data directory to store information", + Value: c.cliConfig.DataDir, }) f.StringFlag(&flagset.StringFlag{ Name: "config", - Value: &c.configFile, Usage: "File for the config file", + Value: &c.configFile, + }) + f.StringFlag(&flagset.StringFlag{ + Name: "syncmode", + Usage: `Blockchain sync mode ("fast", "full", "snap" or "light")`, + Value: c.cliConfig.SyncMode, + }) + f.StringFlag(&flagset.StringFlag{ + Name: "gcmode", + Usage: `Blockchain garbage collection mode ("full", "archive")`, + Value: c.cliConfig.GcMode, + }) + f.BoolFlag(&flagset.BoolFlag{ + Name: "lightkdf", + Usage: "Reduce key-derivation RAM & CPU usage at some expense of KDF strength", + Value: c.cliConfig.UseLightweightKDF, + }) + f.MapStringFlag(&flagset.MapStringFlag{ + Name: "whitelist", + Usage: "Comma separated block number-to-hash mappings to enforce (<number>=<hash>)", + Value: c.cliConfig.Whitelist, + }) + f.BoolFlag(&flagset.BoolFlag{ + Name: "snapshot", + Usage: `Enables snapshot-database mode (default = enable)`, + Value: c.cliConfig.Snapshot, + }) + + // heimdall + f.StringFlag(&flagset.StringFlag{ + Name: "bor.heimdall", + Usage: "URL of Heimdall service", + Value: c.cliConfig.Heimdall.URL, + }) + f.BoolFlag(&flagset.BoolFlag{ + Name: "bor.withoutheimdall", + Usage: "Run without Heimdall service (for testing purpose)", + Value: c.cliConfig.Heimdall.Without, }) // txpool options f.SliceStringFlag(&flagset.SliceStringFlag{ Name: "txpool.locals", - Value: c.cliConfig.TxPool.Locals, Usage: "Comma separated accounts to treat as locals (no flush, priority inclusion)", + Value: c.cliConfig.TxPool.Locals, }) f.BoolFlag(&flagset.BoolFlag{ Name: "txpool.nolocals", - Value: c.cliConfig.TxPool.NoLocals, Usage: "Disables price exemptions for locally submitted transactions", + Value: c.cliConfig.TxPool.NoLocals, + }) + f.StringFlag(&flagset.StringFlag{ + Name: "txpool.journal", + Usage: "Disk journal for local transaction to survive node restarts", + Value: c.cliConfig.TxPool.Journal, + }) + f.DurationFlag(&flagset.DurationFlag{ + Name: "txpool.rejournal", + Usage: "Time interval to regenerate the local transaction journal", + Value: c.cliConfig.TxPool.Rejournal, + }) + f.Uint64Flag(&flagset.Uint64Flag{ + Name: "txpool.pricelimit", + Usage: "Minimum gas price limit to enforce for acceptance into the pool", + Value: c.cliConfig.TxPool.PriceLimit, + }) + f.Uint64Flag(&flagset.Uint64Flag{ + Name: "txpool.pricebump", + Usage: "Price bump percentage to replace an already existing transaction", + Value: c.cliConfig.TxPool.PriceBump, + }) + f.Uint64Flag(&flagset.Uint64Flag{ + Name: "txpool.accountslots", + Usage: "Minimum number of executable transaction slots guaranteed per account", + Value: c.cliConfig.TxPool.AccountSlots, + }) + f.Uint64Flag(&flagset.Uint64Flag{ + Name: "txpool.globalslots", + Usage: "Maximum number of executable transaction slots for all accounts", + Value: c.cliConfig.TxPool.GlobalSlots, + }) + f.Uint64Flag(&flagset.Uint64Flag{ + Name: "txpool.accountqueue", + Usage: "Maximum number of non-executable transaction slots permitted per account", + Value: c.cliConfig.TxPool.AccountQueue, + }) + f.Uint64Flag(&flagset.Uint64Flag{ + Name: "txpool.globalqueue", + Usage: "Maximum number of non-executable transaction slots for all accounts", + Value: c.cliConfig.TxPool.GlobalQueue, + }) + f.DurationFlag(&flagset.DurationFlag{ + Name: "txpool.lifetime", + Usage: "Maximum amount of time non-executable transaction are queued", + Value: c.cliConfig.TxPool.LifeTime, }) // sealer options f.BoolFlag(&flagset.BoolFlag{ Name: "mine", + Usage: "Enable mining", Value: c.cliConfig.Sealer.Enabled, - Usage: "", }) f.StringFlag(&flagset.StringFlag{ Name: "miner.etherbase", + Usage: "Public address for block mining rewards (default = first account)", Value: c.cliConfig.Sealer.Etherbase, - Usage: "", }) f.StringFlag(&flagset.StringFlag{ Name: "miner.extradata", + Usage: "Block extra data set by the miner (default = client version)", Value: c.cliConfig.Sealer.ExtraData, - Usage: "", + }) + f.Uint64Flag(&flagset.Uint64Flag{ + Name: "miner.gaslimit", + Usage: "Target gas ceiling for mined blocks", + Value: c.cliConfig.Sealer.GasCeil, + }) + f.BigIntFlag(&flagset.BigIntFlag{ + Name: "miner.gasprice", + Usage: "Minimum gas price for mining a transaction", + Value: c.cliConfig.Sealer.GasPrice, + }) + + // ethstats + f.StringFlag(&flagset.StringFlag{ + Name: "ethstats", + Usage: "Reporting URL of a ethstats service (nodename:secret@host:port)", + Value: c.cliConfig.Ethstats, + }) + + // gas price oracle + f.Uint64Flag(&flagset.Uint64Flag{ + Name: "gpo.blocks", + Usage: "Number of recent blocks to check for gas prices", + Value: c.cliConfig.Gpo.Blocks, + }) + f.Uint64Flag(&flagset.Uint64Flag{ + Name: "gpo.percentile", + Usage: "Suggested gas price is the given percentile of a set of recent transaction gas prices", + Value: c.cliConfig.Gpo.Percentile, + }) + f.BigIntFlag(&flagset.BigIntFlag{ + Name: "gpo.maxprice", + Usage: "Maximum gas price will be recommended by gpo", + Value: c.cliConfig.Gpo.MaxPrice, + }) + f.BigIntFlag(&flagset.BigIntFlag{ + Name: "gpo.ignoreprice", + Usage: "Gas price below which gpo will ignore transactions", + Value: c.cliConfig.Gpo.IgnorePrice, + }) + + // cache options + f.Uint64Flag(&flagset.Uint64Flag{ + Name: "cache", + Usage: "Megabytes of memory allocated to internal caching (default = 4096 mainnet full node)", + Value: c.cliConfig.Cache.Cache, + }) + f.Uint64Flag(&flagset.Uint64Flag{ + Name: "cache.database", + Usage: "Percentage of cache memory allowance to use for database io", + Value: c.cliConfig.Cache.PercDatabase, + }) + f.Uint64Flag(&flagset.Uint64Flag{ + Name: "cache.trie", + Usage: "Percentage of cache memory allowance to use for trie caching (default = 15% full mode, 30% archive mode)", + Value: c.cliConfig.Cache.PercTrie, + }) + f.StringFlag(&flagset.StringFlag{ + Name: "cache.trie.journal", + Usage: "Disk journal directory for trie cache to survive node restarts", + Value: c.cliConfig.Cache.Journal, + }) + f.DurationFlag(&flagset.DurationFlag{ + Name: "cache.trie.rejournal", + Usage: "Time interval to regenerate the trie cache journal", + Value: c.cliConfig.Cache.Rejournal, + }) + f.Uint64Flag(&flagset.Uint64Flag{ + Name: "cache.gc", + Usage: "Percentage of cache memory allowance to use for trie pruning (default = 25% full mode, 0% archive mode)", + Value: c.cliConfig.Cache.PercGc, + }) + f.Uint64Flag(&flagset.Uint64Flag{ + Name: "cache.snapshot", + Usage: "Percentage of cache memory allowance to use for snapshot caching (default = 10% full mode, 20% archive mode)", + Value: c.cliConfig.Cache.PercSnapshot, + }) + f.BoolFlag(&flagset.BoolFlag{ + Name: "cache.noprefetch", + Usage: "Disable heuristic state prefetch during block import (less CPU and disk IO, more time waiting for data)", + Value: c.cliConfig.Cache.NoPrefetch, + }) + f.BoolFlag(&flagset.BoolFlag{ + Name: "cache.preimages", + Usage: "Enable recording the SHA3/keccak preimages of trie keys", + Value: c.cliConfig.Cache.Preimages, + }) + f.Uint64Flag(&flagset.Uint64Flag{ + Name: "txlookuplimit", + Usage: "Number of recent blocks to maintain transactions index for (default = about one year, 0 = entire chain)", + Value: c.cliConfig.Cache.TxLookupLimit, + }) + + // rpc options + f.Uint64Flag(&flagset.Uint64Flag{ + Name: "rpc.gascap", + Usage: "Sets a cap on gas that can be used in eth_call/estimateGas (0=infinite)", + Value: c.cliConfig.JsonRPC.GasCap, + }) + f.Float64Flag(&flagset.Float64Flag{ + Name: "rpc.txfeecap", + Usage: "Sets a cap on transaction fee (in ether) that can be sent via the RPC APIs (0 = no cap)", + Value: c.cliConfig.JsonRPC.TxFeeCap, + }) + f.BoolFlag(&flagset.BoolFlag{ + Name: "ipcdisable", + Usage: "Disable the IPC-RPC server", + Value: c.cliConfig.JsonRPC.IPCDisable, + }) + f.StringFlag(&flagset.StringFlag{ + Name: "ipcpath", + Usage: "Filename for IPC socket/pipe within the datadir (explicit paths escape it)", + Value: c.cliConfig.JsonRPC.IPCPath, + }) + f.SliceStringFlag(&flagset.SliceStringFlag{ + Name: "rpc.corsdomain", + Usage: "Comma separated list of domains from which to accept cross origin requests (browser enforced)", + Value: c.cliConfig.JsonRPC.Cors, + }) + f.SliceStringFlag(&flagset.SliceStringFlag{ + Name: "rpc.vhosts", + Usage: "Comma separated list of virtual hostnames from which to accept requests (server enforced). Accepts '*' wildcard.", + Value: c.cliConfig.JsonRPC.VHost, + }) + // http options + f.BoolFlag(&flagset.BoolFlag{ + Name: "http", + Usage: "Enable the HTTP-RPC server", + Value: c.cliConfig.JsonRPC.Http.Enabled, + }) + f.Uint64Flag(&flagset.Uint64Flag{ + Name: "http.port", + Usage: "HTTP-RPC server listening port", + Value: c.cliConfig.JsonRPC.Http.Port, + }) + f.StringFlag(&flagset.StringFlag{ + Name: "http.rpcprefix", + Usage: "HTTP path path prefix on which JSON-RPC is served. Use '/' to serve on all paths.", + Value: c.cliConfig.JsonRPC.Http.Prefix, + }) + // ws options + f.BoolFlag(&flagset.BoolFlag{ + Name: "ws", + Usage: "Enable the WS-RPC server", + Value: c.cliConfig.JsonRPC.Ws.Enabled, + }) + f.Uint64Flag(&flagset.Uint64Flag{ + Name: "ws.port", + Usage: "WS-RPC server listening port", + Value: c.cliConfig.JsonRPC.Ws.Port, + }) + f.StringFlag(&flagset.StringFlag{ + Name: "ws.rpcprefix", + Usage: "HTTP path prefix on which JSON-RPC is served. Use '/' to serve on all paths.", + Value: c.cliConfig.JsonRPC.Ws.Prefix, + }) + // graphql options + f.BoolFlag(&flagset.BoolFlag{ + Name: "graphql", + Usage: "Enable GraphQL on the HTTP-RPC server. Note that GraphQL can only be started if an HTTP server is started as well.", + Value: c.cliConfig.JsonRPC.Graphql.Enabled, + }) + + // p2p options + f.StringFlag(&flagset.StringFlag{ + Name: "bind", + Usage: "Network binding address", + Value: c.cliConfig.P2P.Bind, + }) + f.Uint64Flag(&flagset.Uint64Flag{ + Name: "port", + Usage: "Network listening port", + Value: c.cliConfig.P2P.Port, + }) + f.SliceStringFlag(&flagset.SliceStringFlag{ + Name: "bootnodes", + Usage: "Comma separated enode URLs for P2P discovery bootstrap", + Value: c.cliConfig.P2P.Discovery.Bootnodes, + }) + f.Uint64Flag(&flagset.Uint64Flag{ + Name: "maxpeers", + Usage: "Maximum number of network peers (network disabled if set to 0)", + Value: c.cliConfig.P2P.MaxPeers, + }) + f.Uint64Flag(&flagset.Uint64Flag{ + Name: "maxpendpeers", + Usage: "Maximum number of pending connection attempts (defaults used if set to 0)", + Value: c.cliConfig.P2P.MaxPendPeers, + }) + f.StringFlag(&flagset.StringFlag{ + Name: "nat", + Usage: "NAT port mapping mechanism (any|none|upnp|pmp|extip:<IP>)", + Value: c.cliConfig.P2P.NAT, + }) + f.BoolFlag(&flagset.BoolFlag{ + Name: "nodiscover", + Usage: "Disables the peer discovery mechanism (manual peer addition)", + Value: c.cliConfig.P2P.NoDiscover, + }) + f.BoolFlag(&flagset.BoolFlag{ + Name: "v5disc", + Usage: "Enables the experimental RLPx V5 (Topic Discovery) mechanism", + Value: c.cliConfig.P2P.Discovery.V5Enabled, + }) + + // metrics + f.BoolFlag(&flagset.BoolFlag{ + Name: "metrics", + Usage: "Enable metrics collection and reporting", + Value: c.cliConfig.Metrics.Enabled, + }) + f.BoolFlag(&flagset.BoolFlag{ + Name: "metrics.expensive", + Usage: "Enable expensive metrics collection and reporting", + Value: c.cliConfig.Metrics.Expensive, + }) + f.BoolFlag(&flagset.BoolFlag{ + Name: "metrics.influxdb", + Usage: "Enable metrics export/push to an external InfluxDB database (v1)", + Value: c.cliConfig.Metrics.InfluxDB.V1Enabled, + }) + f.StringFlag(&flagset.StringFlag{ + Name: "metrics.influxdb.endpoint", + Usage: "InfluxDB API endpoint to report metrics to", + Value: c.cliConfig.Metrics.InfluxDB.Endpoint, + }) + f.StringFlag(&flagset.StringFlag{ + Name: "metrics.influxdb.database", + Usage: "InfluxDB database name to push reported metrics to", + Value: c.cliConfig.Metrics.InfluxDB.Database, + }) + f.StringFlag(&flagset.StringFlag{ + Name: "metrics.influxdb.username", + Usage: "Username to authorize access to the database", + Value: c.cliConfig.Metrics.InfluxDB.Username, + }) + f.StringFlag(&flagset.StringFlag{ + Name: "metrics.influxdb.password", + Usage: "Password to authorize access to the database", + Value: c.cliConfig.Metrics.InfluxDB.Password, + }) + f.MapStringFlag(&flagset.MapStringFlag{ + Name: "metrics.influxdb.tags", + Usage: "Comma-separated InfluxDB tags (key/values) attached to all measurements", + Value: c.cliConfig.Metrics.InfluxDB.Tags, + }) + // influx db v2 + f.BoolFlag(&flagset.BoolFlag{ + Name: "metrics.influxdbv2", + Usage: "Enable metrics export/push to an external InfluxDB v2 database", + Value: c.cliConfig.Metrics.InfluxDB.V2Enabled, + }) + f.StringFlag(&flagset.StringFlag{ + Name: "metrics.influxdb.token", + Usage: "Token to authorize access to the database (v2 only)", + Value: c.cliConfig.Metrics.InfluxDB.Token, + }) + f.StringFlag(&flagset.StringFlag{ + Name: "metrics.influxdb.bucket", + Usage: "InfluxDB bucket name to push reported metrics to (v2 only)", + Value: c.cliConfig.Metrics.InfluxDB.Bucket, + }) + f.StringFlag(&flagset.StringFlag{ + Name: "metrics.influxdb.organization", + Usage: "InfluxDB organization name (v2 only)", + Value: c.cliConfig.Metrics.InfluxDB.Organization, }) return f