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.
This commit is contained in:
Pedro Pombeiro 2018-09-13 18:31:29 +02:00 committed by GitHub
parent 0480d1a376
commit 3d00af7fa3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
48 changed files with 1466 additions and 1005 deletions

View File

@ -14,7 +14,7 @@
"host": "127.0.0.1", "host": "127.0.0.1",
"program": "${workspaceRoot}/cmd/statusd", "program": "${workspaceRoot}/cmd/statusd",
"env": {}, "env": {},
"args": ["-shh", "-les", "-discovery=true"], "args": ["-c", "${workspaceRoot}/config/cli/fleet-eth.test.json"],
"showLog": true, "showLog": true,
"output": "${workspaceRoot}/build/bin/statusd.debug" "output": "${workspaceRoot}/build/bin/statusd.debug"
} }

View File

@ -198,6 +198,10 @@ xgo:
go get github.com/karalabe/xgo go get github.com/karalabe/xgo
setup: dep-install lint-install mock-install ##@other Prepare project for first build 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 mock-install: ##@other Install mocking tools
go get -u github.com/golang/mock/mockgen go get -u github.com/golang/mock/mockgen

View File

@ -13,24 +13,19 @@ services:
ipv4_address: 172.16.238.10 ipv4_address: 172.16.238.10
wnode: wnode:
image: status-go:latest image: statusteam/status-go:latest
command: command:
- statusd - "-c"
- "-les=false" - "/config/wnode-config.json"
- "-shh"
- "-listenaddr=:30303"
- "-discovery=true"
- "-standalone=false"
- "-bootnodes=enode://3f04db09bedc8d85a198de94c84da73aa7782fafc61b28c525ec5cca5a6cc16be7ebbb5cd001780f71d8408d35a2f6326faa1e524d9d8875294172ebec988743@172.16.238.10:30303"
- "-http"
- "-httphost=0.0.0.0"
- "-log=DEBUG"
ports: ports:
- 8080 - 8080
- 8545 - 8545
- 30303 - 30303
networks: networks:
cluster: cluster:
volumes:
- ./wnode-config.json:/config/wnode-config.json:ro
- ./.ethereumtest:/data/ethereumtest/:rw
depends_on: depends_on:
- bootnode - bootnode

View File

@ -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
}
}

View File

@ -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 := []gethnode.ServiceConstructor{}
services = appendIf(config.UpstreamConfig.Enabled, services, b.rpcFiltersService()) services = appendIf(config.UpstreamConfig.Enabled, services, b.rpcFiltersService())

View File

