- Replace command line flags with `-c` config flag. Part of #1180 - Convert node config private keys to hex-encoded string versions. - Remove `GenerateConfig` from library. - Remove unused `FirebaseConfig` from library. - Fix loading of `config/status-chain-genesis.json` in non-dev machines.
This commit is contained in:
parent
0480d1a376
commit
3d00af7fa3
|
@ -14,7 +14,7 @@
|
|||
"host": "127.0.0.1",
|
||||
"program": "${workspaceRoot}/cmd/statusd",
|
||||
"env": {},
|
||||
"args": ["-shh", "-les", "-discovery=true"],
|
||||
"args": ["-c", "${workspaceRoot}/config/cli/fleet-eth.test.json"],
|
||||
"showLog": true,
|
||||
"output": "${workspaceRoot}/build/bin/statusd.debug"
|
||||
}
|
||||
|
|
4
Makefile
4
Makefile
|
@ -198,6 +198,10 @@ xgo:
|
|||
go get github.com/karalabe/xgo
|
||||
|
||||
setup: dep-install lint-install mock-install ##@other Prepare project for first build
|
||||
go get -u github.com/kevinburke/go-bindata/go-bindata
|
||||
|
||||
generate: ##@other Regenerate assets and other auto-generated stuff
|
||||
go generate ./static
|
||||
|
||||
mock-install: ##@other Install mocking tools
|
||||
go get -u github.com/golang/mock/mockgen
|
||||
|
|
|
@ -13,24 +13,19 @@ services:
|
|||
ipv4_address: 172.16.238.10
|
||||
|
||||
wnode:
|
||||
image: status-go:latest
|
||||
image: statusteam/status-go:latest
|
||||
command:
|
||||
- statusd
|
||||
- "-les=false"
|
||||
- "-shh"
|
||||
- "-listenaddr=:30303"
|
||||
- "-discovery=true"
|
||||
- "-standalone=false"
|
||||
- "-bootnodes=enode://3f04db09bedc8d85a198de94c84da73aa7782fafc61b28c525ec5cca5a6cc16be7ebbb5cd001780f71d8408d35a2f6326faa1e524d9d8875294172ebec988743@172.16.238.10:30303"
|
||||
- "-http"
|
||||
- "-httphost=0.0.0.0"
|
||||
- "-log=DEBUG"
|
||||
- "-c"
|
||||
- "/config/wnode-config.json"
|
||||
ports:
|
||||
- 8080
|
||||
- 8545
|
||||
- 30303
|
||||
networks:
|
||||
cluster:
|
||||
volumes:
|
||||
- ./wnode-config.json:/config/wnode-config.json:ro
|
||||
- ./.ethereumtest:/data/ethereumtest/:rw
|
||||
depends_on:
|
||||
- bootnode
|
||||
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"NetworkId": 777,
|
||||
"DataDir": "/data/ethereumtest/status",
|
||||
"KeyStoreDir": "/data/ethereumtest/keystore",
|
||||
"NoDiscovery": false,
|
||||
"Rendezvous": false,
|
||||
"ListenAddr": ":30303",
|
||||
"RPCEnabled": true,
|
||||
"HTTPHost": "0.0.0.0",
|
||||
"LogEnabled": true,
|
||||
"LogLevel": "DEBUG",
|
||||
"LogToStderr": true,
|
||||
"ClusterConfig": {
|
||||
"Enabled": false,
|
||||
"BootNodes": [
|
||||
"enode://3f04db09bedc8d85a198de94c84da73aa7782fafc61b28c525ec5cca5a6cc16be7ebbb5cd001780f71d8408d35a2f6326faa1e524d9d8875294172ebec988743@172.16.238.10:30303"
|
||||
]
|
||||
},
|
||||
"WhisperConfig": {
|
||||
"Enabled": true
|
||||
}
|
||||
}
|
|
@ -121,6 +121,11 @@ func (b *StatusBackend) startNode(config *params.NodeConfig) (err error) {
|
|||
}
|
||||
}()
|
||||
|
||||
// Start by validating configuration
|
||||
if err := config.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
services := []gethnode.ServiceConstructor{}
|
||||
services = appendIf(config.UpstreamConfig.Enabled, services, b.rpcFiltersService())
|
||||
|
||||
|
|
|
@ -9,13 +9,15 @@ import (
|
|||
"github.com/status-im/status-go/node"
|
||||
"github.com/status-im/status-go/params"
|
||||
"github.com/status-im/status-go/rpc"
|
||||
"github.com/status-im/status-go/t/utils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestBackendStartNodeConcurrently(t *testing.T) {
|
||||
backend := NewStatusBackend()
|
||||
config := params.NodeConfig{}
|
||||
config, err := utils.MakeTestNodeConfig(params.StatusChainNetworkID)
|
||||
require.NoError(t, err)
|
||||
count := 2
|
||||
resultCh := make(chan error)
|
||||
|
||||
|
@ -24,7 +26,7 @@ func TestBackendStartNodeConcurrently(t *testing.T) {
|
|||
|
||||
for i := 0; i < count; i++ {
|
||||
go func() {
|
||||
resultCh <- backend.StartNode(&config)
|
||||
resultCh <- backend.StartNode(config)
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
|
@ -40,16 +42,17 @@ func TestBackendStartNodeConcurrently(t *testing.T) {
|
|||
require.Contains(t, results, nil)
|
||||
require.Contains(t, results, node.ErrNodeRunning)
|
||||
|
||||
err := backend.StopNode()
|
||||
err = backend.StopNode()
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestBackendRestartNodeConcurrently(t *testing.T) {
|
||||
backend := NewStatusBackend()
|
||||
config := params.NodeConfig{}
|
||||
config, err := utils.MakeTestNodeConfig(params.StatusChainNetworkID)
|
||||
require.NoError(t, err)
|
||||
count := 3
|
||||
|
||||
err := backend.StartNode(&config)
|
||||
err = backend.StartNode(config)
|
||||
require.NoError(t, err)
|
||||
defer func() {
|
||||
require.NoError(t, backend.StopNode())
|
||||
|
@ -72,9 +75,10 @@ func TestBackendRestartNodeConcurrently(t *testing.T) {
|
|||
|
||||
func TestBackendGettersConcurrently(t *testing.T) {
|
||||
backend := NewStatusBackend()
|
||||
config := params.NodeConfig{}
|
||||
config, err := utils.MakeTestNodeConfig(params.StatusChainNetworkID)
|
||||
require.NoError(t, err)
|
||||
|
||||
err := backend.StartNode(&config)
|
||||
err = backend.StartNode(config)
|
||||
require.NoError(t, err)
|
||||
defer func() {
|
||||
require.NoError(t, backend.StopNode())
|
||||
|
@ -123,9 +127,10 @@ func TestBackendGettersConcurrently(t *testing.T) {
|
|||
|
||||
func TestBackendAccountsConcurrently(t *testing.T) {
|
||||
backend := NewStatusBackend()
|
||||
config := params.NodeConfig{}
|
||||
config, err := utils.MakeTestNodeConfig(params.StatusChainNetworkID)
|
||||
require.NoError(t, err)
|
||||
|
||||
err := backend.StartNode(&config)
|
||||
err = backend.StartNode(config)
|
||||
require.NoError(t, err)
|
||||
defer func() {
|
||||
require.NoError(t, backend.StopNode())
|
||||
|
@ -208,10 +213,11 @@ func TestBackendConnectionChangesToOffline(t *testing.T) {
|
|||
|
||||
func TestBackendCallRPCConcurrently(t *testing.T) {
|
||||
backend := NewStatusBackend()
|
||||
config := params.NodeConfig{}
|
||||
config, err := utils.MakeTestNodeConfig(params.StatusChainNetworkID)
|
||||
require.NoError(t, err)
|
||||
count := 3
|
||||
|
||||
err := backend.StartNode(&config)
|
||||
err = backend.StartNode(config)
|
||||
require.NoError(t, err)
|
||||
defer func() {
|
||||
require.NoError(t, backend.StopNode())
|
||||
|
@ -278,7 +284,9 @@ func TestAppStateChange(t *testing.T) {
|
|||
|
||||
func TestBlockedRPCMethods(t *testing.T) {
|
||||
backend := NewStatusBackend()
|
||||
err := backend.StartNode(¶ms.NodeConfig{})
|
||||
config, err := utils.MakeTestNodeConfig(params.StatusChainNetworkID)
|
||||
require.NoError(t, err)
|
||||
err = backend.StartNode(config)
|
||||
require.NoError(t, err)
|
||||
defer func() { require.NoError(t, backend.StopNode()) }()
|
||||
|
||||
|
|
|
@ -192,7 +192,7 @@ func makeNodeConfig() (*params.NodeConfig, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
nodeConfig, err := params.NewNodeConfig(path.Join(workDir, ".ethereum"), "", params.FleetUndefined, uint64(params.RopstenNetworkID))
|
||||
nodeConfig, err := params.NewNodeConfigWithDefaults(path.Join(workDir, ".ethereum"), params.FleetUndefined, uint64(params.RopstenNetworkID))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -120,7 +120,7 @@ func newCommandSet(statusBackend *api.StatusBackend) *commandSet {
|
|||
// StartNode loads the configuration out of the passed string and
|
||||
// starts a node with it.
|
||||
func (cs *commandSet) StartNode(config string) error {
|
||||
nodeConfig, err := params.LoadNodeConfig(config)
|
||||
nodeConfig, err := params.NewConfigFromJSON(config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -180,7 +180,9 @@ func mkConfigJSON(name string) (string, func(), error) {
|
|||
configJSON := `{
|
||||
"NetworkId": ` + strconv.Itoa(params.RopstenNetworkID) + `,
|
||||
"DataDir": "` + tmpDir + `",
|
||||
"KeyStoreDir": "` + tmpDir + `/keystore",
|
||||
"LogLevel": "INFO",
|
||||
"NoDiscovery": true,
|
||||
"RPCEnabled": true
|
||||
}`
|
||||
return configJSON, cleanup, nil
|
||||
|
|
|
@ -1,26 +1,37 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ErrorEmpty returned when value is empty.
|
||||
var ErrorEmpty = errors.New("empty value not allowed")
|
||||
// configFlags represents an array of JSON configuration files passed to a command line utility
|
||||
type configFlags []string
|
||||
|
||||
// StringSlice is a type of flag that allows setting multiple string values.
|
||||
type StringSlice []string
|
||||
|
||||
func (s *StringSlice) String() string {
|
||||
return "string slice"
|
||||
func (f *configFlags) String() string {
|
||||
return strings.Join(*f, ", ")
|
||||
}
|
||||
|
||||
// Set trims space from string and stores it.
|
||||
func (s *StringSlice) Set(value string) error {
|
||||
trimmed := strings.TrimSpace(value)
|
||||
if len(trimmed) == 0 {
|
||||
return ErrorEmpty
|
||||
func (f *configFlags) Set(value string) error {
|
||||
if !path.IsAbs(value) {
|
||||
// Convert to absolute path
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*s = append(*s, trimmed)
|
||||
value = path.Join(cwd, value)
|
||||
}
|
||||
|
||||
// Check that the file exists
|
||||
stat, err := os.Stat(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if stat.IsDir() {
|
||||
return fmt.Errorf("path does not represent a file: %s", value)
|
||||
}
|
||||
*f = append(*f, value)
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -68,7 +68,7 @@ func TestStatusFlag(t *testing.T) {
|
|||
for i, s := range scenarios {
|
||||
msg := fmt.Sprintf("scenario %d", i)
|
||||
|
||||
c, err := params.NewNodeConfig("", "", params.FleetBeta, 0)
|
||||
c, err := params.NewNodeConfig("", params.FleetBeta, 0)
|
||||
require.Nil(t, err, msg)
|
||||
|
||||
c.IPCEnabled = s.ipcEnabled
|
||||
|
|
|
@ -12,14 +12,11 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/status-im/status-go/logutils"
|
||||
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
gethmetrics "github.com/ethereum/go-ethereum/metrics"
|
||||
"github.com/ethereum/go-ethereum/p2p/discv5"
|
||||
"github.com/status-im/status-go/api"
|
||||
"github.com/status-im/status-go/cmd/statusd/debug"
|
||||
"github.com/status-im/status-go/cmd/statusd/topics"
|
||||
"github.com/status-im/status-go/logutils"
|
||||
nodemetrics "github.com/status-im/status-go/metrics/node"
|
||||
"github.com/status-im/status-go/node"
|
||||
"github.com/status-im/status-go/params"
|
||||
|
@ -36,90 +33,59 @@ var (
|
|||
)
|
||||
|
||||
var (
|
||||
clusterConfigFile = flag.String("clusterconfig", "", "Cluster configuration file")
|
||||
nodeKeyFile = flag.String("nodekey", "", "P2P node key file (private key)")
|
||||
dataDir = flag.String("datadir", params.DataDir, "Data directory for the databases and keystore")
|
||||
networkID = flag.Int("networkid", params.RopstenNetworkID, "Network identifier (integer, 1=Homestead, 3=Ropsten, 4=Rinkeby, 777=StatusChain)")
|
||||
lesEnabled = flag.Bool("les", false, "Enable LES protocol")
|
||||
whisperEnabled = flag.Bool("shh", false, "Enable Whisper protocol")
|
||||
statusService = flag.String("status", "", `Enable StatusService, possible values: "ipc", "http"`)
|
||||
debugAPI = flag.Bool("debug", false, `Enable debug API endpoints under "debug_" namespace`)
|
||||
swarmEnabled = flag.Bool("swarm", false, "Enable Swarm protocol")
|
||||
maxPeers = flag.Int("maxpeers", 25, "maximum number of p2p peers (including all protocols)")
|
||||
httpEnabled = flag.Bool("http", false, "Enable HTTP RPC endpoint")
|
||||
httpHost = flag.String("httphost", "127.0.0.1", "HTTP RPC host of the listening socket")
|
||||
httpPort = flag.Int("httpport", params.HTTPPort, "HTTP RPC server's listening port")
|
||||
httpModules = flag.String("httpmodules", params.APIModules, "Comma separated list of HTTP RPC APIs")
|
||||
ipcEnabled = flag.Bool("ipc", false, "Enable IPC RPC endpoint")
|
||||
ipcFile = flag.String("ipcfile", "", "Set IPC file path")
|
||||
configFiles configFlags
|
||||
logLevel = flag.String("log", "", `Log level, one of: "ERROR", "WARN", "INFO", "DEBUG", and "TRACE"`)
|
||||
cliEnabled = flag.Bool("cli", false, "Enable debugging CLI server")
|
||||
cliPort = flag.String("cliport", debug.CLIPort, "CLI server's listening port")
|
||||
cliPort = flag.String("cli-port", debug.CLIPort, "CLI server's listening port")
|
||||
pprofEnabled = flag.Bool("pprof", false, "Enable runtime profiling via pprof")
|
||||
pprofPort = flag.Int("pprofport", 52525, "Port for runtime profiling via pprof")
|
||||
logLevel = flag.String("log", "INFO", `Log level, one of: "ERROR", "WARN", "INFO", "DEBUG", and "TRACE"`)
|
||||
logFile = flag.String("logfile", "", "Path to the log file")
|
||||
pprofPort = flag.Int("pprof-port", 52525, "Port for runtime profiling via pprof")
|
||||
logWithoutColors = flag.Bool("log-without-color", false, "Disables log colors")
|
||||
version = flag.Bool("version", false, "Print version")
|
||||
|
||||
listenAddr = flag.String("listenaddr", ":30303", "IP address and port of this node (e.g. 127.0.0.1:30303)")
|
||||
advertiseAddr = flag.String("advertiseaddr", "", "IP address the node wants to reached with (useful if floating IP is used)")
|
||||
fleet = flag.String("fleet", params.FleetBeta, "Name of the fleet like 'eth.staging' (default to 'eth.beta')")
|
||||
standalone = flag.Bool("standalone", false, "Don't actively connect to peers, wait for incoming connections")
|
||||
bootnodes = flag.String("bootnodes", "", "A list of bootnodes separated by comma")
|
||||
discoveryFlag = flag.Bool("discovery", false, "Enable discovery protocol")
|
||||
rendezvous = flag.Bool("rendezvous", false, "Enable rendezvous protocol")
|
||||
rendezvousNodes = StringSlice{}
|
||||
version = flag.Bool("version", false, "Print version and dump configuration")
|
||||
|
||||
// don't change the name of this flag, https://github.com/ethereum/go-ethereum/blob/master/metrics/metrics.go#L41
|
||||
metrics = flag.Bool("metrics", false, "Expose ethereum metrics with debug_metrics jsonrpc call.")
|
||||
// shh stuff
|
||||
passwordFile = flag.String("shh.passwordfile", "", "Password file (password is used for symmetric encryption)")
|
||||
minPow = flag.Float64("shh.pow", params.WhisperMinimumPoW, "PoW for messages to be added to queue, in float format")
|
||||
ttl = flag.Int("shh.ttl", params.WhisperTTL, "Time to live for messages, in seconds")
|
||||
lightClient = flag.Bool("shh.lightclient", false, "Start with empty bloom filter, and don't forward messages")
|
||||
|
||||
// MailServer
|
||||
enableMailServer = flag.Bool("shh.mailserver", false, "Delivers expired messages on demand")
|
||||
|
||||
// Push Notification
|
||||
firebaseAuth = flag.String("shh.firebaseauth", "", "FCM Authorization Key used for sending Push Notifications")
|
||||
metrics = flag.Bool("metrics", false, "Expose ethereum metrics with debug_metrics jsonrpc call")
|
||||
|
||||
syncAndExit = flag.Int("sync-and-exit", -1, "Timeout in minutes for blockchain sync and exit, zero means no timeout unless sync is finished")
|
||||
|
||||
ntpSyncEnabled = flag.Bool("ntp", true, "Enable/disable whisper NTP synchronization")
|
||||
|
||||
// Topics that will be search and registered by discovery v5.
|
||||
searchTopics = topics.TopicLimitsFlag{}
|
||||
registerTopics = topics.TopicFlag{}
|
||||
)
|
||||
|
||||
// All general log messages in this package should be routed through this logger.
|
||||
var logger = log.New("package", "status-go/cmd/statusd")
|
||||
|
||||
func init() {
|
||||
flag.Var(&searchTopics, "topic.search", "Topic that will be searched in discovery v5, e.g (mailserver=1,1)")
|
||||
flag.Var(®isterTopics, "topic.register", "Topic that will be registered using discovery v5.")
|
||||
flag.Var(&rendezvousNodes, "rendezvous-node", "Rendezvous server.")
|
||||
flag.Var(&configFiles, "c", "JSON configuration file(s). Multiple configuration files can be specified, and will be merged in occurrence order")
|
||||
|
||||
colors := terminal.IsTerminal(int(os.Stdin.Fd()))
|
||||
if err := logutils.OverrideRootLog(true, "ERROR", "", colors); err != nil {
|
||||
stdlog.Fatalf("Error initializing logger: %v", err)
|
||||
}
|
||||
|
||||
flag.Usage = printUsage
|
||||
flag.Parse()
|
||||
if flag.NArg() > 0 {
|
||||
stdlog.Printf("Extra args in command line: %v", flag.Args())
|
||||
printUsage()
|
||||
logger.Error("Extra args in command line: %v", flag.Args())
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
colors := !(*logWithoutColors) && terminal.IsTerminal(int(os.Stdin.Fd()))
|
||||
if err := logutils.OverrideRootLog(logEnabled(), *logLevel, *logFile, colors); err != nil {
|
||||
stdlog.Fatalf("Error initializing logger: %s", err)
|
||||
config, err := params.NewNodeConfigWithDefaults("statusd-data", params.FleetBeta, params.RopstenNetworkID)
|
||||
if err == nil {
|
||||
err = parseConfig(configFiles, config)
|
||||
}
|
||||
if err != nil {
|
||||
printUsage()
|
||||
if err != nil {
|
||||
logger.Error(err.Error())
|
||||
}
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
config, err := makeNodeConfig()
|
||||
if err != nil {
|
||||
stdlog.Fatalf("Making config failed, %s", err)
|
||||
colors := !(*logWithoutColors) && terminal.IsTerminal(int(os.Stdin.Fd()))
|
||||
if err = logutils.OverrideRootLog(logEnabled(config), config.LogLevel, config.LogFile, colors); err != nil {
|
||||
stdlog.Fatalf("Error initializing logger: %v", err)
|
||||
}
|
||||
|
||||
// We want statusd to be distinct from StatusIM client.
|
||||
config.Name = serverClientName
|
||||
|
||||
|
@ -206,95 +172,15 @@ func startCollectingNodeMetrics(interruptCh <-chan struct{}, statusNode *node.St
|
|||
<-interruptCh
|
||||
}
|
||||
|
||||
func logEnabled() bool {
|
||||
return *logLevel != "" || *logFile != ""
|
||||
func logEnabled(config *params.NodeConfig) bool {
|
||||
return config.LogLevel != "" || config.LogFile != ""
|
||||
}
|
||||
|
||||
// makeNodeConfig parses incoming CLI options and returns node configuration object
|
||||
func makeNodeConfig() (*params.NodeConfig, error) {
|
||||
nodeConfig, err := params.NewNodeConfig(*dataDir, *clusterConfigFile, *fleet, uint64(*networkID))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
nodeConfig.ListenAddr = *listenAddr
|
||||
nodeConfig.AdvertiseAddr = *advertiseAddr
|
||||
|
||||
// TODO(divan): move this logic into params package
|
||||
if *nodeKeyFile != "" {
|
||||
nodeConfig.NodeKeyFile = *nodeKeyFile
|
||||
}
|
||||
|
||||
if *logLevel != "" {
|
||||
nodeConfig.LogLevel = *logLevel
|
||||
}
|
||||
|
||||
if *logFile != "" {
|
||||
nodeConfig.LogFile = *logFile
|
||||
}
|
||||
|
||||
nodeConfig.LogEnabled = logEnabled()
|
||||
|
||||
nodeConfig.RPCEnabled = *httpEnabled
|
||||
nodeConfig.WhisperConfig.Enabled = *whisperEnabled
|
||||
nodeConfig.MaxPeers = *maxPeers
|
||||
|
||||
nodeConfig.HTTPHost = *httpHost
|
||||
nodeConfig.HTTPPort = *httpPort
|
||||
nodeConfig.APIModules = *httpModules
|
||||
nodeConfig.IPCEnabled = *ipcEnabled
|
||||
|
||||
if *ipcFile != "" {
|
||||
nodeConfig.IPCEnabled = true
|
||||
nodeConfig.IPCFile = *ipcFile
|
||||
}
|
||||
|
||||
nodeConfig.LightEthConfig.Enabled = *lesEnabled
|
||||
nodeConfig.SwarmConfig.Enabled = *swarmEnabled
|
||||
|
||||
if *standalone {
|
||||
nodeConfig.ClusterConfig.Enabled = false
|
||||
nodeConfig.ClusterConfig.BootNodes = nil
|
||||
}
|
||||
|
||||
if len(rendezvousNodes) > 0 {
|
||||
nodeConfig.ClusterConfig.RendezvousNodes = []string(rendezvousNodes)
|
||||
}
|
||||
nodeConfig.NoDiscovery = !(*discoveryFlag)
|
||||
nodeConfig.Rendezvous = *rendezvous
|
||||
nodeConfig.RequireTopics = map[discv5.Topic]params.Limits(searchTopics)
|
||||
nodeConfig.RegisterTopics = []discv5.Topic(registerTopics)
|
||||
|
||||
// Even if standalone is true and discovery is disabled,
|
||||
// it's possible to use bootnodes.
|
||||
if *bootnodes != "" {
|
||||
nodeConfig.ClusterConfig.BootNodes = strings.Split(*bootnodes, ",")
|
||||
}
|
||||
|
||||
if nodeConfig, err = configureStatusService(*statusService, nodeConfig); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
nodeConfig.DebugAPIEnabled = *debugAPI
|
||||
if nodeConfig.DebugAPIEnabled {
|
||||
nodeConfig.AddAPIModule("debug")
|
||||
}
|
||||
|
||||
if *whisperEnabled {
|
||||
return whisperConfig(nodeConfig)
|
||||
}
|
||||
|
||||
// RPC configuration
|
||||
if !*httpEnabled {
|
||||
nodeConfig.HTTPHost = "" // HTTP RPC is disabled
|
||||
}
|
||||
|
||||
return nodeConfig, nil
|
||||
}
|
||||
|
||||
var errStatusServiceRequiresIPC = errors.New("to enable the StatusService on IPC, -ipc flag must be set")
|
||||
var errStatusServiceRequiresHTTP = errors.New("to enable the StatusService on HTTP, -http flag must be set")
|
||||
var errStatusServiceInvalidFlag = errors.New("-status flag valid values are: ipc, http")
|
||||
var (
|
||||
errStatusServiceRequiresIPC = errors.New("to enable the StatusService on IPC, -ipc flag must be set")
|
||||
errStatusServiceRequiresHTTP = errors.New("to enable the StatusService on HTTP, -http flag must be set")
|
||||
errStatusServiceInvalidFlag = errors.New("-status flag valid values are: ipc, http")
|
||||
)
|
||||
|
||||
func configureStatusService(flagValue string, nodeConfig *params.NodeConfig) (*params.NodeConfig, error) {
|
||||
switch flagValue {
|
||||
|
@ -318,6 +204,19 @@ func configureStatusService(flagValue string, nodeConfig *params.NodeConfig) (*p
|
|||
return nodeConfig, nil
|
||||
}
|
||||
|
||||
func parseConfig(configFiles configFlags, config *params.NodeConfig) error {
|
||||
// Merge specified configuration files, in order
|
||||
if err := params.LoadConfigFromFiles(configFiles, config); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if *logLevel != "" {
|
||||
config.LogLevel = *logLevel
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// printVersion prints verbose output about version and config.
|
||||
func printVersion(config *params.NodeConfig, buildStamp string) {
|
||||
fmt.Println(strings.Title(config.Name))
|
||||
|
@ -333,7 +232,6 @@ func printVersion(config *params.NodeConfig, buildStamp string) {
|
|||
fmt.Printf("GOPATH=%s\n", os.Getenv("GOPATH"))
|
||||
fmt.Printf("GOROOT=%s\n", runtime.GOROOT())
|
||||
|
||||
config.LightEthConfig.Genesis = "SKIP"
|
||||
fmt.Println("Loaded Config: ", config)
|
||||
}
|
||||
|
||||
|
@ -341,11 +239,10 @@ func printUsage() {
|
|||
usage := `
|
||||
Usage: statusd [options]
|
||||
Examples:
|
||||
statusd # run status node with defaults
|
||||
statusd -networkid 4 # run node on Rinkeby network
|
||||
statusd -datadir /dir # specify different dir for data
|
||||
statusd -ipc # enable IPC for usage with "geth attach"
|
||||
statusd -cli # enable connection by statusd-cli on default port
|
||||
statusd -c ./default.json # run node with configuration specified in ./default.json file
|
||||
statusd -c ./default.json -c ./standalone.json # run node with configuration specified in ./default.json file, after merging ./standalone.json file
|
||||
statusd -c ./default.json -metrics # run node with configuration specified in ./default.json file, and expose ethereum metrics with debug_metrics jsonrpc call
|
||||
statusd -c ./default.json -cli # run node with configuration specified in ./default.json file, and enable connection by statusd-cli on default port
|
||||
|
||||
Options:
|
||||
`
|
||||
|
|
|
@ -1,60 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/status-im/status-go/params"
|
||||
)
|
||||
|
||||
// whisperConfig creates node configuration object from flags
|
||||
func whisperConfig(nodeConfig *params.NodeConfig) (*params.NodeConfig, error) {
|
||||
whisperConfig := nodeConfig.WhisperConfig
|
||||
|
||||
whisperConfig.Enabled = true
|
||||
whisperConfig.EnableMailServer = *enableMailServer
|
||||
whisperConfig.LightClient = *lightClient
|
||||
whisperConfig.MinimumPoW = *minPow
|
||||
whisperConfig.TTL = *ttl
|
||||
whisperConfig.EnableNTPSync = *ntpSyncEnabled
|
||||
|
||||
if whisperConfig.EnableMailServer {
|
||||
if *passwordFile == "" {
|
||||
return nil, errors.New("passwordfile should be specified if MailServer is enabled")
|
||||
}
|
||||
|
||||
password, err := readFile(*passwordFile)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("password file: %v", err)
|
||||
}
|
||||
|
||||
whisperConfig.MailServerPassword = string(password)
|
||||
}
|
||||
|
||||
// firebase configuration
|
||||
firebaseConfig := whisperConfig.FirebaseConfig
|
||||
firebaseConfig.AuthorizationKeyFile = *firebaseAuth
|
||||
if firebaseConfig.AuthorizationKeyFile != "" {
|
||||
if _, err := firebaseConfig.ReadAuthorizationKeyFile(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return nodeConfig, nil
|
||||
}
|
||||
|
||||
func readFile(path string) ([]byte, error) {
|
||||
data, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data = bytes.TrimRight(data, "\n")
|
||||
|
||||
if len(data) == 0 {
|
||||
return nil, errors.New("file is empty")
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
{
|
||||
"NoDiscovery": true,
|
||||
"Rendezvous": true,
|
||||
"ClusterConfig": {
|
||||
"Enabled": true,
|
||||
"Fleet": "eth.beta",
|
||||
"BootNodes": [
|
||||
"enode://436cc6f674928fdc9a9f7990f2944002b685d1c37f025c1be425185b5b1f0900feaf1ccc2a6130268f9901be4a7d252f37302c8335a2c1a62736e9232691cc3a@174.138.105.243:30404",
|
||||
"enode://5395aab7833f1ecb671b59bf0521cf20224fe8162fc3d2675de4ee4d5636a75ec32d13268fc184df8d1ddfa803943906882da62a4df42d4fccf6d17808156a87@206.189.243.57:30404",
|
||||
"enode://7427dfe38bd4cf7c58bb96417806fab25782ec3e6046a8053370022cbaa281536e8d64ecd1b02e1f8f72768e295d06258ba43d88304db068e6f2417ae8bcb9a6@104.154.88.123:30404",
|
||||
"enode://ebefab39b69bbbe64d8cd86be765b3be356d8c4b24660f65d493143a0c44f38c85a257300178f7845592a1b0332811542e9a58281c835babdd7535babb64efc1@35.202.99.224:30404"
|
||||
],
|
||||
"TrustedMailServers": [
|
||||
"enode://c42f368a23fa98ee546fd247220759062323249ef657d26d357a777443aec04db1b29a3a22ef3e7c548e18493ddaf51a31b0aed6079bd6ebe5ae838fcfaf3a49@206.189.243.162:30504",
|
||||
"enode://7aa648d6e855950b2e3d3bf220c496e0cae4adfddef3e1e6062e6b177aec93bc6cdcf1282cb40d1656932ebfdd565729da440368d7c4da7dbd4d004b1ac02bf8@206.189.243.169:30504",
|
||||
"enode://8a64b3c349a2e0ef4a32ea49609ed6eb3364be1110253c20adc17a3cebbc39a219e5d3e13b151c0eee5d8e0f9a8ba2cd026014e67b41a4ab7d1d5dd67ca27427@206.189.243.168:30504",
|
||||
"enode://7de99e4cb1b3523bd26ca212369540646607c721ad4f3e5c821ed9148150ce6ce2e72631723002210fac1fd52dfa8bbdf3555e05379af79515e1179da37cc3db@35.188.19.210:30504",
|
||||
"enode://015e22f6cd2b44c8a51bd7a23555e271e0759c7d7f52432719665a74966f2da456d28e154e836bee6092b4d686fe67e331655586c57b718be3997c1629d24167@35.226.21.19:30504",
|
||||
"enode://531e252ec966b7e83f5538c19bf1cde7381cc7949026a6e499b6e998e695751aadf26d4c98d5a4eabfb7cefd31c3c88d600a775f14ed5781520a88ecd25da3c6@35.225.227.79:30504"
|
||||
],
|
||||
"StaticNodes": [
|
||||
"enode://a6a2a9b3a7cbb0a15da74301537ebba549c990e3325ae78e1272a19a3ace150d03c184b8ac86cc33f1f2f63691e467d49308f02d613277754c4dccd6773b95e8@206.189.243.176:30304",
|
||||
"enode://207e53d9bf66be7441e3daba36f53bfbda0b6099dba9a865afc6260a2d253fb8a56a72a48598a4f7ba271792c2e4a8e1a43aaef7f34857f520c8c820f63b44c8@35.224.15.65:30304"
|
||||
],
|
||||
"RendezvousNodes": [
|
||||
"/ip4/174.138.105.243/tcp/30703/ethv4/16Uiu2HAmRHPzF3rQg55PgYPcQkyvPVH9n2hWsYPhUJBZ6kVjJgdV",
|
||||
"/ip4/206.189.243.57/tcp/30703/ethv4/16Uiu2HAmLqTXuY4Sb6G28HNooaFUXUKzpzKXCcgyJxgaEE2i5vnf"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
"NoDiscovery": true,
|
||||
"Rendezvous": true,
|
||||
"ClusterConfig": {
|
||||
"Enabled": true,
|
||||
"Fleet": "eth.staging",
|
||||
"BootNodes": [
|
||||
"enode://10a78c17929a7019ef4aa2249d7302f76ae8a06f40b2dc88b7b31ebff4a623fbb44b4a627acba296c1ced3775d91fbe18463c15097a6a36fdb2c804ff3fc5b35@35.238.97.234:30404",
|
||||
"enode://f79fb3919f72ca560ad0434dcc387abfe41e0666201ebdada8ede0462454a13deb05cda15f287d2c4bd85da81f0eb25d0a486bbbc8df427b971ac51533bd00fe@174.138.107.239:30404"
|
||||
],
|
||||
"TrustedMailServers": [
|
||||
"enode://69f72baa7f1722d111a8c9c68c39a31430e9d567695f6108f31ccb6cd8f0adff4991e7fdca8fa770e75bc8a511a87d24690cbc80e008175f40c157d6f6788d48@206.189.240.16:30504",
|
||||
"enode://e4fc10c1f65c8aed83ac26bc1bfb21a45cc1a8550a58077c8d2de2a0e0cd18e40fd40f7e6f7d02dc6cd06982b014ce88d6e468725ffe2c138e958788d0002a7f@35.239.193.41:30504"
|
||||
],
|
||||
"StaticNodes": [
|
||||
"enode://914c0b30f27bab30c1dfd31dad7652a46fda9370542aee1b062498b1345ee0913614b8b9e3e84622e84a7203c5858ae1d9819f63aece13ee668e4f6668063989@167.99.19.148:30305",
|
||||
"enode://2d897c6e846949f9dcf10279f00e9b8325c18fe7fa52d658520ad7be9607c83008b42b06aefd97cfe1fdab571f33a2a9383ff97c5909ed51f63300834913237e@35.192.0.86:30305"
|
||||
],
|
||||
"RendezvousNodes": [
|
||||
"/ip4/174.138.107.239/tcp/30703/ethv4/16Uiu2HAkyJHeetQ4DNpd4NZ2ntzxMo25zcdpvGQRqkD5pB9BE6RU",
|
||||
"/ip4/35.238.97.234/tcp/30703/ethv4/16Uiu2HAm1sVyXmkMNjdeDWqK2urbyC3oBHi8MDpCdYkns1nYafqz"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
"NoDiscovery": true,
|
||||
"Rendezvous": false,
|
||||
"LogLevel": "DEBUG",
|
||||
"ClusterConfig": {
|
||||
"Enabled": true,
|
||||
"Fleet": "eth.test",
|
||||
"BootNodes": [
|
||||
"enode://daae2e72820e86e942fa2a8aa7d6e9954d4043a753483d8bd338e16be82cf962392d5c0e1ae57c3d793c3d3dddd8fd58339262e4234dc966f953cd73b535f5fa@47.52.188.149:30404",
|
||||
"enode://9e0988575eb7717c25dea72fd11c7b37767dc09c1a7686f7c2ec577d308d24b377ceb675de4317474a1a870e47882732967f4fa785b02ba95d669b31d464dec0@206.189.243.164:30404",
|
||||
"enode://c1e5018887c863d64e431b69bf617561087825430e4401733f5ba77c70db14236df381fefb0ebe1ac42294b9e261bbe233dbdb83e32c586c66ae26c8de70cb4c@35.188.168.137:30404"
|
||||
],
|
||||
"TrustedMailServers": [
|
||||
"enode://954c06603a6e755bffe9992615f4755848bda9aadda74d920aa31d1d8e4f6022dc556dca6768f8a0f9459f57b729509db3c8b3bb80acfbd8a2123087f6cbd7bd@47.52.188.196:30504",
|
||||
"enode://e4865fe6c2a9c1a563a6447990d8e9ce672644ae3e08277ce38ec1f1b690eef6320c07a5d60c3b629f5d4494f93d6b86a745a0bf64ab295bbf6579017adc6ed8@206.189.243.161:30504",
|
||||
"enode://707e57453acd3e488c44b9d0e17975371e2f8fb67525eae5baca9b9c8e06c86cde7c794a6c2e36203bf9f56cae8b0e50f3b33c4c2b694a7baeea1754464ce4e3@35.192.229.172:30504"
|
||||
],
|
||||
"StaticNodes": [
|
||||
"enode://ad38f94030a846cc7005b7a1f3b6b01bf4ef59d34e8d3d6f4d12df23d14ba8656702a435d34cf4df3b412c0c1923df5adcce8461321a0d8ffb9435b26e572c2a@47.52.255.194:30305",
|
||||
"enode://1d193635e015918fb85bbaf774863d12f65d70c6977506187ef04420d74ec06c9e8f0dcb57ea042f85df87433dab17a1260ed8dde1bdf9d6d5d2de4b7bf8e993@206.189.243.163:30305",
|
||||
"enode://f593a27731bc0f8eb088e2d39222c2d59dfb9bf0b3950d7a828d51e8ab9e08fffbd9916a82fd993c1a080c57c2bd70ed6c36f489a969de697aff93088dbee1a9@35.194.31.108:30305"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"LightEthConfig": {
|
||||
"Enabled": true
|
||||
}
|
||||
}
|
|
@ -22,26 +22,10 @@ import (
|
|||
// All general log messages in this package should be routed through this logger.
|
||||
var logger = log.New("package", "status-go/lib")
|
||||
|
||||
//GenerateConfig for status node
|
||||
//export GenerateConfig
|
||||
func GenerateConfig(datadir *C.char, fleet *C.char, networkID C.int) *C.char {
|
||||
config, err := params.NewNodeConfig(C.GoString(datadir), "", C.GoString(fleet), uint64(networkID))
|
||||
if err != nil {
|
||||
return makeJSONResponse(err)
|
||||
}
|
||||
|
||||
outBytes, err := json.Marshal(config)
|
||||
if err != nil {
|
||||
return makeJSONResponse(err)
|
||||
}
|
||||
|
||||
return C.CString(string(outBytes))
|
||||
}
|
||||
|
||||
//StartNode - start Status node
|
||||
//export StartNode
|
||||
func StartNode(configJSON *C.char) *C.char {
|
||||
config, err := params.LoadNodeConfig(C.GoString(configJSON))
|
||||
config, err := params.NewConfigFromJSON(C.GoString(configJSON))
|
||||
if err != nil {
|
||||
return makeJSONResponse(err)
|
||||
}
|
||||
|
@ -67,7 +51,7 @@ func StopNode() *C.char {
|
|||
func ValidateNodeConfig(configJSON *C.char) *C.char {
|
||||
var resp APIDetailedResponse
|
||||
|
||||
_, err := params.LoadNodeConfig(C.GoString(configJSON))
|
||||
_, err := params.NewConfigFromJSON(C.GoString(configJSON))
|
||||
|
||||
// Convert errors to APIDetailedResponse
|
||||
switch err := err.(type) {
|
||||
|
|
|
@ -9,6 +9,7 @@ package main
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
|
@ -23,9 +24,9 @@ func TestExportedAPI(t *testing.T) {
|
|||
|
||||
func TestValidateNodeConfig(t *testing.T) {
|
||||
noErrorsCallback := func(resp APIDetailedResponse) {
|
||||
assert.Empty(t, resp.FieldErrors)
|
||||
assert.Empty(t, resp.Message)
|
||||
require.True(t, resp.Status, "expected status equal true")
|
||||
require.Empty(t, resp.FieldErrors)
|
||||
require.Empty(t, resp.Message)
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
|
@ -37,7 +38,15 @@ func TestValidateNodeConfig(t *testing.T) {
|
|||
Name: "response for valid config",
|
||||
Config: `{
|
||||
"NetworkId": 1,
|
||||
"DataDir": "/tmp"
|
||||
"DataDir": "/tmp",
|
||||
"KeyStoreDir": "/tmp",
|
||||
"NoDiscovery": true,
|
||||
"WhisperConfig": {
|
||||
"Enabled": true,
|
||||
"EnableMailServer": true,
|
||||
"DataDir": "/tmp",
|
||||
"MailServerPassword": "status-offline-inbox"
|
||||
}
|
||||
}`,
|
||||
Callback: noErrorsCallback,
|
||||
},
|
||||
|
@ -49,6 +58,72 @@ func TestValidateNodeConfig(t *testing.T) {
|
|||
require.Contains(t, resp.Message, "validation: invalid character '}'")
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "response for config missing DataDir",
|
||||
Config: `{
|
||||
"NetworkId": 3,
|
||||
"KeyStoreDir": "/tmp",
|
||||
"NoDiscovery": true,
|
||||
"WhisperConfig": {
|
||||
"Enabled": false
|
||||
}
|
||||
}`,
|
||||
Callback: func(resp APIDetailedResponse) {
|
||||
require.False(t, resp.Status)
|
||||
require.Equal(t, 1, len(resp.FieldErrors))
|
||||
require.Equal(t, resp.FieldErrors[0].Parameter, "NodeConfig.DataDir")
|
||||
require.Contains(t, resp.Message, "validation: validation failed")
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "response for config missing KeyStoreDir",
|
||||
Config: `{
|
||||
"NetworkId": 3,
|
||||
"DataDir": "/tmp",
|
||||
"NoDiscovery": true,
|
||||
"WhisperConfig": {
|
||||
"Enabled": false
|
||||
}
|
||||
}`,
|
||||
Callback: func(resp APIDetailedResponse) {
|
||||
require.False(t, resp.Status)
|
||||
require.Equal(t, 1, len(resp.FieldErrors))
|
||||
require.Equal(t, resp.FieldErrors[0].Parameter, "NodeConfig.KeyStoreDir")
|
||||
require.Contains(t, resp.Message, "validation: validation failed")
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "response for config missing WhisperConfig.DataDir",
|
||||
Config: `{
|
||||
"NetworkId": 3,
|
||||
"DataDir": "/tmp",
|
||||
"KeyStoreDir": "/tmp",
|
||||
"NoDiscovery": true,
|
||||
"WhisperConfig": {
|
||||
"Enabled": true,
|
||||
"EnableMailServer": true
|
||||
}
|
||||
}`,
|
||||
Callback: func(resp APIDetailedResponse) {
|
||||
require.False(t, resp.Status)
|
||||
require.Empty(t, resp.FieldErrors)
|
||||
require.Contains(t, resp.Message, "WhisperConfig.DataDir must be specified when WhisperConfig.EnableMailServer is true")
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "response for config missing WhisperConfig.DataDir with WhisperConfig.EnableMailServer set to false",
|
||||
Config: `{
|
||||
"NetworkId": 3,
|
||||
"DataDir": "/tmp",
|
||||
"KeyStoreDir": "/tmp",
|
||||
"NoDiscovery": true,
|
||||
"WhisperConfig": {
|
||||
"Enabled": true,
|
||||
"EnableMailServer": false
|
||||
}
|
||||
}`,
|
||||
Callback: noErrorsCallback,
|
||||
},
|
||||
{
|
||||
Name: "response for config with multiple errors",
|
||||
Config: `{}`,
|
||||
|
@ -56,11 +131,12 @@ func TestValidateNodeConfig(t *testing.T) {
|
|||
required := map[string]string{
|
||||
"NodeConfig.NetworkID": "required",
|
||||
"NodeConfig.DataDir": "required",
|
||||
"NodeConfig.KeyStoreDir": "required",
|
||||
}
|
||||
|
||||
require.False(t, resp.Status)
|
||||
require.Contains(t, resp.Message, "validation: validation failed")
|
||||
require.Equal(t, 2, len(resp.FieldErrors))
|
||||
require.Equal(t, 3, len(resp.FieldErrors), resp.FieldErrors)
|
||||
|
||||
for _, err := range resp.FieldErrors {
|
||||
require.Contains(t, required, err.Parameter)
|
||||
|
|
|
@ -12,10 +12,10 @@ package main
|
|||
import "C"
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"math/big"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strconv"
|
||||
|
@ -26,13 +26,10 @@ import (
|
|||
"github.com/ethereum/go-ethereum/accounts/keystore"
|
||||
gethcommon "github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
gethparams "github.com/ethereum/go-ethereum/params"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/status-im/status-go/account"
|
||||
"github.com/status-im/status-go/params"
|
||||
"github.com/status-im/status-go/signal"
|
||||
. "github.com/status-im/status-go/t/utils" //nolint: golint
|
||||
"github.com/status-im/status-go/transactions"
|
||||
|
@ -55,9 +52,18 @@ func init() {
|
|||
nodeConfigJSON = `{
|
||||
"NetworkId": ` + strconv.Itoa(GetNetworkID()) + `,
|
||||
"DataDir": "` + testChainDir + `",
|
||||
"KeyStoreDir": "` + filepath.Join(testChainDir, "keystore") + `",
|
||||
"HTTPPort": ` + strconv.Itoa(TestConfig.Node.HTTPPort) + `,
|
||||
"WSPort": ` + strconv.Itoa(TestConfig.Node.WSPort) + `,
|
||||
"LogLevel": "INFO"
|
||||
"LogLevel": "INFO",
|
||||
"NoDiscovery": true,
|
||||
"LightEthConfig": {
|
||||
"Enabled": true
|
||||
},
|
||||
"WhisperConfig": {
|
||||
"Enabled": true,
|
||||
"DataDir": "` + path.Join(testChainDir, "wnode") + `",
|
||||
"EnableNTPSync": false
|
||||
}
|
||||
}`
|
||||
}
|
||||
|
||||
|
@ -85,10 +91,6 @@ func testExportedAPI(t *testing.T, done chan struct{}) {
|
|||
name string
|
||||
fn func(t *testing.T) bool
|
||||
}{
|
||||
{
|
||||
"check default configuration",
|
||||
testGetDefaultConfig,
|
||||
},
|
||||
{
|
||||
"stop/resume node",
|
||||
testStopResumeNode,
|
||||
|
@ -187,39 +189,6 @@ func testVerifyAccountPassword(t *testing.T) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
func testGetDefaultConfig(t *testing.T) bool {
|
||||
networks := []struct {
|
||||
chainID int
|
||||
refChainConfig *gethparams.ChainConfig
|
||||
}{
|
||||
{params.MainNetworkID, gethparams.MainnetChainConfig},
|
||||
{params.RopstenNetworkID, gethparams.TestnetChainConfig},
|
||||
{params.RinkebyNetworkID, gethparams.RinkebyChainConfig},
|
||||
// TODO(tiabc): The same for params.StatusChainNetworkID
|
||||
}
|
||||
for i := range networks {
|
||||
network := networks[i]
|
||||
|
||||
t.Run(fmt.Sprintf("networkID=%d", network.chainID), func(t *testing.T) {
|
||||
var (
|
||||
nodeConfig = params.NodeConfig{}
|
||||
rawResponse = GenerateConfig(C.CString("/tmp/data-folder"), C.CString("eth.staging"), C.int(network.chainID))
|
||||
)
|
||||
if err := json.Unmarshal([]byte(C.GoString(rawResponse)), &nodeConfig); err != nil {
|
||||
t.Errorf("cannot decode response (%s): %v", C.GoString(rawResponse), err)
|
||||
}
|
||||
|
||||
genesis := new(core.Genesis)
|
||||
if err := json.Unmarshal([]byte(nodeConfig.LightEthConfig.Genesis), genesis); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
require.Equal(t, network.refChainConfig, genesis.Config)
|
||||
})
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
//@TODO(adam): quarantined this test until it uses a different directory.
|
||||
//nolint: deadcode
|
||||
func testResetChainData(t *testing.T) bool {
|
||||
|
|
|
@ -120,7 +120,7 @@ func (s *WMailServer) Init(shh *whisper.Whisper, config *params.WhisperConfig) e
|
|||
return errDirectoryNotProvided
|
||||
}
|
||||
|
||||
if len(config.MailServerPassword) == 0 && config.MailServerAsymKey == nil {
|
||||
if len(config.MailServerPassword) == 0 && len(config.MailServerAsymKey) == 0 {
|
||||
return errDecryptionMethodNotProvided
|
||||
}
|
||||
|
||||
|
@ -172,8 +172,12 @@ func (s *WMailServer) setupRequestMessageDecryptor(config *params.WhisperConfig)
|
|||
s.symFilter = &whisper.Filter{KeySym: symKey}
|
||||
}
|
||||
|
||||
if config.MailServerAsymKey != nil {
|
||||
s.asymFilter = &whisper.Filter{KeyAsym: config.MailServerAsymKey}
|
||||
if config.MailServerAsymKey != "" {
|
||||
keyAsym, err := crypto.HexToECDSA(config.MailServerAsymKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.asymFilter = &whisper.Filter{KeyAsym: keyAsym}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
|
@ -19,11 +19,11 @@ package mailserver
|
|||
import (
|
||||
"crypto/ecdsa"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
@ -72,19 +72,13 @@ func (s *MailserverSuite) SetupTest() {
|
|||
s.dataDir = tmpDir
|
||||
|
||||
// required files to validate mail server decryption method
|
||||
asymKeyFile := filepath.Join(tmpDir, "asymkey")
|
||||
passwordFile := filepath.Join(tmpDir, "password")
|
||||
privateKey, err := crypto.GenerateKey()
|
||||
s.Require().NoError(err)
|
||||
err = crypto.SaveECDSA(asymKeyFile, privateKey)
|
||||
s.Require().NoError(err)
|
||||
err = ioutil.WriteFile(passwordFile, []byte("testpassword"), os.ModePerm)
|
||||
s.Require().NoError(err)
|
||||
|
||||
s.config = ¶ms.WhisperConfig{
|
||||
DataDir: tmpDir,
|
||||
MailServerAsymKeyFile: asymKeyFile,
|
||||
MailServerPasswordFile: passwordFile,
|
||||
MailServerAsymKey: hex.EncodeToString(crypto.FromECDSA(privateKey)),
|
||||
MailServerPassword: "testpassword",
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -118,7 +112,7 @@ func (s *MailserverSuite) TestInit() {
|
|||
config: params.WhisperConfig{
|
||||
DataDir: s.config.DataDir,
|
||||
MailServerPassword: "",
|
||||
MailServerAsymKey: nil,
|
||||
MailServerAsymKey: "",
|
||||
},
|
||||
expectedError: errDecryptionMethodNotProvided,
|
||||
info: "config with an empty password and empty asym key",
|
||||
|
@ -134,7 +128,7 @@ func (s *MailserverSuite) TestInit() {
|
|||
{
|
||||
config: params.WhisperConfig{
|
||||
DataDir: s.config.DataDir,
|
||||
MailServerAsymKey: asymKey,
|
||||
MailServerAsymKey: hex.EncodeToString(crypto.FromECDSA(asymKey)),
|
||||
},
|
||||
expectedError: nil,
|
||||
info: "config with correct DataDir and AsymKey",
|
||||
|
@ -142,7 +136,7 @@ func (s *MailserverSuite) TestInit() {
|
|||
{
|
||||
config: params.WhisperConfig{
|
||||
DataDir: s.config.DataDir,
|
||||
MailServerAsymKey: asymKey,
|
||||
MailServerAsymKey: hex.EncodeToString(crypto.FromECDSA(asymKey)),
|
||||
MailServerPassword: "pwd",
|
||||
},
|
||||
expectedError: nil,
|
||||
|
@ -186,11 +180,13 @@ func (s *MailserverSuite) TestInit() {
|
|||
func (s *MailserverSuite) TestSetupRequestMessageDecryptor() {
|
||||
// without configured Password and AsymKey
|
||||
config := *s.config
|
||||
config.MailServerAsymKey = ""
|
||||
config.MailServerPassword = ""
|
||||
s.Error(errDecryptionMethodNotProvided, s.server.Init(s.shh, &config))
|
||||
|
||||
// Password should work ok
|
||||
config = *s.config
|
||||
s.NoError(config.ReadMailServerPasswordFile())
|
||||
config.MailServerAsymKey = "" // clear asym key field
|
||||
s.NoError(s.server.Init(s.shh, &config))
|
||||
s.Require().NotNil(s.server.symFilter)
|
||||
s.NotNil(s.server.symFilter.KeySym)
|
||||
|
@ -199,17 +195,15 @@ func (s *MailserverSuite) TestSetupRequestMessageDecryptor() {
|
|||
|
||||
// AsymKey can also be used
|
||||
config = *s.config
|
||||
s.NoError(config.ReadMailServerAsymKeyFile())
|
||||
config.MailServerPassword = "" // clear password field
|
||||
s.NoError(s.server.Init(s.shh, &config))
|
||||
s.Nil(s.server.symFilter) // important: symmetric filter should be nil
|
||||
s.Require().NotNil(s.server.asymFilter)
|
||||
s.Equal(config.MailServerAsymKey, s.server.asymFilter.KeyAsym)
|
||||
s.Equal(config.MailServerAsymKey, hex.EncodeToString(crypto.FromECDSA(s.server.asymFilter.KeyAsym)))
|
||||
s.server.Close()
|
||||
|
||||
// when Password and AsymKey are set, both are supported
|
||||
config = *s.config
|
||||
s.NoError(config.ReadMailServerPasswordFile())
|
||||
s.NoError(config.ReadMailServerAsymKeyFile())
|
||||
s.NoError(s.server.Init(s.shh, &config))
|
||||
s.Require().NotNil(s.server.symFilter)
|
||||
s.NotNil(s.server.symFilter.KeySym)
|
||||
|
@ -220,7 +214,7 @@ func (s *MailserverSuite) TestSetupRequestMessageDecryptor() {
|
|||
func (s *MailserverSuite) TestOpenEnvelopeWithSymKey() {
|
||||
// Setup the server with a sym key
|
||||
config := *s.config
|
||||
s.NoError(config.ReadMailServerPasswordFile())
|
||||
config.MailServerAsymKey = "" // clear asym key
|
||||
s.NoError(s.server.Init(s.shh, &config))
|
||||
|
||||
// Prepare a valid envelope
|
||||
|
@ -239,7 +233,7 @@ func (s *MailserverSuite) TestOpenEnvelopeWithSymKey() {
|
|||
func (s *MailserverSuite) TestOpenEnvelopeWithAsymKey() {
|
||||
// Setup the server with an asymetric key
|
||||
config := *s.config
|
||||
s.NoError(config.ReadMailServerAsymKeyFile())
|
||||
config.MailServerPassword = "" // clear password field
|
||||
s.NoError(s.server.Init(s.shh, &config))
|
||||
|
||||
// Prepare a valid envelope
|
||||
|
@ -256,10 +250,10 @@ func (s *MailserverSuite) TestOpenEnvelopeWithAsymKey() {
|
|||
}
|
||||
|
||||
func (s *MailserverSuite) TestArchive() {
|
||||
err := s.config.ReadMailServerPasswordFile()
|
||||
s.Require().NoError(err)
|
||||
config := *s.config
|
||||
config.MailServerAsymKey = "" // clear asym key
|
||||
|
||||
err = s.server.Init(s.shh, s.config)
|
||||
err := s.server.Init(s.shh, &config)
|
||||
s.Require().NoError(err)
|
||||
defer s.server.Close()
|
||||
|
||||
|
|
106
node/node.go
106
node/node.go
|
@ -27,6 +27,7 @@ import (
|
|||
"github.com/status-im/status-go/services/personal"
|
||||
"github.com/status-im/status-go/services/shhext"
|
||||
"github.com/status-im/status-go/services/status"
|
||||
"github.com/status-im/status-go/static"
|
||||
"github.com/status-im/status-go/timesource"
|
||||
"github.com/syndtr/goleveldb/leveldb"
|
||||
)
|
||||
|
@ -36,6 +37,7 @@ var (
|
|||
ErrNodeMakeFailureFormat = "error creating p2p node: %s"
|
||||
ErrWhisperServiceRegistrationFailure = errors.New("failed to register the Whisper service")
|
||||
ErrLightEthRegistrationFailure = errors.New("failed to register the LES service")
|
||||
ErrLightEthRegistrationFailureUpstreamEnabled = errors.New("failed to register the LES service, upstream is also configured")
|
||||
ErrPersonalServiceRegistrationFailure = errors.New("failed to register the personal api service")
|
||||
ErrStatusServiceRegistrationFailure = errors.New("failed to register the Status service")
|
||||
ErrPeerServiceRegistrationFailure = errors.New("failed to register the Peer service")
|
||||
|
@ -44,7 +46,7 @@ var (
|
|||
// All general log messages in this package should be routed through this logger.
|
||||
var logger = log.New("package", "status-go/node")
|
||||
|
||||
// MakeNode create a geth node entity
|
||||
// MakeNode creates a geth node entity
|
||||
func MakeNode(config *params.NodeConfig, db *leveldb.DB) (*node.Node, error) {
|
||||
// If DataDir is empty, it means we want to create an ephemeral node
|
||||
// keeping data only in memory.
|
||||
|
@ -60,17 +62,9 @@ func MakeNode(config *params.NodeConfig, db *leveldb.DB) (*node.Node, error) {
|
|||
}
|
||||
}
|
||||
|
||||
stackConfig := defaultEmbeddedNodeConfig(config)
|
||||
|
||||
if len(config.NodeKeyFile) > 0 {
|
||||
logger.Info("Loading private key file", "file", config.NodeKeyFile)
|
||||
pk, err := crypto.LoadECDSA(config.NodeKeyFile)
|
||||
stackConfig, err := newGethNodeConfig(config)
|
||||
if err != nil {
|
||||
logger.Error("Failed loading private key file", "file", config.NodeKeyFile, "error", err)
|
||||
}
|
||||
|
||||
// override node's private key
|
||||
stackConfig.P2P.PrivateKey = pk
|
||||
return nil, err
|
||||
}
|
||||
|
||||
stack, err := node.New(stackConfig)
|
||||
|
@ -84,6 +78,10 @@ func MakeNode(config *params.NodeConfig, db *leveldb.DB) (*node.Node, error) {
|
|||
return nil, fmt.Errorf("%v: %v", ErrLightEthRegistrationFailure, err)
|
||||
}
|
||||
} else {
|
||||
if config.LightEthConfig.Enabled {
|
||||
return nil, fmt.Errorf("%v: %v", ErrLightEthRegistrationFailureUpstreamEnabled, err)
|
||||
}
|
||||
|
||||
// `personal_sign` and `personal_ecRecover` methods are important to
|
||||
// keep DApps working.
|
||||
// Usually, they are provided by an ETH or a LES service, but when using
|
||||
|
@ -112,8 +110,8 @@ func MakeNode(config *params.NodeConfig, db *leveldb.DB) (*node.Node, error) {
|
|||
return stack, nil
|
||||
}
|
||||
|
||||
// defaultEmbeddedNodeConfig returns default stack configuration for mobile client node
|
||||
func defaultEmbeddedNodeConfig(config *params.NodeConfig) *node.Config {
|
||||
// newGethNodeConfig returns default stack configuration for mobile client node
|
||||
func newGethNodeConfig(config *params.NodeConfig) (*node.Config, error) {
|
||||
nc := &node.Config{
|
||||
DataDir: config.DataDir,
|
||||
KeyStoreDir: config.KeyStoreDir,
|
||||
|
@ -139,26 +137,71 @@ func defaultEmbeddedNodeConfig(config *params.NodeConfig) *node.Config {
|
|||
nc.HTTPPort = config.HTTPPort
|
||||
}
|
||||
|
||||
if config.ClusterConfig != nil && config.ClusterConfig.Enabled {
|
||||
if config.ClusterConfig.Enabled {
|
||||
nc.P2P.BootstrapNodesV5 = parseNodesV5(config.ClusterConfig.BootNodes)
|
||||
nc.P2P.StaticNodes = parseNodes(config.ClusterConfig.StaticNodes)
|
||||
}
|
||||
return nc
|
||||
|
||||
if config.NodeKey != "" {
|
||||
sk, err := crypto.HexToECDSA(config.NodeKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// override node's private key
|
||||
nc.P2P.PrivateKey = sk
|
||||
}
|
||||
|
||||
return nc, nil
|
||||
}
|
||||
|
||||
// calculateGenesis retrieves genesis value for given network
|
||||
func calculateGenesis(networkID uint64) (*core.Genesis, error) {
|
||||
var genesis *core.Genesis
|
||||
|
||||
switch networkID {
|
||||
case params.MainNetworkID:
|
||||
genesis = core.DefaultGenesisBlock()
|
||||
case params.RopstenNetworkID:
|
||||
genesis = core.DefaultTestnetGenesisBlock()
|
||||
case params.RinkebyNetworkID:
|
||||
genesis = core.DefaultRinkebyGenesisBlock()
|
||||
case params.StatusChainNetworkID:
|
||||
var err error
|
||||
if genesis, err = defaultStatusChainGenesisBlock(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
default:
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return genesis, nil
|
||||
}
|
||||
|
||||
// defaultStatusChainGenesisBlock returns the StatusChain network genesis block.
|
||||
func defaultStatusChainGenesisBlock() (*core.Genesis, error) {
|
||||
genesisJSON, err := static.ConfigStatusChainGenesisJsonBytes()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("status-chain-genesis.json could not be loaded: %s", err)
|
||||
}
|
||||
|
||||
var genesis *core.Genesis
|
||||
err = json.Unmarshal(genesisJSON, &genesis)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot unmarshal status-chain-genesis.json: %s", err)
|
||||
}
|
||||
return genesis, nil
|
||||
}
|
||||
|
||||
// activateLightEthService configures and registers the eth.Ethereum service with a given node.
|
||||
func activateLightEthService(stack *node.Node, config *params.NodeConfig) error {
|
||||
if config.LightEthConfig == nil || !config.LightEthConfig.Enabled {
|
||||
if !config.LightEthConfig.Enabled {
|
||||
logger.Info("LES protocol is disabled")
|
||||
return nil
|
||||
}
|
||||
|
||||
var genesis *core.Genesis
|
||||
if config.LightEthConfig.Genesis != "" {
|
||||
genesis = new(core.Genesis)
|
||||
if err := json.Unmarshal([]byte(config.LightEthConfig.Genesis), genesis); err != nil {
|
||||
return fmt.Errorf("invalid genesis spec: %v", err)
|
||||
}
|
||||
genesis, err := calculateGenesis(config.NetworkID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ethConf := eth.DefaultConfig
|
||||
|
@ -202,21 +245,6 @@ func activatePeerService(stack *node.Node) error {
|
|||
}
|
||||
|
||||
func registerMailServer(whisperService *whisper.Whisper, config *params.WhisperConfig) (err error) {
|
||||
// if the Password is already set, do not override it
|
||||
if config.MailServerPassword == "" && config.MailServerPasswordFile != "" {
|
||||
err = config.ReadMailServerPasswordFile()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
// similarly, do not override already configured AsymKey
|
||||
if config.MailServerAsymKey == nil && config.MailServerAsymKeyFile != "" {
|
||||
err = config.ReadMailServerAsymKeyFile()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var mailServer mailserver.WMailServer
|
||||
whisperService.RegisterServer(&mailServer)
|
||||
|
||||
|
@ -225,7 +253,7 @@ func registerMailServer(whisperService *whisper.Whisper, config *params.WhisperC
|
|||
|
||||
// activateShhService configures Whisper and adds it to the given node.
|
||||
func activateShhService(stack *node.Node, config *params.NodeConfig, db *leveldb.DB) (err error) {
|
||||
if config.WhisperConfig == nil || !config.WhisperConfig.Enabled {
|
||||
if !config.WhisperConfig.Enabled {
|
||||
logger.Info("SHH protocol is disabled")
|
||||
return nil
|
||||
}
|
||||
|
@ -254,7 +282,7 @@ func activateShhService(stack *node.Node, config *params.NodeConfig, db *leveldb
|
|||
|
||||
// enable mail service
|
||||
if config.WhisperConfig.EnableMailServer {
|
||||
if err := registerMailServer(whisperService, config.WhisperConfig); err != nil {
|
||||
if err := registerMailServer(whisperService, &config.WhisperConfig); err != nil {
|
||||
return nil, fmt.Errorf("failed to register MailServer: %v", err)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ import (
|
|||
|
||||
func TestWhisperLightModeEnabledSetsEmptyBloomFilter(t *testing.T) {
|
||||
config := params.NodeConfig{
|
||||
WhisperConfig: ¶ms.WhisperConfig{
|
||||
WhisperConfig: params.WhisperConfig{
|
||||
Enabled: true,
|
||||
LightClient: true,
|
||||
},
|
||||
|
@ -33,7 +33,7 @@ func TestWhisperLightModeEnabledSetsEmptyBloomFilter(t *testing.T) {
|
|||
|
||||
func TestWhisperLightModeEnabledSetsNilBloomFilter(t *testing.T) {
|
||||
config := params.NodeConfig{
|
||||
WhisperConfig: ¶ms.WhisperConfig{
|
||||
WhisperConfig: params.WhisperConfig{
|
||||
Enabled: true,
|
||||
LightClient: false,
|
||||
},
|
||||
|
|
|
@ -117,17 +117,18 @@ func (n *StatusNode) startWithDB(config *params.NodeConfig, db *leveldb.DB, serv
|
|||
return nil
|
||||
}
|
||||
|
||||
// Start starts current StatusNode, will fail if it's already started.
|
||||
// Start starts current StatusNode, failing if it's already started.
|
||||
func (n *StatusNode) Start(config *params.NodeConfig, services ...node.ServiceConstructor) error {
|
||||
n.mu.Lock()
|
||||
defer n.mu.Unlock()
|
||||
|
||||
n.log.Debug("starting with NodeConfig", "ClusterConfig", config.ClusterConfig)
|
||||
|
||||
if n.isRunning() {
|
||||
n.log.Debug("cannot start, node already running")
|
||||
return ErrNodeRunning
|
||||
}
|
||||
|
||||
n.log.Debug("starting with NodeConfig", "ClusterConfig", config.ClusterConfig)
|
||||
|
||||
db, err := db.Create(config.DataDir, params.StatusDatabase)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -186,7 +187,7 @@ func (n *StatusNode) setupRPCClient() (err error) {
|
|||
}
|
||||
|
||||
func (n *StatusNode) discoveryEnabled() bool {
|
||||
return n.config != nil && (!n.config.NoDiscovery || n.config.Rendezvous) && n.config.ClusterConfig != nil
|
||||
return n.config != nil && (!n.config.NoDiscovery || n.config.Rendezvous) && n.config.ClusterConfig.Enabled
|
||||
}
|
||||
|
||||
func (n *StatusNode) discoverNode() *discover.Node {
|
||||
|
@ -355,7 +356,7 @@ func (n *StatusNode) isRunning() bool {
|
|||
|
||||
// populateStaticPeers connects current node with our publicly available LES/SHH/Swarm cluster
|
||||
func (n *StatusNode) populateStaticPeers() error {
|
||||
if n.config.ClusterConfig == nil || !n.config.ClusterConfig.Enabled {
|
||||
if !n.config.ClusterConfig.Enabled {
|
||||
n.log.Info("Static peers are disabled")
|
||||
return nil
|
||||
}
|
||||
|
@ -372,7 +373,7 @@ func (n *StatusNode) populateStaticPeers() error {
|
|||
}
|
||||
|
||||
func (n *StatusNode) removeStaticPeers() error {
|
||||
if n.config.ClusterConfig == nil || !n.config.ClusterConfig.Enabled {
|
||||
if !n.config.ClusterConfig.Enabled {
|
||||
n.log.Info("Static peers are disabled")
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -19,13 +19,13 @@ import (
|
|||
"github.com/status-im/status-go/discovery"
|
||||
"github.com/status-im/status-go/params"
|
||||
"github.com/status-im/status-go/t/helpers"
|
||||
"github.com/status-im/status-go/t/utils"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestStatusNodeStart(t *testing.T) {
|
||||
var err error
|
||||
|
||||
config := params.NodeConfig{}
|
||||
config, err := utils.MakeTestNodeConfig(params.StatusChainNetworkID)
|
||||
require.NoError(t, err)
|
||||
n := New()
|
||||
|
||||
// checks before node is started
|
||||
|
@ -39,7 +39,7 @@ func TestStatusNodeStart(t *testing.T) {
|
|||
require.EqualError(t, err, ErrNoGethNode.Error())
|
||||
|
||||
// start node
|
||||
require.NoError(t, n.Start(&config))
|
||||
require.NoError(t, n.Start(config))
|
||||
|
||||
// checks after node is started
|
||||
require.True(t, n.IsRunning())
|
||||
|
@ -54,7 +54,7 @@ func TestStatusNodeStart(t *testing.T) {
|
|||
require.Nil(t, err)
|
||||
require.NotNil(t, keyStore)
|
||||
// try to start already started node
|
||||
require.EqualError(t, n.Start(&config), ErrNodeRunning.Error())
|
||||
require.EqualError(t, n.Start(config), ErrNodeRunning.Error())
|
||||
|
||||
// stop node
|
||||
require.NoError(t, n.Stop())
|
||||
|
@ -97,10 +97,10 @@ func TestStatusNodeWithDataDir(t *testing.T) {
|
|||
|
||||
func TestStatusNodeServiceGetters(t *testing.T) {
|
||||
config := params.NodeConfig{
|
||||
WhisperConfig: ¶ms.WhisperConfig{
|
||||
WhisperConfig: params.WhisperConfig{
|
||||
Enabled: true,
|
||||
},
|
||||
LightEthConfig: ¶ms.LightEthConfig{
|
||||
LightEthConfig: params.LightEthConfig{
|
||||
Enabled: true,
|
||||
},
|
||||
}
|
||||
|
@ -216,7 +216,7 @@ func TestStatusNodeReconnectStaticPeers(t *testing.T) {
|
|||
// start status node
|
||||
config := params.NodeConfig{
|
||||
MaxPeers: math.MaxInt32,
|
||||
ClusterConfig: ¶ms.ClusterConfig{
|
||||
ClusterConfig: params.ClusterConfig{
|
||||
Enabled: true,
|
||||
StaticNodes: []string{peerURL},
|
||||
},
|
||||
|
@ -272,7 +272,7 @@ func TestStatusNodeRendezvousDiscovery(t *testing.T) {
|
|||
config := params.NodeConfig{
|
||||
Rendezvous: true,
|
||||
NoDiscovery: true,
|
||||
ClusterConfig: ¶ms.ClusterConfig{
|
||||
ClusterConfig: params.ClusterConfig{
|
||||
Enabled: true,
|
||||
// not necessarily with id, just valid multiaddr
|
||||
RendezvousNodes: []string{"/ip4/127.0.0.1/tcp/34012", "/ip4/127.0.0.1/tcp/34011"},
|
||||
|
|
|
@ -14,62 +14,3 @@ type Cluster struct {
|
|||
MailServers []string `json:"mailservers"` // list of trusted mail servers
|
||||
RendezvousNodes []string `json:"rendezvousnodes"`
|
||||
}
|
||||
|
||||
// Consult this list with http://fleets.status.im/.
|
||||
var clusters = map[string]func() Cluster{
|
||||
FleetStaging: func() Cluster {
|
||||
return Cluster{
|
||||
BootNodes: []string{
|
||||
"enode://10a78c17929a7019ef4aa2249d7302f76ae8a06f40b2dc88b7b31ebff4a623fbb44b4a627acba296c1ced3775d91fbe18463c15097a6a36fdb2c804ff3fc5b35@35.238.97.234:30404", // boot-01.gc-us-central1-a.eth.staging
|
||||
"enode://f79fb3919f72ca560ad0434dcc387abfe41e0666201ebdada8ede0462454a13deb05cda15f287d2c4bd85da81f0eb25d0a486bbbc8df427b971ac51533bd00fe@174.138.107.239:30404", // boot-01.do-ams3.eth.staging
|
||||
},
|
||||
StaticNodes: []string{
|
||||
"enode://914c0b30f27bab30c1dfd31dad7652a46fda9370542aee1b062498b1345ee0913614b8b9e3e84622e84a7203c5858ae1d9819f63aece13ee668e4f6668063989@167.99.19.148:30305", // node-01.do-ams3.eth.staging
|
||||
"enode://2d897c6e846949f9dcf10279f00e9b8325c18fe7fa52d658520ad7be9607c83008b42b06aefd97cfe1fdab571f33a2a9383ff97c5909ed51f63300834913237e@35.192.0.86:30305", // "node-01.gc-us-central1-a.eth.staging"
|
||||
},
|
||||
MailServers: []string{
|
||||
"enode://69f72baa7f1722d111a8c9c68c39a31430e9d567695f6108f31ccb6cd8f0adff4991e7fdca8fa770e75bc8a511a87d24690cbc80e008175f40c157d6f6788d48@206.189.240.16:30504", // mail-01.do-ams3.eth.staging
|
||||
"enode://e4fc10c1f65c8aed83ac26bc1bfb21a45cc1a8550a58077c8d2de2a0e0cd18e40fd40f7e6f7d02dc6cd06982b014ce88d6e468725ffe2c138e958788d0002a7f@35.239.193.41:30504", // mail-01.gc-us-central1-a.eth.staging
|
||||
},
|
||||
RendezvousNodes: []string{
|
||||
"/ip4/174.138.107.239/tcp/30703/ethv4/16Uiu2HAkyJHeetQ4DNpd4NZ2ntzxMo25zcdpvGQRqkD5pB9BE6RU",
|
||||
"/ip4/35.238.97.234/tcp/30703/ethv4/16Uiu2HAm1sVyXmkMNjdeDWqK2urbyC3oBHi8MDpCdYkns1nYafqz",
|
||||
},
|
||||
}
|
||||
},
|
||||
FleetBeta: func() Cluster {
|
||||
return Cluster{
|
||||
BootNodes: []string{
|
||||
"enode://436cc6f674928fdc9a9f7990f2944002b685d1c37f025c1be425185b5b1f0900feaf1ccc2a6130268f9901be4a7d252f37302c8335a2c1a62736e9232691cc3a@174.138.105.243:30404", // boot-01.do-ams3.eth.beta
|
||||
"enode://5395aab7833f1ecb671b59bf0521cf20224fe8162fc3d2675de4ee4d5636a75ec32d13268fc184df8d1ddfa803943906882da62a4df42d4fccf6d17808156a87@206.189.243.57:30404", // boot-02.do-ams3.eth.beta
|
||||
"enode://7427dfe38bd4cf7c58bb96417806fab25782ec3e6046a8053370022cbaa281536e8d64ecd1b02e1f8f72768e295d06258ba43d88304db068e6f2417ae8bcb9a6@104.154.88.123:30404", // boot-01.gc-us-central1-a.eth.beta
|
||||
"enode://ebefab39b69bbbe64d8cd86be765b3be356d8c4b24660f65d493143a0c44f38c85a257300178f7845592a1b0332811542e9a58281c835babdd7535babb64efc1@35.202.99.224:30404", // boot-02.gc-us-central1-a.eth.beta
|
||||
},
|
||||
StaticNodes: []string{
|
||||
"enode://a6a2a9b3a7cbb0a15da74301537ebba549c990e3325ae78e1272a19a3ace150d03c184b8ac86cc33f1f2f63691e467d49308f02d613277754c4dccd6773b95e8@206.189.243.176:30304", // node-01.do-ams3.eth.beta
|
||||
"enode://207e53d9bf66be7441e3daba36f53bfbda0b6099dba9a865afc6260a2d253fb8a56a72a48598a4f7ba271792c2e4a8e1a43aaef7f34857f520c8c820f63b44c8@35.224.15.65:30304", // node-01.gc-us-central1-a.eth.beta
|
||||
},
|
||||
MailServers: []string{
|
||||
"enode://c42f368a23fa98ee546fd247220759062323249ef657d26d357a777443aec04db1b29a3a22ef3e7c548e18493ddaf51a31b0aed6079bd6ebe5ae838fcfaf3a49@206.189.243.162:30504", // mail-01.do-ams3.eth.beta
|
||||
"enode://7aa648d6e855950b2e3d3bf220c496e0cae4adfddef3e1e6062e6b177aec93bc6cdcf1282cb40d1656932ebfdd565729da440368d7c4da7dbd4d004b1ac02bf8@206.189.243.169:30504", // mail-02.do-ams3.eth.beta
|
||||
"enode://8a64b3c349a2e0ef4a32ea49609ed6eb3364be1110253c20adc17a3cebbc39a219e5d3e13b151c0eee5d8e0f9a8ba2cd026014e67b41a4ab7d1d5dd67ca27427@206.189.243.168:30504", // mail-03.do-ams3.eth.beta
|
||||
"enode://7de99e4cb1b3523bd26ca212369540646607c721ad4f3e5c821ed9148150ce6ce2e72631723002210fac1fd52dfa8bbdf3555e05379af79515e1179da37cc3db@35.188.19.210:30504", // mail-01.gc-us-central1-a.eth.beta
|
||||
"enode://015e22f6cd2b44c8a51bd7a23555e271e0759c7d7f52432719665a74966f2da456d28e154e836bee6092b4d686fe67e331655586c57b718be3997c1629d24167@35.226.21.19:30504", // mail-02.gc-us-central1-a.eth.beta
|
||||
"enode://531e252ec966b7e83f5538c19bf1cde7381cc7949026a6e499b6e998e695751aadf26d4c98d5a4eabfb7cefd31c3c88d600a775f14ed5781520a88ecd25da3c6@35.225.227.79:30504", // mail-03.gc-us-central1-a.eth.beta
|
||||
},
|
||||
RendezvousNodes: []string{
|
||||
"/ip4/174.138.105.243/tcp/30703/ethv4/16Uiu2HAmRHPzF3rQg55PgYPcQkyvPVH9n2hWsYPhUJBZ6kVjJgdV", // boot-01.do-ams3.eth.beta
|
||||
"/ip4/206.189.243.57/tcp/30703/ethv4/16Uiu2HAmLqTXuY4Sb6G28HNooaFUXUKzpzKXCcgyJxgaEE2i5vnf", // boot-02.do-ams3.eth.beta
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// ClusterForFleet returns a cluster for a given fleet.
|
||||
func ClusterForFleet(fleet string) (Cluster, bool) {
|
||||
cluster, ok := clusters[fleet]
|
||||
if ok {
|
||||
return cluster(), true
|
||||
}
|
||||
return Cluster{}, false
|
||||
}
|
||||
|
|
589
params/config.go
589
params/config.go
|
@ -1,34 +1,23 @@
|
|||
package params
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/ecdsa"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/build"
|
||||
"io/ioutil"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/p2p/discv5"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
// errors
|
||||
var (
|
||||
ErrMissingDataDir = errors.New("missing required 'DataDir' parameter")
|
||||
ErrMissingNetworkID = errors.New("missing required 'NetworkID' parameter")
|
||||
ErrEmptyPasswordFile = errors.New("password file cannot be empty")
|
||||
ErrNoPasswordFileValueSet = errors.New("password file path not set")
|
||||
ErrEmptyAuthorizationKeyFile = errors.New("authorization key file cannot be empty")
|
||||
ErrAuthorizationKeyFileNotSet = errors.New("authorization key file is not set")
|
||||
"github.com/status-im/status-go/static"
|
||||
validator "gopkg.in/go-playground/validator.v9"
|
||||
)
|
||||
|
||||
// ----------
|
||||
|
@ -41,46 +30,10 @@ type LightEthConfig struct {
|
|||
// Enabled flag specifies whether protocol is enabled
|
||||
Enabled bool
|
||||
|
||||
// Genesis is JSON to seed the chain database with
|
||||
Genesis string
|
||||
|
||||
// DatabaseCache is memory (in MBs) allocated to internal caching (min 16MB / database forced)
|
||||
DatabaseCache int
|
||||
}
|
||||
|
||||
// ----------
|
||||
// FirebaseConfig
|
||||
// ----------
|
||||
|
||||
// FirebaseConfig holds FCM-related configuration
|
||||
type FirebaseConfig struct {
|
||||
// AuthorizationKeyFile file path that contains FCM authorization key
|
||||
AuthorizationKeyFile string
|
||||
|
||||
// NotificationTriggerURL URL used to send push notification requests to
|
||||
NotificationTriggerURL string
|
||||
}
|
||||
|
||||
// ReadAuthorizationKeyFile reads and loads FCM authorization key
|
||||
func (c *FirebaseConfig) ReadAuthorizationKeyFile() ([]byte, error) {
|
||||
if len(c.AuthorizationKeyFile) == 0 {
|
||||
return nil, ErrAuthorizationKeyFileNotSet
|
||||
}
|
||||
|
||||
key, err := ioutil.ReadFile(c.AuthorizationKeyFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
key = bytes.TrimRight(key, "\n")
|
||||
|
||||
if len(key) == 0 {
|
||||
return nil, ErrEmptyAuthorizationKeyFile
|
||||
}
|
||||
|
||||
return key, nil
|
||||
}
|
||||
|
||||
// ----------
|
||||
// WhisperConfig
|
||||
// ----------
|
||||
|
@ -103,18 +56,12 @@ type WhisperConfig struct {
|
|||
// MinimumPoW minimum PoW for Whisper messages
|
||||
MinimumPoW float64
|
||||
|
||||
// MailServerPasswordFile contains a password for symmetric encryption with MailServer.
|
||||
MailServerPasswordFile string
|
||||
|
||||
// MailServerPassword for symmetric encryption with MailServer.
|
||||
// (if no account file selected, then this password is used for symmetric encryption).
|
||||
MailServerPassword string
|
||||
|
||||
// MailServerAsymKeyFile is a file with an asymmetric key to decrypt messages sent to MailServer.
|
||||
MailServerAsymKeyFile string
|
||||
|
||||
// MailServerAsymKey is an asymmetric key to decrypt messages sent to MailServer.
|
||||
MailServerAsymKey *ecdsa.PrivateKey
|
||||
// MailServerAsymKey is an hex-encoded asymmetric key to decrypt messages sent to MailServer.
|
||||
MailServerAsymKey string
|
||||
|
||||
// RateLimit minimum time between queries to mail server per peer
|
||||
MailServerRateLimit int
|
||||
|
@ -125,40 +72,10 @@ type WhisperConfig struct {
|
|||
// TTL time to live for messages, in seconds
|
||||
TTL int
|
||||
|
||||
// FirebaseConfig extra configuration for Firebase Cloud Messaging
|
||||
FirebaseConfig *FirebaseConfig `json:"FirebaseConfig,"`
|
||||
|
||||
// EnableNTPSync enables NTP synchronizations
|
||||
EnableNTPSync bool
|
||||
}
|
||||
|
||||
// ReadMailServerPasswordFile reads and returns content of the password file
|
||||
func (c *WhisperConfig) ReadMailServerPasswordFile() error {
|
||||
if len(c.MailServerPasswordFile) == 0 {
|
||||
return ErrNoPasswordFileValueSet
|
||||
}
|
||||
|
||||
password, err := ioutil.ReadFile(c.MailServerPasswordFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
password = bytes.TrimRight(password, "\n")
|
||||
|
||||
if len(password) == 0 {
|
||||
return ErrEmptyPasswordFile
|
||||
}
|
||||
|
||||
c.MailServerPassword = string(password)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReadMailServerAsymKeyFile reads and returns a private key from a given file.
|
||||
func (c *WhisperConfig) ReadMailServerAsymKeyFile() (err error) {
|
||||
c.MailServerAsymKey, err = crypto.LoadECDSA(c.MailServerAsymKeyFile)
|
||||
return
|
||||
}
|
||||
|
||||
// String dumps config object as nicely indented JSON
|
||||
func (c *WhisperConfig) String() string {
|
||||
data, _ := json.MarshalIndent(c, "", " ") // nolint: gas
|
||||
|
@ -195,17 +112,16 @@ type ClusterConfig struct {
|
|||
// Fleet is a type of selected fleet.
|
||||
Fleet string
|
||||
|
||||
// StaticNodes lists the static nodes taken from compiled or passed cluster.json
|
||||
// StaticNodes is a list of static nodes for this fleet.
|
||||
StaticNodes []string
|
||||
|
||||
// BootNodes list of cluster peer nodes for a given network (Mainnet, Ropsten, Rinkeby, Homestead),
|
||||
// for a given mode (production vs development)
|
||||
// BootNodes is a list of cluster peer nodes for this fleet.
|
||||
BootNodes []string
|
||||
|
||||
// TrustedMailServers is a list of verified Mail Servers.
|
||||
// TrustedMailServers is a list of verified Mail Servers for this fleet.
|
||||
TrustedMailServers []string
|
||||
|
||||
// RendezvousNodes is a rendezvous discovery server.
|
||||
// RendezvousNodes is a list rendezvous discovery nodes.
|
||||
RendezvousNodes []string
|
||||
}
|
||||
|
||||
|
@ -255,13 +171,11 @@ type NodeConfig struct {
|
|||
DataDir string `validate:"required"`
|
||||
|
||||
// KeyStoreDir is the file system folder that contains private keys.
|
||||
// If KeyStoreDir is empty, the default location is the "keystore" subdirectory of DataDir.
|
||||
KeyStoreDir string
|
||||
KeyStoreDir string `validate:"required"`
|
||||
|
||||
// NodeKeyFile is a filename with node ID (private key)
|
||||
// This file should contain a valid secp256k1 private key that will be used for both
|
||||
// NodeKey is the hex-encoded node ID (private key). Should be a valid secp256k1 private key that will be used for both
|
||||
// remote peer identification as well as network traffic encryption.
|
||||
NodeKeyFile string
|
||||
NodeKey string
|
||||
|
||||
// NoDiscovery set to true will disable discovery protocol.
|
||||
NoDiscovery bool
|
||||
|
@ -330,21 +244,17 @@ type NodeConfig struct {
|
|||
// UpstreamConfig extra config for providing upstream infura server.
|
||||
UpstreamConfig UpstreamRPCConfig `json:"UpstreamConfig"`
|
||||
|
||||
// ClusterConfigFile contains the file name of the cluster configuration. If
|
||||
// empty the statical configuration data will be taken.
|
||||
ClusterConfigFile string `json:"ClusterConfigFile"`
|
||||
|
||||
// ClusterConfig extra configuration for supporting cluster peers.
|
||||
ClusterConfig *ClusterConfig `json:"ClusterConfig," validate:"structonly"`
|
||||
ClusterConfig ClusterConfig `json:"ClusterConfig," validate:"structonly"`
|
||||
|
||||
// LightEthConfig extra configuration for LES
|
||||
LightEthConfig *LightEthConfig `json:"LightEthConfig," validate:"structonly"`
|
||||
LightEthConfig LightEthConfig `json:"LightEthConfig," validate:"structonly"`
|
||||
|
||||
// WhisperConfig extra configuration for SHH
|
||||
WhisperConfig *WhisperConfig `json:"WhisperConfig," validate:"structonly"`
|
||||
WhisperConfig WhisperConfig `json:"WhisperConfig," validate:"structonly"`
|
||||
|
||||
// SwarmConfig extra configuration for Swarm and ENS
|
||||
SwarmConfig *SwarmConfig `json:"SwarmConfig," validate:"structonly"`
|
||||
SwarmConfig SwarmConfig `json:"SwarmConfig," validate:"structonly"`
|
||||
|
||||
// RegisterTopics a list of specific topics where the peer wants to be
|
||||
// discoverable.
|
||||
|
@ -364,91 +274,144 @@ type NodeConfig struct {
|
|||
MailServerRegistryAddress string
|
||||
}
|
||||
|
||||
// NewNodeConfig creates new node configuration object
|
||||
func NewNodeConfig(dataDir, clstrCfgFile, fleet string, networkID uint64) (*NodeConfig, error) {
|
||||
// NewNodeConfigWithDefaults creates new node configuration object with some defaults suitable for adhoc use
|
||||
func NewNodeConfigWithDefaults(dataDir, fleet string, networkID uint64) (*NodeConfig, error) {
|
||||
nodeConfig, err := NewNodeConfig(dataDir, fleet, networkID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if dataDir != "" {
|
||||
nodeConfig.KeyStoreDir = path.Join(dataDir, "keystore")
|
||||
nodeConfig.WhisperConfig.DataDir = path.Join(dataDir, "wnode")
|
||||
}
|
||||
|
||||
if fleet != FleetUndefined {
|
||||
statusConfigJSON, err := static.Asset(fmt.Sprintf("../config/cli/fleet-%s.json", fleet))
|
||||
if err == nil {
|
||||
err = LoadConfigFromJSON(string(statusConfigJSON), nodeConfig)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("default config could not be loaded: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
nodeConfig.HTTPHost = ""
|
||||
nodeConfig.ListenAddr = ":30303"
|
||||
nodeConfig.LogEnabled = true
|
||||
nodeConfig.LogLevel = "INFO"
|
||||
nodeConfig.LogToStderr = true
|
||||
nodeConfig.WhisperConfig.Enabled = true
|
||||
nodeConfig.WhisperConfig.EnableNTPSync = true
|
||||
|
||||
return nodeConfig, nil
|
||||
}
|
||||
|
||||
// NewNodeConfig creates new node configuration object with bare-minimum defaults
|
||||
func NewNodeConfig(dataDir, fleet string, networkID uint64) (*NodeConfig, error) {
|
||||
nodeConfig := &NodeConfig{
|
||||
NetworkID: networkID,
|
||||
DataDir: dataDir,
|
||||
Name: ClientIdentifier,
|
||||
Version: Version,
|
||||
RPCEnabled: RPCEnabledDefault,
|
||||
HTTPHost: HTTPHost,
|
||||
HTTPPort: HTTPPort,
|
||||
ListenAddr: ListenAddr,
|
||||
APIModules: APIModules,
|
||||
MaxPeers: MaxPeers,
|
||||
MaxPendingPeers: MaxPendingPeers,
|
||||
IPCFile: IPCFile,
|
||||
RPCEnabled: false,
|
||||
HTTPHost: "localhost",
|
||||
HTTPPort: 8545,
|
||||
ListenAddr: ":0",
|
||||
APIModules: "eth,net,web3,peer",
|
||||
MaxPeers: 25,
|
||||
MaxPendingPeers: 0,
|
||||
IPCFile: "geth.ipc",
|
||||
log: log.New("package", "status-go/params.NodeConfig"),
|
||||
LogFile: LogFile,
|
||||
LogLevel: LogLevel,
|
||||
LogToStderr: LogToStderr,
|
||||
ClusterConfigFile: clstrCfgFile,
|
||||
ClusterConfig: &ClusterConfig{
|
||||
Enabled: true, // cluster must be enabled by default
|
||||
LogFile: "",
|
||||
LogLevel: "ERROR",
|
||||
UpstreamConfig: UpstreamRPCConfig{
|
||||
URL: getUpstreamURL(networkID),
|
||||
},
|
||||
ClusterConfig: ClusterConfig{
|
||||
Enabled: fleet != FleetUndefined,
|
||||
Fleet: fleet,
|
||||
StaticNodes: []string{},
|
||||
BootNodes: []string{},
|
||||
},
|
||||
LightEthConfig: &LightEthConfig{
|
||||
Enabled: true,
|
||||
DatabaseCache: DatabaseCache,
|
||||
LightEthConfig: LightEthConfig{
|
||||
Enabled: false,
|
||||
DatabaseCache: 16,
|
||||
},
|
||||
WhisperConfig: &WhisperConfig{
|
||||
Enabled: true,
|
||||
WhisperConfig: WhisperConfig{
|
||||
Enabled: false,
|
||||
MinimumPoW: WhisperMinimumPoW,
|
||||
TTL: WhisperTTL,
|
||||
FirebaseConfig: &FirebaseConfig{
|
||||
NotificationTriggerURL: FirebaseNotificationTriggerURL,
|
||||
EnableNTPSync: false,
|
||||
},
|
||||
EnableNTPSync: true,
|
||||
},
|
||||
SwarmConfig: &SwarmConfig{},
|
||||
SwarmConfig: SwarmConfig{},
|
||||
RegisterTopics: []discv5.Topic{},
|
||||
RequireTopics: map[discv5.Topic]Limits{},
|
||||
}
|
||||
|
||||
// adjust dependent values
|
||||
if err := nodeConfig.updateConfig(); err != nil {
|
||||
return nodeConfig, nil
|
||||
}
|
||||
|
||||
// NewConfigFromJSON parses incoming JSON and returned it as Config
|
||||
func NewConfigFromJSON(configJSON string) (*NodeConfig, error) {
|
||||
nodeConfig, err := NewNodeConfig("", FleetUndefined, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := LoadConfigFromJSON(configJSON, nodeConfig); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nodeConfig, nil
|
||||
}
|
||||
|
||||
// LoadNodeConfig parses incoming JSON and returned it as Config
|
||||
func LoadNodeConfig(configJSON string) (*NodeConfig, error) {
|
||||
nodeConfig, err := loadNodeConfig(configJSON)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
// LoadConfigFromJSON parses incoming JSON and returned it as Config
|
||||
func LoadConfigFromJSON(configJSON string, nodeConfig *NodeConfig) error {
|
||||
if err := loadNodeConfig(configJSON, nodeConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := nodeConfig.Validate(); err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
|
||||
return nodeConfig, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func loadNodeConfig(configJSON string) (*NodeConfig, error) {
|
||||
nodeConfig, err := NewNodeConfig("", "", FleetUndefined, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func loadNodeConfig(configJSON string, nodeConfig *NodeConfig) error {
|
||||
decoder := json.NewDecoder(strings.NewReader(configJSON))
|
||||
|
||||
// override default configuration with values by JSON input
|
||||
if err := decoder.Decode(&nodeConfig); err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
|
||||
// repopulate
|
||||
if err := nodeConfig.updateConfig(); err != nil {
|
||||
return nil, err
|
||||
return nil
|
||||
}
|
||||
|
||||
func loadConfigConfigFromFile(path string, config *NodeConfig) error {
|
||||
jsonConfig, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nodeConfig, nil
|
||||
if err = loadNodeConfig(string(jsonConfig), config); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// LoadConfigFromFiles reads the configuration files specified in configFilePaths,
|
||||
// merging the values in order in the config argument
|
||||
func LoadConfigFromFiles(configFilePaths []string, config *NodeConfig) error {
|
||||
for _, path := range configFilePaths {
|
||||
if err := loadConfigConfigFromFile(path, config); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate checks if NodeConfig fields have valid values.
|
||||
|
@ -473,33 +436,159 @@ func (c *NodeConfig) Validate() error {
|
|||
return err
|
||||
}
|
||||
|
||||
if c.ClusterConfig.Enabled {
|
||||
if err := validate.Struct(c.ClusterConfig); err != nil {
|
||||
return err
|
||||
if c.NodeKey != "" {
|
||||
if _, err := crypto.HexToECDSA(c.NodeKey); err != nil {
|
||||
return fmt.Errorf("NodeKey is invalid (%s): %v", c.NodeKey, err)
|
||||
}
|
||||
}
|
||||
|
||||
if c.LightEthConfig.Enabled {
|
||||
if err := validate.Struct(c.LightEthConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
if c.UpstreamConfig.Enabled && c.LightEthConfig.Enabled {
|
||||
return fmt.Errorf("both UpstreamConfig and LightEthConfig are enabled, but they are mutually exclusive")
|
||||
}
|
||||
|
||||
if c.WhisperConfig.Enabled {
|
||||
if err := validate.Struct(c.WhisperConfig); err != nil {
|
||||
if err := c.validateChildStructs(validate); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if c.SwarmConfig.Enabled {
|
||||
if err := validate.Struct(c.SwarmConfig); err != nil {
|
||||
if !c.NoDiscovery && len(c.ClusterConfig.BootNodes) == 0 {
|
||||
// No point in running discovery if we don't have bootnodes.
|
||||
// In case we do have bootnodes, NoDiscovery should be true.
|
||||
return fmt.Errorf("NoDiscovery is false, but ClusterConfig.BootNodes is empty")
|
||||
}
|
||||
|
||||
if len(c.ClusterConfig.RendezvousNodes) == 0 {
|
||||
if c.Rendezvous {
|
||||
return fmt.Errorf("Rendezvous is enabled, but ClusterConfig.RendezvousNodes is empty")
|
||||
}
|
||||
} else if !c.Rendezvous {
|
||||
return fmt.Errorf("Rendezvous is disabled, but ClusterConfig.RendezvousNodes is not empty")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *NodeConfig) validateChildStructs(validate *validator.Validate) error {
|
||||
// Validate child structs
|
||||
if err := c.UpstreamConfig.Validate(validate); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.ClusterConfig.Validate(validate); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.LightEthConfig.Validate(validate); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.WhisperConfig.Validate(validate); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.SwarmConfig.Validate(validate); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate validates the UpstreamRPCConfig struct and returns an error if inconsistent values are found
|
||||
func (c *UpstreamRPCConfig) Validate(validate *validator.Validate) error {
|
||||
if !c.Enabled {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := validate.Struct(c); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := url.ParseRequestURI(c.URL); err != nil {
|
||||
return fmt.Errorf("UpstreamRPCConfig.URL '%s' is invalid: %v", c.URL, err.Error())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate validates the ClusterConfig struct and returns an error if inconsistent values are found
|
||||
func (c *ClusterConfig) Validate(validate *validator.Validate) error {
|
||||
if !c.Enabled {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := validate.Struct(c); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if c.Fleet == "" {
|
||||
return fmt.Errorf("ClusterConfig.Fleet is empty")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate validates the LightEthConfig struct and returns an error if inconsistent values are found
|
||||
func (c *LightEthConfig) Validate(validate *validator.Validate) error {
|
||||
if !c.Enabled {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := validate.Struct(c); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate validates the WhisperConfig struct and returns an error if inconsistent values are found
|
||||
func (c *WhisperConfig) Validate(validate *validator.Validate) error {
|
||||
if !c.Enabled {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := validate.Struct(c); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if c.EnableMailServer {
|
||||
if c.DataDir == "" {
|
||||
return fmt.Errorf("WhisperConfig.DataDir must be specified when WhisperConfig.EnableMailServer is true")
|
||||
}
|
||||
if c.MailServerPassword == "" && c.MailServerAsymKey == "" {
|
||||
return fmt.Errorf("WhisperConfig.MailServerPassword or WhisperConfig.MailServerAsymKey must be specified when WhisperConfig.EnableMailServer is true")
|
||||
}
|
||||
|
||||
if c.MailServerAsymKey != "" {
|
||||
if _, err := crypto.HexToECDSA(c.MailServerAsymKey); err != nil {
|
||||
return fmt.Errorf("WhisperConfig.MailServerAsymKey is invalid: %s", c.MailServerAsymKey)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate validates the SwarmConfig struct and returns an error if inconsistent values are found
|
||||
func (c *SwarmConfig) Validate(validate *validator.Validate) error {
|
||||
if !c.Enabled {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := validate.Struct(c); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getUpstreamURL(networkID uint64) string {
|
||||
switch networkID {
|
||||
case MainNetworkID:
|
||||
return MainnetEthereumNetworkURL
|
||||
case RopstenNetworkID:
|
||||
return RopstenEthereumNetworkURL
|
||||
case RinkebyNetworkID:
|
||||
return RinkebyEthereumNetworkURL
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
// Save dumps configuration to the disk
|
||||
func (c *NodeConfig) Save() error {
|
||||
data, err := json.MarshalIndent(c, "", " ")
|
||||
|
@ -520,176 +609,6 @@ func (c *NodeConfig) Save() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// updateConfig traverses configuration and adjusts dependent fields
|
||||
// (we have a development/production and mobile/full node dependent configurations)
|
||||
func (c *NodeConfig) updateConfig() error {
|
||||
// Update separate configurations.
|
||||
if err := c.updateGenesisConfig(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.updateUpstreamConfig(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.updateClusterConfig(); err != nil {
|
||||
return err
|
||||
}
|
||||
c.updatePeerLimits()
|
||||
return c.updateRelativeDirsConfig()
|
||||
}
|
||||
|
||||
// updateGenesisConfig does necessary adjustments to config object (depending on network node will be running on)
|
||||
func (c *NodeConfig) updateGenesisConfig() error {
|
||||
var genesis *core.Genesis
|
||||
|
||||
switch c.NetworkID {
|
||||
case MainNetworkID:
|
||||
genesis = core.DefaultGenesisBlock()
|
||||
case RopstenNetworkID:
|
||||
genesis = core.DefaultTestnetGenesisBlock()
|
||||
case RinkebyNetworkID:
|
||||
genesis = core.DefaultRinkebyGenesisBlock()
|
||||
case StatusChainNetworkID:
|
||||
var err error
|
||||
genesis, err = c.DefaultStatusChainGenesisBlock()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
|
||||
// encode the genesis into JSON
|
||||
enc, err := json.Marshal(genesis)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.LightEthConfig.Genesis = string(enc)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DefaultStatusChainGenesisBlock returns the StatusChain network genesis block.
|
||||
func (c *NodeConfig) DefaultStatusChainGenesisBlock() (*core.Genesis, error) {
|
||||
genesisJSON, err := ioutil.ReadFile(path.Join(GetStatusHome(), "static/config/status-chain-genesis.json"))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("status-chain-genesis.json could not be loaded: %s", err)
|
||||
}
|
||||
|
||||
var genesis *core.Genesis
|
||||
err = json.Unmarshal(genesisJSON, &genesis)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot unmarshal status-chain-genesis.json: %s", err)
|
||||
}
|
||||
return genesis, nil
|
||||
}
|
||||
|
||||
// updateUpstreamConfig sets the proper UpstreamConfig.URL for the network id being used.
|
||||
func (c *NodeConfig) updateUpstreamConfig() error {
|
||||
|
||||
// If we have a URL already set then keep URL incase
|
||||
// of custom server.
|
||||
if c.UpstreamConfig.URL != "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
switch c.NetworkID {
|
||||
case MainNetworkID:
|
||||
c.UpstreamConfig.URL = MainnetEthereumNetworkURL
|
||||
case RopstenNetworkID:
|
||||
c.UpstreamConfig.URL = RopstenEthereumNetworkURL
|
||||
case RinkebyNetworkID:
|
||||
c.UpstreamConfig.URL = RinkebyEthereumNetworkURL
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// updateClusterConfig loads static peer nodes and CHT for a given network and mode.
|
||||
// This is necessary until we have LES protocol support CHT sync, and better node
|
||||
// discovery on mobile devices)
|
||||
func (c *NodeConfig) updateClusterConfig() error {
|
||||
if !c.ClusterConfig.Enabled {
|
||||
return nil
|
||||
}
|
||||
|
||||
c.log.Info("update cluster config", "configFile", c.ClusterConfigFile, "fleet", c.ClusterConfig.Fleet)
|
||||
|
||||
var cluster Cluster
|
||||
|
||||
if c.ClusterConfigFile != "" {
|
||||
// Load cluster configuration from external file.
|
||||
configFile, err := ioutil.ReadFile(c.ClusterConfigFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cluster configuration file '%s' could not be loaded: %s", c.ClusterConfigFile, err)
|
||||
}
|
||||
err = json.Unmarshal(configFile, &cluster)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to unmarshal cluster configuration file: %s", err)
|
||||
}
|
||||
} else {
|
||||
cluster, _ = ClusterForFleet(c.ClusterConfig.Fleet)
|
||||
}
|
||||
|
||||
// allow to override bootnodes only if they were not defined earlier
|
||||
if len(c.ClusterConfig.BootNodes) == 0 {
|
||||
c.ClusterConfig.BootNodes = cluster.BootNodes
|
||||
}
|
||||
// allow to override static nodes only if they were not defined earlier
|
||||
if len(c.ClusterConfig.StaticNodes) == 0 {
|
||||
c.ClusterConfig.StaticNodes = cluster.StaticNodes
|
||||
}
|
||||
// No point in running discovery if we don't have bootnodes.
|
||||
// In a case when we do have bootnodes, NoDiscovery=true is preserved.
|
||||
if len(cluster.BootNodes) == 0 {
|
||||
c.NoDiscovery = true
|
||||
}
|
||||
if len(c.ClusterConfig.RendezvousNodes) == 0 {
|
||||
c.ClusterConfig.RendezvousNodes = cluster.RendezvousNodes
|
||||
}
|
||||
if len(c.ClusterConfig.RendezvousNodes) != 0 {
|
||||
c.Rendezvous = true
|
||||
}
|
||||
c.ClusterConfig.TrustedMailServers = cluster.MailServers
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// updateRelativeDirsConfig updates directories that should be wrt to DataDir
|
||||
func (c *NodeConfig) updateRelativeDirsConfig() error {
|
||||
makeSubDirPath := func(baseDir, subDir string) string {
|
||||
if len(baseDir) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
return filepath.Join(baseDir, subDir)
|
||||
}
|
||||
if len(c.KeyStoreDir) == 0 {
|
||||
c.KeyStoreDir = makeSubDirPath(c.DataDir, KeyStoreDir)
|
||||
}
|
||||
|
||||
if len(c.WhisperConfig.DataDir) == 0 {
|
||||
c.WhisperConfig.DataDir = makeSubDirPath(c.DataDir, WhisperDataDir)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// updatePeerLimits will set default peer limits expectations based on enabled services.
|
||||
func (c *NodeConfig) updatePeerLimits() {
|
||||
if c.NoDiscovery && !c.Rendezvous {
|
||||
return
|
||||
}
|
||||
if c.WhisperConfig.Enabled {
|
||||
c.RequireTopics[WhisperDiscv5Topic] = WhisperDiscv5Limits
|
||||
// TODO(dshulyak) register mailserver limits when we will change how they are handled.
|
||||
}
|
||||
if c.LightEthConfig.Enabled {
|
||||
c.RequireTopics[discv5.Topic(LesTopic(int(c.NetworkID)))] = LesDiscoveryLimits
|
||||
}
|
||||
}
|
||||
|
||||
// String dumps config object as nicely indented JSON
|
||||
func (c *NodeConfig) String() string {
|
||||
data, _ := json.MarshalIndent(c, "", " ")
|
||||
|
|
|
@ -5,32 +5,82 @@ import (
|
|||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"gopkg.in/go-playground/validator.v9"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
gethparams "github.com/ethereum/go-ethereum/params"
|
||||
"github.com/status-im/status-go/params"
|
||||
"github.com/status-im/status-go/t/utils"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var clusterConfigData = []byte(`{
|
||||
"ClusterConfig": {
|
||||
"staticnodes": [
|
||||
"enode://7ab298cedc4185a894d21d8a4615262ec6bdce66c9b6783878258e0d5b31013d30c9038932432f70e5b2b6a5cd323bf820554fcb22fbc7b45367889522e9c449@10.1.1.1:30303",
|
||||
"enode://f59e8701f18c79c5cbc7618dc7bb928d44dc2f5405c7d693dad97da2d8585975942ec6fd36d3fe608bfdc7270a34a4dd00f38cfe96b2baa24f7cd0ac28d382a1@10.1.1.2:30303"
|
||||
]
|
||||
}
|
||||
}`)
|
||||
|
||||
func TestLoadNodeConfigFromNonExistingFile(t *testing.T) {
|
||||
_, err := params.LoadNodeConfig(`{
|
||||
"NetworkId": 3,
|
||||
"DataDir": "/tmp/statusgo",
|
||||
"ClusterConfigFile": "/file/does/not.exist"
|
||||
}`)
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), "no such file or directory")
|
||||
var clusters = map[string]func() params.Cluster{
|
||||
params.FleetStaging: func() params.Cluster {
|
||||
return params.Cluster{
|
||||
BootNodes: []string{
|
||||
"enode://10a78c17929a7019ef4aa2249d7302f76ae8a06f40b2dc88b7b31ebff4a623fbb44b4a627acba296c1ced3775d91fbe18463c15097a6a36fdb2c804ff3fc5b35@35.238.97.234:30404", // boot-01.gc-us-central1-a.eth.staging
|
||||
"enode://f79fb3919f72ca560ad0434dcc387abfe41e0666201ebdada8ede0462454a13deb05cda15f287d2c4bd85da81f0eb25d0a486bbbc8df427b971ac51533bd00fe@174.138.107.239:30404", // boot-01.do-ams3.eth.staging
|
||||
},
|
||||
StaticNodes: []string{
|
||||
"enode://914c0b30f27bab30c1dfd31dad7652a46fda9370542aee1b062498b1345ee0913614b8b9e3e84622e84a7203c5858ae1d9819f63aece13ee668e4f6668063989@167.99.19.148:30305", // node-01.do-ams3.eth.staging
|
||||
"enode://2d897c6e846949f9dcf10279f00e9b8325c18fe7fa52d658520ad7be9607c83008b42b06aefd97cfe1fdab571f33a2a9383ff97c5909ed51f63300834913237e@35.192.0.86:30305", // "node-01.gc-us-central1-a.eth.staging"
|
||||
},
|
||||
MailServers: []string{
|
||||
"enode://69f72baa7f1722d111a8c9c68c39a31430e9d567695f6108f31ccb6cd8f0adff4991e7fdca8fa770e75bc8a511a87d24690cbc80e008175f40c157d6f6788d48@206.189.240.16:30504", // mail-01.do-ams3.eth.staging
|
||||
"enode://e4fc10c1f65c8aed83ac26bc1bfb21a45cc1a8550a58077c8d2de2a0e0cd18e40fd40f7e6f7d02dc6cd06982b014ce88d6e468725ffe2c138e958788d0002a7f@35.239.193.41:30504", // mail-01.gc-us-central1-a.eth.staging
|
||||
},
|
||||
RendezvousNodes: []string{
|
||||
"/ip4/174.138.107.239/tcp/30703/ethv4/16Uiu2HAkyJHeetQ4DNpd4NZ2ntzxMo25zcdpvGQRqkD5pB9BE6RU",
|
||||
"/ip4/35.238.97.234/tcp/30703/ethv4/16Uiu2HAm1sVyXmkMNjdeDWqK2urbyC3oBHi8MDpCdYkns1nYafqz",
|
||||
},
|
||||
}
|
||||
},
|
||||
params.FleetBeta: func() params.Cluster {
|
||||
return params.Cluster{
|
||||
BootNodes: []string{
|
||||
"enode://436cc6f674928fdc9a9f7990f2944002b685d1c37f025c1be425185b5b1f0900feaf1ccc2a6130268f9901be4a7d252f37302c8335a2c1a62736e9232691cc3a@174.138.105.243:30404", // boot-01.do-ams3.eth.beta
|
||||
"enode://5395aab7833f1ecb671b59bf0521cf20224fe8162fc3d2675de4ee4d5636a75ec32d13268fc184df8d1ddfa803943906882da62a4df42d4fccf6d17808156a87@206.189.243.57:30404", // boot-02.do-ams3.eth.beta
|
||||
"enode://7427dfe38bd4cf7c58bb96417806fab25782ec3e6046a8053370022cbaa281536e8d64ecd1b02e1f8f72768e295d06258ba43d88304db068e6f2417ae8bcb9a6@104.154.88.123:30404", // boot-01.gc-us-central1-a.eth.beta
|
||||
"enode://ebefab39b69bbbe64d8cd86be765b3be356d8c4b24660f65d493143a0c44f38c85a257300178f7845592a1b0332811542e9a58281c835babdd7535babb64efc1@35.202.99.224:30404", // boot-02.gc-us-central1-a.eth.beta
|
||||
},
|
||||
StaticNodes: []string{
|
||||
"enode://a6a2a9b3a7cbb0a15da74301537ebba549c990e3325ae78e1272a19a3ace150d03c184b8ac86cc33f1f2f63691e467d49308f02d613277754c4dccd6773b95e8@206.189.243.176:30304", // node-01.do-ams3.eth.beta
|
||||
"enode://207e53d9bf66be7441e3daba36f53bfbda0b6099dba9a865afc6260a2d253fb8a56a72a48598a4f7ba271792c2e4a8e1a43aaef7f34857f520c8c820f63b44c8@35.224.15.65:30304", // node-01.gc-us-central1-a.eth.beta
|
||||
},
|
||||
MailServers: []string{
|
||||
"enode://c42f368a23fa98ee546fd247220759062323249ef657d26d357a777443aec04db1b29a3a22ef3e7c548e18493ddaf51a31b0aed6079bd6ebe5ae838fcfaf3a49@206.189.243.162:30504", // mail-01.do-ams3.eth.beta
|
||||
"enode://7aa648d6e855950b2e3d3bf220c496e0cae4adfddef3e1e6062e6b177aec93bc6cdcf1282cb40d1656932ebfdd565729da440368d7c4da7dbd4d004b1ac02bf8@206.189.243.169:30504", // mail-02.do-ams3.eth.beta
|
||||
"enode://8a64b3c349a2e0ef4a32ea49609ed6eb3364be1110253c20adc17a3cebbc39a219e5d3e13b151c0eee5d8e0f9a8ba2cd026014e67b41a4ab7d1d5dd67ca27427@206.189.243.168:30504", // mail-03.do-ams3.eth.beta
|
||||
"enode://7de99e4cb1b3523bd26ca212369540646607c721ad4f3e5c821ed9148150ce6ce2e72631723002210fac1fd52dfa8bbdf3555e05379af79515e1179da37cc3db@35.188.19.210:30504", // mail-01.gc-us-central1-a.eth.beta
|
||||
"enode://015e22f6cd2b44c8a51bd7a23555e271e0759c7d7f52432719665a74966f2da456d28e154e836bee6092b4d686fe67e331655586c57b718be3997c1629d24167@35.226.21.19:30504", // mail-02.gc-us-central1-a.eth.beta
|
||||
"enode://531e252ec966b7e83f5538c19bf1cde7381cc7949026a6e499b6e998e695751aadf26d4c98d5a4eabfb7cefd31c3c88d600a775f14ed5781520a88ecd25da3c6@35.225.227.79:30504", // mail-03.gc-us-central1-a.eth.beta
|
||||
},
|
||||
RendezvousNodes: []string{
|
||||
"/ip4/174.138.105.243/tcp/30703/ethv4/16Uiu2HAmRHPzF3rQg55PgYPcQkyvPVH9n2hWsYPhUJBZ6kVjJgdV", // boot-01.do-ams3.eth.beta
|
||||
"/ip4/206.189.243.57/tcp/30703/ethv4/16Uiu2HAmLqTXuY4Sb6G28HNooaFUXUKzpzKXCcgyJxgaEE2i5vnf", // boot-02.do-ams3.eth.beta
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// ClusterForFleet returns a cluster for a given fleet.
|
||||
func ClusterForFleet(fleet string) (params.Cluster, bool) {
|
||||
cluster, ok := clusters[fleet]
|
||||
if ok {
|
||||
return cluster(), true
|
||||
}
|
||||
return params.Cluster{}, false
|
||||
}
|
||||
|
||||
func TestLoadNodeConfigFromFile(t *testing.T) {
|
||||
|
@ -44,14 +94,21 @@ func TestLoadNodeConfigFromFile(t *testing.T) {
|
|||
clusterFile := filepath.Join(tmpDir, "cluster.json")
|
||||
err = ioutil.WriteFile(clusterFile, clusterConfigData, os.ModePerm)
|
||||
require.NoError(t, err)
|
||||
defer os.Remove(clusterFile)
|
||||
|
||||
c, err := params.LoadNodeConfig(`{
|
||||
c, err := params.NewConfigFromJSON(`{
|
||||
"NetworkId": 3,
|
||||
"DataDir": "` + tmpDir + `",
|
||||
"ClusterConfigFile": "` + clusterFile + `"
|
||||
"KeyStoreDir": "` + tmpDir + `",
|
||||
"NoDiscovery": true
|
||||
}`)
|
||||
require.NoError(t, err)
|
||||
require.True(t, c.ClusterConfig.Enabled)
|
||||
err = params.LoadConfigFromFiles([]string{clusterFile}, c)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, uint64(3), c.NetworkID)
|
||||
require.Equal(t, tmpDir, c.DataDir)
|
||||
require.Equal(t, tmpDir, c.KeyStoreDir)
|
||||
require.False(t, c.ClusterConfig.Enabled)
|
||||
require.Len(t, c.ClusterConfig.StaticNodes, 2)
|
||||
}
|
||||
|
||||
|
@ -72,21 +129,16 @@ func TestGenerateAndLoadNodeConfig(t *testing.T) {
|
|||
Validate func(t *testing.T, dataDir string, c *params.NodeConfig)
|
||||
}{
|
||||
{
|
||||
Name: "default KeyStoreDir",
|
||||
Update: func(config *params.NodeConfig) {},
|
||||
Name: "DataDir and KeyStoreDir specified",
|
||||
Update: func(c *params.NodeConfig) {},
|
||||
Validate: func(t *testing.T, dataDir string, c *params.NodeConfig) {
|
||||
require.Equal(t, dataDir, c.DataDir)
|
||||
keyStoreDir := filepath.Join(dataDir, params.KeyStoreDir)
|
||||
require.Equal(t, keyStoreDir, c.KeyStoreDir)
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "non-default KeyStoreDir",
|
||||
Update: func(c *params.NodeConfig) {
|
||||
c.KeyStoreDir = "/foo/bar"
|
||||
},
|
||||
Validate: func(t *testing.T, dataDir string, c *params.NodeConfig) {
|
||||
require.Equal(t, "/foo/bar", c.KeyStoreDir)
|
||||
require.Equal(t, tmpDir, c.DataDir)
|
||||
require.Equal(t, tmpDir, c.KeyStoreDir)
|
||||
require.False(t, c.UpstreamConfig.Enabled)
|
||||
require.Equal(t, c.ClusterConfig.Fleet != params.FleetUndefined, c.ClusterConfig.Enabled)
|
||||
require.True(t, c.WhisperConfig.Enabled)
|
||||
require.False(t, c.LightEthConfig.Enabled)
|
||||
require.False(t, c.SwarmConfig.Enabled)
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -117,32 +169,38 @@ func TestGenerateAndLoadNodeConfig(t *testing.T) {
|
|||
{
|
||||
Name: "loading LES config",
|
||||
NetworkID: params.MainNetworkID,
|
||||
Update: func(c *params.NodeConfig) {},
|
||||
Update: func(c *params.NodeConfig) {
|
||||
c.LightEthConfig.Enabled = true
|
||||
},
|
||||
Validate: func(t *testing.T, dataDir string, c *params.NodeConfig) {
|
||||
var genesis core.Genesis
|
||||
err := json.Unmarshal([]byte(c.LightEthConfig.Genesis), &genesis)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Zero(t, genesis.Config.ChainID.Cmp(gethparams.MainnetChainConfig.ChainID))
|
||||
require.Zero(t, genesis.Config.HomesteadBlock.Cmp(gethparams.MainnetChainConfig.HomesteadBlock))
|
||||
require.Zero(t, genesis.Config.EIP150Block.Cmp(gethparams.MainnetChainConfig.EIP150Block))
|
||||
require.Zero(t, genesis.Config.EIP155Block.Cmp(gethparams.MainnetChainConfig.EIP155Block))
|
||||
require.Zero(t, genesis.Config.EIP158Block.Cmp(gethparams.MainnetChainConfig.EIP158Block))
|
||||
require.True(t, c.LightEthConfig.Enabled)
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "cluster nodes setup",
|
||||
Update: func(c *params.NodeConfig) {},
|
||||
Update: func(c *params.NodeConfig) {
|
||||
cc, _ := ClusterForFleet(c.ClusterConfig.Fleet)
|
||||
c.Rendezvous = true
|
||||
c.ClusterConfig.BootNodes = cc.BootNodes
|
||||
c.ClusterConfig.StaticNodes = cc.StaticNodes
|
||||
c.ClusterConfig.RendezvousNodes = cc.RendezvousNodes
|
||||
c.ClusterConfig.TrustedMailServers = cc.MailServers
|
||||
c.ClusterConfig.Enabled = true
|
||||
c.RequireTopics[params.WhisperDiscv5Topic] = params.WhisperDiscv5Limits
|
||||
},
|
||||
Validate: func(t *testing.T, dataDir string, c *params.NodeConfig) {
|
||||
require.True(t, c.Rendezvous)
|
||||
require.True(t, c.ClusterConfig.Enabled)
|
||||
require.NotEmpty(t, c.ClusterConfig.BootNodes)
|
||||
require.NotEmpty(t, c.ClusterConfig.StaticNodes)
|
||||
require.NotEmpty(t, c.ClusterConfig.TrustedMailServers)
|
||||
require.Equal(t, params.WhisperDiscv5Limits, c.RequireTopics[params.WhisperDiscv5Topic])
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "custom bootnodes",
|
||||
Update: func(c *params.NodeConfig) {
|
||||
c.ClusterConfig.Enabled = true
|
||||
c.ClusterConfig.BootNodes = []string{"a", "b", "c"}
|
||||
},
|
||||
Validate: func(t *testing.T, dataDir string, c *params.NodeConfig) {
|
||||
|
@ -151,9 +209,10 @@ func TestGenerateAndLoadNodeConfig(t *testing.T) {
|
|||
},
|
||||
},
|
||||
{
|
||||
Name: "disabled ClusterConfiguration",
|
||||
Name: "disabled Cluster configuration",
|
||||
Update: func(c *params.NodeConfig) {
|
||||
c.ClusterConfig.Enabled = false
|
||||
c.ClusterConfig.BootNodes = []string{"a", "b", "c"}
|
||||
},
|
||||
Validate: func(t *testing.T, dataDir string, c *params.NodeConfig) {
|
||||
require.False(t, c.ClusterConfig.Enabled)
|
||||
|
@ -161,7 +220,11 @@ func TestGenerateAndLoadNodeConfig(t *testing.T) {
|
|||
},
|
||||
{
|
||||
Name: "peers discovery and topics",
|
||||
Update: func(c *params.NodeConfig) {},
|
||||
Update: func(c *params.NodeConfig) {
|
||||
c.NoDiscovery = false
|
||||
c.ClusterConfig.BootNodes = []string{"a", "b", "c"}
|
||||
c.RequireTopics[params.WhisperDiscv5Topic] = params.Limits{2, 2}
|
||||
},
|
||||
Validate: func(t *testing.T, dataDir string, c *params.NodeConfig) {
|
||||
require.NotNil(t, c.RequireTopics)
|
||||
require.False(t, c.NoDiscovery)
|
||||
|
@ -181,11 +244,16 @@ func TestGenerateAndLoadNodeConfig(t *testing.T) {
|
|||
{
|
||||
Name: "staging fleet",
|
||||
Fleet: params.FleetStaging,
|
||||
Update: func(c *params.NodeConfig) {},
|
||||
Update: func(c *params.NodeConfig) {
|
||||
c.ClusterConfig.Enabled = true
|
||||
c.ClusterConfig.Fleet = "eth.staging"
|
||||
cc, _ := ClusterForFleet(c.ClusterConfig.Fleet)
|
||||
c.ClusterConfig.BootNodes = cc.BootNodes
|
||||
},
|
||||
Validate: func(t *testing.T, dataDir string, c *params.NodeConfig) {
|
||||
staging, ok := params.ClusterForFleet("eth.staging")
|
||||
staging, ok := ClusterForFleet("eth.staging")
|
||||
require.True(t, ok)
|
||||
beta, ok := params.ClusterForFleet("eth.beta")
|
||||
beta, ok := ClusterForFleet("eth.beta")
|
||||
require.True(t, ok)
|
||||
|
||||
require.NotEqual(t, staging, beta)
|
||||
|
@ -198,9 +266,13 @@ func TestGenerateAndLoadNodeConfig(t *testing.T) {
|
|||
{
|
||||
Name: "Whisper light client",
|
||||
Update: func(c *params.NodeConfig) {
|
||||
c.WhisperConfig.Enabled = true
|
||||
c.WhisperConfig.DataDir = path.Join(tmpDir, "wnode")
|
||||
c.WhisperConfig.LightClient = true
|
||||
},
|
||||
Validate: func(t *testing.T, dataDir string, c *params.NodeConfig) {
|
||||
require.Equal(t, path.Join(tmpDir, "wnode"), c.WhisperConfig.DataDir)
|
||||
require.True(t, c.WhisperConfig.Enabled)
|
||||
require.True(t, c.WhisperConfig.LightClient)
|
||||
},
|
||||
},
|
||||
|
@ -221,9 +293,9 @@ func TestGenerateAndLoadNodeConfig(t *testing.T) {
|
|||
for _, networkID := range networks {
|
||||
name := fmt.Sprintf("%s_%s_%d", tc.Name, fleet, networkID)
|
||||
t.Run(name, func(t *testing.T) {
|
||||
// Corresponds to GenerateConfig() binding.
|
||||
config, err := params.NewNodeConfig(tmpDir, "", fleet, uint64(networkID))
|
||||
config, err := utils.MakeTestNodeConfigWithDataDir("", tmpDir, fleet, uint64(networkID))
|
||||
require.NoError(t, err)
|
||||
config.KeyStoreDir = tmpDir
|
||||
|
||||
// Corresponds to config update in status-react.
|
||||
tc.Update(config)
|
||||
|
@ -231,7 +303,7 @@ func TestGenerateAndLoadNodeConfig(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
|
||||
// Corresponds to starting node and loading config from JSON blob.
|
||||
loadedConfig, err := params.LoadNodeConfig(string(configBytes))
|
||||
loadedConfig, err := params.NewConfigFromJSON(string(configBytes))
|
||||
require.NoError(t, err)
|
||||
tc.Validate(t, tmpDir, loadedConfig)
|
||||
})
|
||||
|
@ -245,7 +317,7 @@ func TestConfigWriteRead(t *testing.T) {
|
|||
require.Nil(t, err)
|
||||
defer os.RemoveAll(tmpDir) // nolint: errcheck
|
||||
|
||||
nodeConfig, err := params.NewNodeConfig(tmpDir, "", params.FleetBeta, params.RopstenNetworkID)
|
||||
nodeConfig, err := utils.MakeTestNodeConfigWithDataDir("", tmpDir, params.FleetBeta, params.RopstenNetworkID)
|
||||
require.Nil(t, err, "cannot create new config object")
|
||||
|
||||
err = nodeConfig.Save()
|
||||
|
@ -253,8 +325,10 @@ func TestConfigWriteRead(t *testing.T) {
|
|||
|
||||
loadedConfigData, err := ioutil.ReadFile(filepath.Join(nodeConfig.DataDir, "config.json"))
|
||||
require.Nil(t, err, "cannot read configuration from disk")
|
||||
require.Contains(t, string(loadedConfigData), fmt.Sprintf(`"NetworkId": %d`, params.RopstenNetworkID))
|
||||
require.Contains(t, string(loadedConfigData), fmt.Sprintf(`"DataDir": "%s"`, tmpDir))
|
||||
loadedConfig := string(loadedConfigData)
|
||||
require.Contains(t, loadedConfig, fmt.Sprintf(`"NetworkId": %d`, params.RopstenNetworkID))
|
||||
require.Contains(t, loadedConfig, fmt.Sprintf(`"DataDir": "%s"`, tmpDir))
|
||||
require.Contains(t, loadedConfig, fmt.Sprintf(`"Fleet": "%s"`, params.FleetBeta))
|
||||
}
|
||||
|
||||
// TestNodeConfigValidate checks validation of individual fields.
|
||||
|
@ -264,55 +338,250 @@ func TestNodeConfigValidate(t *testing.T) {
|
|||
Config string
|
||||
Error string
|
||||
FieldErrors map[string]string // map[Field]Tag
|
||||
CheckFunc func(*testing.T, *params.NodeConfig)
|
||||
}{
|
||||
{
|
||||
Name: "Valid JSON config",
|
||||
Config: `{
|
||||
"NetworkId": 1,
|
||||
"DataDir": "/tmp/data"
|
||||
"DataDir": "/tmp/data",
|
||||
"KeyStoreDir": "/tmp/data",
|
||||
"NoDiscovery": true
|
||||
}`,
|
||||
Error: "",
|
||||
FieldErrors: nil,
|
||||
},
|
||||
{
|
||||
Name: "Invalid JSON config",
|
||||
Config: `{"NetworkId": }`,
|
||||
Error: "invalid character '}'",
|
||||
FieldErrors: nil,
|
||||
},
|
||||
{
|
||||
Name: "Invalid field type",
|
||||
Config: `{"NetworkId": "abc"}`,
|
||||
Error: "json: cannot unmarshal string into Go struct field",
|
||||
FieldErrors: nil,
|
||||
},
|
||||
{
|
||||
Name: "Validate all required fields",
|
||||
Config: `{}`,
|
||||
Error: "",
|
||||
FieldErrors: map[string]string{
|
||||
"NetworkID": "required",
|
||||
"DataDir": "required",
|
||||
"KeyStoreDir": "required",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "Validate Name does not contain slash",
|
||||
Name: "Validate that Name does not contain slash",
|
||||
Config: `{
|
||||
"NetworkId": 1,
|
||||
"DataDir": "/some/dir",
|
||||
"KeyStoreDir": "/some/dir",
|
||||
"Name": "invalid/name"
|
||||
}`,
|
||||
Error: "",
|
||||
FieldErrors: map[string]string{
|
||||
"Name": "excludes",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "Validate that NodeKey is checked for validity",
|
||||
Config: `{
|
||||
"NetworkId": 1,
|
||||
"DataDir": "/some/dir",
|
||||
"KeyStoreDir": "/some/dir",
|
||||
"NoDiscovery": true,
|
||||
"NodeKey": "foo"
|
||||
}`,
|
||||
Error: "NodeKey is invalid",
|
||||
},
|
||||
{
|
||||
Name: "Validate that UpstreamConfig.URL is validated if UpstreamConfig is enabled",
|
||||
Config: `{
|
||||
"NetworkId": 1,
|
||||
"DataDir": "/some/dir",
|
||||
"KeyStoreDir": "/some/dir",
|
||||
"NoDiscovery": true,
|
||||
"UpstreamConfig": {
|
||||
"Enabled": true,
|
||||
"URL": "[bad.url]"
|
||||
}
|
||||
}`,
|
||||
Error: "'[bad.url]' is invalid",
|
||||
},
|
||||
{
|
||||
Name: "Validate that UpstreamConfig.URL is not validated if UpstreamConfig is disabled",
|
||||
Config: `{
|
||||
"NetworkId": 1,
|
||||
"DataDir": "/some/dir",
|
||||
"KeyStoreDir": "/some/dir",
|
||||
"NoDiscovery": true,
|
||||
"UpstreamConfig": {
|
||||
"Enabled": false,
|
||||
"URL": "[bad.url]"
|
||||
}
|
||||
}`,
|
||||
},
|
||||
{
|
||||
Name: "Validate that UpstreamConfig.URL validation passes if UpstreamConfig.URL is valid",
|
||||
Config: `{
|
||||
"NetworkId": 1,
|
||||
"DataDir": "/some/dir",
|
||||
"KeyStoreDir": "/some/dir",
|
||||
"NoDiscovery": true,
|
||||
"UpstreamConfig": {
|
||||
"Enabled": true,
|
||||
"URL": "` + params.MainnetEthereumNetworkURL + `"
|
||||
}
|
||||
}`,
|
||||
},
|
||||
{
|
||||
Name: "Validate that ClusterConfig.Fleet is verified to not be empty if ClusterConfig is enabled",
|
||||
Config: `{
|
||||
"NetworkId": 1,
|
||||
"DataDir": "/some/dir",
|
||||
"KeyStoreDir": "/some/dir",
|
||||
"NoDiscovery": true,
|
||||
"ClusterConfig": {
|
||||
"Enabled": true
|
||||
}
|
||||
}`,
|
||||
Error: "ClusterConfig.Fleet is empty",
|
||||
},
|
||||
{
|
||||
Name: "Validate that ClusterConfig.BootNodes is verified to not be empty if discovery is disabled",
|
||||
Config: `{
|
||||
"NetworkId": 1,
|
||||
"DataDir": "/some/dir",
|
||||
"KeyStoreDir": "/some/dir",
|
||||
"NoDiscovery": false
|
||||
}`,
|
||||
Error: "NoDiscovery is false, but ClusterConfig.BootNodes is empty",
|
||||
},
|
||||
{
|
||||
Name: "Validate that ClusterConfig.RendezvousNodes is verified to be empty if Rendezvous is disabled",
|
||||
Config: `{
|
||||
"NetworkId": 1,
|
||||
"DataDir": "/some/dir",
|
||||
"KeyStoreDir": "/some/dir",
|
||||
"NoDiscovery": true,
|
||||
"Rendezvous": true
|
||||
}`,
|
||||
Error: "Rendezvous is enabled, but ClusterConfig.RendezvousNodes is empty",
|
||||
},
|
||||
{
|
||||
Name: "Validate that ClusterConfig.RendezvousNodes is verified to contain nodes if Rendezvous is enabled",
|
||||
Config: `{
|
||||
"NetworkId": 1,
|
||||
"DataDir": "/some/dir",
|
||||
"KeyStoreDir": "/some/dir",
|
||||
"NoDiscovery": true,
|
||||
"Rendezvous": false,
|
||||
"ClusterConfig": {
|
||||
"RendezvousNodes": ["a"]
|
||||
}
|
||||
}`,
|
||||
Error: "Rendezvous is disabled, but ClusterConfig.RendezvousNodes is not empty",
|
||||
},
|
||||
{
|
||||
Name: "Validate that WhisperConfig.DataDir is checked to not be empty if mailserver is enabled",
|
||||
Config: `{
|
||||
"NetworkId": 1,
|
||||
"DataDir": "/some/dir",
|
||||
"KeyStoreDir": "/some/dir",
|
||||
"NoDiscovery": true,
|
||||
"WhisperConfig": {
|
||||
"Enabled": true,
|
||||
"EnableMailServer": true,
|
||||
"MailserverPassword": "foo"
|
||||
}
|
||||
}`,
|
||||
Error: "WhisperConfig.DataDir must be specified when WhisperConfig.EnableMailServer is true",
|
||||
},
|
||||
{
|
||||
Name: "Validate that check for WhisperConfig.DataDir passes if it is not empty and mailserver is enabled",
|
||||
Config: `{
|
||||
"NetworkId": 1,
|
||||
"DataDir": "/some/dir",
|
||||
"KeyStoreDir": "/some/dir",
|
||||
"NoDiscovery": true,
|
||||
"WhisperConfig": {
|
||||
"Enabled": true,
|
||||
"EnableMailServer": true,
|
||||
"DataDir": "/foo",
|
||||
"MailserverPassword": "foo"
|
||||
}
|
||||
}`,
|
||||
CheckFunc: func(t *testing.T, config *params.NodeConfig) {
|
||||
require.Equal(t, "foo", config.WhisperConfig.MailServerPassword)
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "Validate that WhisperConfig.DataDir is checked to not be empty if mailserver is enabled",
|
||||
Config: `{
|
||||
"NetworkId": 1,
|
||||
"DataDir": "/some/dir",
|
||||
"KeyStoreDir": "/some/dir",
|
||||
"NoDiscovery": true,
|
||||
"WhisperConfig": {
|
||||
"Enabled": true,
|
||||
"EnableMailServer": true,
|
||||
"MailserverPassword": "foo"
|
||||
}
|
||||
}`,
|
||||
Error: "WhisperConfig.DataDir must be specified when WhisperConfig.EnableMailServer is true",
|
||||
},
|
||||
{
|
||||
Name: "Validate that WhisperConfig.MailserverPassword and WhisperConfig.MailServerAsymKey are checked to not be empty if mailserver is enabled",
|
||||
Config: `{
|
||||
"NetworkId": 1,
|
||||
"DataDir": "/some/dir",
|
||||
"KeyStoreDir": "/some/dir",
|
||||
"NoDiscovery": true,
|
||||
"WhisperConfig": {
|
||||
"Enabled": true,
|
||||
"EnableMailServer": true,
|
||||
"DataDir": "/foo"
|
||||
}
|
||||
}`,
|
||||
Error: "WhisperConfig.MailServerPassword or WhisperConfig.MailServerAsymKey must be specified when WhisperConfig.EnableMailServer is true",
|
||||
},
|
||||
{
|
||||
Name: "Validate that WhisperConfig.MailServerAsymKey is checked to not be empty if mailserver is enabled",
|
||||
Config: `{
|
||||
"NetworkId": 1,
|
||||
"DataDir": "/some/dir",
|
||||
"KeyStoreDir": "/some/dir",
|
||||
"NoDiscovery": true,
|
||||
"WhisperConfig": {
|
||||
"Enabled": true,
|
||||
"EnableMailServer": true,
|
||||
"DataDir": "/foo",
|
||||
"MailServerAsymKey": "06c365919f1fc8e13ff79a84f1dd14b7e45b869aa5fc0e34940481ee20d32f90"
|
||||
}
|
||||
}`,
|
||||
CheckFunc: func(t *testing.T, config *params.NodeConfig) {
|
||||
require.Equal(t, "06c365919f1fc8e13ff79a84f1dd14b7e45b869aa5fc0e34940481ee20d32f90", config.WhisperConfig.MailServerAsymKey)
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "Validate that WhisperConfig.MailServerAsymKey is checked for validity",
|
||||
Config: `{
|
||||
"NetworkId": 1,
|
||||
"DataDir": "/some/dir",
|
||||
"KeyStoreDir": "/some/dir",
|
||||
"NoDiscovery": true,
|
||||
"WhisperConfig": {
|
||||
"Enabled": true,
|
||||
"EnableMailServer": true,
|
||||
"DataDir": "/foo",
|
||||
"MailServerAsymKey": "bar"
|
||||
}
|
||||
}`,
|
||||
Error: "WhisperConfig.MailServerAsymKey is invalid",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Logf("Test Case %s", tc.Name)
|
||||
|
||||
_, err := params.LoadNodeConfig(tc.Config)
|
||||
config, err := params.NewConfigFromJSON(tc.Config)
|
||||
|
||||
switch err := err.(type) {
|
||||
case validator.ValidationErrors:
|
||||
|
@ -321,10 +590,19 @@ func TestNodeConfigValidate(t *testing.T) {
|
|||
require.Equal(t, tc.FieldErrors[ve.Field()], ve.Tag())
|
||||
}
|
||||
case error:
|
||||
if tc.Error == "" {
|
||||
require.NoError(t, err)
|
||||
} else {
|
||||
require.Contains(t, err.Error(), tc.Error)
|
||||
}
|
||||
case nil:
|
||||
require.Empty(t, tc.Error)
|
||||
if tc.Error != "" {
|
||||
require.Error(t, err, "Error should be '%v'", tc.Error)
|
||||
}
|
||||
require.Nil(t, tc.FieldErrors)
|
||||
if tc.CheckFunc != nil {
|
||||
tc.CheckFunc(t, config)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,38 +3,9 @@ package params
|
|||
import "github.com/ethereum/go-ethereum/p2p/discv5"
|
||||
|
||||
const (
|
||||
// ClientIdentifier is client identifier to advertise over the network
|
||||
ClientIdentifier = "StatusIM"
|
||||
|
||||
// DataDir is default data directory used by statusd executable
|
||||
DataDir = "statusd-data"
|
||||
|
||||
// StatusDatabase path relative to DataDir.
|
||||
StatusDatabase = "status-db"
|
||||
|
||||
// KeyStoreDir is default directory where private keys are stored, relative to DataDir
|
||||
KeyStoreDir = "keystore"
|
||||
|
||||
// IPCFile is filename of exposed IPC RPC Server
|
||||
IPCFile = "geth.ipc"
|
||||
|
||||
// RPCEnabledDefault is the default state of whether the http rpc server is supposed
|
||||
// to be started along with a node.
|
||||
RPCEnabledDefault = false
|
||||
|
||||
// HTTPHost is host interface for the HTTP RPC server
|
||||
HTTPHost = "localhost"
|
||||
|
||||
// HTTPPort is HTTP RPC port (replaced in unit tests)
|
||||
HTTPPort = 8545
|
||||
|
||||
// ListenAddr is an IP address and port of this node (e.g. 127.0.0.1:30303).
|
||||
ListenAddr = ":0"
|
||||
|
||||
// APIModules is a list of modules to expose via HTTP and `CallRPC()` binding.
|
||||
// We also expose all handlers registered with `rpc.Client.RegisterHandler` to `CallRPC()` binding.
|
||||
APIModules = "eth,net,web3,peer"
|
||||
|
||||
// SendTransactionMethodName defines the name for a giving transaction.
|
||||
SendTransactionMethodName = "eth_sendTransaction"
|
||||
|
||||
|
@ -47,47 +18,15 @@ const (
|
|||
// PersonalRecoverMethodName defines the name for `personal.recover` API.
|
||||
PersonalRecoverMethodName = "personal_ecRecover"
|
||||
|
||||
// MaxPeers is the maximum number of global peers
|
||||
MaxPeers = 25
|
||||
|
||||
// MaxPendingPeers is the maximum number of peers that can be pending in the
|
||||
// handshake phase, counted separately for inbound and outbound connections.
|
||||
MaxPendingPeers = 0
|
||||
|
||||
// DefaultGas default amount of gas used for transactions
|
||||
DefaultGas = 180000
|
||||
|
||||
// DefaultFileDescriptorLimit is fd limit that database can use
|
||||
DefaultFileDescriptorLimit = uint64(2048)
|
||||
|
||||
// DatabaseCache is memory (in MBs) allocated to internal caching (min 16MB / database forced)
|
||||
DatabaseCache = 16
|
||||
|
||||
// LogFile defines where to write logs to
|
||||
LogFile = ""
|
||||
|
||||
// LogLevel defines the minimum log level to report
|
||||
LogLevel = "ERROR"
|
||||
|
||||
// LogLevelSuccinct defines the log level when only errors are reported.
|
||||
// Useful when the default INFO level becomes too verbose.
|
||||
LogLevelSuccinct = "ERROR"
|
||||
|
||||
// LogToStderr defines whether logged info should also be output to os.Stderr
|
||||
LogToStderr = true
|
||||
|
||||
// WhisperDataDir is directory where Whisper data is stored, relative to DataDir
|
||||
WhisperDataDir = "wnode"
|
||||
|
||||
// WhisperMinimumPoW amount of work for Whisper message to be added to sending queue
|
||||
WhisperMinimumPoW = 0.001
|
||||
|
||||
// WhisperTTL is time to live for messages, in seconds
|
||||
WhisperTTL = 120
|
||||
|
||||
// FirebaseNotificationTriggerURL is URL where FCM notification requests are sent to
|
||||
FirebaseNotificationTriggerURL = "https://fcm.googleapis.com/fcm/send"
|
||||
|
||||
// MainnetEthereumNetworkURL is URL where the upstream ethereum network is loaded to
|
||||
// allow us avoid syncing node.
|
||||
MainnetEthereumNetworkURL = "https://mainnet.infura.io/nKmXgiFgc2KqtoQ8BCGJ"
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1 +0,0 @@
|
|||
status-offline-inbox
|
|
@ -0,0 +1,4 @@
|
|||
// Package static embeds static (JS, HTML) resources right into the binaries
|
||||
package static
|
||||
|
||||
//go:generate go-bindata -pkg static -o bindata.go ../config/...
|
|
@ -6,14 +6,26 @@
|
|||
Example usage:
|
||||
|
||||
1. Start a Whisper node with mail server capability:
|
||||
./build/bin/statusd \
|
||||
-networkid=4 \
|
||||
-maxpeers=100 \
|
||||
-shh \
|
||||
-shh.pow=0.002 \
|
||||
-shh.mailserver \
|
||||
-shh.passwordfile=./static/keys/wnodepassword \
|
||||
-log DEBUG
|
||||
./build/bin/statusd -c mailserver-config.json
|
||||
|
||||
where mailserver-config.json contains:
|
||||
``` json
|
||||
{
|
||||
"NetworkId": 4,
|
||||
"DataDir": "./ethereumtest/rinkeby_rpc",
|
||||
"KeyStoreDir": "./ethereumtest/keystore",
|
||||
"MaxPeers": 100,
|
||||
"LogLevel": "DEBUG",
|
||||
"WhisperConfig": {
|
||||
"Enabled": true,
|
||||
"EnableMailServer": true,
|
||||
"DataDir": "./ethereumtest/wnode",
|
||||
"MinimumPoW": 0.002,
|
||||
"MailServerPassword": "status-offline-inbox"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
2. Generate some messages:
|
||||
go test -v -timeout=30s -run TestSendMessages ./t/benchmarks \
|
||||
-peerurl=$ENODE_ADDR \
|
||||
|
|
|
@ -54,7 +54,7 @@ mv UTC--2018-01-26T13-47-49.289567120Z--9f04dc05c4c3ec3b8b1f36f7d7d153f3934b1f07
|
|||
popd
|
||||
```
|
||||
|
||||
Update config for tests with new accounts `static/config/public-chain-accounts.json`:
|
||||
Update config for tests with new accounts `t/config/public-chain-accounts.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
|
|
|
@ -231,7 +231,7 @@ func (s *AccountsTestSuite) TestSelectedAccountOnRestart() {
|
|||
s.Equal(selectedAccount.Address.Hex(), address2, "incorrect address selected")
|
||||
|
||||
// resume node
|
||||
s.NoError(s.Backend.StartNode(&preservedNodeConfig))
|
||||
s.Require().NoError(s.Backend.StartNode(&preservedNodeConfig))
|
||||
|
||||
// re-check selected account (account2 MUST be selected)
|
||||
selectedAccount, err = s.Backend.AccountManager().SelectedAccount()
|
||||
|
|
|
@ -2,10 +2,7 @@ package api_test
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"os"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
@ -40,19 +37,6 @@ func (s *APITestSuite) SetupTest() {
|
|||
}
|
||||
|
||||
func (s *APITestSuite) TestCHTUpdate() {
|
||||
tmpDir, err := ioutil.TempDir(os.TempDir(), "cht-updates")
|
||||
s.NoError(err)
|
||||
defer os.RemoveAll(tmpDir) //nolint: errcheck
|
||||
|
||||
configJSON := `{
|
||||
"NetworkId": ` + strconv.Itoa(params.RopstenNetworkID) + `,
|
||||
"DataDir": "` + tmpDir + `",
|
||||
"LogLevel": "INFO",
|
||||
"RPCEnabled": true
|
||||
}`
|
||||
|
||||
_, err = params.LoadNodeConfig(configJSON)
|
||||
s.NoError(err)
|
||||
// TODO(tiabc): Test that CHT is really updated.
|
||||
}
|
||||
|
||||
|
@ -140,12 +124,12 @@ func (s *APITestSuite) TestEventsNodeStartStop() {
|
|||
|
||||
nodeConfig, err := MakeTestNodeConfig(GetNetworkID())
|
||||
s.NoError(err)
|
||||
s.NoError(s.backend.StartNode(nodeConfig))
|
||||
s.Require().NoError(s.backend.StartNode(nodeConfig))
|
||||
s.NoError(s.backend.StopNode())
|
||||
s.verifyEnvelopes(envelopes, signal.EventNodeStarted, signal.EventNodeReady, signal.EventNodeStopped)
|
||||
s.NoError(s.backend.StartNode(nodeConfig))
|
||||
s.Require().NoError(s.backend.StartNode(nodeConfig))
|
||||
s.verifyEnvelopes(envelopes, signal.EventNodeStarted, signal.EventNodeReady)
|
||||
s.NoError(s.backend.RestartNode())
|
||||
s.Require().NoError(s.backend.RestartNode())
|
||||
s.verifyEnvelopes(envelopes, signal.EventNodeStopped, signal.EventNodeStarted, signal.EventNodeReady)
|
||||
s.NoError(s.backend.StopNode())
|
||||
s.verifyEnvelopes(envelopes, signal.EventNodeStopped)
|
||||
|
|
|
@ -27,7 +27,7 @@ func (s *APIBackendTestSuite) TestNetworkSwitching() {
|
|||
s.NoError(err)
|
||||
|
||||
s.False(s.Backend.IsNodeRunning())
|
||||
s.NoError(s.Backend.StartNode(nodeConfig))
|
||||
s.Require().NoError(s.Backend.StartNode(nodeConfig))
|
||||
s.True(s.Backend.IsNodeRunning())
|
||||
|
||||
firstHash, err := e2e.FirstBlockHash(s.Backend.StatusNode())
|
||||
|
@ -42,7 +42,7 @@ func (s *APIBackendTestSuite) TestNetworkSwitching() {
|
|||
s.NoError(err)
|
||||
|
||||
s.False(s.Backend.IsNodeRunning())
|
||||
s.NoError(s.Backend.StartNode(nodeConfig))
|
||||
s.Require().NoError(s.Backend.StartNode(nodeConfig))
|
||||
s.True(s.Backend.IsNodeRunning())
|
||||
|
||||
// make sure we are on another network indeed
|
||||
|
@ -89,7 +89,7 @@ func (s *APIBackendTestSuite) TestRestartNode() {
|
|||
s.NoError(err)
|
||||
|
||||
s.False(s.Backend.IsNodeRunning())
|
||||
s.NoError(s.Backend.StartNode(nodeConfig))
|
||||
require.NoError(s.Backend.StartNode(nodeConfig))
|
||||
s.True(s.Backend.IsNodeRunning())
|
||||
|
||||
firstHash, err := e2e.FirstBlockHash(s.Backend.StatusNode())
|
||||
|
|
|
@ -84,7 +84,7 @@ func (s *BackendTestSuite) TearDownTest() {
|
|||
// StartTestBackend imports some keys and starts a node.
|
||||
func (s *BackendTestSuite) StartTestBackend(opts ...TestNodeOption) {
|
||||
nodeConfig, err := MakeTestNodeConfig(GetNetworkID())
|
||||
s.NoError(err)
|
||||
s.Require().NoError(err)
|
||||
|
||||
// Apply any options altering node config.
|
||||
for i := range opts {
|
||||
|
@ -96,7 +96,7 @@ func (s *BackendTestSuite) StartTestBackend(opts ...TestNodeOption) {
|
|||
|
||||
// start node
|
||||
s.False(s.Backend.IsNodeRunning())
|
||||
s.NoError(s.Backend.StartNode(nodeConfig))
|
||||
s.Require().NoError(s.Backend.StartNode(nodeConfig))
|
||||
s.True(s.Backend.IsNodeRunning())
|
||||
}
|
||||
|
||||
|
@ -110,7 +110,7 @@ func (s *BackendTestSuite) StopTestBackend() {
|
|||
// RestartTestNode restarts a currently running node.
|
||||
func (s *BackendTestSuite) RestartTestNode() {
|
||||
s.True(s.Backend.IsNodeRunning())
|
||||
s.NoError(s.Backend.RestartNode())
|
||||
s.Require().NoError(s.Backend.RestartNode())
|
||||
s.True(s.Backend.IsNodeRunning())
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ package e2e
|
|||
|
||||
import (
|
||||
"context"
|
||||
"path"
|
||||
|
||||
gethcommon "github.com/ethereum/go-ethereum/common"
|
||||
"github.com/status-im/status-go/node"
|
||||
|
@ -20,9 +21,11 @@ func WithUpstream(url string) TestNodeOption {
|
|||
}
|
||||
|
||||
// WithDataDir returns TestNodeOption that allows to set another data dir.
|
||||
func WithDataDir(path string) TestNodeOption {
|
||||
func WithDataDir(dataDir string) TestNodeOption {
|
||||
return func(config *params.NodeConfig) {
|
||||
config.DataDir = path
|
||||
config.DataDir = dataDir
|
||||
config.KeyStoreDir = path.Join(dataDir, "keystore")
|
||||
config.WhisperConfig.DataDir = path.Join(dataDir, "wnode")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -568,7 +568,7 @@ func (s *WhisperMailboxSuite) startMailboxBackend() (*api.StatusBackend, func())
|
|||
mailboxConfig.WhisperConfig.Enabled = true
|
||||
mailboxConfig.KeyStoreDir = datadir
|
||||
mailboxConfig.WhisperConfig.EnableMailServer = true
|
||||
mailboxConfig.WhisperConfig.MailServerPasswordFile = filepath.Join(RootDir, "/static/keys/wnodepassword")
|
||||
mailboxConfig.WhisperConfig.MailServerPassword = "status-offline-inbox"
|
||||
mailboxConfig.WhisperConfig.DataDir = filepath.Join(datadir, "data")
|
||||
mailboxConfig.DataDir = datadir
|
||||
|
||||
|
|
|
@ -173,7 +173,7 @@ func (s *WhisperTestSuite) TestSelectedAccountOnRestart() {
|
|||
s.NoError(s.Backend.StopNode())
|
||||
|
||||
// resume node
|
||||
s.NoError(s.Backend.StartNode(&preservedNodeConfig))
|
||||
s.Require().NoError(s.Backend.StartNode(&preservedNodeConfig))
|
||||
|
||||
// re-check selected account (account2 MUST be selected)
|
||||
selectedAccount, err = s.Backend.AccountManager().SelectedAccount()
|
||||
|
|
|
@ -244,18 +244,28 @@ func MakeTestNodeConfig(networkID int) (*params.NodeConfig, error) {
|
|||
}
|
||||
|
||||
configJSON := `{
|
||||
"Name": "test",
|
||||
"NetworkId": ` + strconv.Itoa(networkID) + `,
|
||||
"DataDir": "` + testDir + `",
|
||||
"KeyStoreDir": "` + path.Join(testDir, "keystore") + `",
|
||||
"HTTPPort": ` + strconv.Itoa(TestConfig.Node.HTTPPort) + `,
|
||||
"WSPort": ` + strconv.Itoa(TestConfig.Node.WSPort) + `,
|
||||
"LogLevel": "` + errorLevel + `"
|
||||
"LogLevel": "` + errorLevel + `",
|
||||
"NoDiscovery": true,
|
||||
"LightEthConfig": {
|
||||
"Enabled": true
|
||||
},
|
||||
"WhisperConfig": {
|
||||
"Enabled": true,
|
||||
"DataDir": "` + path.Join(testDir, "wnode") + `",
|
||||
"EnableNTPSync": false
|
||||
}
|
||||
}`
|
||||
|
||||
nodeConfig, err := params.LoadNodeConfig(configJSON)
|
||||
nodeConfig, err := params.NewConfigFromJSON(configJSON)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nodeConfig.WhisperConfig.EnableNTPSync = false
|
||||
|
||||
return nodeConfig, nil
|
||||
}
|
||||
|
@ -264,14 +274,30 @@ func MakeTestNodeConfig(networkID int) (*params.NodeConfig, error) {
|
|||
// where specific network addresses are assigned based on provided network id, and assigns
|
||||
// a given name and data dir.
|
||||
func MakeTestNodeConfigWithDataDir(name, dataDir, fleet string, networkID uint64) (*params.NodeConfig, error) {
|
||||
cfg, err := params.NewNodeConfig(dataDir, "", fleet, networkID)
|
||||
cfg, err := params.NewNodeConfig(dataDir, fleet, networkID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if name == "" {
|
||||
cfg.Name = "test"
|
||||
} else {
|
||||
cfg.Name = name
|
||||
cfg.NetworkID = uint64(GetNetworkID())
|
||||
}
|
||||
cfg.NoDiscovery = true
|
||||
cfg.LightEthConfig.Enabled = false
|
||||
cfg.WhisperConfig.Enabled = true
|
||||
cfg.WhisperConfig.EnableNTPSync = false
|
||||
if dataDir != "" {
|
||||
cfg.KeyStoreDir = path.Join(dataDir, "keystore")
|
||||
cfg.WhisperConfig.DataDir = path.Join(dataDir, "wnode")
|
||||
}
|
||||
|
||||
// Only attempt to validate if a dataDir is specified, we only support in-memory DB for tests
|
||||
if dataDir != "" {
|
||||
if err := cfg.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return cfg, nil
|
||||
}
|
||||
|
@ -299,19 +325,19 @@ const passphraseEnvName = "ACCOUNT_PASSWORD"
|
|||
func loadTestConfig() (*testConfig, error) {
|
||||
var config testConfig
|
||||
|
||||
pathOfStatic := path.Join(params.GetStatusHome(), "/static")
|
||||
err := getTestConfigFromFile(path.Join(pathOfStatic, "config/test-data.json"), &config)
|
||||
pathOfConfig := path.Join(params.GetStatusHome(), "/t/config")
|
||||
err := getTestConfigFromFile(path.Join(pathOfConfig, "test-data.json"), &config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if GetNetworkID() == params.StatusChainNetworkID {
|
||||
err := getTestConfigFromFile(path.Join(pathOfStatic, "config/status-chain-accounts.json"), &config)
|
||||
err := getTestConfigFromFile(path.Join(pathOfConfig, "status-chain-accounts.json"), &config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
err := getTestConfigFromFile(path.Join(pathOfStatic, "config/public-chain-accounts.json"), &config)
|
||||
err := getTestConfigFromFile(path.Join(pathOfConfig, "public-chain-accounts.json"), &config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -54,7 +54,7 @@ func (s *TransactorSuite) SetupTest() {
|
|||
rpcClient, _ := rpc.NewClient(s.client, params.UpstreamRPCConfig{})
|
||||
// expected by simulated backend
|
||||
chainID := gethparams.AllEthashProtocolChanges.ChainID.Uint64()
|
||||
nodeConfig, err := params.NewNodeConfig("/tmp", "", params.FleetBeta, chainID)
|
||||
nodeConfig, err := MakeTestNodeConfigWithDataDir("", "/tmp", params.FleetBeta, chainID)
|
||||
s.Require().NoError(err)
|
||||
s.nodeConfig = nodeConfig
|
||||
|
||||
|
|
Loading…
Reference in New Issue