From 3d00af7fa3b0a263b306f994dbeafdf94eefc766 Mon Sep 17 00:00:00 2001 From: Pedro Pombeiro Date: Thu, 13 Sep 2018 18:31:29 +0200 Subject: [PATCH] Streamline configuration in status-go. Part of #1180 (#1183) - 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. --- .vscode/launch.example.json | 2 +- Makefile | 4 + .../wnode-test-cluster/docker-compose.yml | 17 +- .../wnode-test-cluster/wnode-config.json | 22 + api/backend.go | 5 + api/backend_test.go | 32 +- cmd/mailserver-canary/main.go | 2 +- cmd/statusd/debug/commands.go | 2 +- cmd/statusd/debug/debug_test.go | 2 + cmd/statusd/flags.go | 39 +- cmd/statusd/flags_test.go | 2 +- cmd/statusd/main.go | 213 ++---- cmd/statusd/whispercfg.go | 60 -- config/cli/fleet-eth.beta.json | 30 + config/cli/fleet-eth.staging.json | 24 + config/cli/fleet-eth.test.json | 24 + config/cli/les-enabled.json | 5 + .../status-chain-genesis.json | 0 lib/library.go | 20 +- lib/library_test.go | 88 ++- lib/library_test_utils.go | 55 +- mailserver/mailserver.go | 10 +- mailserver/mailserver_test.go | 40 +- node/node.go | 120 ++-- node/node_api_test.go | 4 +- node/status_node.go | 13 +- node/status_node_test.go | 18 +- params/cluster.go | 59 -- params/config.go | 605 ++++++++---------- params/config_test.go | 430 ++++++++++--- params/defaults.go | 61 -- static/bindata.go | 333 ++++++++++ static/keys/wnodepassword | 1 - static/static.go | 4 + t/benchmarks/doc_test.go | 28 +- .../config/public-chain-accounts.json | 0 .../config/status-chain-accounts.json | 0 {static => t}/config/test-data.json | 0 t/e2e/README.md | 2 +- t/e2e/accounts/accounts_test.go | 2 +- t/e2e/api/api_test.go | 22 +- t/e2e/api/backend_test.go | 6 +- t/e2e/suites.go | 6 +- t/e2e/testing.go | 7 +- t/e2e/whisper/whisper_mailbox_test.go | 2 +- t/e2e/whisper/whisper_test.go | 2 +- t/utils/utils.go | 46 +- transactions/transactor_test.go | 2 +- 48 files changed, 1466 insertions(+), 1005 deletions(-) create mode 100644 _assets/compose/wnode-test-cluster/wnode-config.json delete mode 100644 cmd/statusd/whispercfg.go create mode 100644 config/cli/fleet-eth.beta.json create mode 100644 config/cli/fleet-eth.staging.json create mode 100644 config/cli/fleet-eth.test.json create mode 100644 config/cli/les-enabled.json rename {static/config => config}/status-chain-genesis.json (100%) create mode 100644 static/bindata.go delete mode 100644 static/keys/wnodepassword create mode 100644 static/static.go rename {static => t}/config/public-chain-accounts.json (100%) rename {static => t}/config/status-chain-accounts.json (100%) rename {static => t}/config/test-data.json (100%) diff --git a/.vscode/launch.example.json b/.vscode/launch.example.json index 5da3bf0ae..6297bccc9 100644 --- a/.vscode/launch.example.json +++ b/.vscode/launch.example.json @@ -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" } diff --git a/Makefile b/Makefile index b4aabd3f3..71a01c7ed 100644 --- a/Makefile +++ b/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 diff --git a/_assets/compose/wnode-test-cluster/docker-compose.yml b/_assets/compose/wnode-test-cluster/docker-compose.yml index e3d22d60d..54edcbe91 100644 --- a/_assets/compose/wnode-test-cluster/docker-compose.yml +++ b/_assets/compose/wnode-test-cluster/docker-compose.yml @@ -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 diff --git a/_assets/compose/wnode-test-cluster/wnode-config.json b/_assets/compose/wnode-test-cluster/wnode-config.json new file mode 100644 index 000000000..a7accd435 --- /dev/null +++ b/_assets/compose/wnode-test-cluster/wnode-config.json @@ -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 + } +} diff --git a/api/backend.go b/api/backend.go index 08c17870f..9a3f8460d 100644 --- a/api/backend.go +++ b/api/backend.go @@ -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()) diff --git a/api/backend_test.go b/api/backend_test.go index b146a7bea..460040019 100644 --- a/api/backend_test.go +++ b/api/backend_test.go @@ -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()) }() diff --git a/cmd/mailserver-canary/main.go b/cmd/mailserver-canary/main.go index d259a3744..aa1c2d9bb 100644 --- a/cmd/mailserver-canary/main.go +++ b/cmd/mailserver-canary/main.go @@ -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 } diff --git a/cmd/statusd/debug/commands.go b/cmd/statusd/debug/commands.go index 89091bdd8..c0a60f7ec 100644 --- a/cmd/statusd/debug/commands.go +++ b/cmd/statusd/debug/commands.go @@ -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 } diff --git a/cmd/statusd/debug/debug_test.go b/cmd/statusd/debug/debug_test.go index 575a16ea1..20112cbde 100644 --- a/cmd/statusd/debug/debug_test.go +++ b/cmd/statusd/debug/debug_test.go @@ -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 diff --git a/cmd/statusd/flags.go b/cmd/statusd/flags.go index 3a6870d1b..be519d51c 100644 --- a/cmd/statusd/flags.go +++ b/cmd/statusd/flags.go @@ -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 + } + value = path.Join(cwd, value) } - *s = append(*s, trimmed) + + // 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 } diff --git a/cmd/statusd/flags_test.go b/cmd/statusd/flags_test.go index 357382b92..43cb28191 100644 --- a/cmd/statusd/flags_test.go +++ b/cmd/statusd/flags_test.go @@ -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 diff --git a/cmd/statusd/main.go b/cmd/statusd/main.go index cd4fcb6d3..cafc1f49e 100644 --- a/cmd/statusd/main.go +++ b/cmd/statusd/main.go @@ -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") - cliEnabled = flag.Bool("cli", false, "Enable debugging CLI server") - cliPort = flag.String("cliport", 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") - 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{} + 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("cli-port", debug.CLIPort, "CLI server's listening port") + pprofEnabled = flag.Bool("pprof", false, "Enable runtime profiling via pprof") + 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 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: ` diff --git a/cmd/statusd/whispercfg.go b/cmd/statusd/whispercfg.go deleted file mode 100644 index 1d856f417..000000000 --- a/cmd/statusd/whispercfg.go +++ /dev/null @@ -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 -} diff --git a/config/cli/fleet-eth.beta.json b/config/cli/fleet-eth.beta.json new file mode 100644 index 000000000..fd28be915 --- /dev/null +++ b/config/cli/fleet-eth.beta.json @@ -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" + ] + } +} diff --git a/config/cli/fleet-eth.staging.json b/config/cli/fleet-eth.staging.json new file mode 100644 index 000000000..5a6a3de8f --- /dev/null +++ b/config/cli/fleet-eth.staging.json @@ -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" + ] + } +} diff --git a/config/cli/fleet-eth.test.json b/config/cli/fleet-eth.test.json new file mode 100644 index 000000000..82a646b73 --- /dev/null +++ b/config/cli/fleet-eth.test.json @@ -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" + ] + } +} diff --git a/config/cli/les-enabled.json b/config/cli/les-enabled.json new file mode 100644 index 000000000..899ede6a2 --- /dev/null +++ b/config/cli/les-enabled.json @@ -0,0 +1,5 @@ +{ + "LightEthConfig": { + "Enabled": true + } +} diff --git a/static/config/status-chain-genesis.json b/config/status-chain-genesis.json similarity index 100% rename from static/config/status-chain-genesis.json rename to config/status-chain-genesis.json diff --git a/lib/library.go b/lib/library.go index e1c6bec39..a91d13cb2 100644 --- a/lib/library.go +++ b/lib/library.go @@ -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) { diff --git a/lib/library_test.go b/lib/library_test.go index 9cf048e77..87616895d 100644 --- a/lib/library_test.go +++ b/lib/library_test.go @@ -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,18 +58,85 @@ 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: `{}`, Callback: func(resp APIDetailedResponse) { required := map[string]string{ - "NodeConfig.NetworkID": "required", - "NodeConfig.DataDir": "required", + "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) diff --git a/lib/library_test_utils.go b/lib/library_test_utils.go index e2e52c350..74e0f2035 100644 --- a/lib/library_test_utils.go +++ b/lib/library_test_utils.go @@ -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 { diff --git a/mailserver/mailserver.go b/mailserver/mailserver.go index ef777d195..7d89a4716 100644 --- a/mailserver/mailserver.go +++ b/mailserver/mailserver.go @@ -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 diff --git a/mailserver/mailserver_test.go b/mailserver/mailserver_test.go index 1370c0895..7d4fc4364 100644 --- a/mailserver/mailserver_test.go +++ b/mailserver/mailserver_test.go @@ -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, + DataDir: tmpDir, + 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() diff --git a/node/node.go b/node/node.go index 0472aab75..82a053e58 100644 --- a/node/node.go +++ b/node/node.go @@ -27,24 +27,26 @@ 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" ) // Errors related to node and services creation. 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") - 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") + 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") ) // 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) - if err != nil { - logger.Error("Failed loading private key file", "file", config.NodeKeyFile, "error", err) - } - - // override node's private key - stackConfig.P2P.PrivateKey = pk + stackConfig, err := newGethNodeConfig(config) + if err != nil { + 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) } } diff --git a/node/node_api_test.go b/node/node_api_test.go index cffebe779..52600a9bd 100644 --- a/node/node_api_test.go +++ b/node/node_api_test.go @@ -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, }, diff --git a/node/status_node.go b/node/status_node.go index 26573b8f8..f5b676e5e 100644 --- a/node/status_node.go +++ b/node/status_node.go @@ -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 } diff --git a/node/status_node_test.go b/node/status_node_test.go index 789c66c4a..3f1c80da5 100644 --- a/node/status_node_test.go +++ b/node/status_node_test.go @@ -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"}, diff --git a/params/cluster.go b/params/cluster.go index c1c31d571..39a5a7d97 100644 --- a/params/cluster.go +++ b/params/cluster.go @@ -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 -} diff --git a/params/config.go b/params/config.go index 20144b7fb..bedb64f98 100644 --- a/params/config.go +++ b/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, - 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 + NetworkID: networkID, + DataDir: dataDir, + Version: Version, + 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: "", + 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, - MinimumPoW: WhisperMinimumPoW, - TTL: WhisperTTL, - FirebaseConfig: &FirebaseConfig{ - NotificationTriggerURL: FirebaseNotificationTriggerURL, - }, - EnableNTPSync: true, + WhisperConfig: WhisperConfig{ + Enabled: false, + MinimumPoW: WhisperMinimumPoW, + TTL: WhisperTTL, + EnableNTPSync: false, }, - 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 { - return err - } + if err := c.validateChildStructs(validate); err != nil { + return err } - if c.SwarmConfig.Enabled { - if err := validate.Struct(c.SwarmConfig); err != nil { - return err + 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, "", " ") diff --git a/params/config_test.go b/params/config_test.go index 203777335..16a1cd207 100644 --- a/params/config_test.go +++ b/params/config_test.go @@ -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(`{ - "staticnodes": [ - "enode://7ab298cedc4185a894d21d8a4615262ec6bdce66c9b6783878258e0d5b31013d30c9038932432f70e5b2b6a5cd323bf820554fcb22fbc7b45367889522e9c449@10.1.1.1:30303", - "enode://f59e8701f18c79c5cbc7618dc7bb928d44dc2f5405c7d693dad97da2d8585975942ec6fd36d3fe608bfdc7270a34a4dd00f38cfe96b2baa24f7cd0ac28d382a1@10.1.1.2:30303" - ] + "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) {}, + Name: "cluster nodes setup", + 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,17 +209,22 @@ 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) }, }, { - Name: "peers discovery and topics", - Update: func(c *params.NodeConfig) {}, + Name: "peers discovery and topics", + 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) @@ -179,13 +242,18 @@ func TestGenerateAndLoadNodeConfig(t *testing.T) { }, }, { - Name: "staging fleet", - Fleet: params.FleetStaging, - Update: func(c *params.NodeConfig) {}, + Name: "staging fleet", + Fleet: params.FleetStaging, + 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 JSON config", + Config: `{"NetworkId": }`, + Error: "invalid character '}'", }, { - Name: "Invalid field type", - Config: `{"NetworkId": "abc"}`, - Error: "json: cannot unmarshal string into Go struct field", - FieldErrors: nil, + Name: "Invalid field type", + Config: `{"NetworkId": "abc"}`, + Error: "json: cannot unmarshal string into Go struct field", }, { Name: "Validate all required fields", Config: `{}`, - Error: "", FieldErrors: map[string]string{ - "NetworkID": "required", - "DataDir": "required", + "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: - require.Contains(t, err.Error(), tc.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) + } } } } diff --git a/params/defaults.go b/params/defaults.go index 1c2d9e19a..442e15e28 100644 --- a/params/defaults.go +++ b/params/defaults.go @@ -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" diff --git a/static/bindata.go b/static/bindata.go new file mode 100644 index 000000000..632943be1 --- /dev/null +++ b/static/bindata.go @@ -0,0 +1,333 @@ +// Code generated by go-bindata. +// sources: +// ../config/cli/fleet-eth.beta.json +// ../config/cli/fleet-eth.staging.json +// ../config/cli/fleet-eth.test.json +// ../config/cli/les-enabled.json +// ../config/status-chain-genesis.json +// DO NOT EDIT! + +package static + +import ( + "bytes" + "compress/gzip" + "fmt" + "io" + "io/ioutil" + "os" + "path/filepath" + "strings" + "time" +) + +func bindataRead(data []byte, name string) ([]byte, error) { + gz, err := gzip.NewReader(bytes.NewBuffer(data)) + if err != nil { + return nil, fmt.Errorf("Read %q: %v", name, err) + } + + var buf bytes.Buffer + _, err = io.Copy(&buf, gz) + clErr := gz.Close() + + if err != nil { + return nil, fmt.Errorf("Read %q: %v", name, err) + } + if clErr != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +type asset struct { + bytes []byte + info os.FileInfo +} + +type bindataFileInfo struct { + name string + size int64 + mode os.FileMode + modTime time.Time +} + +func (fi bindataFileInfo) Name() string { + return fi.name +} +func (fi bindataFileInfo) Size() int64 { + return fi.size +} +func (fi bindataFileInfo) Mode() os.FileMode { + return fi.mode +} +func (fi bindataFileInfo) ModTime() time.Time { + return fi.modTime +} +func (fi bindataFileInfo) IsDir() bool { + return false +} +func (fi bindataFileInfo) Sys() interface{} { + return nil +} + +var _ConfigCliFleetEthBetaJson = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x84\x96\x4d\x73\x55\xc7\xd1\xc7\xf7\xfe\x14\x2a\xad\x9f\xba\x9a\x7e\x9f\xd1\x8a\xc7\x6f\x21\x76\x42\x61\x63\x08\x24\x95\x45\x4f\x77\x8f\x50\xc0\xba\x44\xba\x52\x02\x29\x7f\xf7\xd4\x11\xd8\x01\xca\x88\xc5\xad\x3a\x35\xdd\xf7\xf4\xef\xfc\xeb\xdf\xdd\xf3\x9f\x2f\x8e\x8e\x8e\x8e\x8e\x1f\xd4\xe1\x5f\xfb\xcb\x17\x7f\xcc\xe3\xd3\x23\xfa\xbf\xb7\x67\x5f\xfb\xc1\xbf\x3e\xbf\x3c\x3e\x3d\x3a\xde\x9d\xec\xea\xf0\xbc\x2e\xeb\xfa\xe7\x43\x5d\x1d\x4e\x7e\xdc\xbf\xba\x3a\xd4\xc5\xf1\xbb\xcc\xef\xeb\xf5\xa3\xc3\xfe\xb2\xee\xce\x3e\x79\x51\xaf\xaf\xb6\xb4\x5f\xff\xf6\x60\xff\xf5\xf9\x55\xec\x6f\xea\xf2\xf5\xf1\xe9\xd1\xe1\xf2\xba\xde\x05\x7e\xac\x8b\xac\x37\x37\xfb\xeb\xab\x0f\xcf\xbf\x7a\x79\x7d\x75\xa8\xcb\xaf\xf6\x17\xeb\xfc\xec\xf8\xf4\xe8\x2d\xfc\x6d\xe8\x9b\x0b\x9f\x2f\x2b\x3f\xc8\xbf\x0d\x7c\xfb\xb2\xea\xb0\x51\xd5\xe1\xf9\x6e\xd6\xc1\x8f\xdf\x0b\x7e\xb9\xdf\x1f\x1e\xec\xb3\xb6\x3a\x7f\xfb\xed\xf8\x36\x54\x17\xfb\xac\xd3\x93\x13\x26\x8d\xd0\xa5\xc6\x03\xfb\xca\x18\x3e\x96\x8d\xd1\x16\x0e\xe6\xd6\x70\x6a\x97\x84\x20\x5b\x0d\x25\x60\x16\xa3\x40\x97\x29\x13\x56\x1b\xad\xad\xf2\x05\x11\x81\xae\x40\x0d\xb5\xaf\x31\xda\x96\xe6\x96\x28\xb8\xc8\xa8\x61\x74\x22\x71\x0c\x70\x45\x23\xad\x81\x84\x3a\x20\x82\xfc\x1e\x18\xef\x80\xfa\x0e\x9a\xec\x90\xe9\x94\x1a\x37\x7e\xef\x23\x3e\xa0\x15\x1a\xe2\x3e\xad\x13\x2d\xa8\x98\x6a\x30\x65\xcc\xd5\x04\x21\x16\x36\x44\x5e\xd5\x41\x71\x05\x25\xaa\x49\x16\x57\x71\x8a\x92\xba\x49\x05\x61\x02\x6d\x94\x01\x9d\x73\xf5\x84\xcc\xe5\xbd\xd1\x60\x1a\x4d\x7b\xc7\x74\x45\xe7\x5c\x8c\xc9\x2b\x62\x69\x82\xf5\xd6\x41\xd4\xbb\xdd\xc3\xa6\x3b\xe8\x63\x23\xdd\x89\xdd\x0d\x6b\x8c\x96\xab\xa8\xcf\xe4\x58\x16\xd2\xe7\x1c\xca\xdb\xeb\x74\xf9\x44\xb1\x8e\x15\x54\xda\x58\xbd\x37\x21\xb2\xd6\x10\x63\xba\x63\x07\x21\xad\x9e\xca\x15\x09\xb3\x61\xc1\xea\xcb\xd0\xb4\x17\x0e\xc9\xa6\x28\x7d\x3a\x53\xf6\x4e\x8d\x73\x36\xed\xa5\x0b\x19\xcc\xab\xcf\x98\xc3\xf5\x1e\x34\xde\x81\xf0\xae\xf7\x1d\xe0\x67\x94\xad\x59\xcb\x27\x8d\xa9\x63\xce\x59\xca\xd9\x23\xbb\xce\x32\x95\x49\xb3\x48\x34\x7b\xf0\x44\x56\x6d\x4b\x25\x79\x10\x30\x79\x0b\xe6\x45\x3d\xba\x38\x8a\x51\x6b\x60\x7d\x59\x67\x91\x81\x0e\xb3\x11\x61\x07\x10\xc6\x1a\x2e\x1d\x3b\x44\x27\x99\x3e\x33\x4d\x6e\x1f\xa6\x72\xad\x80\x7b\x24\x3b\x6c\xb8\x1b\x63\x87\xc8\xef\x58\x7f\x43\xfd\xfb\x7b\xa6\xfe\xe9\x72\xeb\x92\xfc\xb3\x9f\xbf\x7c\x54\x97\x37\x75\x79\x87\xbb\x83\x71\x91\x76\x47\x5a\x3e\x7a\x95\xb0\xae\x44\x36\xc4\x66\x32\x9a\x22\x21\x21\x8f\x5a\x2a\x96\xa8\x49\x62\x6e\x66\xcc\xe4\x15\x9b\xac\x30\x71\x38\x39\x62\x2d\x2a\x0b\xe1\x5e\xd0\x79\x50\xa6\x2f\x01\x27\x98\xcd\x2b\xb5\xd9\x98\xa9\x35\x4b\xbc\x3a\xf5\x15\xcb\x17\x39\x8f\x0f\xfc\x02\x8a\xa7\xd4\xe4\x0e\xc3\xb8\x2b\xf7\xd4\xea\x22\x43\xda\xc4\xa2\xa4\xb9\x10\x5b\xf0\xd0\x6a\xe1\xc5\x9e\x2b\x73\x83\x81\xd2\xa6\x58\x3a\xc1\xcc\x2b\x06\xcd\xd0\xc8\x58\x80\x1d\x63\x72\x4b\x50\xd1\x41\x58\x73\x65\x8a\x8a\xe1\x48\x67\x6e\xa4\x3d\x2d\x38\xdd\x72\x26\x67\x6b\x3c\xc1\xa3\xe1\x5c\xfd\x23\xda\x71\x37\x6d\x77\xe5\x49\x41\x3c\x1c\xab\xd5\x62\x27\x2c\xe7\xa1\x6d\xd4\xa6\x05\x91\xf2\x2c\x00\x68\x28\x14\xd8\x3c\x03\xcc\x29\x6a\xce\xa0\xe1\x08\xa3\x24\xa9\x80\x26\x08\x44\xab\x2a\xc9\x5e\x6d\x0d\xef\xd3\x31\xb2\xa1\x36\xe0\x52\x9b\x0c\xce\x3e\x2d\x21\x25\x53\x2d\x1c\xb7\xd6\xfa\x88\xb6\x7f\x46\xdb\xac\x31\x8a\x63\xc2\x24\x41\x9a\x89\x1a\x8e\x80\xa4\x43\xb8\xe9\xe6\x6a\x0b\x43\xf0\xe4\x45\x25\xd1\x11\x2a\x07\x70\x07\x69\x51\x1a\x85\x65\xa8\x04\x86\xb4\x35\x29\xb4\xe5\x01\x2b\x05\xb7\xf1\x31\x67\x2e\x12\x91\x6a\x42\x36\x7c\xd9\x10\x90\x02\xb0\x91\x4e\x16\x41\x39\x37\x7f\xc3\xd6\x88\x63\x87\xd0\xee\x66\x6d\x20\x85\xb8\x34\x12\x27\x73\x74\x17\x98\x69\x8e\xb7\x25\xd0\xa0\x36\xf3\x86\xa5\x2d\x41\x26\x34\x18\xaa\xe2\xc6\x43\x75\x61\x3a\x8b\x26\xf6\x02\xe1\xea\xa4\xb3\x4a\xdb\xc0\xc9\xa9\x5d\x57\xa9\x15\x11\xa8\x88\x74\x0d\xb1\x69\xd0\x67\xd1\x18\x16\xa0\x38\x12\x19\xd4\x6e\x7b\x11\x75\x87\xb0\x83\xcf\x98\x40\x08\x0a\x05\x2b\x86\xea\xb4\xea\xb4\x44\xa8\x07\x8c\xb9\x20\xb2\x8c\x3a\x44\xd8\xe0\xd1\x50\x5d\x8b\xc7\x98\x5a\x63\xf4\xd2\x21\x26\xe0\x9e\x0b\x35\x39\x46\x4f\x71\x2e\x9f\x6b\x5a\xd4\x4a\x82\xa0\xe8\x3d\xb5\x35\x37\x93\x05\x5c\x29\xd6\x41\xb0\x79\xef\x15\x89\x92\x4e\xa1\x6f\x51\xb7\x9f\xed\xec\x57\xd6\xdf\x1d\x1b\x8f\x0e\x7e\x38\x8f\xcf\x6c\x43\x57\x47\x1f\x93\xdc\x62\xce\xe6\x20\xe9\xc6\xd4\x40\xc8\x6a\x4e\x17\x1e\x31\x46\x2b\x22\x14\x2f\xeb\x05\x68\xe8\xb0\x4d\x88\x28\x90\x96\x8d\xb6\xbd\x32\xbb\x47\xd7\x88\x6d\x49\x2d\x5c\x4a\x3a\xa0\x58\x6d\x9b\x99\xad\xaf\x86\xa9\x40\x68\x66\xc2\xc1\x19\x91\x6a\x46\x73\x48\x7d\xd4\x81\xa6\xa7\xd4\xe8\xd3\xe2\x63\xb3\x12\xca\x31\x97\x6e\x83\x9a\x19\x8a\xd2\xa7\x93\x2e\xa1\xb9\x66\x7a\x9b\xda\xc6\xc8\xe9\xc3\xbb\x8a\xaf\x50\xd4\xe6\x98\x28\xb4\x66\x77\x51\x37\x74\xee\x32\xba\xf3\xb2\xe9\x68\x60\x03\x03\x8b\xbd\x17\x38\x93\x7b\x2d\x5b\xc4\x5d\x36\xb3\xb5\xe8\xd1\xb1\x2d\xa5\x5b\x5f\xbe\x15\x7f\xdb\x31\x3b\x95\x77\xa8\xbf\xab\xfd\xff\x2e\x3c\x9f\xd0\xff\xe4\xfc\x15\x9f\x7c\x74\x13\x38\x39\xc4\xab\x13\x6a\xd6\xe8\xa4\x0e\xcf\x6f\xf8\x04\xf4\xf1\xf9\x35\xde\xff\xff\x9f\x7f\xbc\xff\xf0\xcd\xb7\x74\xf9\xc3\x99\xc8\xc3\xb3\x67\x0f\xe3\x87\x17\xaf\x6f\x1e\x3e\xb9\x3f\x2e\xf0\xf9\x5f\xae\x9e\x3d\x7c\xfe\xf8\xbb\x2f\xff\xaa\x2f\x9e\xfc\xe3\xbb\xb3\x7c\xf2\xb1\x76\xb7\x95\x3e\xdc\xe2\x9f\x2e\xf4\xa7\x7f\xfe\xf4\xf4\xfa\x19\x3f\x9a\xfa\x07\xec\xf7\x1f\xec\xf7\xfe\xed\xe3\xa7\x8f\xbf\x7f\xf3\xea\xcd\xf7\x4f\xbf\x8a\xb3\xd7\xdf\xfd\xfb\xcc\xbf\xf9\x06\xcf\xe5\xe6\x62\xbd\xf7\xe5\xb7\x4f\xbf\x7c\xf1\xcb\x17\xff\x0d\x00\x00\xff\xff\xe2\xef\x72\xab\x7e\x0a\x00\x00") + +func ConfigCliFleetEthBetaJsonBytes() ([]byte, error) { + return bindataRead( + _ConfigCliFleetEthBetaJson, + "../config/cli/fleet-eth.beta.json", + ) +} + +func ConfigCliFleetEthBetaJson() (*asset, error) { + bytes, err := ConfigCliFleetEthBetaJsonBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "../config/cli/fleet-eth.beta.json", size: 2686, mode: os.FileMode(420), modTime: time.Unix(1536838782, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + +var _ConfigCliFleetEthStagingJson = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x84\x94\x4f\x6f\x1b\x47\x0f\xc6\xef\xf9\x14\x82\xce\x2f\x56\xe4\x70\xfe\x70\x74\xf2\x6b\x3b\x6d\xda\xc0\x06\xe2\x34\x6d\xd3\xa2\x07\xce\x90\x4c\x54\x3b\x92\x22\xad\xdd\xda\x45\xbe\x7b\xb1\x8e\x9b\xda\x41\x9c\xde\x06\x7c\x38\xcb\x1f\x1f\xce\xf2\xaf\x27\xb3\xd9\x6c\x36\x3f\xb5\xf1\x8f\xcd\xee\xfc\x3b\x9d\x2f\x67\xf4\xbf\x8f\xb1\x63\x19\xe5\x78\xb5\x9b\x2f\x67\xf3\x61\x31\xd8\xf8\xd6\x76\x76\xf9\x6e\xb4\xfd\xb8\x38\xdb\x6c\xf7\xa3\xad\xe7\x77\x99\xcf\xed\xfa\xe5\xb8\xd9\xd9\xd7\xb3\x17\xe7\x76\xbd\x9f\xd2\xfe\xb9\x76\xba\x39\x5e\xed\xfb\xe6\xca\x76\xd7\xf3\xe5\x6c\xdc\x5d\xda\x9d\x70\x66\x6b\xb5\x9b\xab\xcd\xe5\xfe\x61\xfc\xe8\xe2\x72\x3f\xda\xee\x68\xb3\xf6\xd5\x9b\xf9\x72\xf6\x11\xfe\x56\x7a\xba\x96\x76\x61\xfa\x20\xff\x56\xf8\xe6\xc2\x6c\x9c\xa8\x6c\x7c\x3b\xec\x47\x79\xb3\x5a\xbf\x99\xdf\xd3\x0f\x37\x9b\xf1\x74\xa3\x36\x95\xfa\xf5\x53\xf8\x56\xb2\xf5\x46\x6d\xb9\x58\x20\x48\xe1\x8e\xa5\x86\x2a\x05\xb0\x9a\x47\x91\x10\x62\xd5\x42\x10\xbc\x64\x31\x16\xc8\x1e\xa1\x05\xed\xcc\xad\x34\x42\x6b\xee\x51\x72\x20\x6f\x2d\xc6\x36\x1d\x8b\xf4\x26\xa1\xe6\x8e\xdd\x94\x4a\x49\x5a\xd1\x9b\x21\xc7\x4c\x1d\x13\xd4\x22\x59\x28\xbb\xb6\xd0\x19\xa2\x3b\x79\x4f\x8d\xd2\x01\xa5\x21\x10\x0f\xb5\x0c\x81\xe2\x92\x20\x42\xbc\xd7\xc2\x03\x56\x2f\xd5\x1b\x55\xac\x5e\x42\x97\x94\x41\x14\x22\x45\xed\x9d\xb8\x48\x73\x8b\x68\x90\x73\x0e\x80\xd6\x54\x54\xd8\xd4\x20\xe6\x10\x53\x14\x24\xb5\x06\xa9\xab\x60\xf2\xc0\x45\x43\x8f\x4d\x39\xa9\x30\x3a\x58\x0b\x49\x41\x22\xe7\xd6\x5a\x67\xf5\x18\x4a\xab\x05\xa5\x27\x4c\x44\x4d\x01\xdc\x0e\xb0\xc4\x01\x89\x07\x84\x89\xb6\xde\xd1\x7e\x82\xfd\xed\x9e\xf5\x3f\xec\xa6\x71\xea\x89\xac\x2e\x5e\xda\xee\xca\x76\x5f\x99\x41\x9e\x1a\x6a\x22\xc5\xb1\x84\xa0\x88\x28\xdc\x6b\xcf\xdc\xa9\x0a\x61\x24\xb0\xaa\x29\x97\x5c\x93\x67\x04\x76\xc2\xde\x5b\xee\xca\x0e\xa2\xee\xb1\x56\xb4\xe2\xda\x85\x5d\x4a\x01\x2b\xa9\x75\x96\x34\x7d\xa7\x68\x88\xb9\x42\x6f\x9d\xc1\x00\x18\x4b\xf2\x08\x1d\x53\xd1\xec\xb9\x30\x6b\xe4\x83\x00\x79\x40\xae\x43\x88\x30\x60\x5e\x12\xa4\xc7\x87\x60\xd1\x3b\x42\x47\xcf\xa9\xb3\x98\x32\x49\x0f\xb9\x75\x6c\xde\x02\x4a\x4c\xbd\xa3\x70\x4a\x20\x89\xa1\x94\xce\x1a\xd4\x82\x80\x41\x57\x64\x8b\xe0\x1a\xc1\x8b\x65\x2f\x0a\x41\x7b\xee\x0a\xb9\x72\x68\x80\xb1\x1b\xb3\x66\x8b\x99\x4b\x48\xee\x16\x3a\x12\x5b\x4d\x3c\x71\x02\x40\x90\xe2\x1f\x1f\x4c\x1d\xb0\xd2\x10\xf1\x8e\xf5\x8b\x23\x78\x39\xca\xb8\xea\xff\xf1\xfe\x2b\xc6\x0e\x8d\xc0\x43\x69\xd2\x08\x3a\xaa\x2b\xa1\x8a\x96\x9c\x82\xc4\xec\x2a\x95\x0a\xa4\x18\xc4\x0c\x1b\xe4\x10\x2b\x37\xa4\x98\xcc\xa0\x22\x65\x8c\x8d\x5b\x35\x32\x8e\x39\x04\xe3\x28\x25\x00\xf5\xc4\x89\xc5\x50\x2b\x63\xf5\x4c\x62\xdd\x90\xcc\x72\x66\x8b\x9e\x73\x66\xc8\x54\xb9\x1e\x60\x2e\x43\x9d\xda\x19\x30\xf2\x92\x80\x20\x3d\x66\x7d\x50\xae\xa5\xe7\xa9\x50\x8d\xd5\xab\x76\x47\x08\xa5\x3a\x80\xd5\xc6\x14\x52\x47\x76\x2b\x2e\x29\x68\x4e\x9c\x02\x88\x96\x66\x35\x43\xe9\x4c\x00\xdc\x62\x68\x90\xc5\x5c\x6b\xe9\x6e\xe8\x2a\x2d\x15\x74\x22\x09\x52\x89\xc9\xbd\x96\x9e\x2a\x54\xd3\x84\x9e\x69\xba\x44\xb1\x22\x05\x2a\x36\x59\x8f\x35\x0c\x30\x70\xbe\x23\xfd\xa2\xf1\xff\xae\xb8\x47\xcc\x5f\xac\xb6\x71\xf1\xd9\xcf\xb4\x18\xfb\x76\x41\x50\x80\x16\x36\xbe\xbd\x8a\x0b\xcc\xaf\x56\x97\xe1\xd9\xff\xcf\xaf\xbf\x7f\x66\x36\xbe\x88\xc7\xa7\x5b\x8d\xa7\xbf\x84\xf5\x78\xf3\xe7\xc9\x26\xa4\x9b\xae\xdb\xab\x6f\x5f\x9c\xbd\x3f\x3f\x4e\xdb\xc3\x7a\xf8\x34\x9f\xbd\xfa\xdc\xba\xdb\x4a\x0f\x56\xcc\xa3\x75\xde\xe1\xfe\xc7\xeb\x9f\xdf\x9d\x9f\x9c\xfe\xae\x76\xfc\xd3\xfb\xe7\xe1\x72\xd7\xae\x8f\x68\x73\xf8\x6c\xc5\x27\xc7\xdb\x23\x7d\x7d\xbe\xde\xe3\xfa\xb5\xf8\xfb\x9b\x7b\x8d\xdf\x9e\x3e\x3c\xf9\xf0\xe4\xef\x00\x00\x00\xff\xff\x7d\x3c\xe1\x6a\x6f\x06\x00\x00") + +func ConfigCliFleetEthStagingJsonBytes() ([]byte, error) { + return bindataRead( + _ConfigCliFleetEthStagingJson, + "../config/cli/fleet-eth.staging.json", + ) +} + +func ConfigCliFleetEthStagingJson() (*asset, error) { + bytes, err := ConfigCliFleetEthStagingJsonBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "../config/cli/fleet-eth.staging.json", size: 1647, mode: os.FileMode(420), modTime: time.Unix(1536838782, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + +var _ConfigCliFleetEthTestJson = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x84\x94\xcf\x6e\x1c\xc6\x0d\xc6\xef\x7e\x0a\x41\xe7\x62\x3d\xc3\xff\xd4\xc9\x88\x9d\x16\x45\x53\x1f\xea\xf6\x54\xf4\x40\x0e\x39\xb1\x10\x55\x0b\xac\x56\x2e\xdc\x22\xef\x5e\xac\x2c\xa7\x4e\xd0\x38\xb7\xc1\x7c\x9c\xe1\xc7\x99\x1f\xf9\x9f\x17\x57\x57\x57\x57\xd7\x6f\xfb\xfc\xaf\xe3\xe9\x87\x3f\xd6\xf5\xcd\x95\xaa\xfe\xee\xd3\xee\x9b\x38\xc7\x9b\xdb\xd3\xf5\xcd\xd5\xf5\xe1\xe5\xa1\xcf\xef\xfb\xd4\x8f\xff\x3c\xf7\xc3\xf9\xe5\xbb\x73\x9c\x1f\x1f\x5e\xbf\x8f\xdb\xfb\xeb\xe7\xe8\x3f\xf5\xc7\x77\xe7\xe3\xa9\x7f\xfb\xc4\xcb\x1f\xfa\xe3\xc3\x25\xf4\xf3\xd1\xb7\xc7\x37\xb7\x0f\xeb\xf8\xa1\x4f\x1f\xaf\x6f\xae\xce\xa7\xc7\x7e\x16\xfe\xd2\xf7\xd5\xff\xfe\x70\x7c\x7c\xb8\xbe\xb9\xda\x71\xf7\xf0\x59\xf8\xee\xf8\xfd\x77\xfd\xa1\xef\x2e\x99\xde\x7c\xfb\xcd\xdf\xfe\xf0\xf9\xaa\xd7\x77\x8f\x0f\xe7\x3e\xbd\x3e\xde\xef\xdb\xef\xaf\x6f\xae\x3e\x15\xf8\x24\x7d\x7b\x1f\x79\xd7\xf5\xb3\x0c\x4f\xc2\xef\xef\xba\xcf\x97\x9b\xfa\xfc\xfe\x70\x31\x7b\xfd\x85\xf8\xcd\xf1\x78\x7e\x7b\xac\xbe\x38\xf8\xfb\x4f\xdb\x4f\x52\xdf\x1f\xab\x6f\x5e\xbe\xac\x88\x86\x56\x30\x18\x6d\xd2\x4e\xb0\x03\xc2\x22\xb4\xa4\xdd\x99\x8a\x06\x61\x28\x23\x19\x96\x65\x21\x5a\x4f\xc9\x36\x58\xdb\x05\xd0\xa1\x78\x8d\x9e\xd1\xac\x0b\x4b\x1d\x17\x16\x56\x55\xd9\x2e\x36\x44\x07\x81\x26\x40\xaa\xe5\x22\xdb\x19\x57\x29\x26\x23\x6f\xde\xf1\x8a\xf4\xc0\x70\x98\x66\x87\x49\x7e\x83\x83\x06\x7d\x51\xc2\xcf\xbc\x7a\x0f\x37\x63\xe5\x4e\xd5\xa9\x0b\xb8\x3a\x14\x76\xcd\xb9\x34\x51\x55\xb4\xd6\xf0\x35\x43\xc5\x64\xeb\x82\x5e\xac\x5a\x38\xac\x80\x2e\x01\xab\x53\x94\xab\x09\xa7\x92\x52\xcc\x30\x1d\x4d\x6a\x06\x8a\xe0\xa2\x9b\x76\xa8\x71\x0e\xc8\x70\x2e\x11\x4f\x9c\x45\x42\xd5\x6b\xbc\x82\x21\x87\x69\x7e\x00\xc2\xc3\x14\xfa\xba\xdb\x35\x9b\xc7\x34\x33\x5d\x26\x58\x42\x97\xac\x29\x9e\x5b\xa6\xb2\xcc\x61\x6a\xc0\x84\xa3\x89\xc6\x54\xc4\xcd\x19\xaa\x4b\x47\xe5\x24\x40\xa9\x8d\x36\x77\xef\x1c\x9d\x3d\x63\x11\x80\x53\x7a\x83\xcc\xcc\x06\xc4\xca\x4a\xc3\x46\x58\x6c\xb2\x44\xa2\x41\x96\x55\xeb\x58\x49\xeb\x15\xf2\xa7\x67\x15\x3b\x4c\xd4\x67\xb3\x3f\x79\xfd\xc7\x17\x9c\xfc\xf5\x74\x01\xaf\xfe\x1c\xb7\x77\xef\xfa\xf4\xa1\x4f\x5f\x01\xc6\x99\xd6\x10\x19\x18\xd2\xca\x9c\x7b\xb7\xbb\x83\x4c\xde\xa4\xcc\x46\x96\x15\x1e\x51\x15\x4a\xe5\x30\x22\x70\xd6\x2c\x6b\xda\x32\x00\x6a\x31\x4b\xad\x10\x15\xdb\x16\x63\x3b\xb1\x6f\xd6\x54\x70\x1e\x5e\x89\xcb\x12\x33\x6d\xc4\xda\x59\x16\x30\x01\x87\xe9\x96\x95\xa5\x59\x5f\x02\xe3\x72\x83\x83\x7f\xfd\x0b\x9a\x4c\x78\xb7\x2c\x88\x0b\x15\x2c\x18\x42\xa4\xee\xa3\xac\x7d\xb5\x28\x08\x51\x34\xf6\x30\xb8\xd0\x81\xd6\x6b\xee\xcb\x2f\x8d\xee\x2d\x08\x63\x0d\x0d\x2e\x19\x0b\x53\xc0\x37\x17\x91\xd3\x76\x2c\x49\x93\x50\xe2\x18\xb9\x85\x22\xc1\x39\x73\x0b\xab\x8f\xa9\x51\x4b\xba\xec\x17\xc0\xcc\xaf\xbb\xd5\xa1\xcd\x4a\x8c\xb1\x0a\x9b\xcc\x16\x51\x7a\x8d\x9e\xea\xca\xa8\xb3\x61\xdb\xbe\x00\x0c\xdc\xd1\x9c\xb1\xc2\xd3\x97\xf5\x90\x65\xb2\xaa\x75\xa9\x53\xc8\x82\x46\x81\x81\xb9\x7d\xb3\xac\x68\xcb\xd1\x3c\x36\x26\xe2\xa2\x05\x29\x4e\xa1\x19\xdd\x31\x95\x89\x84\x56\x53\xe3\x13\x30\x0e\x07\x00\x3f\x4c\x85\x67\xb3\xff\x17\x98\xcb\x40\xbc\x5d\xbf\x31\x5a\xa2\xd0\xb6\xd3\xc0\x11\x46\xb2\x96\x8e\xc1\xa9\x31\x37\xa6\xe4\x98\xb9\xa9\x37\x7b\x21\xb5\x15\x96\x6c\xaa\x09\xb5\x01\x6b\x52\x86\x09\x8b\x0e\x08\x42\x2e\xa4\xb5\xa9\x36\x26\x4d\x58\x63\x4d\x07\xac\xcd\x51\x6b\xb5\x91\x4c\x84\x19\xa3\x6c\xef\x74\x42\x4e\x90\x66\x85\x05\x9f\x47\x0b\xf0\xa5\xac\x4b\xb3\xe2\xe0\x5f\x7b\xfb\x59\xd3\x51\x90\x7b\x4c\xf6\x69\x3b\x8d\x33\x63\xab\xd2\xa5\x77\x27\x6c\xe1\xd2\xb1\xc4\x55\x79\xc8\x34\xed\x3d\x88\x60\x94\x52\xaf\x21\xcb\xdb\xf6\xa8\x95\xac\x1d\x83\x60\x1b\xd7\x36\x25\xc4\x8a\x9c\x1a\x13\x64\x74\x59\x55\xcf\xac\xed\x25\xc5\x05\xd5\x94\x9a\xdb\xda\x1d\x7f\x41\x0a\x7e\xdd\xed\x66\xc7\x00\x55\x9c\xb9\xc6\xb6\xce\x61\xd6\x50\xe8\x00\xb0\xa0\xd8\x6b\xa7\xe7\x1e\x89\xce\xa3\x34\x0c\xac\x78\xb6\x45\x7a\x0f\xdb\x7b\x67\xb9\x4f\x09\x83\x5d\xee\xb8\x66\x0c\x1b\x8b\x75\x41\x96\x8e\x2e\x59\x28\x9b\xcc\xc3\xc5\xab\xc5\x35\xf6\x76\x1c\x66\x95\xdd\x33\xfc\x13\x29\x74\xc0\x79\x98\xc3\x9e\xbd\xfe\x0f\x94\xa7\xd5\x8f\x2f\x7e\x7c\xf1\xdf\x00\x00\x00\xff\xff\x71\x71\x1d\xe1\xa2\x07\x00\x00") + +func ConfigCliFleetEthTestJsonBytes() ([]byte, error) { + return bindataRead( + _ConfigCliFleetEthTestJson, + "../config/cli/fleet-eth.test.json", + ) +} + +func ConfigCliFleetEthTestJson() (*asset, error) { + bytes, err := ConfigCliFleetEthTestJsonBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "../config/cli/fleet-eth.test.json", size: 1954, mode: os.FileMode(420), modTime: time.Unix(1536838782, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + +var _ConfigCliLesEnabledJson = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xaa\xe6\x52\x50\x50\x50\x50\xf2\xc9\x4c\xcf\x28\x71\x2d\xc9\x70\xce\xcf\x4b\xcb\x4c\x57\xb2\x52\x80\x88\x83\xe5\x5c\xf3\x12\x93\x72\x52\x53\x94\xac\x14\x4a\x8a\x4a\x53\xc1\xe2\xb5\x5c\xb5\x5c\x80\x00\x00\x00\xff\xff\xa6\x74\x24\x05\x3a\x00\x00\x00") + +func ConfigCliLesEnabledJsonBytes() ([]byte, error) { + return bindataRead( + _ConfigCliLesEnabledJson, + "../config/cli/les-enabled.json", + ) +} + +func ConfigCliLesEnabledJson() (*asset, error) { + bytes, err := ConfigCliLesEnabledJsonBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "../config/cli/les-enabled.json", size: 58, mode: os.FileMode(420), modTime: time.Unix(1536838782, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + +var _ConfigStatusChainGenesisJson = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x9c\x90\x41\x6f\x13\x31\x10\x85\xef\xf9\x15\x96\xcf\x1c\xc6\xf6\x78\x66\xbd\x37\x4a\x59\x81\xc4\x8d\x5f\x30\xb6\xc7\xcd\xaa\x9b\x6c\x94\x6c\xa4\x16\xd4\xff\x8e\x92\x2c\xa1\x42\x9c\xf0\xcd\x7e\x33\xef\x7b\x7e\x3f\x37\xc6\x58\x99\xa6\xb9\xd8\xde\x5c\x2e\xc6\x18\x9b\x07\x47\x58\x24\xa0\x0b\x9e\x04\x42\x8e\xc8\x05\xe2\x43\xc0\x90\xfd\x67\xef\x74\xf8\xa8\x1e\x73\xfa\xb3\x62\x6c\x96\x49\xf6\x45\x6d\x6f\x2c\xbc\x38\x18\xe0\xd3\x00\x84\x8f\x8f\x31\x79\xb8\x1c\x7b\x1d\x7c\xfb\xb0\x22\x3c\x44\x89\x15\x94\x7d\x6b\x14\x81\x53\x2b\x92\x33\x0a\x05\x0d\xa1\xfa\x4e\x84\xaa\x36\x5f\xfe\x03\xb1\x59\x31\xb6\xcc\xfb\x36\x3e\xdd\x1d\x6c\xd9\xca\xb8\xff\x5a\x6d\x6f\x98\xf9\x16\xc4\x6e\xe7\x9d\x9e\x16\x95\xfa\x30\xcd\xe5\xd9\xf6\x06\x56\xa1\xca\x3c\xcc\xc7\xe7\xef\xe7\xc3\x61\x3e\x2e\xb6\x37\xcb\xf1\xac\xab\x96\x5f\x7f\xc8\x7e\x19\xcf\xbb\xbf\x97\x74\x3c\xb8\x08\x5f\xe4\xb4\xbd\x85\x8c\xa0\xa4\x35\xa3\x26\xa8\x89\x1c\x49\xe9\x72\x63\xe7\x92\x6a\xe0\x82\x80\x5d\x41\x27\x15\x52\xf0\x1d\x31\xa9\xcb\x21\xd5\x42\x9d\x80\x96\x96\x43\xb5\xef\x7d\xe3\x3f\x69\xdd\xfd\xf5\xfe\xef\x3a\xb6\x36\x96\xf3\xb4\xbc\xfe\xae\xea\xda\xce\x55\xd3\x97\xe5\x28\x8f\xb2\xc8\x1a\x30\x30\x92\x63\xe4\xc8\x01\x03\x75\xe4\x28\x91\x7a\x88\xc0\x9e\x12\xd3\x45\xa3\xe8\x21\x22\x45\xbe\xcc\x26\x52\x62\x0f\xa8\x14\x19\x99\xa9\xb1\xa7\x7c\xb3\x7e\x92\xd3\xb7\x71\x37\x2e\xef\xa1\x60\x37\x6f\x9b\x5f\x01\x00\x00\xff\xff\x91\xc6\xb3\x58\x64\x02\x00\x00") + +func ConfigStatusChainGenesisJsonBytes() ([]byte, error) { + return bindataRead( + _ConfigStatusChainGenesisJson, + "../config/status-chain-genesis.json", + ) +} + +func ConfigStatusChainGenesisJson() (*asset, error) { + bytes, err := ConfigStatusChainGenesisJsonBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "../config/status-chain-genesis.json", size: 612, mode: os.FileMode(420), modTime: time.Unix(1531118119, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + +// Asset loads and returns the asset for the given name. +// It returns an error if the asset could not be found or +// could not be loaded. +func Asset(name string) ([]byte, error) { + cannonicalName := strings.Replace(name, "\\", "/", -1) + if f, ok := _bindata[cannonicalName]; ok { + a, err := f() + if err != nil { + return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err) + } + return a.bytes, nil + } + return nil, fmt.Errorf("Asset %s not found", name) +} + +// MustAsset is like Asset but panics when Asset would return an error. +// It simplifies safe initialization of global variables. +func MustAsset(name string) []byte { + a, err := Asset(name) + if err != nil { + panic("asset: Asset(" + name + "): " + err.Error()) + } + + return a +} + +// AssetInfo loads and returns the asset info for the given name. +// It returns an error if the asset could not be found or +// could not be loaded. +func AssetInfo(name string) (os.FileInfo, error) { + cannonicalName := strings.Replace(name, "\\", "/", -1) + if f, ok := _bindata[cannonicalName]; ok { + a, err := f() + if err != nil { + return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err) + } + return a.info, nil + } + return nil, fmt.Errorf("AssetInfo %s not found", name) +} + +// AssetNames returns the names of the assets. +func AssetNames() []string { + names := make([]string, 0, len(_bindata)) + for name := range _bindata { + names = append(names, name) + } + return names +} + +// _bindata is a table, holding each asset generator, mapped to its name. +var _bindata = map[string]func() (*asset, error){ + "../config/cli/fleet-eth.beta.json": ConfigCliFleetEthBetaJson, + "../config/cli/fleet-eth.staging.json": ConfigCliFleetEthStagingJson, + "../config/cli/fleet-eth.test.json": ConfigCliFleetEthTestJson, + "../config/cli/les-enabled.json": ConfigCliLesEnabledJson, + "../config/status-chain-genesis.json": ConfigStatusChainGenesisJson, +} + +// AssetDir returns the file names below a certain +// directory embedded in the file by go-bindata. +// For example if you run go-bindata on data/... and data contains the +// following hierarchy: +// data/ +// foo.txt +// img/ +// a.png +// b.png +// then AssetDir("data") would return []string{"foo.txt", "img"} +// AssetDir("data/img") would return []string{"a.png", "b.png"} +// AssetDir("foo.txt") and AssetDir("notexist") would return an error +// AssetDir("") will return []string{"data"}. +func AssetDir(name string) ([]string, error) { + node := _bintree + if len(name) != 0 { + cannonicalName := strings.Replace(name, "\\", "/", -1) + pathList := strings.Split(cannonicalName, "/") + for _, p := range pathList { + node = node.Children[p] + if node == nil { + return nil, fmt.Errorf("Asset %s not found", name) + } + } + } + if node.Func != nil { + return nil, fmt.Errorf("Asset %s not found", name) + } + rv := make([]string, 0, len(node.Children)) + for childName := range node.Children { + rv = append(rv, childName) + } + return rv, nil +} + +type bintree struct { + Func func() (*asset, error) + Children map[string]*bintree +} +var _bintree = &bintree{nil, map[string]*bintree{ + "..": &bintree{nil, map[string]*bintree{ + "config": &bintree{nil, map[string]*bintree{ + "cli": &bintree{nil, map[string]*bintree{ + "fleet-eth.beta.json": &bintree{ConfigCliFleetEthBetaJson, map[string]*bintree{}}, + "fleet-eth.staging.json": &bintree{ConfigCliFleetEthStagingJson, map[string]*bintree{}}, + "fleet-eth.test.json": &bintree{ConfigCliFleetEthTestJson, map[string]*bintree{}}, + "les-enabled.json": &bintree{ConfigCliLesEnabledJson, map[string]*bintree{}}, + }}, + "status-chain-genesis.json": &bintree{ConfigStatusChainGenesisJson, map[string]*bintree{}}, + }}, + }}, +}} + +// RestoreAsset restores an asset under the given directory +func RestoreAsset(dir, name string) error { + data, err := Asset(name) + if err != nil { + return err + } + info, err := AssetInfo(name) + if err != nil { + return err + } + err = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755)) + if err != nil { + return err + } + err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode()) + if err != nil { + return err + } + err = os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime()) + if err != nil { + return err + } + return nil +} + +// RestoreAssets restores an asset under the given directory recursively +func RestoreAssets(dir, name string) error { + children, err := AssetDir(name) + // File + if err != nil { + return RestoreAsset(dir, name) + } + // Dir + for _, child := range children { + err = RestoreAssets(dir, filepath.Join(name, child)) + if err != nil { + return err + } + } + return nil +} + +func _filePath(dir, name string) string { + cannonicalName := strings.Replace(name, "\\", "/", -1) + return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...) +} + diff --git a/static/keys/wnodepassword b/static/keys/wnodepassword deleted file mode 100644 index 3fa88ecfc..000000000 --- a/static/keys/wnodepassword +++ /dev/null @@ -1 +0,0 @@ -status-offline-inbox diff --git a/static/static.go b/static/static.go new file mode 100644 index 000000000..24d688e13 --- /dev/null +++ b/static/static.go @@ -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/... diff --git a/t/benchmarks/doc_test.go b/t/benchmarks/doc_test.go index f7e1b6c7a..e847a88c1 100644 --- a/t/benchmarks/doc_test.go +++ b/t/benchmarks/doc_test.go @@ -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 \ diff --git a/static/config/public-chain-accounts.json b/t/config/public-chain-accounts.json similarity index 100% rename from static/config/public-chain-accounts.json rename to t/config/public-chain-accounts.json diff --git a/static/config/status-chain-accounts.json b/t/config/status-chain-accounts.json similarity index 100% rename from static/config/status-chain-accounts.json rename to t/config/status-chain-accounts.json diff --git a/static/config/test-data.json b/t/config/test-data.json similarity index 100% rename from static/config/test-data.json rename to t/config/test-data.json diff --git a/t/e2e/README.md b/t/e2e/README.md index 5cc9bb6ef..79b6f4bec 100644 --- a/t/e2e/README.md +++ b/t/e2e/README.md @@ -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 { diff --git a/t/e2e/accounts/accounts_test.go b/t/e2e/accounts/accounts_test.go index 47dbfe489..8d7f824e8 100644 --- a/t/e2e/accounts/accounts_test.go +++ b/t/e2e/accounts/accounts_test.go @@ -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() diff --git a/t/e2e/api/api_test.go b/t/e2e/api/api_test.go index 3b8e73899..9186ac7d8 100644 --- a/t/e2e/api/api_test.go +++ b/t/e2e/api/api_test.go @@ -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) diff --git a/t/e2e/api/backend_test.go b/t/e2e/api/backend_test.go index add311676..c91a1bf07 100644 --- a/t/e2e/api/backend_test.go +++ b/t/e2e/api/backend_test.go @@ -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()) diff --git a/t/e2e/suites.go b/t/e2e/suites.go index c85c5f4e4..6acb1fdaf 100644 --- a/t/e2e/suites.go +++ b/t/e2e/suites.go @@ -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()) } diff --git a/t/e2e/testing.go b/t/e2e/testing.go index c723c12e8..5f739633e 100644 --- a/t/e2e/testing.go +++ b/t/e2e/testing.go @@ -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") } } diff --git a/t/e2e/whisper/whisper_mailbox_test.go b/t/e2e/whisper/whisper_mailbox_test.go index b02cd9f2e..1adebd642 100644 --- a/t/e2e/whisper/whisper_mailbox_test.go +++ b/t/e2e/whisper/whisper_mailbox_test.go @@ -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 diff --git a/t/e2e/whisper/whisper_test.go b/t/e2e/whisper/whisper_test.go index 0dcf608eb..93b3241bf 100644 --- a/t/e2e/whisper/whisper_test.go +++ b/t/e2e/whisper/whisper_test.go @@ -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() diff --git a/t/utils/utils.go b/t/utils/utils.go index 321a2fcb7..59f818f18 100644 --- a/t/utils/utils.go +++ b/t/utils/utils.go @@ -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 } - cfg.Name = name - cfg.NetworkID = uint64(GetNetworkID()) + if name == "" { + cfg.Name = "test" + } else { + cfg.Name = name + } + 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 } diff --git a/transactions/transactor_test.go b/transactions/transactor_test.go index c9115bf29..5572fefb8 100644 --- a/transactions/transactor_test.go +++ b/transactions/transactor_test.go @@ -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