mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-27 23:26:44 +00:00
Finish flags
This commit is contained in:
parent
a9b5e5dfb8
commit
7225eec0f6
9 changed files with 389 additions and 131 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -47,3 +47,4 @@ profile.cov
|
|||
/dashboard/assets/package-lock.json
|
||||
|
||||
**/yarn-error.log
|
||||
./test
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
60
command/flagset/flagset_test.go
Normal file
60
command/flagset/flagset_test.go
Normal 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"})
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
90
command/server/config_test.go
Normal file
90
command/server/config_test.go
Normal 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)
|
||||
})
|
||||
}
|
||||
|
|
@ -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
1
go.mod
|
|
@ -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
3
go.sum
|
|
@ -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=
|
||||
|
|
|
|||
Loading…
Reference in a new issue