Finish flags

This commit is contained in:
ferranbt 2021-10-17 16:26:25 +00:00
parent a9b5e5dfb8
commit 7225eec0f6
9 changed files with 389 additions and 131 deletions

1
.gitignore vendored
View file

@ -47,3 +47,4 @@ profile.cov
/dashboard/assets/package-lock.json
**/yarn-error.log
./test

View file

@ -78,10 +78,9 @@ func (f *Flagset) StringFlag(b *StringFlag) {
}
type IntFlag struct {
Name string
Usage string
Default int
Value *int
Name string
Usage string
Value *int
}
func (f *Flagset) IntFlag(i *IntFlag) {
@ -89,14 +88,13 @@ func (f *Flagset) IntFlag(i *IntFlag) {
Name: i.Name,
Usage: i.Usage,
})
f.set.IntVar(i.Value, i.Name, i.Default, i.Usage)
f.set.IntVar(i.Value, i.Name, *i.Value, i.Usage)
}
type Uint64Flag struct {
Name string
Usage string
Default uint64
Value *uint64
Name string
Usage string
Value *uint64
}
func (f *Flagset) Uint64Flag(i *Uint64Flag) {
@ -104,17 +102,19 @@ func (f *Flagset) Uint64Flag(i *Uint64Flag) {
Name: i.Name,
Usage: i.Usage,
})
f.set.Uint64Var(i.Value, i.Name, i.Default, i.Usage)
f.set.Uint64Var(i.Value, i.Name, *i.Value, i.Usage)
}
type BigIntFlag struct {
Name string
Usage string
Default *big.Int
Value *big.Int
Name string
Usage string
Value *big.Int
}
func (b *BigIntFlag) String() string {
if b.Value == nil {
return ""
}
return b.Value.String()
}
@ -145,15 +145,18 @@ func (f *Flagset) BigIntFlag(b *BigIntFlag) {
type SliceStringFlag struct {
Name string
Usage string
Value []string
Value *[]string
}
func (i *SliceStringFlag) String() string {
return strings.Join(i.Value, ",")
if i.Value == nil {
return ""
}
return strings.Join(*i.Value, ",")
}
func (i *SliceStringFlag) Set(value string) error {
i.Value = append(i.Value, value)
*i.Value = append(*i.Value, strings.Split(value, ",")...)
return nil
}
@ -171,25 +174,12 @@ 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)
f.set.DurationVar(d.Value, d.Name, *d.Value, "")
}
type MapStringFlag struct {
@ -199,6 +189,9 @@ type MapStringFlag struct {
}
func (m *MapStringFlag) String() string {
if m.Value == nil {
return ""
}
ls := []string{}
for k, v := range *m.Value {
ls = append(ls, k+"="+v)

View file

@ -0,0 +1,60 @@
package flagset
import (
"testing"
"time"
"github.com/stretchr/testify/assert"
)
func TestFlagsetBool(t *testing.T) {
f := NewFlagSet("")
value := false
f.BoolFlag(&BoolFlag{
Name: "flag",
Value: &value,
})
assert.NoError(t, f.Parse([]string{"--flag", "true"}))
assert.Equal(t, value, true)
}
func TestFlagsetSliceString(t *testing.T) {
f := NewFlagSet("")
value := []string{}
f.SliceStringFlag(&SliceStringFlag{
Name: "flag",
Value: &value,
})
assert.NoError(t, f.Parse([]string{"--flag", "a,b", "--flag", "c"}))
assert.Equal(t, value, []string{"a", "b", "c"})
}
func TestFlagsetDuration(t *testing.T) {
f := NewFlagSet("")
value := time.Duration(0)
f.DurationFlag(&DurationFlag{
Name: "flag",
Value: &value,
})
assert.NoError(t, f.Parse([]string{"--flag", "1m"}))
assert.Equal(t, value, 1*time.Minute)
}
func TestFlagsetMapString(t *testing.T) {
f := NewFlagSet("")
value := map[string]string{}
f.MapStringFlag(&MapStringFlag{
Name: "flag",
Value: &value,
})
assert.NoError(t, f.Parse([]string{"--flag", "a=b,c=d"}))
assert.Equal(t, value, map[string]string{"a": "b", "c": "d"})
}

View file

@ -3,13 +3,16 @@ package server
import (
"fmt"
"io"
"io/ioutil"
"os"
"os/signal"
"strings"
"syscall"
"time"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/accounts/keystore"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/eth/tracers"
"github.com/ethereum/go-ethereum/ethstats"
@ -30,6 +33,9 @@ type Command struct {
// cli configuration
cliConfig *Config
// final configuration
config *Config
configFile string
// bor node
@ -74,6 +80,7 @@ func (c *Command) Run(args []string) int {
c.UI.Error(err.Error())
return 1
}
c.config = config
// start the logger
setupLogger(*config.LogLevel)
@ -121,7 +128,7 @@ func (c *Command) Run(args []string) int {
}
// register ethash service
if config.EthStats != nil {
if *config.EthStats != "" {
if err := ethstats.New(stack, backend.APIBackend, backend.Engine(), *config.EthStats); err != nil {
c.UI.Error(err.Error())
return 1
@ -129,13 +136,23 @@ func (c *Command) Run(args []string) int {
}
// setup account manager (only keystore)
var borKeystore *keystore.KeyStore
{
keydir := stack.KeyStoreDir()
n, p := keystore.StandardScryptN, keystore.StandardScryptP
if *config.UseLightweightKDF {
if *config.Accounts.UseLightweightKDF {
n, p = keystore.LightScryptN, keystore.LightScryptP
}
stack.AccountManager().AddBackend(keystore.NewKeyStore(keydir, n, p))
borKeystore = keystore.NewKeyStore(keydir, n, p)
stack.AccountManager().AddBackend(borKeystore)
}
// unlock accounts if necessary
if len(config.Accounts.Unlock) != 0 {
if err := c.unlockAccounts(borKeystore); err != nil {
c.UI.Error(fmt.Sprintf("failed to unlock: %v", err))
return 1
}
}
// sealing (if enabled)
@ -159,6 +176,49 @@ func (c *Command) Run(args []string) int {
return c.handleSignals()
}
func (c *Command) unlockAccounts(borKeystore *keystore.KeyStore) error {
// If insecure account unlocking is not allowed if node's APIs are exposed to external.
if !c.node.Config().InsecureUnlockAllowed && c.node.Config().ExtRPCEnabled() {
return fmt.Errorf("account unlock with HTTP access is forbidden")
}
// read passwords from file if possible
passwords := []string{}
if *c.config.Accounts.PasswordFile != "" {
var err error
if passwords, err = readMultilineFile(*c.config.Accounts.PasswordFile); err != nil {
return err
}
}
decodePassword := func(addr common.Address, index int) (string, error) {
if len(passwords) > 0 {
if index < len(passwords) {
return passwords[index], nil
}
return passwords[len(passwords)-1], nil
}
// ask for the password
return c.UI.AskSecret(fmt.Sprintf("Please give a password to unlock '%s'", addr.String()))
}
for index, addrStr := range c.config.Accounts.Unlock {
if !common.IsHexAddress(addrStr) {
return fmt.Errorf("unlock value '%s' is not an address", addrStr)
}
acct := accounts.Account{Address: common.HexToAddress(addrStr)}
password, err := decodePassword(acct.Address, index)
if err != nil {
return err
}
if err := borKeystore.Unlock(acct, password); err != nil {
return err
}
log.Info("Unlocked account", "address", acct.Address.Hex())
}
return nil
}
func (c *Command) setupMetrics(config *MetricsConfig) error {
metrics.Enabled = *config.Enabled
metrics.EnabledExpensive = *config.Expensive
@ -208,17 +268,19 @@ func (c *Command) handleSignals() int {
gracefulCh := make(chan struct{})
go func() {
c.node.Close()
c.node.Wait()
close(gracefulCh)
}()
select {
case <-signalCh:
return 1
case <-time.After(5 * time.Second):
return 1
case <-gracefulCh:
return 0
for i := 10; i > 0; i-- {
select {
case <-signalCh:
log.Warn("Already shutting down, interrupt more force stop.", "times", i-1)
case <-gracefulCh:
return 0
}
}
return 1
}
func setupLogger(logLevel string) {
@ -239,3 +301,16 @@ func setupLogger(logLevel string) {
}
log.Root().SetHandler(glogger)
}
func readMultilineFile(path string) ([]string, error) {
text, err := ioutil.ReadFile(path)
if err != nil {
return nil, err
}
lines := strings.Split(string(text), "\n")
// Sanitise DOS line endings.
for i := range lines {
lines[i] = strings.TrimRight(lines[i], "\r")
}
return lines, nil
}

View file

@ -1,13 +1,13 @@
package server
import (
"crypto/ecdsa"
"fmt"
"io/ioutil"
"math"
"math/big"
"os"
"path/filepath"
"runtime"
"strconv"
"time"
@ -16,7 +16,6 @@ import (
"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"
@ -27,6 +26,7 @@ import (
"github.com/ethereum/go-ethereum/p2p/nat"
"github.com/ethereum/go-ethereum/params"
"github.com/imdario/mergo"
"github.com/mitchellh/go-homedir"
gopsutil "github.com/shirou/gopsutil/mem"
)
@ -57,25 +57,25 @@ func durPtr(d time.Duration) *time.Duration {
type Config struct {
chain *chains.Chain
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
Chain *string
Debug *bool
Whitelist *map[string]string
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
Accounts *AccountsConfig
}
type P2PConfig struct {
@ -132,7 +132,6 @@ type JsonRPCConfig struct {
Modules []string
VHost []string
Cors []string
Bind *string
GasCap *uint64
TxFeeCap *float64
@ -146,6 +145,7 @@ type APIConfig struct {
Enabled *bool
Port *uint64
Prefix *string
Host *string
}
type GpoConfig struct {
@ -187,14 +187,20 @@ type CacheConfig struct {
TxLookupLimit *uint64
}
type AccountsConfig struct {
Unlock []string
PasswordFile *string
AllowInsecureUnlock *bool
UseLightweightKDF *bool
}
func DefaultConfig() *Config {
return &Config{
Chain: stringPtr("mainnet"),
Debug: boolPtr(false),
UseLightweightKDF: boolPtr(false),
Whitelist: mapStrPtr(map[string]string{}),
LogLevel: stringPtr("INFO"),
DataDir: stringPtr(node.DefaultDataDir()),
Chain: stringPtr("mainnet"),
Debug: boolPtr(false),
Whitelist: mapStrPtr(map[string]string{}),
LogLevel: stringPtr("INFO"),
DataDir: stringPtr(defaultDataDir()),
P2P: &P2PConfig{
MaxPeers: uint64Ptr(30),
MaxPendPeers: uint64Ptr(50),
@ -235,6 +241,7 @@ func DefaultConfig() *Config {
},
Sealer: &SealerConfig{
Enabled: boolPtr(false),
Etherbase: stringPtr(""),
GasCeil: uint64Ptr(8000000),
GasPrice: big.NewInt(params.GWei),
ExtraData: stringPtr(""),
@ -247,6 +254,7 @@ func DefaultConfig() *Config {
},
JsonRPC: &JsonRPCConfig{
IPCDisable: boolPtr(false),
IPCPath: stringPtr(""),
Modules: []string{"web3", "net"},
Cors: []string{"*"},
VHost: []string{"*"},
@ -256,11 +264,13 @@ func DefaultConfig() *Config {
Enabled: boolPtr(false),
Port: uint64Ptr(8545),
Prefix: stringPtr(""),
Host: stringPtr("localhost"),
},
Ws: &APIConfig{
Enabled: boolPtr(false),
Port: uint64Ptr(8546),
Prefix: stringPtr(""),
Host: stringPtr("localhost"),
},
Graphql: &APIConfig{
Enabled: boolPtr(false),
@ -295,6 +305,12 @@ func DefaultConfig() *Config {
Preimages: boolPtr(false),
TxLookupLimit: uint64Ptr(2350000),
},
Accounts: &AccountsConfig{
Unlock: []string{},
PasswordFile: stringPtr(""),
AllowInsecureUnlock: boolPtr(false),
UseLightweightKDF: boolPtr(false),
},
}
}
@ -321,11 +337,6 @@ func (c *Config) loadChain() error {
c.chain = chain
// 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
}
@ -368,11 +379,11 @@ func (c *Config) buildEth() (*ethconfig.Config, error) {
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)
if etherbase := *c.Sealer.Etherbase; etherbase != "" {
if !common.IsHexAddress(etherbase) {
return nil, fmt.Errorf("etherbase is not an address: %s", etherbase)
}
n.Miner.Etherbase = common.HexToAddress(*etherbase)
n.Miner.Etherbase = common.HexToAddress(etherbase)
}
}
@ -495,17 +506,18 @@ func (c *Config) buildNode() (*node.Config, error) {
ipcPath := ""
if !*c.JsonRPC.IPCDisable {
ipcPath = clientIdentifier + ".ipc"
if c.JsonRPC.IPCPath != nil {
if *c.JsonRPC.IPCPath != "" {
ipcPath = *c.JsonRPC.IPCPath
}
}
cfg := &node.Config{
Name: clientIdentifier,
DataDir: *c.DataDir,
UseLightweightKDF: *c.UseLightweightKDF,
Version: params.VersionWithCommit(gitCommit, gitDate),
IPCPath: ipcPath,
Name: clientIdentifier,
DataDir: *c.DataDir,
UseLightweightKDF: *c.Accounts.UseLightweightKDF,
InsecureUnlockAllowed: *c.Accounts.AllowInsecureUnlock,
Version: params.VersionWithCommit(gitCommit, gitDate),
IPCPath: ipcPath,
P2P: p2p.Config{
MaxPeers: int(*c.P2P.MaxPeers),
MaxPendingPeers: int(*c.P2P.MaxPendPeers),
@ -526,11 +538,11 @@ func (c *Config) buildNode() (*node.Config, error) {
// enable jsonrpc endpoints
{
if *c.JsonRPC.Http.Enabled {
cfg.HTTPHost = *c.JsonRPC.Bind
cfg.HTTPHost = *c.JsonRPC.Http.Host
cfg.HTTPPort = int(*c.JsonRPC.Http.Port)
}
if *c.JsonRPC.Ws.Enabled {
cfg.WSHost = *c.JsonRPC.Bind
cfg.WSHost = *c.JsonRPC.Ws.Host
cfg.WSPort = int(*c.JsonRPC.Ws.Port)
}
}
@ -541,15 +553,13 @@ func (c *Config) buildNode() (*node.Config, error) {
}
cfg.P2P.NAT = natif
// setup private key for DevP2P if not found
devP2PPrivKey, err := readDevP2PKey(*c.DataDir)
if err != nil {
return nil, err
}
cfg.P2P.PrivateKey = devP2PPrivKey
// Discovery
if cfg.P2P.BootstrapNodes, err = parseBootnodes(c.P2P.Discovery.Bootnodes); err != nil {
// if no bootnodes are defined, use the ones from the chain file.
bootnodes := c.P2P.Discovery.Bootnodes
if len(bootnodes) == 0 {
bootnodes = c.chain.Bootnodes
}
if cfg.P2P.BootstrapNodes, err = parseBootnodes(bootnodes); err != nil {
return nil, err
}
if cfg.P2P.BootstrapNodesV5, err = parseBootnodes(c.P2P.Discovery.BootnodesV5); err != nil {
@ -572,8 +582,8 @@ func (c *Config) buildNode() (*node.Config, error) {
func (c *Config) Merge(cc ...*Config) error {
for _, elem := range cc {
if err := mergo.Merge(&c, elem); err != nil {
return err
if err := mergo.Merge(c, elem, mergo.WithOverride, mergo.WithAppendSlice); err != nil {
return fmt.Errorf("failed to merge configurations: %v", err)
}
}
return nil
@ -605,30 +615,24 @@ func parseBootnodes(urls []string) ([]*enode.Node, error) {
return dst, nil
}
const devP2PKeyPath = "devp2p.key"
func readDevP2PKey(dataDir string) (*ecdsa.PrivateKey, error) {
path := filepath.Join(dataDir, devP2PKeyPath)
_, err := os.Stat(path)
if err != nil && !os.IsNotExist(err) {
return nil, fmt.Errorf("failed to stat (%s): %v", path, err)
func defaultDataDir() string {
// Try to place the data folder in the user's home dir
home, _ := homedir.Dir()
if home == "" {
// we cannot guess a stable location
return ""
}
if os.IsNotExist(err) {
priv, err := crypto.GenerateKey()
if err != nil {
return nil, err
switch runtime.GOOS {
case "darwin":
return filepath.Join(home, "Library", "Bor")
case "windows":
appdata := os.Getenv("LOCALAPPDATA")
if appdata == "" {
// Windows XP and below don't have LocalAppData.
panic("environment variable LocalAppData is undefined")
}
if err := crypto.SaveECDSA(path, priv); err != nil {
return nil, err
}
return priv, nil
return filepath.Join(appdata, "Bor")
default:
return filepath.Join(home, ".bor")
}
// exists
priv, err := crypto.LoadECDSA(path)
if err != nil {
return nil, err
}
return priv, nil
}

View file

@ -0,0 +1,90 @@
package server
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestConfigDefault(t *testing.T) {
// the default config should work out of the box
config := DefaultConfig()
assert.NoError(t, config.loadChain())
_, err := config.buildNode()
assert.NoError(t, err)
_, err = config.buildEth()
assert.NoError(t, err)
}
func TestConfigMerge(t *testing.T) {
c0 := &Config{
Chain: stringPtr("0"),
Debug: boolPtr(false),
Whitelist: mapStrPtr(map[string]string{
"a": "b",
}),
P2P: &P2PConfig{
Discovery: &P2PDiscovery{
StaticNodes: []string{
"a",
},
},
},
}
c1 := &Config{
Chain: stringPtr("1"),
Whitelist: mapStrPtr(map[string]string{
"b": "c",
}),
P2P: &P2PConfig{
Discovery: &P2PDiscovery{
StaticNodes: []string{
"b",
},
},
},
}
expected := &Config{
Chain: stringPtr("1"),
Debug: boolPtr(false),
Whitelist: mapStrPtr(map[string]string{
"a": "b",
"b": "c",
}),
P2P: &P2PConfig{
Discovery: &P2PDiscovery{
StaticNodes: []string{
"a",
"b",
},
},
},
}
assert.NoError(t, c0.Merge(c1))
assert.Equal(t, c0, expected)
}
var dummyEnodeAddr = "enode://0cb82b395094ee4a2915e9714894627de9ed8498fb881cec6db7c65e8b9a5bd7f2f25cc84e71e89d0947e51c76e85d0847de848c7782b13c0255247a6758178c@44.232.55.71:30303"
func TestConfigBootnodesDefault(t *testing.T) {
t.Run("EmptyBootnodes", func(t *testing.T) {
// if no bootnodes are specific, we use the ones from the genesis chain
config := DefaultConfig()
assert.NoError(t, config.loadChain())
cfg, err := config.buildNode()
assert.NoError(t, err)
assert.NotEmpty(t, cfg.P2P.BootstrapNodes)
})
t.Run("NotEmptyBootnodes", func(t *testing.T) {
// if bootnodes specific, DO NOT load the genesis bootnodes
config := DefaultConfig()
config.P2P.Discovery.Bootnodes = []string{dummyEnodeAddr}
cfg, err := config.buildNode()
assert.NoError(t, err)
assert.Len(t, cfg.P2P.BootstrapNodes, 1)
})
}

View file

@ -44,11 +44,6 @@ func (c *Command) Flags() *flagset.Flagset {
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>)",
@ -76,7 +71,7 @@ func (c *Command) Flags() *flagset.Flagset {
f.SliceStringFlag(&flagset.SliceStringFlag{
Name: "txpool.locals",
Usage: "Comma separated accounts to treat as locals (no flush, priority inclusion)",
Value: c.cliConfig.TxPool.Locals,
Value: &c.cliConfig.TxPool.Locals,
})
f.BoolFlag(&flagset.BoolFlag{
Name: "txpool.nolocals",
@ -259,21 +254,32 @@ func (c *Command) Flags() *flagset.Flagset {
Value: c.cliConfig.JsonRPC.IPCPath,
})
f.SliceStringFlag(&flagset.SliceStringFlag{
Name: "rpc.corsdomain",
Name: "jsonrpc.corsdomain",
Usage: "Comma separated list of domains from which to accept cross origin requests (browser enforced)",
Value: c.cliConfig.JsonRPC.Cors,
Value: &c.cliConfig.JsonRPC.Cors,
})
f.SliceStringFlag(&flagset.SliceStringFlag{
Name: "rpc.vhosts",
Name: "jsonrpc.vhosts",
Usage: "Comma separated list of virtual hostnames from which to accept requests (server enforced). Accepts '*' wildcard.",
Value: c.cliConfig.JsonRPC.VHost,
Value: &c.cliConfig.JsonRPC.VHost,
})
f.SliceStringFlag(&flagset.SliceStringFlag{
Name: "jsonrpc.modules",
Usage: "API's offered over the HTTP-RPC interface",
Value: &c.cliConfig.JsonRPC.Modules,
})
// http options
f.BoolFlag(&flagset.BoolFlag{
Name: "http",
Usage: "Enable the HTTP-RPC server",
Value: c.cliConfig.JsonRPC.Http.Enabled,
})
f.StringFlag(&flagset.StringFlag{
Name: "http.addr",
Usage: "HTTP-RPC server listening interface",
Value: c.cliConfig.JsonRPC.Http.Host,
})
f.Uint64Flag(&flagset.Uint64Flag{
Name: "http.port",
Usage: "HTTP-RPC server listening port",
@ -290,6 +296,11 @@ func (c *Command) Flags() *flagset.Flagset {
Usage: "Enable the WS-RPC server",
Value: c.cliConfig.JsonRPC.Ws.Enabled,
})
f.StringFlag(&flagset.StringFlag{
Name: "ws.addr",
Usage: "WS-RPC server listening interface",
Value: c.cliConfig.JsonRPC.Ws.Host,
})
f.Uint64Flag(&flagset.Uint64Flag{
Name: "ws.port",
Usage: "WS-RPC server listening port",
@ -321,7 +332,7 @@ func (c *Command) Flags() *flagset.Flagset {
f.SliceStringFlag(&flagset.SliceStringFlag{
Name: "bootnodes",
Usage: "Comma separated enode URLs for P2P discovery bootstrap",
Value: c.cliConfig.P2P.Discovery.Bootnodes,
Value: &c.cliConfig.P2P.Discovery.Bootnodes,
})
f.Uint64Flag(&flagset.Uint64Flag{
Name: "maxpeers",
@ -412,5 +423,27 @@ func (c *Command) Flags() *flagset.Flagset {
Value: c.cliConfig.Metrics.InfluxDB.Organization,
})
// account
f.SliceStringFlag(&flagset.SliceStringFlag{
Name: "unlock",
Usage: "Comma separated list of accounts to unlock",
Value: &c.cliConfig.Accounts.Unlock,
})
f.StringFlag(&flagset.StringFlag{
Name: "password",
Usage: "Password file to use for non-interactive password input",
Value: c.cliConfig.Accounts.PasswordFile,
})
f.BoolFlag(&flagset.BoolFlag{
Name: "allow-insecure-unlock",
Usage: "Allow insecure account unlocking when account-related RPCs are exposed by http",
Value: c.cliConfig.Accounts.AllowInsecureUnlock,
})
f.BoolFlag(&flagset.BoolFlag{
Name: "lightkdf",
Usage: "Reduce key-derivation RAM & CPU usage at some expense of KDF strength",
Value: c.cliConfig.Accounts.UseLightweightKDF,
})
return f
}

1
go.mod
View file

@ -51,6 +51,7 @@ require (
github.com/mattn/go-colorable v0.1.8
github.com/mattn/go-isatty v0.0.12
github.com/mitchellh/cli v1.1.2
github.com/mitchellh/go-homedir v1.1.0
github.com/naoina/go-stringutil v0.1.0 // indirect
github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416
github.com/olekukonko/tablewriter v0.0.5

3
go.sum
View file

@ -33,7 +33,6 @@ github.com/Azure/go-autorest/autorest/date v0.2.0 h1:yW+Zlqf26583pE43KhfnhFcdmSW
github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g=
github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
github.com/Azure/go-autorest/autorest/mocks v0.3.0 h1:qJumjCaCudz+OcqE9/XtEPfvtOjOmKaui4EOpFI6zZc=
github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM=
github.com/Azure/go-autorest/logger v0.1.0 h1:ruG4BSDXONFRrZZJ2GUXDiUyVpayPmb1GnWeHDdaNKY=
github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
@ -331,6 +330,8 @@ github.com/mitchellh/cli v1.1.2 h1:PvH+lL2B7IQ101xQL63Of8yFS2y+aDlsFcsqNc+u/Kw=
github.com/mitchellh/cli v1.1.2/go.mod h1:6iaV0fGdElS6dPBx0EApTxHrcWvmJphyh2n8YBLPPZ4=
github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ=
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY=
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=