@ -9,13 +9,15 @@ import (
"github.com/status-im/status-go/node" "github.com/status-im/status-go/node"
"github.com/status-im/status-go/params" "github.com/status-im/status-go/params"
"github.com/status-im/status-go/rpc" "github.com/status-im/status-go/rpc"
"github.com/status-im/status-go/t/utils"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
func TestBackendStartNodeConcurrently(t *testing.T) { func TestBackendStartNodeConcurrently(t *testing.T) {
backend := NewStatusBackend() backend := NewStatusBackend()
config := params.NodeConfig{} config, err := utils.MakeTestNodeConfig(params.StatusChainNetworkID)
require.NoError(t, err)
count := 2 count := 2
resultCh := make(chan error) resultCh := make(chan error)
@ -24,7 +26,7 @@ func TestBackendStartNodeConcurrently(t *testing.T) {
for i := 0; i < count; i++ { for i := 0; i < count; i++ {
go func() { go func() {
resultCh <- backend.StartNode(&config) resultCh <- backend.StartNode(config)
wg.Done() wg.Done()
}() }()
} }
@ -40,16 +42,17 @@ func TestBackendStartNodeConcurrently(t *testing.T) {
require.Contains(t, results, nil) require.Contains(t, results, nil)
require.Contains(t, results, node.ErrNodeRunning) require.Contains(t, results, node.ErrNodeRunning)
err := backend.StopNode() err = backend.StopNode()
require.NoError(t, err) require.NoError(t, err)
} }
func TestBackendRestartNodeConcurrently(t *testing.T) { func TestBackendRestartNodeConcurrently(t *testing.T) {
backend := NewStatusBackend() backend := NewStatusBackend()
config := params.NodeConfig{} config, err := utils.MakeTestNodeConfig(params.StatusChainNetworkID)
require.NoError(t, err)
count := 3 count := 3
err := backend.StartNode(&config) err = backend.StartNode(config)
require.NoError(t, err) require.NoError(t, err)
defer func() { defer func() {
require.NoError(t, backend.StopNode()) require.NoError(t, backend.StopNode())
@ -72,9 +75,10 @@ func TestBackendRestartNodeConcurrently(t *testing.T) {
func TestBackendGettersConcurrently(t *testing.T) { func TestBackendGettersConcurrently(t *testing.T) {
backend := NewStatusBackend() 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) require.NoError(t, err)
defer func() { defer func() {
require.NoError(t, backend.StopNode()) require.NoError(t, backend.StopNode())
@ -123,9 +127,10 @@ func TestBackendGettersConcurrently(t *testing.T) {
func TestBackendAccountsConcurrently(t *testing.T) { func TestBackendAccountsConcurrently(t *testing.T) {
backend := NewStatusBackend() 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) require.NoError(t, err)
defer func() { defer func() {
require.NoError(t, backend.StopNode()) require.NoError(t, backend.StopNode())
@ -208,10 +213,11 @@ func TestBackendConnectionChangesToOffline(t *testing.T) {
func TestBackendCallRPCConcurrently(t *testing.T) { func TestBackendCallRPCConcurrently(t *testing.T) {
backend := NewStatusBackend() backend := NewStatusBackend()
config := params.NodeConfig{} config, err := utils.MakeTestNodeConfig(params.StatusChainNetworkID)
require.NoError(t, err)
count := 3 count := 3
err := backend.StartNode(&config) err = backend.StartNode(config)
require.NoError(t, err) require.NoError(t, err)
defer func() { defer func() {
require.NoError(t, backend.StopNode()) require.NoError(t, backend.StopNode())
@ -278,7 +284,9 @@ func TestAppStateChange(t *testing.T) {
func TestBlockedRPCMethods(t *testing.T) { func TestBlockedRPCMethods(t *testing.T) {
backend := NewStatusBackend() backend := NewStatusBackend()
err := backend.StartNode(&params.NodeConfig{}) config, err := utils.MakeTestNodeConfig(params.StatusChainNetworkID)
require.NoError(t, err)
err = backend.StartNode(config)
require.NoError(t, err) require.NoError(t, err)
defer func() { require.NoError(t, backend.StopNode()) }() defer func() { require.NoError(t, backend.StopNode()) }()

View File

@ -192,7 +192,7 @@ func makeNodeConfig() (*params.NodeConfig, error) {
return nil, err 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 { if err != nil {
return nil, err return nil, err
} }

View File

@ -120,7 +120,7 @@ func newCommandSet(statusBackend *api.StatusBackend) *commandSet {
// StartNode loads the configuration out of the passed string and // StartNode loads the configuration out of the passed string and
// starts a node with it. // starts a node with it.
func (cs *commandSet) StartNode(config string) error { func (cs *commandSet) StartNode(config string) error {
nodeConfig, err := params.LoadNodeConfig(config) nodeConfig, err := params.NewConfigFromJSON(config)
if err != nil { if err != nil {
return err return err
} }

View File

@ -180,7 +180,9 @@ func mkConfigJSON(name string) (string, func(), error) {
configJSON := `{ configJSON := `{
"NetworkId": ` + strconv.Itoa(params.RopstenNetworkID) + `, "NetworkId": ` + strconv.Itoa(params.RopstenNetworkID) + `,
"DataDir": "` + tmpDir + `", "DataDir": "` + tmpDir + `",
"KeyStoreDir": "` + tmpDir + `/keystore",
"LogLevel": "INFO", "LogLevel": "INFO",
"NoDiscovery": true,
"RPCEnabled": true "RPCEnabled": true
}` }`
return configJSON, cleanup, nil return configJSON, cleanup, nil

View File

@ -1,26 +1,37 @@
package main package main
import ( import (
"errors" "fmt"
"os"
"path"
"strings" "strings"
) )
// ErrorEmpty returned when value is empty. // configFlags represents an array of JSON configuration files passed to a command line utility
var ErrorEmpty = errors.New("empty value not allowed") type configFlags []string
// StringSlice is a type of flag that allows setting multiple string values. func (f *configFlags) String() string {
type StringSlice []string return strings.Join(*f, ", ")
func (s *StringSlice) String() string {
return "string slice"
} }
// Set trims space from string and stores it. func (f *configFlags) Set(value string) error {
func (s *StringSlice) Set(value string) error { if !path.IsAbs(value) {
trimmed := strings.TrimSpace(value) // Convert to absolute path
if len(trimmed) == 0 { cwd, err := os.Getwd()
return ErrorEmpty if err != nil {
return err
} }
*s = append(*s, trimmed) value = path.Join(cwd, value)
}
// Check that the file exists
stat, err := os.Stat(value)
if err != nil {
return err
}
if stat.IsDir() {
return fmt.Errorf("path does not represent a file: %s", value)
}
*f = append(*f, value)
return nil return nil
} }

View File

@ -68,7 +68,7 @@ func TestStatusFlag(t *testing.T) {
for i, s := range scenarios { for i, s := range scenarios {
msg := fmt.Sprintf("scenario %d", i) 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) require.Nil(t, err, msg)
c.IPCEnabled = s.ipcEnabled c.IPCEnabled = s.ipcEnabled

View File

@ -12,14 +12,11 @@ import (
"strings" "strings"
"time" "time"
"github.com/status-im/status-go/logutils"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
gethmetrics "github.com/ethereum/go-ethereum/metrics" 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/api"
"github.com/status-im/status-go/cmd/statusd/debug" "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" nodemetrics "github.com/status-im/status-go/metrics/node"
"github.com/status-im/status-go/node" "github.com/status-im/status-go/node"
"github.com/status-im/status-go/params" "github.com/status-im/status-go/params"
@ -36,90 +33,59 @@ var (
) )
var ( var (
clusterConfigFile = flag.String("clusterconfig", "", "Cluster configuration file") configFiles configFlags
nodeKeyFile = flag.String("nodekey", "", "P2P node key file (private key)") logLevel = flag.String("log", "", `Log level, one of: "ERROR", "WARN", "INFO", "DEBUG", and "TRACE"`)
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") cliEnabled = flag.Bool("cli", false, "Enable debugging CLI server")
cliPort = flag.String("cliport", debug.CLIPort, "CLI server's listening port") cliPort = flag.String("cli-port", debug.CLIPort, "CLI server's listening port")
pprofEnabled = flag.Bool("pprof", false, "Enable runtime profiling via pprof") pprofEnabled = flag.Bool("pprof", false, "Enable runtime profiling via pprof")
pprofPort = flag.Int("pprofport", 52525, "Port for runtime profiling via pprof") pprofPort = flag.Int("pprof-port", 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") logWithoutColors = flag.Bool("log-without-color", false, "Disables log colors")
version = flag.Bool("version", false, "Print version") version = flag.Bool("version", false, "Print version and dump configuration")
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{}
// don't change the name of this flag, https://github.com/ethereum/go-ethereum/blob/master/metrics/metrics.go#L41 // 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.") 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")
syncAndExit = flag.Int("sync-and-exit", -1, "Timeout in minutes for blockchain sync and exit, zero means no timeout unless sync is finished") 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. // All general log messages in this package should be routed through this logger.
var logger = log.New("package", "status-go/cmd/statusd") var logger = log.New("package", "status-go/cmd/statusd")
func init() { func init() {
flag.Var(&searchTopics, "topic.search", "Topic that will be searched in discovery v5, e.g (mailserver=1,1)") flag.Var(&configFiles, "c", "JSON configuration file(s). Multiple configuration files can be specified, and will be merged in occurrence order")
flag.Var(&registerTopics, "topic.register", "Topic that will be registered using discovery v5.")
flag.Var(&rendezvousNodes, "rendezvous-node", "Rendezvous server.") 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.Usage = printUsage
flag.Parse() flag.Parse()
if flag.NArg() > 0 { if flag.NArg() > 0 {
stdlog.Printf("Extra args in command line: %v", flag.Args())
printUsage() printUsage()
logger.Error("Extra args in command line: %v", flag.Args())
os.Exit(1) os.Exit(1)
} }
} }
func main() { func main() {
colors := !(*logWithoutColors) && terminal.IsTerminal(int(os.Stdin.Fd())) config, err := params.NewNodeConfigWithDefaults("statusd-data", params.FleetBeta, params.RopstenNetworkID)
if err := logutils.OverrideRootLog(logEnabled(), *logLevel, *logFile, colors); err != nil { if err == nil {
stdlog.Fatalf("Error initializing logger: %s", err) err = parseConfig(configFiles, config)
}
if err != nil {
printUsage()
if err != nil {
logger.Error(err.Error())
}
os.Exit(1)
} }
config, err := makeNodeConfig() colors := !(*logWithoutColors) && terminal.IsTerminal(int(os.Stdin.Fd()))
if err != nil { if err = logutils.OverrideRootLog(logEnabled(config), config.LogLevel, config.LogFile, colors); err != nil {
stdlog.Fatalf("Making config failed, %s", err) stdlog.Fatalf("Error initializing logger: %v", err)
} }
// We want statusd to be distinct from StatusIM client. // We want statusd to be distinct from StatusIM client.
config.Name = serverClientName config.Name = serverClientName
@ -206,95 +172,15 @@ func startCollectingNodeMetrics(interruptCh <-chan struct{}, statusNode *node.St
<-interruptCh <-interruptCh
} }
func logEnabled() bool { func logEnabled(config *params.NodeConfig) bool {
return *logLevel != "" || *logFile != "" return config.LogLevel != "" || config.LogFile != ""
} }
// makeNodeConfig parses incoming CLI options and returns node configuration object var (
func makeNodeConfig() (*params.NodeConfig, error) { errStatusServiceRequiresIPC = errors.New("to enable the StatusService on IPC, -ipc flag must be set")
nodeConfig, err := params.NewNodeConfig(*dataDir, *clusterConfigFile, *fleet, uint64(*networkID)) errStatusServiceRequiresHTTP = errors.New("to enable the StatusService on HTTP, -http flag must be set")
if err != nil { errStatusServiceInvalidFlag = errors.New("-status flag valid values are: ipc, http")
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")
func configureStatusService(flagValue string, nodeConfig *params.NodeConfig) (*params.NodeConfig, error) { func configureStatusService(flagValue string, nodeConfig *params.NodeConfig) (*params.NodeConfig, error) {
switch flagValue { switch flagValue {
@ -318,6 +204,19 @@ func configureStatusService(flagValue string, nodeConfig *params.NodeConfig) (*p
return nodeConfig, nil 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. // printVersion prints verbose output about version and config.
func printVersion(config *params.NodeConfig, buildStamp string) { func printVersion(config *params.NodeConfig, buildStamp string) {
fmt.Println(strings.Title(config.Name)) 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("GOPATH=%s\n", os.Getenv("GOPATH"))
fmt.Printf("GOROOT=%s\n", runtime.GOROOT()) fmt.Printf("GOROOT=%s\n", runtime.GOROOT())
config.LightEthConfig.Genesis = "SKIP"
fmt.Println("Loaded Config: ", config) fmt.Println("Loaded Config: ", config)
} }
@ -341,11 +239,10 @@ func printUsage() {
usage := ` usage := `
Usage: statusd [options] Usage: statusd [options]
Examples: Examples:
statusd # run status node with defaults statusd -c ./default.json # run node with configuration specified in ./default.json file
statusd -networkid 4 # run node on Rinkeby network statusd -c ./default.json -c ./standalone.json # run node with configuration specified in ./default.json file, after merging ./standalone.json file
statusd -datadir /dir # specify different dir for data statusd -c ./default.json -metrics # run node with configuration specified in ./default.json file, and expose ethereum metrics with debug_metrics jsonrpc call
statusd -ipc # enable IPC for usage with "geth attach" statusd -c ./default.json -cli # run node with configuration specified in ./default.json file, and enable connection by statusd-cli on default port
statusd -cli # enable connection by statusd-cli on default port
Options: Options:
` `

View File

@ -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
}

View File

@ -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"
]
}
}

View File

@ -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"
]
}
}

View File

@ -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"
]
}
}

View File

@ -0,0 +1,5 @@
{
"LightEthConfig": {
"Enabled": true
}
}

View File

@ -22,26 +22,10 @@ import (
// All general log messages in this package should be routed through this logger. // All general log messages in this package should be routed through this logger.
var logger = log.New("package", "status-go/lib") 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 //StartNode - start Status node
//export StartNode //export StartNode
func StartNode(configJSON *C.char) *C.char { func StartNode(configJSON *C.char) *C.char {
config, err := params.LoadNodeConfig(C.GoString(configJSON)) config, err := params.NewConfigFromJSON(C.GoString(configJSON))
if err != nil { if err != nil {
return makeJSONResponse(err) return makeJSONResponse(err)
} }
@ -67,7 +51,7 @@ func StopNode() *C.char {
func ValidateNodeConfig(configJSON *C.char) *C.char { func ValidateNodeConfig(configJSON *C.char) *C.char {
var resp APIDetailedResponse var resp APIDetailedResponse
_, err := params.LoadNodeConfig(C.GoString(configJSON)) _, err := params.NewConfigFromJSON(C.GoString(configJSON))
// Convert errors to APIDetailedResponse // Convert errors to APIDetailedResponse
switch err := err.(type) { switch err := err.(type) {

View File

@ -9,6 +9,7 @@ package main
import ( import (
"testing" "testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@ -23,9 +24,9 @@ func TestExportedAPI(t *testing.T) {
func TestValidateNodeConfig(t *testing.T) { func TestValidateNodeConfig(t *testing.T) {
noErrorsCallback := func(resp APIDetailedResponse) { noErrorsCallback := func(resp APIDetailedResponse) {
assert.Empty(t, resp.FieldErrors)
assert.Empty(t, resp.Message)
require.True(t, resp.Status, "expected status equal true") require.True(t, resp.Status, "expected status equal true")
require.Empty(t, resp.FieldErrors)
require.Empty(t, resp.Message)
} }
testCases := []struct { testCases := []struct {
@ -37,7 +38,15 @@ func TestValidateNodeConfig(t *testing.T) {
Name: "response for valid config", Name: "response for valid config",
Config: `{ Config: `{
"NetworkId": 1, "NetworkId": 1,
"DataDir": "/tmp" "DataDir": "/tmp",
"KeyStoreDir": "/tmp",
"NoDiscovery": true,
"WhisperConfig": {
"Enabled": true,
"EnableMailServer": true,
"DataDir": "/tmp",
"MailServerPassword": "status-offline-inbox"
}
}`, }`,
Callback: noErrorsCallback, Callback: noErrorsCallback,
}, },
@ -49,6 +58,72 @@ func TestValidateNodeConfig(t *testing.T) {
require.Contains(t, resp.Message, "validation: invalid character '}'") 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", Name: "response for config with multiple errors",
Config: `{}`, Config: `{}`,
@ -56,11 +131,12 @@ func TestValidateNodeConfig(t *testing.T) {
required := map[string]string{ required := map[string]string{
"NodeConfig.NetworkID": "required", "NodeConfig.NetworkID": "required",
"NodeConfig.DataDir": "required", "NodeConfig.DataDir": "required",
"NodeConfig.KeyStoreDir": "required",
} }
require.False(t, resp.Status) require.False(t, resp.Status)
require.Contains(t, resp.Message, "validation: validation failed") 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 { for _, err := range resp.FieldErrors {
require.Contains(t, required, err.Parameter) require.Contains(t, required, err.Parameter)

View File

@ -12,10 +12,10 @@ package main
import "C" import "C"
import ( import (
"encoding/json" "encoding/json"
"fmt"
"io/ioutil" "io/ioutil"
"math/big" "math/big"
"os" "os"
"path"
"path/filepath" "path/filepath"
"reflect" "reflect"
"strconv" "strconv"
@ -26,13 +26,10 @@ import (
"github.com/ethereum/go-ethereum/accounts/keystore" "github.com/ethereum/go-ethereum/accounts/keystore"
gethcommon "github.com/ethereum/go-ethereum/common" gethcommon "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil" "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/stretchr/testify/require"
"github.com/status-im/status-go/account" "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/signal"
. "github.com/status-im/status-go/t/utils" //nolint: golint . "github.com/status-im/status-go/t/utils" //nolint: golint
"github.com/status-im/status-go/transactions" "github.com/status-im/status-go/transactions"
@ -55,9 +52,18 @@ func init() {
nodeConfigJSON = `{ nodeConfigJSON = `{
"NetworkId": ` + strconv.Itoa(GetNetworkID()) + `, "NetworkId": ` + strconv.Itoa(GetNetworkID()) + `,
"DataDir": "` + testChainDir + `", "DataDir": "` + testChainDir + `",
"KeyStoreDir": "` + filepath.Join(testChainDir, "keystore") + `",
"HTTPPort": ` + strconv.Itoa(TestConfig.Node.HTTPPort) + `, "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 name string
fn func(t *testing.T) bool fn func(t *testing.T) bool
}{ }{
{
"check default configuration",
testGetDefaultConfig,
},
{ {
"stop/resume node", "stop/resume node",
testStopResumeNode, testStopResumeNode,
@ -187,39 +189,6 @@ func testVerifyAccountPassword(t *testing.T) bool {
return true 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. //@TODO(adam): quarantined this test until it uses a different directory.
//nolint: deadcode //nolint: deadcode
func testResetChainData(t *testing.T) bool { func testResetChainData(t *testing.T) bool {

View File

@ -120,7 +120,7 @@ func (s *WMailServer) Init(shh *whisper.Whisper, config *params.WhisperConfig) e
return errDirectoryNotProvided return errDirectoryNotProvided
} }
if len(config.MailServerPassword) == 0 && config.MailServerAsymKey == nil { if len(config.MailServerPassword) == 0 && len(config.MailServerAsymKey) == 0 {
return errDecryptionMethodNotProvided return errDecryptionMethodNotProvided
} }
@ -172,8 +172,12 @@ func (s *WMailServer) setupRequestMessageDecryptor(config *params.WhisperConfig)
s.symFilter = &whisper.Filter{KeySym: symKey} s.symFilter = &whisper.Filter{KeySym: symKey}
} }
if config.MailServerAsymKey != nil { if config.MailServerAsymKey != "" {
s.asymFilter = &whisper.Filter{KeyAsym: config.MailServerAsymKey} keyAsym, err := crypto.HexToECDSA(config.MailServerAsymKey)
if err != nil {
return err
}
s.asymFilter = &whisper.Filter{KeyAsym: keyAsym}
} }
return nil return nil

View File

@ -19,11 +19,11 @@ package mailserver
import ( import (
"crypto/ecdsa" "crypto/ecdsa"
"encoding/binary" "encoding/binary"
"encoding/hex"
"errors" "errors"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"os" "os"
"path/filepath"
"testing" "testing"
"time" "time"
@ -72,19 +72,13 @@ func (s *MailserverSuite) SetupTest() {
s.dataDir = tmpDir s.dataDir = tmpDir
// required files to validate mail server decryption method // required files to validate mail server decryption method
asymKeyFile := filepath.Join(tmpDir, "asymkey")
passwordFile := filepath.Join(tmpDir, "password")
privateKey, err := crypto.GenerateKey() privateKey, err := crypto.GenerateKey()
s.Require().NoError(err) 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 = &params.WhisperConfig{ s.config = &params.WhisperConfig{
DataDir: tmpDir, DataDir: tmpDir,
MailServerAsymKeyFile: asymKeyFile, MailServerAsymKey: hex.EncodeToString(crypto.FromECDSA(privateKey)),
MailServerPasswordFile: passwordFile, MailServerPassword: "testpassword",
} }
} }
@ -118,7 +112,7 @@ func (s *MailserverSuite) TestInit() {
config: params.WhisperConfig{ config: params.WhisperConfig{
DataDir: s.config.DataDir, DataDir: s.config.DataDir,
MailServerPassword: "", MailServerPassword: "",
MailServerAsymKey: nil, MailServerAsymKey: "",
}, },
expectedError: errDecryptionMethodNotProvided, expectedError: errDecryptionMethodNotProvided,
info: "config with an empty password and empty asym key", info: "config with an empty password and empty asym key",
@ -134,7 +128,7 @@ func (s *MailserverSuite) TestInit() {
{ {
config: params.WhisperConfig{ config: params.WhisperConfig{
DataDir: s.config.DataDir, DataDir: s.config.DataDir,
MailServerAsymKey: asymKey, MailServerAsymKey: hex.EncodeToString(crypto.FromECDSA(asymKey)),
}, },
expectedError: nil, expectedError: nil,
info: "config with correct DataDir and AsymKey", info: "config with correct DataDir and AsymKey",
@ -142,7 +136,7 @@ func (s *MailserverSuite) TestInit() {
{ {
config: params.WhisperConfig{ config: params.WhisperConfig{
DataDir: s.config.DataDir, DataDir: s.config.DataDir,
MailServerAsymKey: asymKey, MailServerAsymKey: hex.EncodeToString(crypto.FromECDSA(asymKey)),
MailServerPassword: "pwd", MailServerPassword: "pwd",
}, },
expectedError: nil, expectedError: nil,
@ -186,11 +180,13 @@ func (s *MailserverSuite) TestInit() {
func (s *MailserverSuite) TestSetupRequestMessageDecryptor() { func (s *MailserverSuite) TestSetupRequestMessageDecryptor() {
// without configured Password and AsymKey // without configured Password and AsymKey
config := *s.config config := *s.config
config.MailServerAsymKey = ""
config.MailServerPassword = ""
s.Error(errDecryptionMethodNotProvided, s.server.Init(s.shh, &config)) s.Error(errDecryptionMethodNotProvided, s.server.Init(s.shh, &config))
// Password should work ok // Password should work ok
config = *s.config config = *s.config
s.NoError(config.ReadMailServerPasswordFile()) config.MailServerAsymKey = "" // clear asym key field
s.NoError(s.server.Init(s.shh, &config)) s.NoError(s.server.Init(s.shh, &config))
s.Require().NotNil(s.server.symFilter) s.Require().NotNil(s.server.symFilter)
s.NotNil(s.server.symFilter.KeySym) s.NotNil(s.server.symFilter.KeySym)
@ -199,17 +195,15 @@ func (s *MailserverSuite) TestSetupRequestMessageDecryptor() {
// AsymKey can also be used // AsymKey can also be used
config = *s.config config = *s.config
s.NoError(config.ReadMailServerAsymKeyFile()) config.MailServerPassword = "" // clear password field
s.NoError(s.server.Init(s.shh, &config)) s.NoError(s.server.Init(s.shh, &config))
s.Nil(s.server.symFilter) // important: symmetric filter should be nil s.Nil(s.server.symFilter) // important: symmetric filter should be nil
s.Require().NotNil(s.server.asymFilter) 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() s.server.Close()
// when Password and AsymKey are set, both are supported // when Password and AsymKey are set, both are supported
config = *s.config config = *s.config
s.NoError(config.ReadMailServerPasswordFile())
s.NoError(config.ReadMailServerAsymKeyFile())
s.NoError(s.server.Init(s.shh, &config)) s.NoError(s.server.Init(s.shh, &config))
s.Require().NotNil(s.server.symFilter) s.Require().NotNil(s.server.symFilter)
s.NotNil(s.server.symFilter.KeySym) s.NotNil(s.server.symFilter.KeySym)
@ -220,7 +214,7 @@ func (s *MailserverSuite) TestSetupRequestMessageDecryptor() {
func (s *MailserverSuite) TestOpenEnvelopeWithSymKey() { func (s *MailserverSuite) TestOpenEnvelopeWithSymKey() {
// Setup the server with a sym key // Setup the server with a sym key
config := *s.config config := *s.config
s.NoError(config.ReadMailServerPasswordFile()) config.MailServerAsymKey = "" // clear asym key
s.NoError(s.server.Init(s.shh, &config)) s.NoError(s.server.Init(s.shh, &config))
// Prepare a valid envelope // Prepare a valid envelope
@ -239,7 +233,7 @@ func (s *MailserverSuite) TestOpenEnvelopeWithSymKey() {
func (s *MailserverSuite) TestOpenEnvelopeWithAsymKey() { func (s *MailserverSuite) TestOpenEnvelopeWithAsymKey() {
// Setup the server with an asymetric key // Setup the server with an asymetric key
config := *s.config config := *s.config
s.NoError(config.ReadMailServerAsymKeyFile()) config.MailServerPassword = "" // clear password field
s.NoError(s.server.Init(s.shh, &config)) s.NoError(s.server.Init(s.shh, &config))
// Prepare a valid envelope // Prepare a valid envelope
@ -256,10 +250,10 @@ func (s *MailserverSuite) TestOpenEnvelopeWithAsymKey() {
} }
func (s *MailserverSuite) TestArchive() { func (s *MailserverSuite) TestArchive() {
err := s.config.ReadMailServerPasswordFile() config := *s.config
s.Require().NoError(err) config.MailServerAsymKey = "" // clear asym key
err = s.server.Init(s.shh, s.config) err := s.server.Init(s.shh, &config)
s.Require().NoError(err) s.Require().NoError(err)
defer s.server.Close() defer s.server.Close()

View File

@ -27,6 +27,7 @@ import (
"github.com/status-im/status-go/services/personal" "github.com/status-im/status-go/services/personal"
"github.com/status-im/status-go/services/shhext" "github.com/status-im/status-go/services/shhext"
"github.com/status-im/status-go/services/status" "github.com/status-im/status-go/services/status"
"github.com/status-im/status-go/static"
"github.com/status-im/status-go/timesource" "github.com/status-im/status-go/timesource"
"github.com/syndtr/goleveldb/leveldb" "github.com/syndtr/goleveldb/leveldb"
) )
@ -36,6 +37,7 @@ var (
ErrNodeMakeFailureFormat = "error creating p2p node: %s" ErrNodeMakeFailureFormat = "error creating p2p node: %s"
ErrWhisperServiceRegistrationFailure = errors.New("failed to register the Whisper service") ErrWhisperServiceRegistrationFailure = errors.New("failed to register the Whisper service")
ErrLightEthRegistrationFailure = errors.New("failed to register the LES 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") ErrPersonalServiceRegistrationFailure = errors.New("failed to register the personal api service")
ErrStatusServiceRegistrationFailure = errors.New("failed to register the Status service") ErrStatusServiceRegistrationFailure = errors.New("failed to register the Status service")
ErrPeerServiceRegistrationFailure = errors.New("failed to register the Peer service") ErrPeerServiceRegistrationFailure = errors.New("failed to register the Peer service")
@ -44,7 +46,7 @@ var (
// All general log messages in this package should be routed through this logger. // All general log messages in this package should be routed through this logger.
var logger = log.New("package", "status-go/node") 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) { func MakeNode(config *params.NodeConfig, db *leveldb.DB) (*node.Node, error) {
// If DataDir is empty, it means we want to create an ephemeral node // If DataDir is empty, it means we want to create an ephemeral node
// keeping data only in memory. // keeping data only in memory.
@ -60,17 +62,9 @@ func MakeNode(config *params.NodeConfig, db *leveldb.DB) (*node.Node, error) {
} }
} }
stackConfig := defaultEmbeddedNodeConfig(config) stackConfig, err := newGethNodeConfig(config)
if len(config.NodeKeyFile) > 0 {
logger.Info("Loading private key file", "file", config.NodeKeyFile)
pk, err := crypto.LoadECDSA(config.NodeKeyFile)
if err != nil { if err != nil {
logger.Error("Failed loading private key file", "file", config.NodeKeyFile, "error", err) return nil, err
}
// override node's private key
stackConfig.P2P.PrivateKey = pk
} }
stack, err := node.New(stackConfig) 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) return nil, fmt.Errorf("%v: %v", ErrLightEthRegistrationFailure, err)
} }
} else { } else {
if config.LightEthConfig.Enabled {
return nil, fmt.Errorf("%v: %v", ErrLightEthRegistrationFailureUpstreamEnabled, err)
}
// `personal_sign` and `personal_ecRecover` methods are important to // `personal_sign` and `personal_ecRecover` methods are important to
// keep DApps working. // keep DApps working.
// Usually, they are provided by an ETH or a LES service, but when using // 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 return stack, nil
} }
// defaultEmbeddedNodeConfig returns default stack configuration for mobile client node // newGethNodeConfig returns default stack configuration for mobile client node
func defaultEmbeddedNodeConfig(config *params.NodeConfig) *node.Config { func newGethNodeConfig(config *params.NodeConfig) (*node.Config, error) {
nc := &node.Config{ nc := &node.Config{
DataDir: config.DataDir, DataDir: config.DataDir,
KeyStoreDir: config.KeyStoreDir, KeyStoreDir: config.KeyStoreDir,
@ -139,26 +137,71 @@ func defaultEmbeddedNodeConfig(config *params.NodeConfig) *node.Config {
nc.HTTPPort = config.HTTPPort nc.HTTPPort = config.HTTPPort
} }
if config.ClusterConfig != nil && config.ClusterConfig.Enabled { if config.ClusterConfig.Enabled {
nc.P2P.BootstrapNodesV5 = parseNodesV5(config.ClusterConfig.BootNodes) nc.P2P.BootstrapNodesV5 = parseNodesV5(config.ClusterConfig.BootNodes)
nc.P2P.StaticNodes = parseNodes(config.ClusterConfig.StaticNodes) 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. // activateLightEthService configures and registers the eth.Ethereum service with a given node.
func activateLightEthService(stack *node.Node, config *params.NodeConfig) error { 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") logger.Info("LES protocol is disabled")
return nil return nil
} }
var genesis *core.Genesis genesis, err := calculateGenesis(config.NetworkID)
if config.LightEthConfig.Genesis != "" { if err != nil {
genesis = new(core.Genesis) return err
if err := json.Unmarshal([]byte(config.LightEthConfig.Genesis), genesis); err != nil {
return fmt.Errorf("invalid genesis spec: %v", err)
}
} }
ethConf := eth.DefaultConfig ethConf := eth.DefaultConfig
@ -202,21 +245,6 @@ func activatePeerService(stack *node.Node) error {
} }
func registerMailServer(whisperService *whisper.Whisper, config *params.WhisperConfig) (err 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 var mailServer mailserver.WMailServer
whisperService.RegisterServer(&mailServer) 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. // activateShhService configures Whisper and adds it to the given node.
func activateShhService(stack *node.Node, config *params.NodeConfig, db *leveldb.DB) (err error) { 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") logger.Info("SHH protocol is disabled")
return nil return nil
} }
@ -254,7 +282,7 @@ func activateShhService(stack *node.Node, config *params.NodeConfig, db *leveldb
// enable mail service // enable mail service
if config.WhisperConfig.EnableMailServer { 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) return nil, fmt.Errorf("failed to register MailServer: %v", err)
} }
} }

View File

@ -11,7 +11,7 @@ import (
func TestWhisperLightModeEnabledSetsEmptyBloomFilter(t *testing.T) { func TestWhisperLightModeEnabledSetsEmptyBloomFilter(t *testing.T) {
config := params.NodeConfig{ config := params.NodeConfig{
WhisperConfig: &params.WhisperConfig{ WhisperConfig: params.WhisperConfig{
Enabled: true, Enabled: true,
LightClient: true, LightClient: true,
}, },
@ -33,7 +33,7 @@ func TestWhisperLightModeEnabledSetsEmptyBloomFilter(t *testing.T) {
func TestWhisperLightModeEnabledSetsNilBloomFilter(t *testing.T) { func TestWhisperLightModeEnabledSetsNilBloomFilter(t *testing.T) {
config := params.NodeConfig{ config := params.NodeConfig{
WhisperConfig: &params.WhisperConfig{ WhisperConfig: params.WhisperConfig{
Enabled: true, Enabled: true,
LightClient: false, LightClient: false,
}, },

View File

@ -117,17 +117,18 @@ func (n *StatusNode) startWithDB(config *params.NodeConfig, db *leveldb.DB, serv
return nil 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 { func (n *StatusNode) Start(config *params.NodeConfig, services ...node.ServiceConstructor) error {
n.mu.Lock() n.mu.Lock()
defer n.mu.Unlock() defer n.mu.Unlock()
n.log.Debug("starting with NodeConfig", "ClusterConfig", config.ClusterConfig)
if n.isRunning() { if n.isRunning() {
n.log.Debug("cannot start, node already running")
return ErrNodeRunning return ErrNodeRunning
} }
n.log.Debug("starting with NodeConfig", "ClusterConfig", config.ClusterConfig)
db, err := db.Create(config.DataDir, params.StatusDatabase) db, err := db.Create(config.DataDir, params.StatusDatabase)
if err != nil { if err != nil {
return err return err
@ -186,7 +187,7 @@ func (n *StatusNode) setupRPCClient() (err error) {
} }
func (n *StatusNode) discoveryEnabled() bool { 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 { 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 // populateStaticPeers connects current node with our publicly available LES/SHH/Swarm cluster
func (n *StatusNode) populateStaticPeers() error { 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") n.log.Info("Static peers are disabled")
return nil return nil
} }
@ -372,7 +373,7 @@ func (n *StatusNode) populateStaticPeers() error {
} }
func (n *StatusNode) removeStaticPeers() 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") n.log.Info("Static peers are disabled")
return nil return nil
} }

View File

@ -19,13 +19,13 @@ import (
"github.com/status-im/status-go/discovery" "github.com/status-im/status-go/discovery"
"github.com/status-im/status-go/params" "github.com/status-im/status-go/params"
"github.com/status-im/status-go/t/helpers" "github.com/status-im/status-go/t/helpers"
"github.com/status-im/status-go/t/utils"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
func TestStatusNodeStart(t *testing.T) { func TestStatusNodeStart(t *testing.T) {
var err error config, err := utils.MakeTestNodeConfig(params.StatusChainNetworkID)
require.NoError(t, err)
config := params.NodeConfig{}
n := New() n := New()
// checks before node is started // checks before node is started
@ -39,7 +39,7 @@ func TestStatusNodeStart(t *testing.T) {
require.EqualError(t, err, ErrNoGethNode.Error()) require.EqualError(t, err, ErrNoGethNode.Error())
// start node // start node
require.NoError(t, n.Start(&config)) require.NoError(t, n.Start(config))
// checks after node is started // checks after node is started
require.True(t, n.IsRunning()) require.True(t, n.IsRunning())
@ -54,7 +54,7 @@ func TestStatusNodeStart(t *testing.T) {
require.Nil(t, err) require.Nil(t, err)
require.NotNil(t, keyStore) require.NotNil(t, keyStore)
// try to start already started node // try to start already started node
require.EqualError(t, n.Start(&config), ErrNodeRunning.Error()) require.EqualError(t, n.Start(config), ErrNodeRunning.Error())
// stop node // stop node
require.NoError(t, n.Stop()) require.NoError(t, n.Stop())
@ -97,10 +97,10 @@ func TestStatusNodeWithDataDir(t *testing.T) {
func TestStatusNodeServiceGetters(t *testing.T) { func TestStatusNodeServiceGetters(t *testing.T) {
config := params.NodeConfig{ config := params.NodeConfig{
WhisperConfig: &params.WhisperConfig{ WhisperConfig: params.WhisperConfig{
Enabled: true, Enabled: true,
}, },
LightEthConfig: &params.LightEthConfig{ LightEthConfig: params.LightEthConfig{
Enabled: true, Enabled: true,
}, },
} }
@ -216,7 +216,7 @@ func TestStatusNodeReconnectStaticPeers(t *testing.T) {
// start status node // start status node
config := params.NodeConfig{ config := params.NodeConfig{
MaxPeers: math.MaxInt32, MaxPeers: math.MaxInt32,
ClusterConfig: &params.ClusterConfig{ ClusterConfig: params.ClusterConfig{
Enabled: true, Enabled: true,
StaticNodes: []string{peerURL}, StaticNodes: []string{peerURL},
}, },
@ -272,7 +272,7 @@ func TestStatusNodeRendezvousDiscovery(t *testing.T) {
config := params.NodeConfig{ config := params.NodeConfig{
Rendezvous: true, Rendezvous: true,
NoDiscovery: true, NoDiscovery: true,
ClusterConfig: &params.ClusterConfig{ ClusterConfig: params.ClusterConfig{
Enabled: true, Enabled: true,
// not necessarily with id, just valid multiaddr // not necessarily with id, just valid multiaddr
RendezvousNodes: []string{"/ip4/127.0.0.1/tcp/34012", "/ip4/127.0.0.1/tcp/34011"}, RendezvousNodes: []string{"/ip4/127.0.0.1/tcp/34012", "/ip4/127.0.0.1/tcp/34011"},

View File

@ -14,62 +14,3 @@ type Cluster struct {
MailServers []string `json:"mailservers"` // list of trusted mail servers MailServers []string `json:"mailservers"` // list of trusted mail servers
RendezvousNodes []string `json:"rendezvousnodes"` 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
}

View File

@ -1,34 +1,23 @@
package params package params
import ( import (
"bytes"
"crypto/ecdsa"
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"go/build" "go/build"
"io/ioutil" "io/ioutil"
"net/url"
"os" "os"
"path" "path"
"path/filepath" "path/filepath"
"strings" "strings"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/p2p/discv5" "github.com/ethereum/go-ethereum/p2p/discv5"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
) "github.com/status-im/status-go/static"
validator "gopkg.in/go-playground/validator.v9"
// 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")
) )
// ---------- // ----------
@ -41,46 +30,10 @@ type LightEthConfig struct {
// Enabled flag specifies whether protocol is enabled // Enabled flag specifies whether protocol is enabled
Enabled bool 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 is memory (in MBs) allocated to internal caching (min 16MB / database forced)
DatabaseCache int 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 // WhisperConfig
// ---------- // ----------
@ -103,18 +56,12 @@ type WhisperConfig struct {
// MinimumPoW minimum PoW for Whisper messages // MinimumPoW minimum PoW for Whisper messages
MinimumPoW float64 MinimumPoW float64
// MailServerPasswordFile contains a password for symmetric encryption with MailServer.
MailServerPasswordFile string
// MailServerPassword for symmetric encryption with MailServer. // MailServerPassword for symmetric encryption with MailServer.
// (if no account file selected, then this password is used for symmetric encryption). // (if no account file selected, then this password is used for symmetric encryption).
MailServerPassword string MailServerPassword string
// MailServerAsymKeyFile is a file with an asymmetric key to decrypt messages sent to MailServer. // MailServerAsymKey is an hex-encoded asymmetric key to decrypt messages sent to MailServer.
MailServerAsymKeyFile string MailServerAsymKey string
// MailServerAsymKey is an asymmetric key to decrypt messages sent to MailServer.
MailServerAsymKey *ecdsa.PrivateKey
// RateLimit minimum time between queries to mail server per peer // RateLimit minimum time between queries to mail server per peer
MailServerRateLimit int MailServerRateLimit int
@ -125,40 +72,10 @@ type WhisperConfig struct {
// TTL time to live for messages, in seconds // TTL time to live for messages, in seconds
TTL int TTL int
// FirebaseConfig extra configuration for Firebase Cloud Messaging
FirebaseConfig *FirebaseConfig `json:"FirebaseConfig,"`
// EnableNTPSync enables NTP synchronizations // EnableNTPSync enables NTP synchronizations
EnableNTPSync bool 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 // String dumps config object as nicely indented JSON
func (c *WhisperConfig) String() string { func (c *WhisperConfig) String() string {
data, _ := json.MarshalIndent(c, "", " ") // nolint: gas data, _ := json.MarshalIndent(c, "", " ") // nolint: gas
@ -195,17 +112,16 @@ type ClusterConfig struct {
// Fleet is a type of selected fleet. // Fleet is a type of selected fleet.
Fleet string 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 StaticNodes []string
// BootNodes list of cluster peer nodes for a given network (Mainnet, Ropsten, Rinkeby, Homestead), // BootNodes is a list of cluster peer nodes for this fleet.
// for a given mode (production vs development)
BootNodes []string BootNodes []string
// TrustedMailServers is a list of verified Mail Servers. // TrustedMailServers is a list of verified Mail Servers for this fleet.
TrustedMailServers []string TrustedMailServers []string
// RendezvousNodes is a rendezvous discovery server. // RendezvousNodes is a list rendezvous discovery nodes.
RendezvousNodes []string RendezvousNodes []string
} }
@ -255,13 +171,11 @@ type NodeConfig struct {
DataDir string `validate:"required"` DataDir string `validate:"required"`
// KeyStoreDir is the file system folder that contains private keys. // 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 `validate:"required"`
KeyStoreDir string
// NodeKeyFile is a filename with node ID (private key) // NodeKey is the hex-encoded node ID (private key). Should be a valid secp256k1 private key that will be used for both
// This file should contain a valid secp256k1 private key that will be used for both
// remote peer identification as well as network traffic encryption. // remote peer identification as well as network traffic encryption.
NodeKeyFile string NodeKey string
// NoDiscovery set to true will disable discovery protocol. // NoDiscovery set to true will disable discovery protocol.
NoDiscovery bool NoDiscovery bool
@ -330,21 +244,17 @@ type NodeConfig struct {
// UpstreamConfig extra config for providing upstream infura server. // UpstreamConfig extra config for providing upstream infura server.
UpstreamConfig UpstreamRPCConfig `json:"UpstreamConfig"` 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 extra configuration for supporting cluster peers.
ClusterConfig *ClusterConfig `json:"ClusterConfig," validate:"structonly"` ClusterConfig ClusterConfig `json:"ClusterConfig," validate:"structonly"`
// LightEthConfig extra configuration for LES // LightEthConfig extra configuration for LES
LightEthConfig *LightEthConfig `json:"LightEthConfig," validate:"structonly"` LightEthConfig LightEthConfig `json:"LightEthConfig," validate:"structonly"`
// WhisperConfig extra configuration for SHH // 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 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 // RegisterTopics a list of specific topics where the peer wants to be
// discoverable. // discoverable.
@ -364,91 +274,144 @@ type NodeConfig struct {
MailServerRegistryAddress string MailServerRegistryAddress string
} }
// NewNodeConfig creates new node configuration object // NewNodeConfigWithDefaults creates new node configuration object with some defaults suitable for adhoc use
func NewNodeConfig(dataDir, clstrCfgFile, fleet string, networkID uint64) (*NodeConfig, error) { 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{ nodeConfig := &NodeConfig{
NetworkID: networkID, NetworkID: networkID,
DataDir: dataDir, DataDir: dataDir,
Name: ClientIdentifier,
Version: Version, Version: Version,
RPCEnabled: RPCEnabledDefault, RPCEnabled: false,
HTTPHost: HTTPHost, HTTPHost: "localhost",
HTTPPort: HTTPPort, HTTPPort: 8545,
ListenAddr: ListenAddr, ListenAddr: ":0",
APIModules: APIModules, APIModules: "eth,net,web3,peer",
MaxPeers: MaxPeers, MaxPeers: 25,
MaxPendingPeers: MaxPendingPeers, MaxPendingPeers: 0,
IPCFile: IPCFile, IPCFile: "geth.ipc",
log: log.New("package", "status-go/params.NodeConfig"), log: log.New("package", "status-go/params.NodeConfig"),
LogFile: LogFile, LogFile: "",
LogLevel: LogLevel, LogLevel: "ERROR",
LogToStderr: LogToStderr, UpstreamConfig: UpstreamRPCConfig{
ClusterConfigFile: clstrCfgFile, URL: getUpstreamURL(networkID),
ClusterConfig: &ClusterConfig{ },
Enabled: true, // cluster must be enabled by default ClusterConfig: ClusterConfig{
Enabled: fleet != FleetUndefined,
Fleet: fleet, Fleet: fleet,
StaticNodes: []string{}, StaticNodes: []string{},
BootNodes: []string{}, BootNodes: []string{},
}, },
LightEthConfig: &LightEthConfig{ LightEthConfig: LightEthConfig{
Enabled: true, Enabled: false,
DatabaseCache: DatabaseCache, DatabaseCache: 16,
}, },
WhisperConfig: &WhisperConfig{ WhisperConfig: WhisperConfig{
Enabled: true, Enabled: false,
MinimumPoW: WhisperMinimumPoW, MinimumPoW: WhisperMinimumPoW,
TTL: WhisperTTL, TTL: WhisperTTL,
FirebaseConfig: &FirebaseConfig{ EnableNTPSync: false,
NotificationTriggerURL: FirebaseNotificationTriggerURL,
}, },
EnableNTPSync: true, SwarmConfig: SwarmConfig{},
},
SwarmConfig: &SwarmConfig{},
RegisterTopics: []discv5.Topic{}, RegisterTopics: []discv5.Topic{},
RequireTopics: map[discv5.Topic]Limits{}, RequireTopics: map[discv5.Topic]Limits{},
} }
// adjust dependent values return nodeConfig, nil
if err := nodeConfig.updateConfig(); err != 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 nil, err
} }
return nodeConfig, nil return nodeConfig, nil
} }
// LoadNodeConfig parses incoming JSON and returned it as Config // LoadConfigFromJSON parses incoming JSON and returned it as Config
func LoadNodeConfig(configJSON string) (*NodeConfig, error) { func LoadConfigFromJSON(configJSON string, nodeConfig *NodeConfig) error {
nodeConfig, err := loadNodeConfig(configJSON) if err := loadNodeConfig(configJSON, nodeConfig); err != nil {
if err != nil { return err
return nil, err
} }
if err := nodeConfig.Validate(); err != nil { if err := nodeConfig.Validate(); err != nil {
return nil, err return err
} }
return nodeConfig, nil return nil
} }
func loadNodeConfig(configJSON string) (*NodeConfig, error) { func loadNodeConfig(configJSON string, nodeConfig *NodeConfig) error {
nodeConfig, err := NewNodeConfig("", "", FleetUndefined, 0)
if err != nil {
return nil, err
}
decoder := json.NewDecoder(strings.NewReader(configJSON)) decoder := json.NewDecoder(strings.NewReader(configJSON))
// override default configuration with values by JSON input // override default configuration with values by JSON input
if err := decoder.Decode(&nodeConfig); err != nil { if err := decoder.Decode(&nodeConfig); err != nil {
return nil, err return err
} }
// repopulate return nil
if err := nodeConfig.updateConfig(); err != nil { }
return nil, err
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. // Validate checks if NodeConfig fields have valid values.
@ -473,33 +436,159 @@ func (c *NodeConfig) Validate() error {
return err return err
} }
if c.ClusterConfig.Enabled { if c.NodeKey != "" {
if err := validate.Struct(c.ClusterConfig); err != nil { if _, err := crypto.HexToECDSA(c.NodeKey); err != nil {
return err return fmt.Errorf("NodeKey is invalid (%s): %v", c.NodeKey, err)
} }
} }
if c.LightEthConfig.Enabled { if c.UpstreamConfig.Enabled && c.LightEthConfig.Enabled {
if err := validate.Struct(c.LightEthConfig); err != nil { return fmt.Errorf("both UpstreamConfig and LightEthConfig are enabled, but they are mutually exclusive")
return err
}
} }
if c.WhisperConfig.Enabled { if err := c.validateChildStructs(validate); err != nil {
if err := validate.Struct(c.WhisperConfig); err != nil {
return err return err
} }
}
if c.SwarmConfig.Enabled { if !c.NoDiscovery && len(c.ClusterConfig.BootNodes) == 0 {
if err := validate.Struct(c.SwarmConfig); err != nil { // 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 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 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 // Save dumps configuration to the disk
func (c *NodeConfig) Save() error { func (c *NodeConfig) Save() error {
data, err := json.MarshalIndent(c, "", " ") data, err := json.MarshalIndent(c, "", " ")
@ -520,176 +609,6 @@ func (c *NodeConfig) Save() error {
return nil 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 // String dumps config object as nicely indented JSON
func (c *NodeConfig) String() string { func (c *NodeConfig) String() string {
data, _ := json.MarshalIndent(c, "", " ") data, _ := json.MarshalIndent(c, "", " ")

View File

@ -5,32 +5,82 @@ import (
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"os" "os"
"path"
"path/filepath" "path/filepath"
"testing" "testing"
"gopkg.in/go-playground/validator.v9" "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/params"
"github.com/status-im/status-go/t/utils"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
var clusterConfigData = []byte(`{ var clusterConfigData = []byte(`{
"ClusterConfig": {
"staticnodes": [ "staticnodes": [
"enode://7ab298cedc4185a894d21d8a4615262ec6bdce66c9b6783878258e0d5b31013d30c9038932432f70e5b2b6a5cd323bf820554fcb22fbc7b45367889522e9c449@10.1.1.1:30303", "enode://7ab298cedc4185a894d21d8a4615262ec6bdce66c9b6783878258e0d5b31013d30c9038932432f70e5b2b6a5cd323bf820554fcb22fbc7b45367889522e9c449@10.1.1.1:30303",
"enode://f59e8701f18c79c5cbc7618dc7bb928d44dc2f5405c7d693dad97da2d8585975942ec6fd36d3fe608bfdc7270a34a4dd00f38cfe96b2baa24f7cd0ac28d382a1@10.1.1.2:30303" "enode://f59e8701f18c79c5cbc7618dc7bb928d44dc2f5405c7d693dad97da2d8585975942ec6fd36d3fe608bfdc7270a34a4dd00f38cfe96b2baa24f7cd0ac28d382a1@10.1.1.2:30303"
] ]
}
}`) }`)
func TestLoadNodeConfigFromNonExistingFile(t *testing.T) { var clusters = map[string]func() params.Cluster{
_, err := params.LoadNodeConfig(`{ params.FleetStaging: func() params.Cluster {
"NetworkId": 3, return params.Cluster{
"DataDir": "/tmp/statusgo", BootNodes: []string{
"ClusterConfigFile": "/file/does/not.exist" "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
require.Error(t, err) },
require.Contains(t, err.Error(), "no such file or directory") 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) { func TestLoadNodeConfigFromFile(t *testing.T) {
@ -44,14 +94,21 @@ func TestLoadNodeConfigFromFile(t *testing.T) {
clusterFile := filepath.Join(tmpDir, "cluster.json") clusterFile := filepath.Join(tmpDir, "cluster.json")
err = ioutil.WriteFile(clusterFile, clusterConfigData, os.ModePerm) err = ioutil.WriteFile(clusterFile, clusterConfigData, os.ModePerm)
require.NoError(t, err) require.NoError(t, err)
defer os.Remove(clusterFile)
c, err := params.LoadNodeConfig(`{ c, err := params.NewConfigFromJSON(`{
"NetworkId": 3, "NetworkId": 3,
"DataDir": "` + tmpDir + `", "DataDir": "` + tmpDir + `",
"ClusterConfigFile": "` + clusterFile + `" "KeyStoreDir": "` + tmpDir + `",
"NoDiscovery": true
}`) }`)
require.NoError(t, err) 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) 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) Validate func(t *testing.T, dataDir string, c *params.NodeConfig)
}{ }{
{ {
Name: "default KeyStoreDir", Name: "DataDir and KeyStoreDir specified",
Update: func(config *params.NodeConfig) {}, Update: func(c *params.NodeConfig) {},
Validate: func(t *testing.T, dataDir string, c *params.NodeConfig) { Validate: func(t *testing.T, dataDir string, c *params.NodeConfig) {
require.Equal(t, dataDir, c.DataDir) require.Equal(t, tmpDir, c.DataDir)
keyStoreDir := filepath.Join(dataDir, params.KeyStoreDir) require.Equal(t, tmpDir, c.KeyStoreDir)
require.Equal(t, keyStoreDir, 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)
Name: "non-default KeyStoreDir", require.False(t, c.SwarmConfig.Enabled)
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)
}, },
}, },
{ {
@ -117,32 +169,38 @@ func TestGenerateAndLoadNodeConfig(t *testing.T) {
{ {
Name: "loading LES config", Name: "loading LES config",
NetworkID: params.MainNetworkID, 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) { Validate: func(t *testing.T, dataDir string, c *params.NodeConfig) {
var genesis core.Genesis require.True(t, c.LightEthConfig.Enabled)
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))
}, },
}, },
{ {
Name: "cluster nodes setup", Name: "cluster nodes setup",
Update: func(c *params.NodeConfig) {}, Update: func(c *params.NodeConfig) {
cc, _ := ClusterForFleet(c.ClusterConfig.Fleet)
c.Rendezvous = true
c.ClusterConfig.BootNodes = cc.BootNodes
c.ClusterConfig.StaticNodes = cc.StaticNodes
c.ClusterConfig.RendezvousNodes = cc.RendezvousNodes
c.ClusterConfig.TrustedMailServers = cc.MailServers
c.ClusterConfig.Enabled = true
c.RequireTopics[params.WhisperDiscv5Topic] = params.WhisperDiscv5Limits
},
Validate: func(t *testing.T, dataDir string, c *params.NodeConfig) { Validate: func(t *testing.T, dataDir string, c *params.NodeConfig) {
require.True(t, c.Rendezvous)
require.True(t, c.ClusterConfig.Enabled) require.True(t, c.ClusterConfig.Enabled)
require.NotEmpty(t, c.ClusterConfig.BootNodes) require.NotEmpty(t, c.ClusterConfig.BootNodes)
require.NotEmpty(t, c.ClusterConfig.StaticNodes) require.NotEmpty(t, c.ClusterConfig.StaticNodes)
require.NotEmpty(t, c.ClusterConfig.TrustedMailServers) require.NotEmpty(t, c.ClusterConfig.TrustedMailServers)
require.Equal(t, params.WhisperDiscv5Limits, c.RequireTopics[params.WhisperDiscv5Topic])
}, },
}, },
{ {
Name: "custom bootnodes", Name: "custom bootnodes",
Update: func(c *params.NodeConfig) { Update: func(c *params.NodeConfig) {
c.ClusterConfig.Enabled = true
c.ClusterConfig.BootNodes = []string{"a", "b", "c"} c.ClusterConfig.BootNodes = []string{"a", "b", "c"}
}, },
Validate: func(t *testing.T, dataDir string, c *params.NodeConfig) { Validate: func(t *testing.T, dataDir string, c *params.NodeConfig) {
@ -151,9 +209,10 @@ func TestGenerateAndLoadNodeConfig(t *testing.T) {
}, },
}, },
{ {
Name: "disabled ClusterConfiguration", Name: "disabled Cluster configuration",
Update: func(c *params.NodeConfig) { Update: func(c *params.NodeConfig) {
c.ClusterConfig.Enabled = false c.ClusterConfig.Enabled = false
c.ClusterConfig.BootNodes = []string{"a", "b", "c"}
}, },
Validate: func(t *testing.T, dataDir string, c *params.NodeConfig) { Validate: func(t *testing.T, dataDir string, c *params.NodeConfig) {
require.False(t, c.ClusterConfig.Enabled) require.False(t, c.ClusterConfig.Enabled)
@ -161,7 +220,11 @@ func TestGenerateAndLoadNodeConfig(t *testing.T) {
}, },
{ {
Name: "peers discovery and topics", Name: "peers discovery and topics",
Update: func(c *params.NodeConfig) {}, Update: func(c *params.NodeConfig) {
c.NoDiscovery = false
c.ClusterConfig.BootNodes = []string{"a", "b", "c"}
c.RequireTopics[params.WhisperDiscv5Topic] = params.Limits{2, 2}
},
Validate: func(t *testing.T, dataDir string, c *params.NodeConfig) { Validate: func(t *testing.T, dataDir string, c *params.NodeConfig) {
require.NotNil(t, c.RequireTopics) require.NotNil(t, c.RequireTopics)
require.False(t, c.NoDiscovery) require.False(t, c.NoDiscovery)
@ -181,11 +244,16 @@ func TestGenerateAndLoadNodeConfig(t *testing.T) {
{ {
Name: "staging fleet", Name: "staging fleet",
Fleet: params.FleetStaging, Fleet: params.FleetStaging,
Update: func(c *params.NodeConfig) {}, Update: func(c *params.NodeConfig) {
c.ClusterConfig.Enabled = true
c.ClusterConfig.Fleet = "eth.staging"
cc, _ := ClusterForFleet(c.ClusterConfig.Fleet)
c.ClusterConfig.BootNodes = cc.BootNodes
},
Validate: func(t *testing.T, dataDir string, c *params.NodeConfig) { 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) require.True(t, ok)
beta, ok := params.ClusterForFleet("eth.beta") beta, ok := ClusterForFleet("eth.beta")
require.True(t, ok) require.True(t, ok)
require.NotEqual(t, staging, beta) require.NotEqual(t, staging, beta)
@ -198,9 +266,13 @@ func TestGenerateAndLoadNodeConfig(t *testing.T) {
{ {
Name: "Whisper light client", Name: "Whisper light client",
Update: func(c *params.NodeConfig) { Update: func(c *params.NodeConfig) {
c.WhisperConfig.Enabled = true
c.WhisperConfig.DataDir = path.Join(tmpDir, "wnode")
c.WhisperConfig.LightClient = true c.WhisperConfig.LightClient = true
}, },
Validate: func(t *testing.T, dataDir string, c *params.NodeConfig) { 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) require.True(t, c.WhisperConfig.LightClient)
}, },
}, },
@ -221,9 +293,9 @@ func TestGenerateAndLoadNodeConfig(t *testing.T) {
for _, networkID := range networks { for _, networkID := range networks {
name := fmt.Sprintf("%s_%s_%d", tc.Name, fleet, networkID) name := fmt.Sprintf("%s_%s_%d", tc.Name, fleet, networkID)
t.Run(name, func(t *testing.T) { t.Run(name, func(t *testing.T) {
// Corresponds to GenerateConfig() binding. config, err := utils.MakeTestNodeConfigWithDataDir("", tmpDir, fleet, uint64(networkID))
config, err := params.NewNodeConfig(tmpDir, "", fleet, uint64(networkID))
require.NoError(t, err) require.NoError(t, err)
config.KeyStoreDir = tmpDir
// Corresponds to config update in status-react. // Corresponds to config update in status-react.
tc.Update(config) tc.Update(config)
@ -231,7 +303,7 @@ func TestGenerateAndLoadNodeConfig(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
// Corresponds to starting node and loading config from JSON blob. // 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) require.NoError(t, err)
tc.Validate(t, tmpDir, loadedConfig) tc.Validate(t, tmpDir, loadedConfig)
}) })
@ -245,7 +317,7 @@ func TestConfigWriteRead(t *testing.T) {
require.Nil(t, err) require.Nil(t, err)
defer os.RemoveAll(tmpDir) // nolint: errcheck 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") require.Nil(t, err, "cannot create new config object")
err = nodeConfig.Save() err = nodeConfig.Save()
@ -253,8 +325,10 @@ func TestConfigWriteRead(t *testing.T) {
loadedConfigData, err := ioutil.ReadFile(filepath.Join(nodeConfig.DataDir, "config.json")) loadedConfigData, err := ioutil.ReadFile(filepath.Join(nodeConfig.DataDir, "config.json"))
require.Nil(t, err, "cannot read configuration from disk") require.Nil(t, err, "cannot read configuration from disk")
require.Contains(t, string(loadedConfigData), fmt.Sprintf(`"NetworkId": %d`, params.RopstenNetworkID)) loadedConfig := string(loadedConfigData)
require.Contains(t, string(loadedConfigData), fmt.Sprintf(`"DataDir": "%s"`, tmpDir)) 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. // TestNodeConfigValidate checks validation of individual fields.
@ -264,55 +338,250 @@ func TestNodeConfigValidate(t *testing.T) {
Config string Config string
Error string Error string
FieldErrors map[string]string // map[Field]Tag FieldErrors map[string]string // map[Field]Tag
CheckFunc func(*testing.T, *params.NodeConfig)
}{ }{
{ {
Name: "Valid JSON config", Name: "Valid JSON config",
Config: `{ Config: `{
"NetworkId": 1, "NetworkId": 1,
"DataDir": "/tmp/data" "DataDir": "/tmp/data",
"KeyStoreDir": "/tmp/data",
"NoDiscovery": true
}`, }`,
Error: "",
FieldErrors: nil,
}, },
{ {
Name: "Invalid JSON config", Name: "Invalid JSON config",
Config: `{"NetworkId": }`, Config: `{"NetworkId": }`,
Error: "invalid character '}'", Error: "invalid character '}'",
FieldErrors: nil,
}, },
{ {
Name: "Invalid field type", Name: "Invalid field type",
Config: `{"NetworkId": "abc"}`, Config: `{"NetworkId": "abc"}`,
Error: "json: cannot unmarshal string into Go struct field", Error: "json: cannot unmarshal string into Go struct field",
FieldErrors: nil,
}, },
{ {
Name: "Validate all required fields", Name: "Validate all required fields",
Config: `{}`, Config: `{}`,
Error: "",
FieldErrors: map[string]string{ FieldErrors: map[string]string{
"NetworkID": "required", "NetworkID": "required",
"DataDir": "required", "DataDir": "required",
"KeyStoreDir": "required",
}, },
}, },
{ {
Name: "Validate Name does not contain slash", Name: "Validate that Name does not contain slash",
Config: `{ Config: `{
"NetworkId": 1, "NetworkId": 1,
"DataDir": "/some/dir", "DataDir": "/some/dir",
"KeyStoreDir": "/some/dir",
"Name": "invalid/name" "Name": "invalid/name"
}`, }`,
Error: "",
FieldErrors: map[string]string{ FieldErrors: map[string]string{
"Name": "excludes", "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 { for _, tc := range testCases {
t.Logf("Test Case %s", tc.Name) t.Logf("Test Case %s", tc.Name)
_, err := params.LoadNodeConfig(tc.Config) config, err := params.NewConfigFromJSON(tc.Config)
switch err := err.(type) { switch err := err.(type) {
case validator.ValidationErrors: case validator.ValidationErrors:
@ -321,10 +590,19 @@ func TestNodeConfigValidate(t *testing.T) {
require.Equal(t, tc.FieldErrors[ve.Field()], ve.Tag()) require.Equal(t, tc.FieldErrors[ve.Field()], ve.Tag())
} }
case error: case error:
if tc.Error == "" {
require.NoError(t, err)
} else {
require.Contains(t, err.Error(), tc.Error) require.Contains(t, err.Error(), tc.Error)
}
case nil: 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) require.Nil(t, tc.FieldErrors)
if tc.CheckFunc != nil {
tc.CheckFunc(t, config)
}
} }
} }
} }

View File

@ -3,38 +3,9 @@ package params
import "github.com/ethereum/go-ethereum/p2p/discv5" import "github.com/ethereum/go-ethereum/p2p/discv5"
const ( 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 path relative to DataDir.
StatusDatabase = "status-db" 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 defines the name for a giving transaction.
SendTransactionMethodName = "eth_sendTransaction" SendTransactionMethodName = "eth_sendTransaction"
@ -47,47 +18,15 @@ const (
// PersonalRecoverMethodName defines the name for `personal.recover` API. // PersonalRecoverMethodName defines the name for `personal.recover` API.
PersonalRecoverMethodName = "personal_ecRecover" 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 default amount of gas used for transactions
DefaultGas = 180000 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 amount of work for Whisper message to be added to sending queue
WhisperMinimumPoW = 0.001 WhisperMinimumPoW = 0.001
// WhisperTTL is time to live for messages, in seconds // WhisperTTL is time to live for messages, in seconds
WhisperTTL = 120 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 // MainnetEthereumNetworkURL is URL where the upstream ethereum network is loaded to
// allow us avoid syncing node. // allow us avoid syncing node.
MainnetEthereumNetworkURL = "https://mainnet.infura.io/nKmXgiFgc2KqtoQ8BCGJ" MainnetEthereumNetworkURL = "https://mainnet.infura.io/nKmXgiFgc2KqtoQ8BCGJ"

333
static/bindata.go Normal file

File diff suppressed because one or more lines are too long

View File

@ -1 +0,0 @@
status-offline-inbox

4
static/static.go Normal file
View File

@ -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/...

View File

@ -6,14 +6,26 @@
Example usage: Example usage:
1. Start a Whisper node with mail server capability: 1. Start a Whisper node with mail server capability:
./build/bin/statusd \ ./build/bin/statusd -c mailserver-config.json
-networkid=4 \
-maxpeers=100 \ where mailserver-config.json contains:
-shh \ ``` json
-shh.pow=0.002 \ {
-shh.mailserver \ "NetworkId": 4,
-shh.passwordfile=./static/keys/wnodepassword \ "DataDir": "./ethereumtest/rinkeby_rpc",
-log DEBUG "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: 2. Generate some messages:
go test -v -timeout=30s -run TestSendMessages ./t/benchmarks \ go test -v -timeout=30s -run TestSendMessages ./t/benchmarks \
-peerurl=$ENODE_ADDR \ -peerurl=$ENODE_ADDR \

View File

@ -54,7 +54,7 @@ mv UTC--2018-01-26T13-47-49.289567120Z--9f04dc05c4c3ec3b8b1f36f7d7d153f3934b1f07
popd 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 ```json
{ {

View File

@ -231,7 +231,7 @@ func (s *AccountsTestSuite) TestSelectedAccountOnRestart() {
s.Equal(selectedAccount.Address.Hex(), address2, "incorrect address selected") s.Equal(selectedAccount.Address.Hex(), address2, "incorrect address selected")
// resume node // resume node
s.NoError(s.Backend.StartNode(&preservedNodeConfig)) s.Require().NoError(s.Backend.StartNode(&preservedNodeConfig))
// re-check selected account (account2 MUST be selected) // re-check selected account (account2 MUST be selected)
selectedAccount, err = s.Backend.AccountManager().SelectedAccount() selectedAccount, err = s.Backend.AccountManager().SelectedAccount()

View File

@ -2,10 +2,7 @@ package api_test
import ( import (
"encoding/json" "encoding/json"
"io/ioutil"
"math/rand" "math/rand"
"os"
"strconv"
"testing" "testing"
"time" "time"
@ -40,19 +37,6 @@ func (s *APITestSuite) SetupTest() {
} }
func (s *APITestSuite) TestCHTUpdate() { 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. // TODO(tiabc): Test that CHT is really updated.
} }
@ -140,12 +124,12 @@ func (s *APITestSuite) TestEventsNodeStartStop() {
nodeConfig, err := MakeTestNodeConfig(GetNetworkID()) nodeConfig, err := MakeTestNodeConfig(GetNetworkID())
s.NoError(err) s.NoError(err)
s.NoError(s.backend.StartNode(nodeConfig)) s.Require().NoError(s.backend.StartNode(nodeConfig))
s.NoError(s.backend.StopNode()) s.NoError(s.backend.StopNode())
s.verifyEnvelopes(envelopes, signal.EventNodeStarted, signal.EventNodeReady, signal.EventNodeStopped) 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.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.verifyEnvelopes(envelopes, signal.EventNodeStopped, signal.EventNodeStarted, signal.EventNodeReady)
s.NoError(s.backend.StopNode()) s.NoError(s.backend.StopNode())
s.verifyEnvelopes(envelopes, signal.EventNodeStopped) s.verifyEnvelopes(envelopes, signal.EventNodeStopped)

View File

@ -27,7 +27,7 @@ func (s *APIBackendTestSuite) TestNetworkSwitching() {
s.NoError(err) s.NoError(err)
s.False(s.Backend.IsNodeRunning()) s.False(s.Backend.IsNodeRunning())
s.NoError(s.Backend.StartNode(nodeConfig)) s.Require().NoError(s.Backend.StartNode(nodeConfig))
s.True(s.Backend.IsNodeRunning()) s.True(s.Backend.IsNodeRunning())
firstHash, err := e2e.FirstBlockHash(s.Backend.StatusNode()) firstHash, err := e2e.FirstBlockHash(s.Backend.StatusNode())
@ -42,7 +42,7 @@ func (s *APIBackendTestSuite) TestNetworkSwitching() {
s.NoError(err) s.NoError(err)
s.False(s.Backend.IsNodeRunning()) s.False(s.Backend.IsNodeRunning())
s.NoError(s.Backend.StartNode(nodeConfig)) s.Require().NoError(s.Backend.StartNode(nodeConfig))
s.True(s.Backend.IsNodeRunning()) s.True(s.Backend.IsNodeRunning())
// make sure we are on another network indeed // make sure we are on another network indeed
@ -89,7 +89,7 @@ func (s *APIBackendTestSuite) TestRestartNode() {
s.NoError(err) s.NoError(err)
s.False(s.Backend.IsNodeRunning()) s.False(s.Backend.IsNodeRunning())
s.NoError(s.Backend.StartNode(nodeConfig)) require.NoError(s.Backend.StartNode(nodeConfig))
s.True(s.Backend.IsNodeRunning()) s.True(s.Backend.IsNodeRunning())
firstHash, err := e2e.FirstBlockHash(s.Backend.StatusNode()) firstHash, err := e2e.FirstBlockHash(s.Backend.StatusNode())

View File

@ -84,7 +84,7 @@ func (s *BackendTestSuite) TearDownTest() {
// StartTestBackend imports some keys and starts a node. // StartTestBackend imports some keys and starts a node.
func (s *BackendTestSuite) StartTestBackend(opts ...TestNodeOption) { func (s *BackendTestSuite) StartTestBackend(opts ...TestNodeOption) {
nodeConfig, err := MakeTestNodeConfig(GetNetworkID()) nodeConfig, err := MakeTestNodeConfig(GetNetworkID())
s.NoError(err) s.Require().NoError(err)
// Apply any options altering node config. // Apply any options altering node config.
for i := range opts { for i := range opts {
@ -96,7 +96,7 @@ func (s *BackendTestSuite) StartTestBackend(opts ...TestNodeOption) {
// start node // start node
s.False(s.Backend.IsNodeRunning()) s.False(s.Backend.IsNodeRunning())
s.NoError(s.Backend.StartNode(nodeConfig)) s.Require().NoError(s.Backend.StartNode(nodeConfig))
s.True(s.Backend.IsNodeRunning()) s.True(s.Backend.IsNodeRunning())
} }
@ -110,7 +110,7 @@ func (s *BackendTestSuite) StopTestBackend() {
// RestartTestNode restarts a currently running node. // RestartTestNode restarts a currently running node.
func (s *BackendTestSuite) RestartTestNode() { func (s *BackendTestSuite) RestartTestNode() {
s.True(s.Backend.IsNodeRunning()) s.True(s.Backend.IsNodeRunning())
s.NoError(s.Backend.RestartNode()) s.Require().NoError(s.Backend.RestartNode())
s.True(s.Backend.IsNodeRunning()) s.True(s.Backend.IsNodeRunning())
} }

View File

@ -2,6 +2,7 @@ package e2e
import ( import (
"context" "context"
"path"
gethcommon "github.com/ethereum/go-ethereum/common" gethcommon "github.com/ethereum/go-ethereum/common"
"github.com/status-im/status-go/node" "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. // 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) { return func(config *params.NodeConfig) {
config.DataDir = path config.DataDir = dataDir
config.KeyStoreDir = path.Join(dataDir, "keystore")
config.WhisperConfig.DataDir = path.Join(dataDir, "wnode")
} }
} }

View File

@ -568,7 +568,7 @@ func (s *WhisperMailboxSuite) startMailboxBackend() (*api.StatusBackend, func())
mailboxConfig.WhisperConfig.Enabled = true mailboxConfig.WhisperConfig.Enabled = true
mailboxConfig.KeyStoreDir = datadir mailboxConfig.KeyStoreDir = datadir
mailboxConfig.WhisperConfig.EnableMailServer = true 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.WhisperConfig.DataDir = filepath.Join(datadir, "data")
mailboxConfig.DataDir = datadir mailboxConfig.DataDir = datadir

View File

@ -173,7 +173,7 @@ func (s *WhisperTestSuite) TestSelectedAccountOnRestart() {
s.NoError(s.Backend.StopNode()) s.NoError(s.Backend.StopNode())
// resume node // resume node
s.NoError(s.Backend.StartNode(&preservedNodeConfig)) s.Require().NoError(s.Backend.StartNode(&preservedNodeConfig))
// re-check selected account (account2 MUST be selected) // re-check selected account (account2 MUST be selected)
selectedAccount, err = s.Backend.AccountManager().SelectedAccount() selectedAccount, err = s.Backend.AccountManager().SelectedAccount()

View File

@ -244,18 +244,28 @@ func MakeTestNodeConfig(networkID int) (*params.NodeConfig, error) {
} }
configJSON := `{ configJSON := `{
"Name": "test",
"NetworkId": ` + strconv.Itoa(networkID) + `, "NetworkId": ` + strconv.Itoa(networkID) + `,
"DataDir": "` + testDir + `", "DataDir": "` + testDir + `",
"KeyStoreDir": "` + path.Join(testDir, "keystore") + `",
"HTTPPort": ` + strconv.Itoa(TestConfig.Node.HTTPPort) + `, "HTTPPort": ` + strconv.Itoa(TestConfig.Node.HTTPPort) + `,
"WSPort": ` + strconv.Itoa(TestConfig.Node.WSPort) + `, "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 { if err != nil {
return nil, err return nil, err
} }
nodeConfig.WhisperConfig.EnableNTPSync = false
return nodeConfig, nil 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 // where specific network addresses are assigned based on provided network id, and assigns
// a given name and data dir. // a given name and data dir.
func MakeTestNodeConfigWithDataDir(name, dataDir, fleet string, networkID uint64) (*params.NodeConfig, error) { 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 { if err != nil {
return nil, err return nil, err
} }
if name == "" {
cfg.Name = "test"
} else {
cfg.Name = name cfg.Name = name
cfg.NetworkID = uint64(GetNetworkID()) }
cfg.NoDiscovery = true
cfg.LightEthConfig.Enabled = false cfg.LightEthConfig.Enabled = false
cfg.WhisperConfig.Enabled = true
cfg.WhisperConfig.EnableNTPSync = false 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 return cfg, nil
} }
@ -299,19 +325,19 @@ const passphraseEnvName = "ACCOUNT_PASSWORD"
func loadTestConfig() (*testConfig, error) { func loadTestConfig() (*testConfig, error) {
var config testConfig var config testConfig
pathOfStatic := path.Join(params.GetStatusHome(), "/static") pathOfConfig := path.Join(params.GetStatusHome(), "/t/config")
err := getTestConfigFromFile(path.Join(pathOfStatic, "config/test-data.json"), &config) err := getTestConfigFromFile(path.Join(pathOfConfig, "test-data.json"), &config)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if GetNetworkID() == params.StatusChainNetworkID { 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 { if err != nil {
return nil, err return nil, err
} }
} else { } 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 { if err != nil {
return nil, err return nil, err
} }

View File

@ -54,7 +54,7 @@ func (s *TransactorSuite) SetupTest() {
rpcClient, _ := rpc.NewClient(s.client, params.UpstreamRPCConfig{}) rpcClient, _ := rpc.NewClient(s.client, params.UpstreamRPCConfig{})
// expected by simulated backend // expected by simulated backend
chainID := gethparams.AllEthashProtocolChanges.ChainID.Uint64() 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.Require().NoError(err)
s.nodeConfig = nodeConfig s.nodeConfig = nodeConfig