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",
"program": "${workspaceRoot}/cmd/statusd",
"env": {},
"args": ["-shh", "-les", "-discovery=true"],
"args": ["-c", "${workspaceRoot}/config/cli/fleet-eth.test.json"],
"showLog": true,
"output": "${workspaceRoot}/build/bin/statusd.debug"
}

View File

@ -198,6 +198,10 @@ xgo:
go get github.com/karalabe/xgo
setup: dep-install lint-install mock-install ##@other Prepare project for first build
go get -u github.com/kevinburke/go-bindata/go-bindata
generate: ##@other Regenerate assets and other auto-generated stuff
go generate ./static
mock-install: ##@other Install mocking tools
go get -u github.com/golang/mock/mockgen

View File

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

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

View File

@ -192,7 +192,7 @@ func makeNodeConfig() (*params.NodeConfig, error) {
return nil, err
}
nodeConfig, err := params.NewNodeConfig(path.Join(workDir, ".ethereum"), "", params.FleetUndefined, uint64(params.RopstenNetworkID))
nodeConfig, err := params.NewNodeConfigWithDefaults(path.Join(workDir, ".ethereum"), params.FleetUndefined, uint64(params.RopstenNetworkID))
if err != nil {
return nil, err
}

View File

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

View File

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

View File

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

View File

