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 /dashboard/assets/package-lock.json
**/yarn-error.log **/yarn-error.log
./test

View file

@ -80,7 +80,6 @@ func (f *Flagset) StringFlag(b *StringFlag) {
type IntFlag struct { type IntFlag struct {
Name string Name string
Usage string Usage string
Default int
Value *int Value *int
} }
@ -89,13 +88,12 @@ func (f *Flagset) IntFlag(i *IntFlag) {
Name: i.Name, Name: i.Name,
Usage: i.Usage, 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 { type Uint64Flag struct {
Name string Name string
Usage string Usage string
Default uint64
Value *uint64 Value *uint64
} }
@ -104,17 +102,19 @@ func (f *Flagset) Uint64Flag(i *Uint64Flag) {
Name: i.Name, Name: i.Name,
Usage: i.Usage, 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 { type BigIntFlag struct {
Name string Name string
Usage string Usage string
Default *big.Int
Value *big.Int Value *big.Int
} }
func (b *BigIntFlag) String() string { func (b *BigIntFlag) String() string {
if b.Value == nil {
return ""
}
return b.Value.String() return b.Value.String()
} }
@ -145,15 +145,18 @@ func (f *Flagset) BigIntFlag(b *BigIntFlag) {
type SliceStringFlag struct { type SliceStringFlag struct {
Name string Name string
Usage string Usage string
Value []string Value *[]string
} }
func (i *SliceStringFlag) String() 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 { func (i *SliceStringFlag) Set(value string) error {
i.Value = append(i.Value, value) *i.Value = append(*i.Value, strings.Split(value, ",")...)
return nil return nil
} }
@ -171,25 +174,12 @@ type DurationFlag struct {
Value *time.Duration 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) { func (f *Flagset) DurationFlag(d *DurationFlag) {
f.addFlag(&FlagVar{ f.addFlag(&FlagVar{
Name: d.Name, Name: d.Name,
Usage: d.Usage, Usage: d.Usage,
}) })
f.set.Var(d, d.Name, d.Usage) f.set.DurationVar(d.Value, d.Name, *d.Value, "")
} }
type MapStringFlag struct { type MapStringFlag struct {
@ -199,6 +189,9 @@ type MapStringFlag struct {
} }
func (m *MapStringFlag) String() string { func (m *MapStringFlag) String() string {
if m.Value == nil {
return ""
}
ls := []string{} ls := []string{}
for k, v := range *m.Value { for k, v := range *m.Value {
ls = append(ls, k+"="+v) 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 ( import (
"fmt" "fmt"
"io" "io"
"io/ioutil"
"os" "os"
"os/signal" "os/signal"
"strings" "strings"
"syscall" "syscall"
"time" "time"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/accounts/keystore" "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"
"github.com/ethereum/go-ethereum/eth/tracers" "github.com/ethereum/go-ethereum/eth/tracers"
"github.com/ethereum/go-ethereum/ethstats" "github.com/ethereum/go-ethereum/ethstats"
@ -30,6 +33,9 @@ type Command struct {
// cli configuration // cli configuration
cliConfig *Config cliConfig *Config
// final configuration
config *Config
configFile string configFile string
// bor node // bor node
@ -74,6 +80,7 @@ func (c *Command) Run(args []string) int {
c.UI.Error(err.Error()) c.UI.Error(err.Error())
return 1 return 1
} }
c.config = config
// start the logger // start the logger
setupLogger(*config.LogLevel) setupLogger(*config.LogLevel)
@ -121,7 +128,7 @@ func (c *Command) Run(args []string) int {
} }
// register ethash service // register ethash service
if config.EthStats != nil { if *config.EthStats != "" {
if err := ethstats.New(stack, backend.APIBackend, backend.Engine(), *config.EthStats); err != nil { if err := ethstats.New(stack, backend.APIBackend, backend.Engine(), *config.EthStats); err != nil {
c.UI.Error(err.Error()) c.UI.Error(err.Error())
return 1 return 1
@ -129,13 +136,23 @@ func (c *Command) Run(args []string) int {
} }
// setup account manager (only keystore) // setup account manager (only keystore)
var borKeystore *keystore.KeyStore
{ {
keydir := stack.KeyStoreDir() keydir := stack.KeyStoreDir()
n, p := keystore.StandardScryptN, keystore.StandardScryptP n, p := keystore.StandardScryptN, keystore.StandardScryptP
if *config.UseLightweightKDF { if *config.Accounts.UseLightweightKDF {
n, p = keystore.LightScryptN, keystore.LightScryptP 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) // sealing (if enabled)
@ -159,6 +176,49 @@ func (c *Command) Run(args []string) int {
return c.handleSignals() 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 { func (c *Command) setupMetrics(config *MetricsConfig) error {
metrics.Enabled = *config.Enabled metrics.Enabled = *config.Enabled
metrics.EnabledExpensive = *config.Expensive metrics.EnabledExpensive = *config.Expensive
@ -208,17 +268,19 @@ func (c *Command) handleSignals() int {
gracefulCh := make(chan struct{}) gracefulCh := make(chan struct{})
go func() { go func() {
c.node.Close() c.node.Close()
c.node.Wait()
close(gracefulCh) close(gracefulCh)
}() }()
for i := 10; i > 0; i-- {
select { select {
case <-signalCh: case <-signalCh:
return 1 log.Warn("Already shutting down, interrupt more force stop.", "times", i-1)
case <-time.After(5 * time.Second):
return 1
case <-gracefulCh: case <-gracefulCh:
return 0 return 0
} }
}
return 1
} }
func setupLogger(logLevel string) { func setupLogger(logLevel string) {
@ -239,3 +301,16 @@ func setupLogger(logLevel string) {
} }
log.Root().SetHandler(glogger) 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 package server
import ( import (
"crypto/ecdsa"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"math" "math"
"math/big" "math/big"
"os" "os"
"path/filepath" "path/filepath"
"runtime"
"strconv" "strconv"
"time" "time"
@ -16,7 +16,6 @@ import (
"github.com/ethereum/go-ethereum/command/server/chains" "github.com/ethereum/go-ethereum/command/server/chains"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/fdlimit" "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/downloader"
"github.com/ethereum/go-ethereum/eth/ethconfig" "github.com/ethereum/go-ethereum/eth/ethconfig"
"github.com/ethereum/go-ethereum/eth/gasprice" "github.com/ethereum/go-ethereum/eth/gasprice"
@ -27,6 +26,7 @@ import (
"github.com/ethereum/go-ethereum/p2p/nat" "github.com/ethereum/go-ethereum/p2p/nat"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/imdario/mergo" "github.com/imdario/mergo"
"github.com/mitchellh/go-homedir"
gopsutil "github.com/shirou/gopsutil/mem" gopsutil "github.com/shirou/gopsutil/mem"
) )
@ -60,7 +60,6 @@ type Config struct {
Chain *string Chain *string
Debug *bool Debug *bool
Whitelist *map[string]string Whitelist *map[string]string
UseLightweightKDF *bool
LogLevel *string LogLevel *string
DataDir *string DataDir *string
P2P *P2PConfig P2P *P2PConfig
@ -76,6 +75,7 @@ type Config struct {
Ethstats *string Ethstats *string
Metrics *MetricsConfig Metrics *MetricsConfig
Cache *CacheConfig Cache *CacheConfig
Accounts *AccountsConfig
} }
type P2PConfig struct { type P2PConfig struct {
@ -132,7 +132,6 @@ type JsonRPCConfig struct {
Modules []string Modules []string
VHost []string VHost []string
Cors []string Cors []string
Bind *string
GasCap *uint64 GasCap *uint64
TxFeeCap *float64 TxFeeCap *float64
@ -146,6 +145,7 @@ type APIConfig struct {
Enabled *bool Enabled *bool
Port *uint64 Port *uint64
Prefix *string Prefix *string
Host *string
} }
type GpoConfig struct { type GpoConfig struct {
@ -187,14 +187,20 @@ type CacheConfig struct {
TxLookupLimit *uint64 TxLookupLimit *uint64
} }
type AccountsConfig struct {
Unlock []string
PasswordFile *string
AllowInsecureUnlock *bool
UseLightweightKDF *bool
}
func DefaultConfig() *Config { func DefaultConfig() *Config {
return &Config{ return &Config{
Chain: stringPtr("mainnet"), Chain: stringPtr("mainnet"),
Debug: boolPtr(false), Debug: boolPtr(false),
UseLightweightKDF: boolPtr(false),
Whitelist: mapStrPtr(map[string]string{}), Whitelist: mapStrPtr(map[string]string{}),
LogLevel: stringPtr("INFO"), LogLevel: stringPtr("INFO"),
DataDir: stringPtr(node.DefaultDataDir()), DataDir: stringPtr(defaultDataDir()),
P2P: &P2PConfig{ P2P: &P2PConfig{
MaxPeers: uint64Ptr(30), MaxPeers: uint64Ptr(30),
MaxPendPeers: uint64Ptr(50), MaxPendPeers: uint64Ptr(50),
@ -235,6 +241,7 @@ func DefaultConfig() *Config {
}, },
Sealer: &SealerConfig{ Sealer: &SealerConfig{
Enabled: boolPtr(false), Enabled: boolPtr(false),
Etherbase: stringPtr(""),
GasCeil: uint64Ptr(8000000), GasCeil: uint64Ptr(8000000),
GasPrice: big.NewInt(params.GWei), GasPrice: big.NewInt(params.GWei),
ExtraData: stringPtr(""), ExtraData: stringPtr(""),
@ -247,6 +254,7 @@ func DefaultConfig() *Config {
}, },
JsonRPC: &JsonRPCConfig{ JsonRPC: &JsonRPCConfig{
IPCDisable: boolPtr(false), IPCDisable: boolPtr(false),
IPCPath: stringPtr(""),
Modules: []string{"web3", "net"}, Modules: []string{"web3", "net"},
Cors: []string{"*"}, Cors: []string{"*"},
VHost: []string{"*"}, VHost: []string{"*"},
@ -256,11 +264,13 @@ func DefaultConfig() *Config {
Enabled: boolPtr(false), Enabled: boolPtr(false),
Port: uint64Ptr(8545), Port: uint64Ptr(8545),
Prefix: stringPtr(""), Prefix: stringPtr(""),
Host: stringPtr("localhost"),
}, },
Ws: &APIConfig{ Ws: &APIConfig{
Enabled: boolPtr(false), Enabled: boolPtr(false),
Port: uint64Ptr(8546), Port: uint64Ptr(8546),
Prefix: stringPtr(""), Prefix: stringPtr(""),
Host: stringPtr("localhost"),
}, },
Graphql: &APIConfig{ Graphql: &APIConfig{
Enabled: boolPtr(false), Enabled: boolPtr(false),
@ -295,6 +305,12 @@ func DefaultConfig() *Config {
Preimages: boolPtr(false), Preimages: boolPtr(false),
TxLookupLimit: uint64Ptr(2350000), 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 c.chain = chain
// preload some default values that depend 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 { if c.P2P.Discovery.DNS == nil {
c.P2P.Discovery.DNS = c.chain.DNS 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.GasCeil = *c.Sealer.GasCeil
n.Miner.ExtraData = []byte(*c.Sealer.ExtraData) n.Miner.ExtraData = []byte(*c.Sealer.ExtraData)
if etherbase := c.Sealer.Etherbase; etherbase != nil { if etherbase := *c.Sealer.Etherbase; etherbase != "" {
if !common.IsHexAddress(*etherbase) { if !common.IsHexAddress(etherbase) {
return nil, fmt.Errorf("etherbase is not an address: %s", *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,7 +506,7 @@ func (c *Config) buildNode() (*node.Config, error) {
ipcPath := "" ipcPath := ""
if !*c.JsonRPC.IPCDisable { if !*c.JsonRPC.IPCDisable {
ipcPath = clientIdentifier + ".ipc" ipcPath = clientIdentifier + ".ipc"
if c.JsonRPC.IPCPath != nil { if *c.JsonRPC.IPCPath != "" {
ipcPath = *c.JsonRPC.IPCPath ipcPath = *c.JsonRPC.IPCPath
} }
} }
@ -503,7 +514,8 @@ func (c *Config) buildNode() (*node.Config, error) {
cfg := &node.Config{ cfg := &node.Config{
Name: clientIdentifier, Name: clientIdentifier,
DataDir: *c.DataDir, DataDir: *c.DataDir,
UseLightweightKDF: *c.UseLightweightKDF, UseLightweightKDF: *c.Accounts.UseLightweightKDF,
InsecureUnlockAllowed: *c.Accounts.AllowInsecureUnlock,
Version: params.VersionWithCommit(gitCommit, gitDate), Version: params.VersionWithCommit(gitCommit, gitDate),
IPCPath: ipcPath, IPCPath: ipcPath,
P2P: p2p.Config{ P2P: p2p.Config{
@ -526,11 +538,11 @@ func (c *Config) buildNode() (*node.Config, error) {
// enable jsonrpc endpoints // enable jsonrpc endpoints
{ {
if *c.JsonRPC.Http.Enabled { if *c.JsonRPC.Http.Enabled {
cfg.HTTPHost = *c.JsonRPC.Bind cfg.HTTPHost = *c.JsonRPC.Http.Host
cfg.HTTPPort = int(*c.JsonRPC.Http.Port) cfg.HTTPPort = int(*c.JsonRPC.Http.Port)
} }
if *c.JsonRPC.Ws.Enabled { if *c.JsonRPC.Ws.Enabled {
cfg.WSHost = *c.JsonRPC.Bind cfg.WSHost = *c.JsonRPC.Ws.Host
cfg.WSPort = int(*c.JsonRPC.Ws.Port) cfg.WSPort = int(*c.JsonRPC.Ws.Port)
} }
} }
@ -541,15 +553,13 @@ func (c *Config) buildNode() (*node.Config, error) {
} }
cfg.P2P.NAT = natif 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 // 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 return nil, err
} }
if cfg.P2P.BootstrapNodesV5, err = parseBootnodes(c.P2P.Discovery.BootnodesV5); err != nil { 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 { func (c *Config) Merge(cc ...*Config) error {
for _, elem := range cc { for _, elem := range cc {
if err := mergo.Merge(&c, elem); err != nil { if err := mergo.Merge(c, elem, mergo.WithOverride, mergo.WithAppendSlice); err != nil {
return err return fmt.Errorf("failed to merge configurations: %v", err)
} }
} }
return nil return nil
@ -605,30 +615,24 @@ func parseBootnodes(urls []string) ([]*enode.Node, error) {
return dst, nil return dst, nil
} }
const devP2PKeyPath = "devp2p.key" func defaultDataDir() string {
// Try to place the data folder in the user's home dir
func readDevP2PKey(dataDir string) (*ecdsa.PrivateKey, error) { home, _ := homedir.Dir()
path := filepath.Join(dataDir, devP2PKeyPath) if home == "" {
_, err := os.Stat(path) // we cannot guess a stable location
if err != nil && !os.IsNotExist(err) { return ""
return nil, fmt.Errorf("failed to stat (%s): %v", path, err)
} }
switch runtime.GOOS {
if os.IsNotExist(err) { case "darwin":
priv, err := crypto.GenerateKey() return filepath.Join(home, "Library", "Bor")
if err != nil { case "windows":
return nil, err 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 filepath.Join(appdata, "Bor")
return nil, err default:
return filepath.Join(home, ".bor")
} }
return priv, nil
}
// 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")`, Usage: `Blockchain garbage collection mode ("full", "archive")`,
Value: c.cliConfig.GcMode, 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{ f.MapStringFlag(&flagset.MapStringFlag{
Name: "whitelist", Name: "whitelist",
Usage: "Comma separated block number-to-hash mappings to enforce (<number>=<hash>)", 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{ f.SliceStringFlag(&flagset.SliceStringFlag{
Name: "txpool.locals", Name: "txpool.locals",
Usage: "Comma separated accounts to treat as locals (no flush, priority inclusion)", 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{ f.BoolFlag(&flagset.BoolFlag{
Name: "txpool.nolocals", Name: "txpool.nolocals",
@ -259,21 +254,32 @@ func (c *Command) Flags() *flagset.Flagset {
Value: c.cliConfig.JsonRPC.IPCPath, Value: c.cliConfig.JsonRPC.IPCPath,
}) })
f.SliceStringFlag(&flagset.SliceStringFlag{ 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)", 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{ 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.", 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 // http options
f.BoolFlag(&flagset.BoolFlag{ f.BoolFlag(&flagset.BoolFlag{
Name: "http", Name: "http",
Usage: "Enable the HTTP-RPC server", Usage: "Enable the HTTP-RPC server",
Value: c.cliConfig.JsonRPC.Http.Enabled, 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{ f.Uint64Flag(&flagset.Uint64Flag{
Name: "http.port", Name: "http.port",
Usage: "HTTP-RPC server listening port", Usage: "HTTP-RPC server listening port",
@ -290,6 +296,11 @@ func (c *Command) Flags() *flagset.Flagset {
Usage: "Enable the WS-RPC server", Usage: "Enable the WS-RPC server",
Value: c.cliConfig.JsonRPC.Ws.Enabled, 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{ f.Uint64Flag(&flagset.Uint64Flag{
Name: "ws.port", Name: "ws.port",
Usage: "WS-RPC server listening port", Usage: "WS-RPC server listening port",
@ -321,7 +332,7 @@ func (c *Command) Flags() *flagset.Flagset {
f.SliceStringFlag(&flagset.SliceStringFlag{ f.SliceStringFlag(&flagset.SliceStringFlag{
Name: "bootnodes", Name: "bootnodes",
Usage: "Comma separated enode URLs for P2P discovery bootstrap", 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{ f.Uint64Flag(&flagset.Uint64Flag{
Name: "maxpeers", Name: "maxpeers",
@ -412,5 +423,27 @@ func (c *Command) Flags() *flagset.Flagset {
Value: c.cliConfig.Metrics.InfluxDB.Organization, 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 return f
} }

1
go.mod
View file

@ -51,6 +51,7 @@ require (
github.com/mattn/go-colorable v0.1.8 github.com/mattn/go-colorable v0.1.8
github.com/mattn/go-isatty v0.0.12 github.com/mattn/go-isatty v0.0.12
github.com/mitchellh/cli v1.1.2 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/go-stringutil v0.1.0 // indirect
github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416 github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416
github.com/olekukonko/tablewriter v0.0.5 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/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.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.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/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 h1:ruG4BSDXONFRrZZJ2GUXDiUyVpayPmb1GnWeHDdaNKY=
github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= 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/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 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ=
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= 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 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY=
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= 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= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=