@ -68,7 +68,7 @@ func TestStatusFlag(t *testing.T) {
for i, s := range scenarios {
msg := fmt.Sprintf("scenario %d", i)
c, err := params.NewNodeConfig("", "", params.FleetBeta, 0)
c, err := params.NewNodeConfig("", params.FleetBeta, 0)
require.Nil(t, err, msg)
c.IPCEnabled = s.ipcEnabled

View File

@ -12,14 +12,11 @@ import (
"strings"
"time"
"github.com/status-im/status-go/logutils"
"github.com/ethereum/go-ethereum/log"
gethmetrics "github.com/ethereum/go-ethereum/metrics"
"github.com/ethereum/go-ethereum/p2p/discv5"
"github.com/status-im/status-go/api"
"github.com/status-im/status-go/cmd/statusd/debug"
"github.com/status-im/status-go/cmd/statusd/topics"
"github.com/status-im/status-go/logutils"
nodemetrics "github.com/status-im/status-go/metrics/node"
"github.com/status-im/status-go/node"
"github.com/status-im/status-go/params"
@ -36,90 +33,59 @@ var (
)
var (
clusterConfigFile = flag.String("clusterconfig", "", "Cluster configuration file")
nodeKeyFile = flag.String("nodekey", "", "P2P node key file (private key)")
dataDir = flag.String("datadir", params.DataDir, "Data directory for the databases and keystore")
networkID = flag.Int("networkid", params.RopstenNetworkID, "Network identifier (integer, 1=Homestead, 3=Ropsten, 4=Rinkeby, 777=StatusChain)")
lesEnabled = flag.Bool("les", false, "Enable LES protocol")
whisperEnabled = flag.Bool("shh", false, "Enable Whisper protocol")
statusService = flag.String("status", "", `Enable StatusService, possible values: "ipc", "http"`)
debugAPI = flag.Bool("debug", false, `Enable debug API endpoints under "debug_" namespace`)
swarmEnabled = flag.Bool("swarm", false, "Enable Swarm protocol")
maxPeers = flag.Int("maxpeers", 25, "maximum number of p2p peers (including all protocols)")
httpEnabled = flag.Bool("http", false, "Enable HTTP RPC endpoint")
httpHost = flag.String("httphost", "127.0.0.1", "HTTP RPC host of the listening socket")
httpPort = flag.Int("httpport", params.HTTPPort, "HTTP RPC server's listening port")
httpModules = flag.String("httpmodules", params.APIModules, "Comma separated list of HTTP RPC APIs")
ipcEnabled = flag.Bool("ipc", false, "Enable IPC RPC endpoint")
ipcFile = flag.String("ipcfile", "", "Set IPC file path")
cliEnabled = flag.Bool("cli", false, "Enable debugging CLI server")
cliPort = flag.String("cliport", debug.CLIPort, "CLI server's listening port")
pprofEnabled = flag.Bool("pprof", false, "Enable runtime profiling via pprof")
pprofPort = flag.Int("pprofport", 52525, "Port for runtime profiling via pprof")
logLevel = flag.String("log", "INFO", `Log level, one of: "ERROR", "WARN", "INFO", "DEBUG", and "TRACE"`)
logFile = flag.String("logfile", "", "Path to the log file")
logWithoutColors = flag.Bool("log-without-color", false, "Disables log colors")
version = flag.Bool("version", false, "Print version")
listenAddr = flag.String("listenaddr", ":30303", "IP address and port of this node (e.g. 127.0.0.1:30303)")
advertiseAddr = flag.String("advertiseaddr", "", "IP address the node wants to reached with (useful if floating IP is used)")
fleet = flag.String("fleet", params.FleetBeta, "Name of the fleet like 'eth.staging' (default to 'eth.beta')")
standalone = flag.Bool("standalone", false, "Don't actively connect to peers, wait for incoming connections")
bootnodes = flag.String("bootnodes", "", "A list of bootnodes separated by comma")
discoveryFlag = flag.Bool("discovery", false, "Enable discovery protocol")
rendezvous = flag.Bool("rendezvous", false, "Enable rendezvous protocol")
rendezvousNodes = StringSlice{}
configFiles configFlags
logLevel = flag.String("log", "", `Log level, one of: "ERROR", "WARN", "INFO", "DEBUG", and "TRACE"`)
cliEnabled = flag.Bool("cli", false, "Enable debugging CLI server")
cliPort = flag.String("cli-port", debug.CLIPort, "CLI server's listening port")
pprofEnabled = flag.Bool("pprof", false, "Enable runtime profiling via pprof")
pprofPort = flag.Int("pprof-port", 52525, "Port for runtime profiling via pprof")
logWithoutColors = flag.Bool("log-without-color", false, "Disables log colors")
version = flag.Bool("version", false, "Print version and dump configuration")
// don't change the name of this flag, https://github.com/ethereum/go-ethereum/blob/master/metrics/metrics.go#L41
metrics = flag.Bool("metrics", false, "Expose ethereum metrics with debug_metrics jsonrpc call.")
// shh stuff
passwordFile = flag.String("shh.passwordfile", "", "Password file (password is used for symmetric encryption)")
minPow = flag.Float64("shh.pow", params.WhisperMinimumPoW, "PoW for messages to be added to queue, in float format")
ttl = flag.Int("shh.ttl", params.WhisperTTL, "Time to live for messages, in seconds")
lightClient = flag.Bool("shh.lightclient", false, "Start with empty bloom filter, and don't forward messages")
// MailServer
enableMailServer = flag.Bool("shh.mailserver", false, "Delivers expired messages on demand")
// Push Notification
firebaseAuth = flag.String("shh.firebaseauth", "", "FCM Authorization Key used for sending Push Notifications")
metrics = flag.Bool("metrics", false, "Expose ethereum metrics with debug_metrics jsonrpc call")
syncAndExit = flag.Int("sync-and-exit", -1, "Timeout in minutes for blockchain sync and exit, zero means no timeout unless sync is finished")
ntpSyncEnabled = flag.Bool("ntp", true, "Enable/disable whisper NTP synchronization")
// Topics that will be search and registered by discovery v5.
searchTopics = topics.TopicLimitsFlag{}
registerTopics = topics.TopicFlag{}
)
// All general log messages in this package should be routed through this logger.
var logger = log.New("package", "status-go/cmd/statusd")
func init() {
flag.Var(&searchTopics, "topic.search", "Topic that will be searched in discovery v5, e.g (mailserver=1,1)")
flag.Var(&registerTopics, "topic.register", "Topic that will be registered using discovery v5.")
flag.Var(&rendezvousNodes, "rendezvous-node", "Rendezvous server.")
flag.Var(&configFiles, "c", "JSON configuration file(s). Multiple configuration files can be specified, and will be merged in occurrence order")
colors := terminal.IsTerminal(int(os.Stdin.Fd()))
if err := logutils.OverrideRootLog(true, "ERROR", "", colors); err != nil {
stdlog.Fatalf("Error initializing logger: %v", err)
}
flag.Usage = printUsage
flag.Parse()
if flag.NArg() > 0 {
stdlog.Printf("Extra args in command line: %v", flag.Args())
printUsage()
logger.Error("Extra args in command line: %v", flag.Args())
os.Exit(1)
}
}
func main() {
colors := !(*logWithoutColors) && terminal.IsTerminal(int(os.Stdin.Fd()))
if err := logutils.OverrideRootLog(logEnabled(), *logLevel, *logFile, colors); err != nil {
stdlog.Fatalf("Error initializing logger: %s", err)
config, err := params.NewNodeConfigWithDefaults("statusd-data", params.FleetBeta, params.RopstenNetworkID)
if err == nil {
err = parseConfig(configFiles, config)
}
if err != nil {
printUsage()
if err != nil {
logger.Error(err.Error())
}
os.Exit(1)
}
config, err := makeNodeConfig()
if err != nil {
stdlog.Fatalf("Making config failed, %s", err)
colors := !(*logWithoutColors) && terminal.IsTerminal(int(os.Stdin.Fd()))
if err = logutils.OverrideRootLog(logEnabled(config), config.LogLevel, config.LogFile, colors); err != nil {
stdlog.Fatalf("Error initializing logger: %v", err)
}
// We want statusd to be distinct from StatusIM client.
config.Name = serverClientName
@ -206,95 +172,15 @@ func startCollectingNodeMetrics(interruptCh <-chan struct{}, statusNode *node.St
<-interruptCh
}
func logEnabled() bool {
return *logLevel != "" || *logFile != ""
func logEnabled(config *params.NodeConfig) bool {
return config.LogLevel != "" || config.LogFile != ""
}
// makeNodeConfig parses incoming CLI options and returns node configuration object
func makeNodeConfig() (*params.NodeConfig, error) {
nodeConfig, err := params.NewNodeConfig(*dataDir, *clusterConfigFile, *fleet, uint64(*networkID))
if err != nil {
return nil, err
}
nodeConfig.ListenAddr = *listenAddr
nodeConfig.AdvertiseAddr = *advertiseAddr
// TODO(divan): move this logic into params package
if *nodeKeyFile != "" {
nodeConfig.NodeKeyFile = *nodeKeyFile
}
if *logLevel != "" {
nodeConfig.LogLevel = *logLevel
}
if *logFile != "" {
nodeConfig.LogFile = *logFile
}
nodeConfig.LogEnabled = logEnabled()
nodeConfig.RPCEnabled = *httpEnabled
nodeConfig.WhisperConfig.Enabled = *whisperEnabled
nodeConfig.MaxPeers = *maxPeers
nodeConfig.HTTPHost = *httpHost
nodeConfig.HTTPPort = *httpPort
nodeConfig.APIModules = *httpModules
nodeConfig.IPCEnabled = *ipcEnabled
if *ipcFile != "" {
nodeConfig.IPCEnabled = true
nodeConfig.IPCFile = *ipcFile
}
nodeConfig.LightEthConfig.Enabled = *lesEnabled
nodeConfig.SwarmConfig.Enabled = *swarmEnabled
if *standalone {
nodeConfig.ClusterConfig.Enabled = false
nodeConfig.ClusterConfig.BootNodes = nil
}
if len(rendezvousNodes) > 0 {
nodeConfig.ClusterConfig.RendezvousNodes = []string(rendezvousNodes)
}
nodeConfig.NoDiscovery = !(*discoveryFlag)
nodeConfig.Rendezvous = *rendezvous
nodeConfig.RequireTopics = map[discv5.Topic]params.Limits(searchTopics)
nodeConfig.RegisterTopics = []discv5.Topic(registerTopics)
// Even if standalone is true and discovery is disabled,
// it's possible to use bootnodes.
if *bootnodes != "" {
nodeConfig.ClusterConfig.BootNodes = strings.Split(*bootnodes, ",")
}
if nodeConfig, err = configureStatusService(*statusService, nodeConfig); err != nil {
return nil, err
}
nodeConfig.DebugAPIEnabled = *debugAPI
if nodeConfig.DebugAPIEnabled {
nodeConfig.AddAPIModule("debug")
}
if *whisperEnabled {
return whisperConfig(nodeConfig)
}
// RPC configuration
if !*httpEnabled {
nodeConfig.HTTPHost = "" // HTTP RPC is disabled
}
return nodeConfig, nil
}
var errStatusServiceRequiresIPC = errors.New("to enable the StatusService on IPC, -ipc flag must be set")
var errStatusServiceRequiresHTTP = errors.New("to enable the StatusService on HTTP, -http flag must be set")
var errStatusServiceInvalidFlag = errors.New("-status flag valid values are: ipc, http")
var (
errStatusServiceRequiresIPC = errors.New("to enable the StatusService on IPC, -ipc flag must be set")
errStatusServiceRequiresHTTP = errors.New("to enable the StatusService on HTTP, -http flag must be set")
errStatusServiceInvalidFlag = errors.New("-status flag valid values are: ipc, http")
)
func configureStatusService(flagValue string, nodeConfig *params.NodeConfig) (*params.NodeConfig, error) {
switch flagValue {
@ -318,6 +204,19 @@ func configureStatusService(flagValue string, nodeConfig *params.NodeConfig) (*p
return nodeConfig, nil
}
func parseConfig(configFiles configFlags, config *params.NodeConfig) error {
// Merge specified configuration files, in order
if err := params.LoadConfigFromFiles(configFiles, config); err != nil {
return err
}
if *logLevel != "" {
config.LogLevel = *logLevel
}
return nil
}
// printVersion prints verbose output about version and config.
func printVersion(config *params.NodeConfig, buildStamp string) {
fmt.Println(strings.Title(config.Name))
@ -333,7 +232,6 @@ func printVersion(config *params.NodeConfig, buildStamp string) {
fmt.Printf("GOPATH=%s\n", os.Getenv("GOPATH"))
fmt.Printf("GOROOT=%s\n", runtime.GOROOT())
config.LightEthConfig.Genesis = "SKIP"
fmt.Println("Loaded Config: ", config)
}
@ -341,11 +239,10 @@ func printUsage() {
usage := `
Usage: statusd [options]
Examples:
statusd # run status node with defaults
statusd -networkid 4 # run node on Rinkeby network
statusd -datadir /dir # specify different dir for data
statusd -ipc # enable IPC for usage with "geth attach"
statusd -cli # enable connection by statusd-cli on default port
statusd -c ./default.json # run node with configuration specified in ./default.json file
statusd -c ./default.json -c ./standalone.json # run node with configuration specified in ./default.json file, after merging ./standalone.json file
statusd -c ./default.json -metrics # run node with configuration specified in ./default.json file, and expose ethereum metrics with debug_metrics jsonrpc call
statusd -c ./default.json -cli # run node with configuration specified in ./default.json file, and enable connection by statusd-cli on default port
Options:
`

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.
var logger = log.New("package", "status-go/lib")
//GenerateConfig for status node
//export GenerateConfig
func GenerateConfig(datadir *C.char, fleet *C.char, networkID C.int) *C.char {
config, err := params.NewNodeConfig(C.GoString(datadir), "", C.GoString(fleet), uint64(networkID))
if err != nil {
return makeJSONResponse(err)
}
outBytes, err := json.Marshal(config)
if err != nil {
return makeJSONResponse(err)
}
return C.CString(string(outBytes))
}
//StartNode - start Status node
//export StartNode
func StartNode(configJSON *C.char) *C.char {
config, err := params.LoadNodeConfig(C.GoString(configJSON))
config, err := params.NewConfigFromJSON(C.GoString(configJSON))
if err != nil {
return makeJSONResponse(err)
}
@ -67,7 +51,7 @@ func StopNode() *C.char {
func ValidateNodeConfig(configJSON *C.char) *C.char {
var resp APIDetailedResponse
_, err := params.LoadNodeConfig(C.GoString(configJSON))
_, err := params.NewConfigFromJSON(C.GoString(configJSON))
// Convert errors to APIDetailedResponse
switch err := err.(type) {

View File

@ -9,6 +9,7 @@ package main
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@ -23,9 +24,9 @@ func TestExportedAPI(t *testing.T) {
func TestValidateNodeConfig(t *testing.T) {
noErrorsCallback := func(resp APIDetailedResponse) {
assert.Empty(t, resp.FieldErrors)
assert.Empty(t, resp.Message)
require.True(t, resp.Status, "expected status equal true")
require.Empty(t, resp.FieldErrors)
require.Empty(t, resp.Message)
}
testCases := []struct {
@ -37,7 +38,15 @@ func TestValidateNodeConfig(t *testing.T) {
Name: "response for valid config",
Config: `{
"NetworkId": 1,
"DataDir": "/tmp"
"DataDir": "/tmp",
"KeyStoreDir": "/tmp",
"NoDiscovery": true,
"WhisperConfig": {
"Enabled": true,
"EnableMailServer": true,
"DataDir": "/tmp",
"MailServerPassword": "status-offline-inbox"
}
}`,
Callback: noErrorsCallback,
},
@ -49,18 +58,85 @@ func TestValidateNodeConfig(t *testing.T) {
require.Contains(t, resp.Message, "validation: invalid character '}'")
},
},
{
Name: "response for config missing DataDir",
Config: `{
"NetworkId": 3,
"KeyStoreDir": "/tmp",
"NoDiscovery": true,
"WhisperConfig": {
"Enabled": false
}
}`,
Callback: func(resp APIDetailedResponse) {
require.False(t, resp.Status)
require.Equal(t, 1, len(resp.FieldErrors))
require.Equal(t, resp.FieldErrors[0].Parameter, "NodeConfig.DataDir")
require.Contains(t, resp.Message, "validation: validation failed")
},
},
{
Name: "response for config missing KeyStoreDir",
Config: `{
"NetworkId": 3,
"DataDir": "/tmp",
"NoDiscovery": true,
"WhisperConfig": {
"Enabled": false
}
}`,
Callback: func(resp APIDetailedResponse) {
require.False(t, resp.Status)
require.Equal(t, 1, len(resp.FieldErrors))
require.Equal(t, resp.FieldErrors[0].Parameter, "NodeConfig.KeyStoreDir")
require.Contains(t, resp.Message, "validation: validation failed")
},
},
{
Name: "response for config missing WhisperConfig.DataDir",
Config: `{
"NetworkId": 3,
"DataDir": "/tmp",
"KeyStoreDir": "/tmp",
"NoDiscovery": true,
"WhisperConfig": {
"Enabled": true,
"EnableMailServer": true
}
}`,
Callback: func(resp APIDetailedResponse) {
require.False(t, resp.Status)
require.Empty(t, resp.FieldErrors)
require.Contains(t, resp.Message, "WhisperConfig.DataDir must be specified when WhisperConfig.EnableMailServer is true")
},
},
{
Name: "response for config missing WhisperConfig.DataDir with WhisperConfig.EnableMailServer set to false",
Config: `{
"NetworkId": 3,
"DataDir": "/tmp",
"KeyStoreDir": "/tmp",
"NoDiscovery": true,
"WhisperConfig": {
"Enabled": true,
"EnableMailServer": false
}
}`,
Callback: noErrorsCallback,
},
{
Name: "response for config with multiple errors",
Config: `{}`,
Callback: func(resp APIDetailedResponse) {
required := map[string]string{
"NodeConfig.NetworkID": "required",
"NodeConfig.DataDir": "required",
"NodeConfig.NetworkID": "required",
"NodeConfig.DataDir": "required",
"NodeConfig.KeyStoreDir": "required",
}
require.False(t, resp.Status)
require.Contains(t, resp.Message, "validation: validation failed")
require.Equal(t, 2, len(resp.FieldErrors))
require.Equal(t, 3, len(resp.FieldErrors), resp.FieldErrors)
for _, err := range resp.FieldErrors {
require.Contains(t, required, err.Parameter)

View File

@ -12,10 +12,10 @@ package main
import "C"
import (
"encoding/json"
"fmt"
"io/ioutil"
"math/big"
"os"
"path"
"path/filepath"
"reflect"
"strconv"
@ -26,13 +26,10 @@ import (
"github.com/ethereum/go-ethereum/accounts/keystore"
gethcommon "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core"
gethparams "github.com/ethereum/go-ethereum/params"
"github.com/stretchr/testify/require"
"github.com/status-im/status-go/account"
"github.com/status-im/status-go/params"
"github.com/status-im/status-go/signal"
. "github.com/status-im/status-go/t/utils" //nolint: golint
"github.com/status-im/status-go/transactions"
@ -55,9 +52,18 @@ func init() {
nodeConfigJSON = `{
"NetworkId": ` + strconv.Itoa(GetNetworkID()) + `,
"DataDir": "` + testChainDir + `",
"KeyStoreDir": "` + filepath.Join(testChainDir, "keystore") + `",
"HTTPPort": ` + strconv.Itoa(TestConfig.Node.HTTPPort) + `,
"WSPort": ` + strconv.Itoa(TestConfig.Node.WSPort) + `,
"LogLevel": "INFO"
"LogLevel": "INFO",
"NoDiscovery": true,
"LightEthConfig": {
"Enabled": true
},
"WhisperConfig": {
"Enabled": true,
"DataDir": "` + path.Join(testChainDir, "wnode") + `",
"EnableNTPSync": false
}
}`
}
@ -85,10 +91,6 @@ func testExportedAPI(t *testing.T, done chan struct{}) {
name string
fn func(t *testing.T) bool
}{
{
"check default configuration",
testGetDefaultConfig,
},
{
"stop/resume node",
testStopResumeNode,
@ -187,39 +189,6 @@ func testVerifyAccountPassword(t *testing.T) bool {
return true
}
func testGetDefaultConfig(t *testing.T) bool {
networks := []struct {
chainID int
refChainConfig *gethparams.ChainConfig
}{
{params.MainNetworkID, gethparams.MainnetChainConfig},
{params.RopstenNetworkID, gethparams.TestnetChainConfig},
{params.RinkebyNetworkID, gethparams.RinkebyChainConfig},
// TODO(tiabc): The same for params.StatusChainNetworkID
}
for i := range networks {
network := networks[i]
t.Run(fmt.Sprintf("networkID=%d", network.chainID), func(t *testing.T) {
var (
nodeConfig = params.NodeConfig{}
rawResponse = GenerateConfig(C.CString("/tmp/data-folder"), C.CString("eth.staging"), C.int(network.chainID))
)
if err := json.Unmarshal([]byte(C.GoString(rawResponse)), &nodeConfig); err != nil {
t.Errorf("cannot decode response (%s): %v", C.GoString(rawResponse), err)
}
genesis := new(core.Genesis)
if err := json.Unmarshal([]byte(nodeConfig.LightEthConfig.Genesis), genesis); err != nil {
t.Error(err)
}
require.Equal(t, network.refChainConfig, genesis.Config)
})
}
return true
}
//@TODO(adam): quarantined this test until it uses a different directory.
//nolint: deadcode
func testResetChainData(t *testing.T) bool {

View File

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

View File

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

View File

@ -27,24 +27,26 @@ import (
"github.com/status-im/status-go/services/personal"
"github.com/status-im/status-go/services/shhext"
"github.com/status-im/status-go/services/status"
"github.com/status-im/status-go/static"
"github.com/status-im/status-go/timesource"
"github.com/syndtr/goleveldb/leveldb"
)
// Errors related to node and services creation.
var (
ErrNodeMakeFailureFormat = "error creating p2p node: %s"
ErrWhisperServiceRegistrationFailure = errors.New("failed to register the Whisper service")
ErrLightEthRegistrationFailure = errors.New("failed to register the LES service")
ErrPersonalServiceRegistrationFailure = errors.New("failed to register the personal api service")
ErrStatusServiceRegistrationFailure = errors.New("failed to register the Status service")
ErrPeerServiceRegistrationFailure = errors.New("failed to register the Peer service")
ErrNodeMakeFailureFormat = "error creating p2p node: %s"
ErrWhisperServiceRegistrationFailure = errors.New("failed to register the Whisper service")
ErrLightEthRegistrationFailure = errors.New("failed to register the LES service")
ErrLightEthRegistrationFailureUpstreamEnabled = errors.New("failed to register the LES service, upstream is also configured")
ErrPersonalServiceRegistrationFailure = errors.New("failed to register the personal api service")
ErrStatusServiceRegistrationFailure = errors.New("failed to register the Status service")
ErrPeerServiceRegistrationFailure = errors.New("failed to register the Peer service")
)
// All general log messages in this package should be routed through this logger.
var logger = log.New("package", "status-go/node")
// MakeNode create a geth node entity
// MakeNode creates a geth node entity
func MakeNode(config *params.NodeConfig, db *leveldb.DB) (*node.Node, error) {
// If DataDir is empty, it means we want to create an ephemeral node
// keeping data only in memory.
@ -60,17 +62,9 @@ func MakeNode(config *params.NodeConfig, db *leveldb.DB) (*node.Node, error) {
}
}
stackConfig := defaultEmbeddedNodeConfig(config)
if len(config.NodeKeyFile) > 0 {
logger.Info("Loading private key file", "file", config.NodeKeyFile)
pk, err := crypto.LoadECDSA(config.NodeKeyFile)
if err != nil {
logger.Error("Failed loading private key file", "file", config.NodeKeyFile, "error", err)
}
// override node's private key
stackConfig.P2P.PrivateKey = pk
stackConfig, err := newGethNodeConfig(config)
if err != nil {
return nil, err
}
stack, err := node.New(stackConfig)
@ -84,6 +78,10 @@ func MakeNode(config *params.NodeConfig, db *leveldb.DB) (*node.Node, error) {
return nil, fmt.Errorf("%v: %v", ErrLightEthRegistrationFailure, err)
}
} else {
if config.LightEthConfig.Enabled {
return nil, fmt.Errorf("%v: %v", ErrLightEthRegistrationFailureUpstreamEnabled, err)
}
// `personal_sign` and `personal_ecRecover` methods are important to
// keep DApps working.
// Usually, they are provided by an ETH or a LES service, but when using
@ -112,8 +110,8 @@ func MakeNode(config *params.NodeConfig, db *leveldb.DB) (*node.Node, error) {
return stack, nil
}
// defaultEmbeddedNodeConfig returns default stack configuration for mobile client node
func defaultEmbeddedNodeConfig(config *params.NodeConfig) *node.Config {
// newGethNodeConfig returns default stack configuration for mobile client node
func newGethNodeConfig(config *params.NodeConfig) (*node.Config, error) {
nc := &node.Config{
DataDir: config.DataDir,
KeyStoreDir: config.KeyStoreDir,
@ -139,26 +137,71 @@ func defaultEmbeddedNodeConfig(config *params.NodeConfig) *node.Config {
nc.HTTPPort = config.HTTPPort
}
if config.ClusterConfig != nil && config.ClusterConfig.Enabled {
if config.ClusterConfig.Enabled {
nc.P2P.BootstrapNodesV5 = parseNodesV5(config.ClusterConfig.BootNodes)
nc.P2P.StaticNodes = parseNodes(config.ClusterConfig.StaticNodes)
}
return nc
if config.NodeKey != "" {
sk, err := crypto.HexToECDSA(config.NodeKey)
if err != nil {
return nil, err
}
// override node's private key
nc.P2P.PrivateKey = sk
}
return nc, nil
}
// calculateGenesis retrieves genesis value for given network
func calculateGenesis(networkID uint64) (*core.Genesis, error) {
var genesis *core.Genesis
switch networkID {
case params.MainNetworkID:
genesis = core.DefaultGenesisBlock()
case params.RopstenNetworkID:
genesis = core.DefaultTestnetGenesisBlock()
case params.RinkebyNetworkID:
genesis = core.DefaultRinkebyGenesisBlock()
case params.StatusChainNetworkID:
var err error
if genesis, err = defaultStatusChainGenesisBlock(); err != nil {
return nil, err
}
default:
return nil, nil
}
return genesis, nil
}
// defaultStatusChainGenesisBlock returns the StatusChain network genesis block.
func defaultStatusChainGenesisBlock() (*core.Genesis, error) {
genesisJSON, err := static.ConfigStatusChainGenesisJsonBytes()
if err != nil {
return nil, fmt.Errorf("status-chain-genesis.json could not be loaded: %s", err)
}
var genesis *core.Genesis
err = json.Unmarshal(genesisJSON, &genesis)
if err != nil {
return nil, fmt.Errorf("cannot unmarshal status-chain-genesis.json: %s", err)
}
return genesis, nil
}
// activateLightEthService configures and registers the eth.Ethereum service with a given node.
func activateLightEthService(stack *node.Node, config *params.NodeConfig) error {
if config.LightEthConfig == nil || !config.LightEthConfig.Enabled {
if !config.LightEthConfig.Enabled {
logger.Info("LES protocol is disabled")
return nil
}
var genesis *core.Genesis
if config.LightEthConfig.Genesis != "" {
genesis = new(core.Genesis)
if err := json.Unmarshal([]byte(config.LightEthConfig.Genesis), genesis); err != nil {
return fmt.Errorf("invalid genesis spec: %v", err)
}
genesis, err := calculateGenesis(config.NetworkID)
if err != nil {
return err
}
ethConf := eth.DefaultConfig
@ -202,21 +245,6 @@ func activatePeerService(stack *node.Node) error {
}
func registerMailServer(whisperService *whisper.Whisper, config *params.WhisperConfig) (err error) {
// if the Password is already set, do not override it
if config.MailServerPassword == "" && config.MailServerPasswordFile != "" {
err = config.ReadMailServerPasswordFile()
if err != nil {
return
}
}
// similarly, do not override already configured AsymKey
if config.MailServerAsymKey == nil && config.MailServerAsymKeyFile != "" {
err = config.ReadMailServerAsymKeyFile()
if err != nil {
return
}
}
var mailServer mailserver.WMailServer
whisperService.RegisterServer(&mailServer)
@ -225,7 +253,7 @@ func registerMailServer(whisperService *whisper.Whisper, config *params.WhisperC
// activateShhService configures Whisper and adds it to the given node.
func activateShhService(stack *node.Node, config *params.NodeConfig, db *leveldb.DB) (err error) {
if config.WhisperConfig == nil || !config.WhisperConfig.Enabled {
if !config.WhisperConfig.Enabled {
logger.Info("SHH protocol is disabled")
return nil
}
@ -254,7 +282,7 @@ func activateShhService(stack *node.Node, config *params.NodeConfig, db *leveldb
// enable mail service
if config.WhisperConfig.EnableMailServer {
if err := registerMailServer(whisperService, config.WhisperConfig); err != nil {
if err := registerMailServer(whisperService, &config.WhisperConfig); err != nil {
return nil, fmt.Errorf("failed to register MailServer: %v", err)
}
}

View File

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

View File

@ -117,17 +117,18 @@ func (n *StatusNode) startWithDB(config *params.NodeConfig, db *leveldb.DB, serv
return nil
}
// Start starts current StatusNode, will fail if it's already started.
// Start starts current StatusNode, failing if it's already started.
func (n *StatusNode) Start(config *params.NodeConfig, services ...node.ServiceConstructor) error {
n.mu.Lock()
defer n.mu.Unlock()
n.log.Debug("starting with NodeConfig", "ClusterConfig", config.ClusterConfig)
if n.isRunning() {
n.log.Debug("cannot start, node already running")
return ErrNodeRunning
}
n.log.Debug("starting with NodeConfig", "ClusterConfig", config.ClusterConfig)
db, err := db.Create(config.DataDir, params.StatusDatabase)
if err != nil {
return err
@ -186,7 +187,7 @@ func (n *StatusNode) setupRPCClient() (err error) {
}
func (n *StatusNode) discoveryEnabled() bool {
return n.config != nil && (!n.config.NoDiscovery || n.config.Rendezvous) && n.config.ClusterConfig != nil
return n.config != nil && (!n.config.NoDiscovery || n.config.Rendezvous) && n.config.ClusterConfig.Enabled
}
func (n *StatusNode) discoverNode() *discover.Node {
@ -355,7 +356,7 @@ func (n *StatusNode) isRunning() bool {
// populateStaticPeers connects current node with our publicly available LES/SHH/Swarm cluster
func (n *StatusNode) populateStaticPeers() error {
if n.config.ClusterConfig == nil || !n.config.ClusterConfig.Enabled {
if !n.config.ClusterConfig.Enabled {
n.log.Info("Static peers are disabled")
return nil
}
@ -372,7 +373,7 @@ func (n *StatusNode) populateStaticPeers() error {
}
func (n *StatusNode) removeStaticPeers() error {
if n.config.ClusterConfig == nil || !n.config.ClusterConfig.Enabled {
if !n.config.ClusterConfig.Enabled {
n.log.Info("Static peers are disabled")
return nil
}

View File

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

View File

@ -14,62 +14,3 @@ type Cluster struct {
MailServers []string `json:"mailservers"` // list of trusted mail servers
RendezvousNodes []string `json:"rendezvousnodes"`
}
// Consult this list with http://fleets.status.im/.
var clusters = map[string]func() Cluster{
FleetStaging: func() Cluster {
return Cluster{
BootNodes: []string{
"enode://10a78c17929a7019ef4aa2249d7302f76ae8a06f40b2dc88b7b31ebff4a623fbb44b4a627acba296c1ced3775d91fbe18463c15097a6a36fdb2c804ff3fc5b35@35.238.97.234:30404", // boot-01.gc-us-central1-a.eth.staging
"enode://f79fb3919f72ca560ad0434dcc387abfe41e0666201ebdada8ede0462454a13deb05cda15f287d2c4bd85da81f0eb25d0a486bbbc8df427b971ac51533bd00fe@174.138.107.239:30404", // boot-01.do-ams3.eth.staging
},
StaticNodes: []string{
"enode://914c0b30f27bab30c1dfd31dad7652a46fda9370542aee1b062498b1345ee0913614b8b9e3e84622e84a7203c5858ae1d9819f63aece13ee668e4f6668063989@167.99.19.148:30305", // node-01.do-ams3.eth.staging
"enode://2d897c6e846949f9dcf10279f00e9b8325c18fe7fa52d658520ad7be9607c83008b42b06aefd97cfe1fdab571f33a2a9383ff97c5909ed51f63300834913237e@35.192.0.86:30305", // "node-01.gc-us-central1-a.eth.staging"
},
MailServers: []string{
"enode://69f72baa7f1722d111a8c9c68c39a31430e9d567695f6108f31ccb6cd8f0adff4991e7fdca8fa770e75bc8a511a87d24690cbc80e008175f40c157d6f6788d48@206.189.240.16:30504", // mail-01.do-ams3.eth.staging
"enode://e4fc10c1f65c8aed83ac26bc1bfb21a45cc1a8550a58077c8d2de2a0e0cd18e40fd40f7e6f7d02dc6cd06982b014ce88d6e468725ffe2c138e958788d0002a7f@35.239.193.41:30504", // mail-01.gc-us-central1-a.eth.staging
},
RendezvousNodes: []string{
"/ip4/174.138.107.239/tcp/30703/ethv4/16Uiu2HAkyJHeetQ4DNpd4NZ2ntzxMo25zcdpvGQRqkD5pB9BE6RU",
"/ip4/35.238.97.234/tcp/30703/ethv4/16Uiu2HAm1sVyXmkMNjdeDWqK2urbyC3oBHi8MDpCdYkns1nYafqz",
},
}
},
FleetBeta: func() Cluster {
return Cluster{
BootNodes: []string{
"enode://436cc6f674928fdc9a9f7990f2944002b685d1c37f025c1be425185b5b1f0900feaf1ccc2a6130268f9901be4a7d252f37302c8335a2c1a62736e9232691cc3a@174.138.105.243:30404", // boot-01.do-ams3.eth.beta
"enode://5395aab7833f1ecb671b59bf0521cf20224fe8162fc3d2675de4ee4d5636a75ec32d13268fc184df8d1ddfa803943906882da62a4df42d4fccf6d17808156a87@206.189.243.57:30404", // boot-02.do-ams3.eth.beta
"enode://7427dfe38bd4cf7c58bb96417806fab25782ec3e6046a8053370022cbaa281536e8d64ecd1b02e1f8f72768e295d06258ba43d88304db068e6f2417ae8bcb9a6@104.154.88.123:30404", // boot-01.gc-us-central1-a.eth.beta
"enode://ebefab39b69bbbe64d8cd86be765b3be356d8c4b24660f65d493143a0c44f38c85a257300178f7845592a1b0332811542e9a58281c835babdd7535babb64efc1@35.202.99.224:30404", // boot-02.gc-us-central1-a.eth.beta
},
StaticNodes: []string{
"enode://a6a2a9b3a7cbb0a15da74301537ebba549c990e3325ae78e1272a19a3ace150d03c184b8ac86cc33f1f2f63691e467d49308f02d613277754c4dccd6773b95e8@206.189.243.176:30304", // node-01.do-ams3.eth.beta
"enode://207e53d9bf66be7441e3daba36f53bfbda0b6099dba9a865afc6260a2d253fb8a56a72a48598a4f7ba271792c2e4a8e1a43aaef7f34857f520c8c820f63b44c8@35.224.15.65:30304", // node-01.gc-us-central1-a.eth.beta
},
MailServers: []string{
"enode://c42f368a23fa98ee546fd247220759062323249ef657d26d357a777443aec04db1b29a3a22ef3e7c548e18493ddaf51a31b0aed6079bd6ebe5ae838fcfaf3a49@206.189.243.162:30504", // mail-01.do-ams3.eth.beta
"enode://7aa648d6e855950b2e3d3bf220c496e0cae4adfddef3e1e6062e6b177aec93bc6cdcf1282cb40d1656932ebfdd565729da440368d7c4da7dbd4d004b1ac02bf8@206.189.243.169:30504", // mail-02.do-ams3.eth.beta
"enode://8a64b3c349a2e0ef4a32ea49609ed6eb3364be1110253c20adc17a3cebbc39a219e5d3e13b151c0eee5d8e0f9a8ba2cd026014e67b41a4ab7d1d5dd67ca27427@206.189.243.168:30504", // mail-03.do-ams3.eth.beta
"enode://7de99e4cb1b3523bd26ca212369540646607c721ad4f3e5c821ed9148150ce6ce2e72631723002210fac1fd52dfa8bbdf3555e05379af79515e1179da37cc3db@35.188.19.210:30504", // mail-01.gc-us-central1-a.eth.beta
"enode://015e22f6cd2b44c8a51bd7a23555e271e0759c7d7f52432719665a74966f2da456d28e154e836bee6092b4d686fe67e331655586c57b718be3997c1629d24167@35.226.21.19:30504", // mail-02.gc-us-central1-a.eth.beta
"enode://531e252ec966b7e83f5538c19bf1cde7381cc7949026a6e499b6e998e695751aadf26d4c98d5a4eabfb7cefd31c3c88d600a775f14ed5781520a88ecd25da3c6@35.225.227.79:30504", // mail-03.gc-us-central1-a.eth.beta
},
RendezvousNodes: []string{
"/ip4/174.138.105.243/tcp/30703/ethv4/16Uiu2HAmRHPzF3rQg55PgYPcQkyvPVH9n2hWsYPhUJBZ6kVjJgdV", // boot-01.do-ams3.eth.beta
"/ip4/206.189.243.57/tcp/30703/ethv4/16Uiu2HAmLqTXuY4Sb6G28HNooaFUXUKzpzKXCcgyJxgaEE2i5vnf", // boot-02.do-ams3.eth.beta
},
}
},
}
// ClusterForFleet returns a cluster for a given fleet.
func ClusterForFleet(fleet string) (Cluster, bool) {
cluster, ok := clusters[fleet]
if ok {
return cluster(), true
}
return Cluster{}, false
}

View File

@ -1,34 +1,23 @@
package params
import (
"bytes"
"crypto/ecdsa"
"encoding/json"
"errors"
"fmt"
"go/build"
"io/ioutil"
"net/url"
"os"
"path"
"path/filepath"
"strings"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/p2p/discv5"
"github.com/ethereum/go-ethereum/params"
)
// errors
var (
ErrMissingDataDir = errors.New("missing required 'DataDir' parameter")
ErrMissingNetworkID = errors.New("missing required 'NetworkID' parameter")
ErrEmptyPasswordFile = errors.New("password file cannot be empty")
ErrNoPasswordFileValueSet = errors.New("password file path not set")
ErrEmptyAuthorizationKeyFile = errors.New("authorization key file cannot be empty")
ErrAuthorizationKeyFileNotSet = errors.New("authorization key file is not set")
"github.com/status-im/status-go/static"
validator "gopkg.in/go-playground/validator.v9"
)
// ----------
@ -41,46 +30,10 @@ type LightEthConfig struct {
// Enabled flag specifies whether protocol is enabled
Enabled bool
// Genesis is JSON to seed the chain database with
Genesis string
// DatabaseCache is memory (in MBs) allocated to internal caching (min 16MB / database forced)
DatabaseCache int
}
// ----------
// FirebaseConfig
// ----------
// FirebaseConfig holds FCM-related configuration
type FirebaseConfig struct {
// AuthorizationKeyFile file path that contains FCM authorization key
AuthorizationKeyFile string
// NotificationTriggerURL URL used to send push notification requests to
NotificationTriggerURL string
}
// ReadAuthorizationKeyFile reads and loads FCM authorization key
func (c *FirebaseConfig) ReadAuthorizationKeyFile() ([]byte, error) {
if len(c.AuthorizationKeyFile) == 0 {
return nil, ErrAuthorizationKeyFileNotSet
}
key, err := ioutil.ReadFile(c.AuthorizationKeyFile)
if err != nil {
return nil, err
}
key = bytes.TrimRight(key, "\n")
if len(key) == 0 {
return nil, ErrEmptyAuthorizationKeyFile
}
return key, nil
}
// ----------
// WhisperConfig
// ----------
@ -103,18 +56,12 @@ type WhisperConfig struct {
// MinimumPoW minimum PoW for Whisper messages
MinimumPoW float64
// MailServerPasswordFile contains a password for symmetric encryption with MailServer.
MailServerPasswordFile string
// MailServerPassword for symmetric encryption with MailServer.
// (if no account file selected, then this password is used for symmetric encryption).
MailServerPassword string
// MailServerAsymKeyFile is a file with an asymmetric key to decrypt messages sent to MailServer.
MailServerAsymKeyFile string
// MailServerAsymKey is an asymmetric key to decrypt messages sent to MailServer.
MailServerAsymKey *ecdsa.PrivateKey
// MailServerAsymKey is an hex-encoded asymmetric key to decrypt messages sent to MailServer.
MailServerAsymKey string
// RateLimit minimum time between queries to mail server per peer
MailServerRateLimit int
@ -125,40 +72,10 @@ type WhisperConfig struct {
// TTL time to live for messages, in seconds
TTL int
// FirebaseConfig extra configuration for Firebase Cloud Messaging
FirebaseConfig *FirebaseConfig `json:"FirebaseConfig,"`
// EnableNTPSync enables NTP synchronizations
EnableNTPSync bool
}
// ReadMailServerPasswordFile reads and returns content of the password file
func (c *WhisperConfig) ReadMailServerPasswordFile() error {
if len(c.MailServerPasswordFile) == 0 {
return ErrNoPasswordFileValueSet
}
password, err := ioutil.ReadFile(c.MailServerPasswordFile)
if err != nil {
return err
}
password = bytes.TrimRight(password, "\n")
if len(password) == 0 {
return ErrEmptyPasswordFile
}
c.MailServerPassword = string(password)
return nil
}
// ReadMailServerAsymKeyFile reads and returns a private key from a given file.
func (c *WhisperConfig) ReadMailServerAsymKeyFile() (err error) {
c.MailServerAsymKey, err = crypto.LoadECDSA(c.MailServerAsymKeyFile)
return
}
// String dumps config object as nicely indented JSON
func (c *WhisperConfig) String() string {
data, _ := json.MarshalIndent(c, "", " ") // nolint: gas
@ -195,17 +112,16 @@ type ClusterConfig struct {
// Fleet is a type of selected fleet.
Fleet string
// StaticNodes lists the static nodes taken from compiled or passed cluster.json
// StaticNodes is a list of static nodes for this fleet.
StaticNodes []string
// BootNodes list of cluster peer nodes for a given network (Mainnet, Ropsten, Rinkeby, Homestead),
// for a given mode (production vs development)
// BootNodes is a list of cluster peer nodes for this fleet.
BootNodes []string
// TrustedMailServers is a list of verified Mail Servers.
// TrustedMailServers is a list of verified Mail Servers for this fleet.
TrustedMailServers []string
// RendezvousNodes is a rendezvous discovery server.
// RendezvousNodes is a list rendezvous discovery nodes.
RendezvousNodes []string
}
@ -255,13 +171,11 @@ type NodeConfig struct {
DataDir string `validate:"required"`
// KeyStoreDir is the file system folder that contains private keys.
// If KeyStoreDir is empty, the default location is the "keystore" subdirectory of DataDir.
KeyStoreDir string
KeyStoreDir string `validate:"required"`
// NodeKeyFile is a filename with node ID (private key)
// This file should contain a valid secp256k1 private key that will be used for both
// NodeKey is the hex-encoded node ID (private key). Should be a valid secp256k1 private key that will be used for both
// remote peer identification as well as network traffic encryption.
NodeKeyFile string
NodeKey string
// NoDiscovery set to true will disable discovery protocol.
NoDiscovery bool
@ -330,21 +244,17 @@ type NodeConfig struct {
// UpstreamConfig extra config for providing upstream infura server.
UpstreamConfig UpstreamRPCConfig `json:"UpstreamConfig"`
// ClusterConfigFile contains the file name of the cluster configuration. If
// empty the statical configuration data will be taken.
ClusterConfigFile string `json:"ClusterConfigFile"`
// ClusterConfig extra configuration for supporting cluster peers.
ClusterConfig *ClusterConfig `json:"ClusterConfig," validate:"structonly"`
ClusterConfig ClusterConfig `json:"ClusterConfig," validate:"structonly"`
// LightEthConfig extra configuration for LES
LightEthConfig *LightEthConfig `json:"LightEthConfig," validate:"structonly"`
LightEthConfig LightEthConfig `json:"LightEthConfig," validate:"structonly"`
// WhisperConfig extra configuration for SHH
WhisperConfig *WhisperConfig `json:"WhisperConfig," validate:"structonly"`
WhisperConfig WhisperConfig `json:"WhisperConfig," validate:"structonly"`
// SwarmConfig extra configuration for Swarm and ENS
SwarmConfig *SwarmConfig `json:"SwarmConfig," validate:"structonly"`
SwarmConfig SwarmConfig `json:"SwarmConfig," validate:"structonly"`
// RegisterTopics a list of specific topics where the peer wants to be
// discoverable.
@ -364,91 +274,144 @@ type NodeConfig struct {
MailServerRegistryAddress string
}
// NewNodeConfig creates new node configuration object
func NewNodeConfig(dataDir, clstrCfgFile, fleet string, networkID uint64) (*NodeConfig, error) {
// NewNodeConfigWithDefaults creates new node configuration object with some defaults suitable for adhoc use
func NewNodeConfigWithDefaults(dataDir, fleet string, networkID uint64) (*NodeConfig, error) {
nodeConfig, err := NewNodeConfig(dataDir, fleet, networkID)
if err != nil {
return nil, err
}
if dataDir != "" {
nodeConfig.KeyStoreDir = path.Join(dataDir, "keystore")
nodeConfig.WhisperConfig.DataDir = path.Join(dataDir, "wnode")
}
if fleet != FleetUndefined {
statusConfigJSON, err := static.Asset(fmt.Sprintf("../config/cli/fleet-%s.json", fleet))
if err == nil {
err = LoadConfigFromJSON(string(statusConfigJSON), nodeConfig)
}
if err != nil {
return nil, fmt.Errorf("default config could not be loaded: %s", err)
}
}
nodeConfig.HTTPHost = ""
nodeConfig.ListenAddr = ":30303"
nodeConfig.LogEnabled = true
nodeConfig.LogLevel = "INFO"
nodeConfig.LogToStderr = true
nodeConfig.WhisperConfig.Enabled = true
nodeConfig.WhisperConfig.EnableNTPSync = true
return nodeConfig, nil
}
// NewNodeConfig creates new node configuration object with bare-minimum defaults
func NewNodeConfig(dataDir, fleet string, networkID uint64) (*NodeConfig, error) {
nodeConfig := &NodeConfig{
NetworkID: networkID,
DataDir: dataDir,
Name: ClientIdentifier,
Version: Version,
RPCEnabled: RPCEnabledDefault,
HTTPHost: HTTPHost,
HTTPPort: HTTPPort,
ListenAddr: ListenAddr,
APIModules: APIModules,
MaxPeers: MaxPeers,
MaxPendingPeers: MaxPendingPeers,
IPCFile: IPCFile,
log: log.New("package", "status-go/params.NodeConfig"),
LogFile: LogFile,
LogLevel: LogLevel,
LogToStderr: LogToStderr,
ClusterConfigFile: clstrCfgFile,
ClusterConfig: &ClusterConfig{
Enabled: true, // cluster must be enabled by default
NetworkID: networkID,
DataDir: dataDir,
Version: Version,
RPCEnabled: false,
HTTPHost: "localhost",
HTTPPort: 8545,
ListenAddr: ":0",
APIModules: "eth,net,web3,peer",
MaxPeers: 25,
MaxPendingPeers: 0,
IPCFile: "geth.ipc",
log: log.New("package", "status-go/params.NodeConfig"),
LogFile: "",
LogLevel: "ERROR",
UpstreamConfig: UpstreamRPCConfig{
URL: getUpstreamURL(networkID),
},
ClusterConfig: ClusterConfig{
Enabled: fleet != FleetUndefined,
Fleet: fleet,
StaticNodes: []string{},
BootNodes: []string{},
},
LightEthConfig: &LightEthConfig{
Enabled: true,
DatabaseCache: DatabaseCache,
LightEthConfig: LightEthConfig{
Enabled: false,
DatabaseCache: 16,
},
WhisperConfig: &WhisperConfig{
Enabled: true,
MinimumPoW: WhisperMinimumPoW,
TTL: WhisperTTL,
FirebaseConfig: &FirebaseConfig{
NotificationTriggerURL: FirebaseNotificationTriggerURL,
},
EnableNTPSync: true,
WhisperConfig: WhisperConfig{
Enabled: false,
MinimumPoW: WhisperMinimumPoW,
TTL: WhisperTTL,
EnableNTPSync: false,
},
SwarmConfig: &SwarmConfig{},
SwarmConfig: SwarmConfig{},
RegisterTopics: []discv5.Topic{},
RequireTopics: map[discv5.Topic]Limits{},
}
// adjust dependent values
if err := nodeConfig.updateConfig(); err != nil {
return nodeConfig, nil
}
// NewConfigFromJSON parses incoming JSON and returned it as Config
func NewConfigFromJSON(configJSON string) (*NodeConfig, error) {
nodeConfig, err := NewNodeConfig("", FleetUndefined, 0)
if err != nil {
return nil, err
}
if err := LoadConfigFromJSON(configJSON, nodeConfig); err != nil {
return nil, err
}
return nodeConfig, nil
}
// LoadNodeConfig parses incoming JSON and returned it as Config
func LoadNodeConfig(configJSON string) (*NodeConfig, error) {
nodeConfig, err := loadNodeConfig(configJSON)
if err != nil {
return nil, err
// LoadConfigFromJSON parses incoming JSON and returned it as Config
func LoadConfigFromJSON(configJSON string, nodeConfig *NodeConfig) error {
if err := loadNodeConfig(configJSON, nodeConfig); err != nil {
return err
}
if err := nodeConfig.Validate(); err != nil {
return nil, err
return err
}
return nodeConfig, nil
return nil
}
func loadNodeConfig(configJSON string) (*NodeConfig, error) {
nodeConfig, err := NewNodeConfig("", "", FleetUndefined, 0)
if err != nil {
return nil, err
}
func loadNodeConfig(configJSON string, nodeConfig *NodeConfig) error {
decoder := json.NewDecoder(strings.NewReader(configJSON))
// override default configuration with values by JSON input
if err := decoder.Decode(&nodeConfig); err != nil {
return nil, err
return err
}
// repopulate
if err := nodeConfig.updateConfig(); err != nil {
return nil, err
return nil
}
func loadConfigConfigFromFile(path string, config *NodeConfig) error {
jsonConfig, err := ioutil.ReadFile(path)
if err != nil {
return err
}
return nodeConfig, nil
if err = loadNodeConfig(string(jsonConfig), config); err != nil {
return err
}
return nil
}
// LoadConfigFromFiles reads the configuration files specified in configFilePaths,
// merging the values in order in the config argument
func LoadConfigFromFiles(configFilePaths []string, config *NodeConfig) error {
for _, path := range configFilePaths {
if err := loadConfigConfigFromFile(path, config); err != nil {
return err
}
}
return nil
}
// Validate checks if NodeConfig fields have valid values.
@ -473,33 +436,159 @@ func (c *NodeConfig) Validate() error {
return err
}
if c.ClusterConfig.Enabled {
if err := validate.Struct(c.ClusterConfig); err != nil {
return err
if c.NodeKey != "" {
if _, err := crypto.HexToECDSA(c.NodeKey); err != nil {
return fmt.Errorf("NodeKey is invalid (%s): %v", c.NodeKey, err)
}
}
if c.LightEthConfig.Enabled {
if err := validate.Struct(c.LightEthConfig); err != nil {
return err
}
if c.UpstreamConfig.Enabled && c.LightEthConfig.Enabled {
return fmt.Errorf("both UpstreamConfig and LightEthConfig are enabled, but they are mutually exclusive")
}
if c.WhisperConfig.Enabled {
if err := validate.Struct(c.WhisperConfig); err != nil {
return err
}
if err := c.validateChildStructs(validate); err != nil {
return err
}
if c.SwarmConfig.Enabled {
if err := validate.Struct(c.SwarmConfig); err != nil {
return err
if !c.NoDiscovery && len(c.ClusterConfig.BootNodes) == 0 {
// No point in running discovery if we don't have bootnodes.
// In case we do have bootnodes, NoDiscovery should be true.
return fmt.Errorf("NoDiscovery is false, but ClusterConfig.BootNodes is empty")
}
if len(c.ClusterConfig.RendezvousNodes) == 0 {
if c.Rendezvous {
return fmt.Errorf("Rendezvous is enabled, but ClusterConfig.RendezvousNodes is empty")
}
} else if !c.Rendezvous {
return fmt.Errorf("Rendezvous is disabled, but ClusterConfig.RendezvousNodes is not empty")
}
return nil
}
func (c *NodeConfig) validateChildStructs(validate *validator.Validate) error {
// Validate child structs
if err := c.UpstreamConfig.Validate(validate); err != nil {
return err
}
if err := c.ClusterConfig.Validate(validate); err != nil {
return err
}
if err := c.LightEthConfig.Validate(validate); err != nil {
return err
}
if err := c.WhisperConfig.Validate(validate); err != nil {
return err
}
if err := c.SwarmConfig.Validate(validate); err != nil {
return err
}
return nil
}
// Validate validates the UpstreamRPCConfig struct and returns an error if inconsistent values are found
func (c *UpstreamRPCConfig) Validate(validate *validator.Validate) error {
if !c.Enabled {
return nil
}
if err := validate.Struct(c); err != nil {
return err
}
if _, err := url.ParseRequestURI(c.URL); err != nil {
return fmt.Errorf("UpstreamRPCConfig.URL '%s' is invalid: %v", c.URL, err.Error())
}
return nil
}
// Validate validates the ClusterConfig struct and returns an error if inconsistent values are found
func (c *ClusterConfig) Validate(validate *validator.Validate) error {
if !c.Enabled {
return nil
}
if err := validate.Struct(c); err != nil {
return err
}
if c.Fleet == "" {
return fmt.Errorf("ClusterConfig.Fleet is empty")
}
return nil
}
// Validate validates the LightEthConfig struct and returns an error if inconsistent values are found
func (c *LightEthConfig) Validate(validate *validator.Validate) error {
if !c.Enabled {
return nil
}
if err := validate.Struct(c); err != nil {
return err
}
return nil
}
// Validate validates the WhisperConfig struct and returns an error if inconsistent values are found
func (c *WhisperConfig) Validate(validate *validator.Validate) error {
if !c.Enabled {
return nil
}
if err := validate.Struct(c); err != nil {
return err
}
if c.EnableMailServer {
if c.DataDir == "" {
return fmt.Errorf("WhisperConfig.DataDir must be specified when WhisperConfig.EnableMailServer is true")
}
if c.MailServerPassword == "" && c.MailServerAsymKey == "" {
return fmt.Errorf("WhisperConfig.MailServerPassword or WhisperConfig.MailServerAsymKey must be specified when WhisperConfig.EnableMailServer is true")
}
if c.MailServerAsymKey != "" {
if _, err := crypto.HexToECDSA(c.MailServerAsymKey); err != nil {
return fmt.Errorf("WhisperConfig.MailServerAsymKey is invalid: %s", c.MailServerAsymKey)
}
}
}
return nil
}
// Validate validates the SwarmConfig struct and returns an error if inconsistent values are found
func (c *SwarmConfig) Validate(validate *validator.Validate) error {
if !c.Enabled {
return nil
}
if err := validate.Struct(c); err != nil {
return err
}
return nil
}
func getUpstreamURL(networkID uint64) string {
switch networkID {
case MainNetworkID:
return MainnetEthereumNetworkURL
case RopstenNetworkID:
return RopstenEthereumNetworkURL
case RinkebyNetworkID:
return RinkebyEthereumNetworkURL
}
return ""
}
// Save dumps configuration to the disk
func (c *NodeConfig) Save() error {
data, err := json.MarshalIndent(c, "", " ")
@ -520,176 +609,6 @@ func (c *NodeConfig) Save() error {
return nil
}
// updateConfig traverses configuration and adjusts dependent fields
// (we have a development/production and mobile/full node dependent configurations)
func (c *NodeConfig) updateConfig() error {
// Update separate configurations.
if err := c.updateGenesisConfig(); err != nil {
return err
}
if err := c.updateUpstreamConfig(); err != nil {
return err
}
if err := c.updateClusterConfig(); err != nil {
return err
}
c.updatePeerLimits()
return c.updateRelativeDirsConfig()
}
// updateGenesisConfig does necessary adjustments to config object (depending on network node will be running on)
func (c *NodeConfig) updateGenesisConfig() error {
var genesis *core.Genesis
switch c.NetworkID {
case MainNetworkID:
genesis = core.DefaultGenesisBlock()
case RopstenNetworkID:
genesis = core.DefaultTestnetGenesisBlock()
case RinkebyNetworkID:
genesis = core.DefaultRinkebyGenesisBlock()
case StatusChainNetworkID:
var err error
genesis, err = c.DefaultStatusChainGenesisBlock()
if err != nil {
return err
}
default:
return nil
}
// encode the genesis into JSON
enc, err := json.Marshal(genesis)
if err != nil {
return err
}
c.LightEthConfig.Genesis = string(enc)
return nil
}
// DefaultStatusChainGenesisBlock returns the StatusChain network genesis block.
func (c *NodeConfig) DefaultStatusChainGenesisBlock() (*core.Genesis, error) {
genesisJSON, err := ioutil.ReadFile(path.Join(GetStatusHome(), "static/config/status-chain-genesis.json"))
if err != nil {
return nil, fmt.Errorf("status-chain-genesis.json could not be loaded: %s", err)
}
var genesis *core.Genesis
err = json.Unmarshal(genesisJSON, &genesis)
if err != nil {
return nil, fmt.Errorf("cannot unmarshal status-chain-genesis.json: %s", err)
}
return genesis, nil
}
// updateUpstreamConfig sets the proper UpstreamConfig.URL for the network id being used.
func (c *NodeConfig) updateUpstreamConfig() error {
// If we have a URL already set then keep URL incase
// of custom server.
if c.UpstreamConfig.URL != "" {
return nil
}
switch c.NetworkID {
case MainNetworkID:
c.UpstreamConfig.URL = MainnetEthereumNetworkURL
case RopstenNetworkID:
c.UpstreamConfig.URL = RopstenEthereumNetworkURL
case RinkebyNetworkID:
c.UpstreamConfig.URL = RinkebyEthereumNetworkURL
}
return nil
}
// updateClusterConfig loads static peer nodes and CHT for a given network and mode.
// This is necessary until we have LES protocol support CHT sync, and better node
// discovery on mobile devices)
func (c *NodeConfig) updateClusterConfig() error {
if !c.ClusterConfig.Enabled {
return nil
}
c.log.Info("update cluster config", "configFile", c.ClusterConfigFile, "fleet", c.ClusterConfig.Fleet)
var cluster Cluster
if c.ClusterConfigFile != "" {
// Load cluster configuration from external file.
configFile, err := ioutil.ReadFile(c.ClusterConfigFile)
if err != nil {
return fmt.Errorf("cluster configuration file '%s' could not be loaded: %s", c.ClusterConfigFile, err)
}
err = json.Unmarshal(configFile, &cluster)
if err != nil {
return fmt.Errorf("failed to unmarshal cluster configuration file: %s", err)
}
} else {
cluster, _ = ClusterForFleet(c.ClusterConfig.Fleet)
}
// allow to override bootnodes only if they were not defined earlier
if len(c.ClusterConfig.BootNodes) == 0 {
c.ClusterConfig.BootNodes = cluster.BootNodes
}
// allow to override static nodes only if they were not defined earlier
if len(c.ClusterConfig.StaticNodes) == 0 {
c.ClusterConfig.StaticNodes = cluster.StaticNodes
}
// No point in running discovery if we don't have bootnodes.
// In a case when we do have bootnodes, NoDiscovery=true is preserved.
if len(cluster.BootNodes) == 0 {
c.NoDiscovery = true
}
if len(c.ClusterConfig.RendezvousNodes) == 0 {
c.ClusterConfig.RendezvousNodes = cluster.RendezvousNodes
}
if len(c.ClusterConfig.RendezvousNodes) != 0 {
c.Rendezvous = true
}
c.ClusterConfig.TrustedMailServers = cluster.MailServers
return nil
}
// updateRelativeDirsConfig updates directories that should be wrt to DataDir
func (c *NodeConfig) updateRelativeDirsConfig() error {
makeSubDirPath := func(baseDir, subDir string) string {
if len(baseDir) == 0 {
return ""
}
return filepath.Join(baseDir, subDir)
}
if len(c.KeyStoreDir) == 0 {
c.KeyStoreDir = makeSubDirPath(c.DataDir, KeyStoreDir)
}
if len(c.WhisperConfig.DataDir) == 0 {
c.WhisperConfig.DataDir = makeSubDirPath(c.DataDir, WhisperDataDir)
}
return nil
}
// updatePeerLimits will set default peer limits expectations based on enabled services.
func (c *NodeConfig) updatePeerLimits() {
if c.NoDiscovery && !c.Rendezvous {
return
}
if c.WhisperConfig.Enabled {
c.RequireTopics[WhisperDiscv5Topic] = WhisperDiscv5Limits
// TODO(dshulyak) register mailserver limits when we will change how they are handled.
}
if c.LightEthConfig.Enabled {
c.RequireTopics[discv5.Topic(LesTopic(int(c.NetworkID)))] = LesDiscoveryLimits
}
}
// String dumps config object as nicely indented JSON
func (c *NodeConfig) String() string {
data, _ := json.MarshalIndent(c, "", " ")

View File

@ -5,32 +5,82 @@ import (
"fmt"
"io/ioutil"
"os"
"path"
"path/filepath"
"testing"
"gopkg.in/go-playground/validator.v9"
"github.com/ethereum/go-ethereum/core"
gethparams "github.com/ethereum/go-ethereum/params"
"github.com/status-im/status-go/params"
"github.com/status-im/status-go/t/utils"
"github.com/stretchr/testify/require"
)
var clusterConfigData = []byte(`{
"staticnodes": [
"enode://7ab298cedc4185a894d21d8a4615262ec6bdce66c9b6783878258e0d5b31013d30c9038932432f70e5b2b6a5cd323bf820554fcb22fbc7b45367889522e9c449@10.1.1.1:30303",
"enode://f59e8701f18c79c5cbc7618dc7bb928d44dc2f5405c7d693dad97da2d8585975942ec6fd36d3fe608bfdc7270a34a4dd00f38cfe96b2baa24f7cd0ac28d382a1@10.1.1.2:30303"
]
"ClusterConfig": {
"staticnodes": [
"enode://7ab298cedc4185a894d21d8a4615262ec6bdce66c9b6783878258e0d5b31013d30c9038932432f70e5b2b6a5cd323bf820554fcb22fbc7b45367889522e9c449@10.1.1.1:30303",
"enode://f59e8701f18c79c5cbc7618dc7bb928d44dc2f5405c7d693dad97da2d8585975942ec6fd36d3fe608bfdc7270a34a4dd00f38cfe96b2baa24f7cd0ac28d382a1@10.1.1.2:30303"
]
}
}`)
func TestLoadNodeConfigFromNonExistingFile(t *testing.T) {
_, err := params.LoadNodeConfig(`{
"NetworkId": 3,
"DataDir": "/tmp/statusgo",
"ClusterConfigFile": "/file/does/not.exist"
}`)
require.Error(t, err)
require.Contains(t, err.Error(), "no such file or directory")
var clusters = map[string]func() params.Cluster{
params.FleetStaging: func() params.Cluster {
return params.Cluster{
BootNodes: []string{
"enode://10a78c17929a7019ef4aa2249d7302f76ae8a06f40b2dc88b7b31ebff4a623fbb44b4a627acba296c1ced3775d91fbe18463c15097a6a36fdb2c804ff3fc5b35@35.238.97.234:30404", // boot-01.gc-us-central1-a.eth.staging
"enode://f79fb3919f72ca560ad0434dcc387abfe41e0666201ebdada8ede0462454a13deb05cda15f287d2c4bd85da81f0eb25d0a486bbbc8df427b971ac51533bd00fe@174.138.107.239:30404", // boot-01.do-ams3.eth.staging
},
StaticNodes: []string{
"enode://914c0b30f27bab30c1dfd31dad7652a46fda9370542aee1b062498b1345ee0913614b8b9e3e84622e84a7203c5858ae1d9819f63aece13ee668e4f6668063989@167.99.19.148:30305", // node-01.do-ams3.eth.staging
"enode://2d897c6e846949f9dcf10279f00e9b8325c18fe7fa52d658520ad7be9607c83008b42b06aefd97cfe1fdab571f33a2a9383ff97c5909ed51f63300834913237e@35.192.0.86:30305", // "node-01.gc-us-central1-a.eth.staging"
},
MailServers: []string{
"enode://69f72baa7f1722d111a8c9c68c39a31430e9d567695f6108f31ccb6cd8f0adff4991e7fdca8fa770e75bc8a511a87d24690cbc80e008175f40c157d6f6788d48@206.189.240.16:30504", // mail-01.do-ams3.eth.staging
"enode://e4fc10c1f65c8aed83ac26bc1bfb21a45cc1a8550a58077c8d2de2a0e0cd18e40fd40f7e6f7d02dc6cd06982b014ce88d6e468725ffe2c138e958788d0002a7f@35.239.193.41:30504", // mail-01.gc-us-central1-a.eth.staging
},
RendezvousNodes: []string{
"/ip4/174.138.107.239/tcp/30703/ethv4/16Uiu2HAkyJHeetQ4DNpd4NZ2ntzxMo25zcdpvGQRqkD5pB9BE6RU",
"/ip4/35.238.97.234/tcp/30703/ethv4/16Uiu2HAm1sVyXmkMNjdeDWqK2urbyC3oBHi8MDpCdYkns1nYafqz",
},
}
},
params.FleetBeta: func() params.Cluster {
return params.Cluster{
BootNodes: []string{
"enode://436cc6f674928fdc9a9f7990f2944002b685d1c37f025c1be425185b5b1f0900feaf1ccc2a6130268f9901be4a7d252f37302c8335a2c1a62736e9232691cc3a@174.138.105.243:30404", // boot-01.do-ams3.eth.beta
"enode://5395aab7833f1ecb671b59bf0521cf20224fe8162fc3d2675de4ee4d5636a75ec32d13268fc184df8d1ddfa803943906882da62a4df42d4fccf6d17808156a87@206.189.243.57:30404", // boot-02.do-ams3.eth.beta
"enode://7427dfe38bd4cf7c58bb96417806fab25782ec3e6046a8053370022cbaa281536e8d64ecd1b02e1f8f72768e295d06258ba43d88304db068e6f2417ae8bcb9a6@104.154.88.123:30404", // boot-01.gc-us-central1-a.eth.beta
"enode://ebefab39b69bbbe64d8cd86be765b3be356d8c4b24660f65d493143a0c44f38c85a257300178f7845592a1b0332811542e9a58281c835babdd7535babb64efc1@35.202.99.224:30404", // boot-02.gc-us-central1-a.eth.beta
},
StaticNodes: []string{
"enode://a6a2a9b3a7cbb0a15da74301537ebba549c990e3325ae78e1272a19a3ace150d03c184b8ac86cc33f1f2f63691e467d49308f02d613277754c4dccd6773b95e8@206.189.243.176:30304", // node-01.do-ams3.eth.beta
"enode://207e53d9bf66be7441e3daba36f53bfbda0b6099dba9a865afc6260a2d253fb8a56a72a48598a4f7ba271792c2e4a8e1a43aaef7f34857f520c8c820f63b44c8@35.224.15.65:30304", // node-01.gc-us-central1-a.eth.beta
},
MailServers: []string{
"enode://c42f368a23fa98ee546fd247220759062323249ef657d26d357a777443aec04db1b29a3a22ef3e7c548e18493ddaf51a31b0aed6079bd6ebe5ae838fcfaf3a49@206.189.243.162:30504", // mail-01.do-ams3.eth.beta
"enode://7aa648d6e855950b2e3d3bf220c496e0cae4adfddef3e1e6062e6b177aec93bc6cdcf1282cb40d1656932ebfdd565729da440368d7c4da7dbd4d004b1ac02bf8@206.189.243.169:30504", // mail-02.do-ams3.eth.beta
"enode://8a64b3c349a2e0ef4a32ea49609ed6eb3364be1110253c20adc17a3cebbc39a219e5d3e13b151c0eee5d8e0f9a8ba2cd026014e67b41a4ab7d1d5dd67ca27427@206.189.243.168:30504", // mail-03.do-ams3.eth.beta
"enode://7de99e4cb1b3523bd26ca212369540646607c721ad4f3e5c821ed9148150ce6ce2e72631723002210fac1fd52dfa8bbdf3555e05379af79515e1179da37cc3db@35.188.19.210:30504", // mail-01.gc-us-central1-a.eth.beta
"enode://015e22f6cd2b44c8a51bd7a23555e271e0759c7d7f52432719665a74966f2da456d28e154e836bee6092b4d686fe67e331655586c57b718be3997c1629d24167@35.226.21.19:30504", // mail-02.gc-us-central1-a.eth.beta
"enode://531e252ec966b7e83f5538c19bf1cde7381cc7949026a6e499b6e998e695751aadf26d4c98d5a4eabfb7cefd31c3c88d600a775f14ed5781520a88ecd25da3c6@35.225.227.79:30504", // mail-03.gc-us-central1-a.eth.beta
},
RendezvousNodes: []string{
"/ip4/174.138.105.243/tcp/30703/ethv4/16Uiu2HAmRHPzF3rQg55PgYPcQkyvPVH9n2hWsYPhUJBZ6kVjJgdV", // boot-01.do-ams3.eth.beta
"/ip4/206.189.243.57/tcp/30703/ethv4/16Uiu2HAmLqTXuY4Sb6G28HNooaFUXUKzpzKXCcgyJxgaEE2i5vnf", // boot-02.do-ams3.eth.beta
},
}
},
}
// ClusterForFleet returns a cluster for a given fleet.
func ClusterForFleet(fleet string) (params.Cluster, bool) {
cluster, ok := clusters[fleet]
if ok {
return cluster(), true
}
return params.Cluster{}, false
}
func TestLoadNodeConfigFromFile(t *testing.T) {
@ -44,14 +94,21 @@ func TestLoadNodeConfigFromFile(t *testing.T) {
clusterFile := filepath.Join(tmpDir, "cluster.json")
err = ioutil.WriteFile(clusterFile, clusterConfigData, os.ModePerm)
require.NoError(t, err)
defer os.Remove(clusterFile)
c, err := params.LoadNodeConfig(`{
c, err := params.NewConfigFromJSON(`{
"NetworkId": 3,
"DataDir": "` + tmpDir + `",
"ClusterConfigFile": "` + clusterFile + `"
"KeyStoreDir": "` + tmpDir + `",
"NoDiscovery": true
}`)
require.NoError(t, err)
require.True(t, c.ClusterConfig.Enabled)
err = params.LoadConfigFromFiles([]string{clusterFile}, c)
require.NoError(t, err)
require.Equal(t, uint64(3), c.NetworkID)
require.Equal(t, tmpDir, c.DataDir)
require.Equal(t, tmpDir, c.KeyStoreDir)
require.False(t, c.ClusterConfig.Enabled)
require.Len(t, c.ClusterConfig.StaticNodes, 2)
}
@ -72,21 +129,16 @@ func TestGenerateAndLoadNodeConfig(t *testing.T) {
Validate func(t *testing.T, dataDir string, c *params.NodeConfig)
}{
{
Name: "default KeyStoreDir",
Update: func(config *params.NodeConfig) {},
Name: "DataDir and KeyStoreDir specified",
Update: func(c *params.NodeConfig) {},
Validate: func(t *testing.T, dataDir string, c *params.NodeConfig) {
require.Equal(t, dataDir, c.DataDir)
keyStoreDir := filepath.Join(dataDir, params.KeyStoreDir)
require.Equal(t, keyStoreDir, c.KeyStoreDir)
},
},
{
Name: "non-default KeyStoreDir",
Update: func(c *params.NodeConfig) {
c.KeyStoreDir = "/foo/bar"
},
Validate: func(t *testing.T, dataDir string, c *params.NodeConfig) {
require.Equal(t, "/foo/bar", c.KeyStoreDir)
require.Equal(t, tmpDir, c.DataDir)
require.Equal(t, tmpDir, c.KeyStoreDir)
require.False(t, c.UpstreamConfig.Enabled)
require.Equal(t, c.ClusterConfig.Fleet != params.FleetUndefined, c.ClusterConfig.Enabled)
require.True(t, c.WhisperConfig.Enabled)
require.False(t, c.LightEthConfig.Enabled)
require.False(t, c.SwarmConfig.Enabled)
},
},
{
@ -117,32 +169,38 @@ func TestGenerateAndLoadNodeConfig(t *testing.T) {
{
Name: "loading LES config",
NetworkID: params.MainNetworkID,
Update: func(c *params.NodeConfig) {},
Update: func(c *params.NodeConfig) {
c.LightEthConfig.Enabled = true
},
Validate: func(t *testing.T, dataDir string, c *params.NodeConfig) {
var genesis core.Genesis
err := json.Unmarshal([]byte(c.LightEthConfig.Genesis), &genesis)
require.NoError(t, err)
require.Zero(t, genesis.Config.ChainID.Cmp(gethparams.MainnetChainConfig.ChainID))
require.Zero(t, genesis.Config.HomesteadBlock.Cmp(gethparams.MainnetChainConfig.HomesteadBlock))
require.Zero(t, genesis.Config.EIP150Block.Cmp(gethparams.MainnetChainConfig.EIP150Block))
require.Zero(t, genesis.Config.EIP155Block.Cmp(gethparams.MainnetChainConfig.EIP155Block))
require.Zero(t, genesis.Config.EIP158Block.Cmp(gethparams.MainnetChainConfig.EIP158Block))
require.True(t, c.LightEthConfig.Enabled)
},
},
{
Name: "cluster nodes setup",
Update: func(c *params.NodeConfig) {},
Name: "cluster nodes setup",
Update: func(c *params.NodeConfig) {
cc, _ := ClusterForFleet(c.ClusterConfig.Fleet)
c.Rendezvous = true
c.ClusterConfig.BootNodes = cc.BootNodes
c.ClusterConfig.StaticNodes = cc.StaticNodes
c.ClusterConfig.RendezvousNodes = cc.RendezvousNodes
c.ClusterConfig.TrustedMailServers = cc.MailServers
c.ClusterConfig.Enabled = true
c.RequireTopics[params.WhisperDiscv5Topic] = params.WhisperDiscv5Limits
},
Validate: func(t *testing.T, dataDir string, c *params.NodeConfig) {
require.True(t, c.Rendezvous)
require.True(t, c.ClusterConfig.Enabled)
require.NotEmpty(t, c.ClusterConfig.BootNodes)
require.NotEmpty(t, c.ClusterConfig.StaticNodes)
require.NotEmpty(t, c.ClusterConfig.TrustedMailServers)
require.Equal(t, params.WhisperDiscv5Limits, c.RequireTopics[params.WhisperDiscv5Topic])
},
},
{
Name: "custom bootnodes",
Update: func(c *params.NodeConfig) {
c.ClusterConfig.Enabled = true
c.ClusterConfig.BootNodes = []string{"a", "b", "c"}
},
Validate: func(t *testing.T, dataDir string, c *params.NodeConfig) {
@ -151,17 +209,22 @@ func TestGenerateAndLoadNodeConfig(t *testing.T) {
},
},
{
Name: "disabled ClusterConfiguration",
Name: "disabled Cluster configuration",
Update: func(c *params.NodeConfig) {
c.ClusterConfig.Enabled = false
c.ClusterConfig.BootNodes = []string{"a", "b", "c"}
},
Validate: func(t *testing.T, dataDir string, c *params.NodeConfig) {
require.False(t, c.ClusterConfig.Enabled)
},
},
{
Name: "peers discovery and topics",
Update: func(c *params.NodeConfig) {},
Name: "peers discovery and topics",
Update: func(c *params.NodeConfig) {
c.NoDiscovery = false
c.ClusterConfig.BootNodes = []string{"a", "b", "c"}
c.RequireTopics[params.WhisperDiscv5Topic] = params.Limits{2, 2}
},
Validate: func(t *testing.T, dataDir string, c *params.NodeConfig) {
require.NotNil(t, c.RequireTopics)
require.False(t, c.NoDiscovery)
@ -179,13 +242,18 @@ func TestGenerateAndLoadNodeConfig(t *testing.T) {
},
},
{
Name: "staging fleet",
Fleet: params.FleetStaging,
Update: func(c *params.NodeConfig) {},
Name: "staging fleet",
Fleet: params.FleetStaging,
Update: func(c *params.NodeConfig) {
c.ClusterConfig.Enabled = true
c.ClusterConfig.Fleet = "eth.staging"
cc, _ := ClusterForFleet(c.ClusterConfig.Fleet)
c.ClusterConfig.BootNodes = cc.BootNodes
},
Validate: func(t *testing.T, dataDir string, c *params.NodeConfig) {
staging, ok := params.ClusterForFleet("eth.staging")
staging, ok := ClusterForFleet("eth.staging")
require.True(t, ok)
beta, ok := params.ClusterForFleet("eth.beta")
beta, ok := ClusterForFleet("eth.beta")
require.True(t, ok)
require.NotEqual(t, staging, beta)
@ -198,9 +266,13 @@ func TestGenerateAndLoadNodeConfig(t *testing.T) {
{
Name: "Whisper light client",
Update: func(c *params.NodeConfig) {
c.WhisperConfig.Enabled = true
c.WhisperConfig.DataDir = path.Join(tmpDir, "wnode")
c.WhisperConfig.LightClient = true
},
Validate: func(t *testing.T, dataDir string, c *params.NodeConfig) {
require.Equal(t, path.Join(tmpDir, "wnode"), c.WhisperConfig.DataDir)
require.True(t, c.WhisperConfig.Enabled)
require.True(t, c.WhisperConfig.LightClient)
},
},
@ -221,9 +293,9 @@ func TestGenerateAndLoadNodeConfig(t *testing.T) {
for _, networkID := range networks {
name := fmt.Sprintf("%s_%s_%d", tc.Name, fleet, networkID)
t.Run(name, func(t *testing.T) {
// Corresponds to GenerateConfig() binding.
config, err := params.NewNodeConfig(tmpDir, "", fleet, uint64(networkID))
config, err := utils.MakeTestNodeConfigWithDataDir("", tmpDir, fleet, uint64(networkID))
require.NoError(t, err)
config.KeyStoreDir = tmpDir
// Corresponds to config update in status-react.
tc.Update(config)
@ -231,7 +303,7 @@ func TestGenerateAndLoadNodeConfig(t *testing.T) {
require.NoError(t, err)
// Corresponds to starting node and loading config from JSON blob.
loadedConfig, err := params.LoadNodeConfig(string(configBytes))
loadedConfig, err := params.NewConfigFromJSON(string(configBytes))
require.NoError(t, err)
tc.Validate(t, tmpDir, loadedConfig)
})
@ -245,7 +317,7 @@ func TestConfigWriteRead(t *testing.T) {
require.Nil(t, err)
defer os.RemoveAll(tmpDir) // nolint: errcheck
nodeConfig, err := params.NewNodeConfig(tmpDir, "", params.FleetBeta, params.RopstenNetworkID)
nodeConfig, err := utils.MakeTestNodeConfigWithDataDir("", tmpDir, params.FleetBeta, params.RopstenNetworkID)
require.Nil(t, err, "cannot create new config object")
err = nodeConfig.Save()
@ -253,8 +325,10 @@ func TestConfigWriteRead(t *testing.T) {
loadedConfigData, err := ioutil.ReadFile(filepath.Join(nodeConfig.DataDir, "config.json"))
require.Nil(t, err, "cannot read configuration from disk")
require.Contains(t, string(loadedConfigData), fmt.Sprintf(`"NetworkId": %d`, params.RopstenNetworkID))
require.Contains(t, string(loadedConfigData), fmt.Sprintf(`"DataDir": "%s"`, tmpDir))
loadedConfig := string(loadedConfigData)
require.Contains(t, loadedConfig, fmt.Sprintf(`"NetworkId": %d`, params.RopstenNetworkID))
require.Contains(t, loadedConfig, fmt.Sprintf(`"DataDir": "%s"`, tmpDir))
require.Contains(t, loadedConfig, fmt.Sprintf(`"Fleet": "%s"`, params.FleetBeta))
}
// TestNodeConfigValidate checks validation of individual fields.
@ -264,55 +338,250 @@ func TestNodeConfigValidate(t *testing.T) {
Config string
Error string
FieldErrors map[string]string // map[Field]Tag
CheckFunc func(*testing.T, *params.NodeConfig)
}{
{
Name: "Valid JSON config",
Config: `{
"NetworkId": 1,
"DataDir": "/tmp/data"
"DataDir": "/tmp/data",
"KeyStoreDir": "/tmp/data",
"NoDiscovery": true
}`,
Error: "",
FieldErrors: nil,
},
{
Name: "Invalid JSON config",
Config: `{"NetworkId": }`,
Error: "invalid character '}'",
FieldErrors: nil,
Name: "Invalid JSON config",
Config: `{"NetworkId": }`,
Error: "invalid character '}'",
},
{
Name: "Invalid field type",
Config: `{"NetworkId": "abc"}`,
Error: "json: cannot unmarshal string into Go struct field",
FieldErrors: nil,
Name: "Invalid field type",
Config: `{"NetworkId": "abc"}`,
Error: "json: cannot unmarshal string into Go struct field",
},
{
Name: "Validate all required fields",
Config: `{}`,
Error: "",
FieldErrors: map[string]string{
"NetworkID": "required",
"DataDir": "required",
"NetworkID": "required",
"DataDir": "required",
"KeyStoreDir": "required",
},
},
{
Name: "Validate Name does not contain slash",
Name: "Validate that Name does not contain slash",
Config: `{
"NetworkId": 1,
"DataDir": "/some/dir",
"KeyStoreDir": "/some/dir",
"Name": "invalid/name"
}`,
Error: "",
FieldErrors: map[string]string{
"Name": "excludes",
},
},
{
Name: "Validate that NodeKey is checked for validity",
Config: `{
"NetworkId": 1,
"DataDir": "/some/dir",
"KeyStoreDir": "/some/dir",
"NoDiscovery": true,
"NodeKey": "foo"
}`,
Error: "NodeKey is invalid",
},
{
Name: "Validate that UpstreamConfig.URL is validated if UpstreamConfig is enabled",
Config: `{
"NetworkId": 1,
"DataDir": "/some/dir",
"KeyStoreDir": "/some/dir",
"NoDiscovery": true,
"UpstreamConfig": {
"Enabled": true,
"URL": "[bad.url]"
}
}`,
Error: "'[bad.url]' is invalid",
},
{
Name: "Validate that UpstreamConfig.URL is not validated if UpstreamConfig is disabled",
Config: `{
"NetworkId": 1,
"DataDir": "/some/dir",
"KeyStoreDir": "/some/dir",
"NoDiscovery": true,
"UpstreamConfig": {
"Enabled": false,
"URL": "[bad.url]"
}
}`,
},
{
Name: "Validate that UpstreamConfig.URL validation passes if UpstreamConfig.URL is valid",
Config: `{
"NetworkId": 1,
"DataDir": "/some/dir",
"KeyStoreDir": "/some/dir",
"NoDiscovery": true,
"UpstreamConfig": {
"Enabled": true,
"URL": "` + params.MainnetEthereumNetworkURL + `"
}
}`,
},
{
Name: "Validate that ClusterConfig.Fleet is verified to not be empty if ClusterConfig is enabled",
Config: `{
"NetworkId": 1,
"DataDir": "/some/dir",
"KeyStoreDir": "/some/dir",
"NoDiscovery": true,
"ClusterConfig": {
"Enabled": true
}
}`,
Error: "ClusterConfig.Fleet is empty",
},
{
Name: "Validate that ClusterConfig.BootNodes is verified to not be empty if discovery is disabled",
Config: `{
"NetworkId": 1,
"DataDir": "/some/dir",
"KeyStoreDir": "/some/dir",
"NoDiscovery": false
}`,
Error: "NoDiscovery is false, but ClusterConfig.BootNodes is empty",
},
{
Name: "Validate that ClusterConfig.RendezvousNodes is verified to be empty if Rendezvous is disabled",
Config: `{
"NetworkId": 1,
"DataDir": "/some/dir",
"KeyStoreDir": "/some/dir",
"NoDiscovery": true,
"Rendezvous": true
}`,
Error: "Rendezvous is enabled, but ClusterConfig.RendezvousNodes is empty",
},
{
Name: "Validate that ClusterConfig.RendezvousNodes is verified to contain nodes if Rendezvous is enabled",
Config: `{
"NetworkId": 1,
"DataDir": "/some/dir",
"KeyStoreDir": "/some/dir",
"NoDiscovery": true,
"Rendezvous": false,
"ClusterConfig": {
"RendezvousNodes": ["a"]
}
}`,
Error: "Rendezvous is disabled, but ClusterConfig.RendezvousNodes is not empty",
},
{
Name: "Validate that WhisperConfig.DataDir is checked to not be empty if mailserver is enabled",
Config: `{
"NetworkId": 1,
"DataDir": "/some/dir",
"KeyStoreDir": "/some/dir",
"NoDiscovery": true,
"WhisperConfig": {
"Enabled": true,
"EnableMailServer": true,
"MailserverPassword": "foo"
}
}`,
Error: "WhisperConfig.DataDir must be specified when WhisperConfig.EnableMailServer is true",
},
{
Name: "Validate that check for WhisperConfig.DataDir passes if it is not empty and mailserver is enabled",
Config: `{
"NetworkId": 1,
"DataDir": "/some/dir",
"KeyStoreDir": "/some/dir",
"NoDiscovery": true,
"WhisperConfig": {
"Enabled": true,
"EnableMailServer": true,
"DataDir": "/foo",
"MailserverPassword": "foo"
}
}`,
CheckFunc: func(t *testing.T, config *params.NodeConfig) {
require.Equal(t, "foo", config.WhisperConfig.MailServerPassword)
},
},
{
Name: "Validate that WhisperConfig.DataDir is checked to not be empty if mailserver is enabled",
Config: `{
"NetworkId": 1,
"DataDir": "/some/dir",
"KeyStoreDir": "/some/dir",
"NoDiscovery": true,
"WhisperConfig": {
"Enabled": true,
"EnableMailServer": true,
"MailserverPassword": "foo"
}
}`,
Error: "WhisperConfig.DataDir must be specified when WhisperConfig.EnableMailServer is true",
},
{
Name: "Validate that WhisperConfig.MailserverPassword and WhisperConfig.MailServerAsymKey are checked to not be empty if mailserver is enabled",
Config: `{
"NetworkId": 1,
"DataDir": "/some/dir",
"KeyStoreDir": "/some/dir",
"NoDiscovery": true,
"WhisperConfig": {
"Enabled": true,
"EnableMailServer": true,
"DataDir": "/foo"
}
}`,
Error: "WhisperConfig.MailServerPassword or WhisperConfig.MailServerAsymKey must be specified when WhisperConfig.EnableMailServer is true",
},
{
Name: "Validate that WhisperConfig.MailServerAsymKey is checked to not be empty if mailserver is enabled",
Config: `{
"NetworkId": 1,
"DataDir": "/some/dir",
"KeyStoreDir": "/some/dir",
"NoDiscovery": true,
"WhisperConfig": {
"Enabled": true,
"EnableMailServer": true,
"DataDir": "/foo",
"MailServerAsymKey": "06c365919f1fc8e13ff79a84f1dd14b7e45b869aa5fc0e34940481ee20d32f90"
}
}`,
CheckFunc: func(t *testing.T, config *params.NodeConfig) {
require.Equal(t, "06c365919f1fc8e13ff79a84f1dd14b7e45b869aa5fc0e34940481ee20d32f90", config.WhisperConfig.MailServerAsymKey)
},
},
{
Name: "Validate that WhisperConfig.MailServerAsymKey is checked for validity",
Config: `{
"NetworkId": 1,
"DataDir": "/some/dir",
"KeyStoreDir": "/some/dir",
"NoDiscovery": true,
"WhisperConfig": {
"Enabled": true,
"EnableMailServer": true,
"DataDir": "/foo",
"MailServerAsymKey": "bar"
}
}`,
Error: "WhisperConfig.MailServerAsymKey is invalid",
},
}
for _, tc := range testCases {
t.Logf("Test Case %s", tc.Name)
_, err := params.LoadNodeConfig(tc.Config)
config, err := params.NewConfigFromJSON(tc.Config)
switch err := err.(type) {
case validator.ValidationErrors:
@ -321,10 +590,19 @@ func TestNodeConfigValidate(t *testing.T) {
require.Equal(t, tc.FieldErrors[ve.Field()], ve.Tag())
}
case error:
require.Contains(t, err.Error(), tc.Error)
if tc.Error == "" {
require.NoError(t, err)
} else {
require.Contains(t, err.Error(), tc.Error)
}
case nil:
require.Empty(t, tc.Error)
if tc.Error != "" {
require.Error(t, err, "Error should be '%v'", tc.Error)
}
require.Nil(t, tc.FieldErrors)
if tc.CheckFunc != nil {
tc.CheckFunc(t, config)
}
}
}
}

View File

@ -3,38 +3,9 @@ package params
import "github.com/ethereum/go-ethereum/p2p/discv5"
const (
// ClientIdentifier is client identifier to advertise over the network
ClientIdentifier = "StatusIM"
// DataDir is default data directory used by statusd executable
DataDir = "statusd-data"
// StatusDatabase path relative to DataDir.
StatusDatabase = "status-db"
// KeyStoreDir is default directory where private keys are stored, relative to DataDir
KeyStoreDir = "keystore"
// IPCFile is filename of exposed IPC RPC Server
IPCFile = "geth.ipc"
// RPCEnabledDefault is the default state of whether the http rpc server is supposed
// to be started along with a node.
RPCEnabledDefault = false
// HTTPHost is host interface for the HTTP RPC server
HTTPHost = "localhost"
// HTTPPort is HTTP RPC port (replaced in unit tests)
HTTPPort = 8545
// ListenAddr is an IP address and port of this node (e.g. 127.0.0.1:30303).
ListenAddr = ":0"
// APIModules is a list of modules to expose via HTTP and `CallRPC()` binding.
// We also expose all handlers registered with `rpc.Client.RegisterHandler` to `CallRPC()` binding.
APIModules = "eth,net,web3,peer"
// SendTransactionMethodName defines the name for a giving transaction.
SendTransactionMethodName = "eth_sendTransaction"
@ -47,47 +18,15 @@ const (
// PersonalRecoverMethodName defines the name for `personal.recover` API.
PersonalRecoverMethodName = "personal_ecRecover"
// MaxPeers is the maximum number of global peers
MaxPeers = 25
// MaxPendingPeers is the maximum number of peers that can be pending in the
// handshake phase, counted separately for inbound and outbound connections.
MaxPendingPeers = 0
// DefaultGas default amount of gas used for transactions
DefaultGas = 180000
// DefaultFileDescriptorLimit is fd limit that database can use
DefaultFileDescriptorLimit = uint64(2048)
// DatabaseCache is memory (in MBs) allocated to internal caching (min 16MB / database forced)
DatabaseCache = 16
// LogFile defines where to write logs to
LogFile = ""
// LogLevel defines the minimum log level to report
LogLevel = "ERROR"
// LogLevelSuccinct defines the log level when only errors are reported.
// Useful when the default INFO level becomes too verbose.
LogLevelSuccinct = "ERROR"
// LogToStderr defines whether logged info should also be output to os.Stderr
LogToStderr = true
// WhisperDataDir is directory where Whisper data is stored, relative to DataDir
WhisperDataDir = "wnode"
// WhisperMinimumPoW amount of work for Whisper message to be added to sending queue
WhisperMinimumPoW = 0.001
// WhisperTTL is time to live for messages, in seconds
WhisperTTL = 120
// FirebaseNotificationTriggerURL is URL where FCM notification requests are sent to
FirebaseNotificationTriggerURL = "https://fcm.googleapis.com/fcm/send"
// MainnetEthereumNetworkURL is URL where the upstream ethereum network is loaded to
// allow us avoid syncing node.
MainnetEthereumNetworkURL = "https://mainnet.infura.io/nKmXgiFgc2KqtoQ8BCGJ"

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:
1. Start a Whisper node with mail server capability:
./build/bin/statusd \
-networkid=4 \
-maxpeers=100 \
-shh \
-shh.pow=0.002 \
-shh.mailserver \
-shh.passwordfile=./static/keys/wnodepassword \
-log DEBUG
./build/bin/statusd -c mailserver-config.json
where mailserver-config.json contains:
``` json
{
"NetworkId": 4,
"DataDir": "./ethereumtest/rinkeby_rpc",
"KeyStoreDir": "./ethereumtest/keystore",
"MaxPeers": 100,
"LogLevel": "DEBUG",
"WhisperConfig": {
"Enabled": true,
"EnableMailServer": true,
"DataDir": "./ethereumtest/wnode",
"MinimumPoW": 0.002,
"MailServerPassword": "status-offline-inbox"
}
}
```
2. Generate some messages:
go test -v -timeout=30s -run TestSendMessages ./t/benchmarks \
-peerurl=$ENODE_ADDR \

View File

@ -54,7 +54,7 @@ mv UTC--2018-01-26T13-47-49.289567120Z--9f04dc05c4c3ec3b8b1f36f7d7d153f3934b1f07
popd
```
Update config for tests with new accounts `static/config/public-chain-accounts.json`:
Update config for tests with new accounts `t/config/public-chain-accounts.json`:
```json
{

View File

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

View File

@ -2,10 +2,7 @@ package api_test
import (
"encoding/json"
"io/ioutil"
"math/rand"
"os"
"strconv"
"testing"
"time"
@ -40,19 +37,6 @@ func (s *APITestSuite) SetupTest() {
}
func (s *APITestSuite) TestCHTUpdate() {
tmpDir, err := ioutil.TempDir(os.TempDir(), "cht-updates")
s.NoError(err)
defer os.RemoveAll(tmpDir) //nolint: errcheck
configJSON := `{
"NetworkId": ` + strconv.Itoa(params.RopstenNetworkID) + `,
"DataDir": "` + tmpDir + `",
"LogLevel": "INFO",
"RPCEnabled": true
}`
_, err = params.LoadNodeConfig(configJSON)
s.NoError(err)
// TODO(tiabc): Test that CHT is really updated.
}
@ -140,12 +124,12 @@ func (s *APITestSuite) TestEventsNodeStartStop() {
nodeConfig, err := MakeTestNodeConfig(GetNetworkID())
s.NoError(err)
s.NoError(s.backend.StartNode(nodeConfig))
s.Require().NoError(s.backend.StartNode(nodeConfig))
s.NoError(s.backend.StopNode())
s.verifyEnvelopes(envelopes, signal.EventNodeStarted, signal.EventNodeReady, signal.EventNodeStopped)
s.NoError(s.backend.StartNode(nodeConfig))
s.Require().NoError(s.backend.StartNode(nodeConfig))
s.verifyEnvelopes(envelopes, signal.EventNodeStarted, signal.EventNodeReady)
s.NoError(s.backend.RestartNode())
s.Require().NoError(s.backend.RestartNode())
s.verifyEnvelopes(envelopes, signal.EventNodeStopped, signal.EventNodeStarted, signal.EventNodeReady)
s.NoError(s.backend.StopNode())
s.verifyEnvelopes(envelopes, signal.EventNodeStopped)

View File

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

View File

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

View File

@ -2,6 +2,7 @@ package e2e
import (
"context"
"path"
gethcommon "github.com/ethereum/go-ethereum/common"
"github.com/status-im/status-go/node"
@ -20,9 +21,11 @@ func WithUpstream(url string) TestNodeOption {
}
// WithDataDir returns TestNodeOption that allows to set another data dir.
func WithDataDir(path string) TestNodeOption {
func WithDataDir(dataDir string) TestNodeOption {
return func(config *params.NodeConfig) {
config.DataDir = path
config.DataDir = dataDir
config.KeyStoreDir = path.Join(dataDir, "keystore")
config.WhisperConfig.DataDir = path.Join(dataDir, "wnode")
}
}

View File

@ -568,7 +568,7 @@ func (s *WhisperMailboxSuite) startMailboxBackend() (*api.StatusBackend, func())
mailboxConfig.WhisperConfig.Enabled = true
mailboxConfig.KeyStoreDir = datadir
mailboxConfig.WhisperConfig.EnableMailServer = true
mailboxConfig.WhisperConfig.MailServerPasswordFile = filepath.Join(RootDir, "/static/keys/wnodepassword")
mailboxConfig.WhisperConfig.MailServerPassword = "status-offline-inbox"
mailboxConfig.WhisperConfig.DataDir = filepath.Join(datadir, "data")
mailboxConfig.DataDir = datadir

View File

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

View File

@ -244,18 +244,28 @@ func MakeTestNodeConfig(networkID int) (*params.NodeConfig, error) {
}
configJSON := `{
"Name": "test",
"NetworkId": ` + strconv.Itoa(networkID) + `,
"DataDir": "` + testDir + `",
"KeyStoreDir": "` + path.Join(testDir, "keystore") + `",
"HTTPPort": ` + strconv.Itoa(TestConfig.Node.HTTPPort) + `,
"WSPort": ` + strconv.Itoa(TestConfig.Node.WSPort) + `,
"LogLevel": "` + errorLevel + `"
"LogLevel": "` + errorLevel + `",
"NoDiscovery": true,
"LightEthConfig": {
"Enabled": true
},
"WhisperConfig": {
"Enabled": true,
"DataDir": "` + path.Join(testDir, "wnode") + `",
"EnableNTPSync": false
}
}`
nodeConfig, err := params.LoadNodeConfig(configJSON)
nodeConfig, err := params.NewConfigFromJSON(configJSON)
if err != nil {
return nil, err
}
nodeConfig.WhisperConfig.EnableNTPSync = false
return nodeConfig, nil
}
@ -264,14 +274,30 @@ func MakeTestNodeConfig(networkID int) (*params.NodeConfig, error) {
// where specific network addresses are assigned based on provided network id, and assigns
// a given name and data dir.
func MakeTestNodeConfigWithDataDir(name, dataDir, fleet string, networkID uint64) (*params.NodeConfig, error) {
cfg, err := params.NewNodeConfig(dataDir, "", fleet, networkID)
cfg, err := params.NewNodeConfig(dataDir, fleet, networkID)
if err != nil {
return nil, err
}
cfg.Name = name
cfg.NetworkID = uint64(GetNetworkID())
if name == "" {
cfg.Name = "test"
} else {
cfg.Name = name
}
cfg.NoDiscovery = true
cfg.LightEthConfig.Enabled = false
cfg.WhisperConfig.Enabled = true
cfg.WhisperConfig.EnableNTPSync = false
if dataDir != "" {
cfg.KeyStoreDir = path.Join(dataDir, "keystore")
cfg.WhisperConfig.DataDir = path.Join(dataDir, "wnode")
}
// Only attempt to validate if a dataDir is specified, we only support in-memory DB for tests
if dataDir != "" {
if err := cfg.Validate(); err != nil {
return nil, err
}
}
return cfg, nil
}
@ -299,19 +325,19 @@ const passphraseEnvName = "ACCOUNT_PASSWORD"
func loadTestConfig() (*testConfig, error) {
var config testConfig
pathOfStatic := path.Join(params.GetStatusHome(), "/static")
err := getTestConfigFromFile(path.Join(pathOfStatic, "config/test-data.json"), &config)
pathOfConfig := path.Join(params.GetStatusHome(), "/t/config")
err := getTestConfigFromFile(path.Join(pathOfConfig, "test-data.json"), &config)
if err != nil {
return nil, err
}
if GetNetworkID() == params.StatusChainNetworkID {
err := getTestConfigFromFile(path.Join(pathOfStatic, "config/status-chain-accounts.json"), &config)
err := getTestConfigFromFile(path.Join(pathOfConfig, "status-chain-accounts.json"), &config)
if err != nil {
return nil, err
}
} else {
err := getTestConfigFromFile(path.Join(pathOfStatic, "config/public-chain-accounts.json"), &config)
err := getTestConfigFromFile(path.Join(pathOfConfig, "public-chain-accounts.json"), &config)
if err != nil {
return nil, err
}

View File

@ -54,7 +54,7 @@ func (s *TransactorSuite) SetupTest() {
rpcClient, _ := rpc.NewClient(s.client, params.UpstreamRPCConfig{})
// expected by simulated backend
chainID := gethparams.AllEthashProtocolChanges.ChainID.Uint64()
nodeConfig, err := params.NewNodeConfig("/tmp", "", params.FleetBeta, chainID)
nodeConfig, err := MakeTestNodeConfigWithDataDir("", "/tmp", params.FleetBeta, chainID)
s.Require().NoError(err)
s.nodeConfig = nodeConfig