520 lines
14 KiB
Go
520 lines
14 KiB
Go
package params_test
|
|
|
|
import (
|
|
"fmt"
|
|
"io/ioutil"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
validator "gopkg.in/go-playground/validator.v9"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/ethereum/go-ethereum/p2p/discv5"
|
|
|
|
"github.com/status-im/status-go/params"
|
|
"github.com/status-im/status-go/t/utils"
|
|
)
|
|
|
|
func TestNewNodeConfigWithDefaults(t *testing.T) {
|
|
c, err := params.NewNodeConfigWithDefaults(
|
|
"/some/data/path",
|
|
params.RopstenNetworkID,
|
|
params.WithFleet(params.FleetBeta),
|
|
params.WithLES(),
|
|
params.WithMailserver(),
|
|
)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "/some/data/path", c.DataDir)
|
|
assert.Equal(t, "/some/data/path/keystore", c.KeyStoreDir)
|
|
assert.Equal(t, true, c.EnableNTPSync)
|
|
// assert Whisper
|
|
assert.Equal(t, true, c.WhisperConfig.Enabled)
|
|
assert.Equal(t, "/some/data/path/wnode", c.WhisperConfig.DataDir)
|
|
// assert MailServer
|
|
assert.Equal(t, true, c.WhisperConfig.EnableMailServer)
|
|
assert.NotEmpty(t, c.WhisperConfig.MailServerPassword)
|
|
// assert cluster
|
|
assert.Equal(t, false, c.NoDiscovery)
|
|
assert.Equal(t, params.FleetBeta, c.ClusterConfig.Fleet)
|
|
assert.NotEmpty(t, c.ClusterConfig.BootNodes)
|
|
assert.NotEmpty(t, c.ClusterConfig.StaticNodes)
|
|
assert.NotEmpty(t, c.ClusterConfig.RendezvousNodes)
|
|
// assert LES
|
|
assert.Equal(t, true, c.LightEthConfig.Enabled)
|
|
// assert peers limits
|
|
assert.Contains(t, c.RequireTopics, params.WhisperDiscv5Topic)
|
|
assert.Contains(t, c.RequireTopics, discv5.Topic(params.LesTopic(int(c.NetworkID))))
|
|
// assert incentivisation
|
|
assert.Equal(t, false, c.IncentivisationConfig.Enabled)
|
|
// assert other
|
|
assert.Equal(t, false, c.HTTPEnabled)
|
|
assert.Equal(t, false, c.IPCEnabled)
|
|
}
|
|
|
|
func TestNewConfigFromJSON(t *testing.T) {
|
|
tmpDir, err := ioutil.TempDir(os.TempDir(), "geth-config-tests")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer os.RemoveAll(tmpDir) // nolint: errcheck
|
|
json := `{
|
|
"NetworkId": 3,
|
|
"DataDir": "` + tmpDir + `",
|
|
"KeyStoreDir": "` + tmpDir + `",
|
|
"NoDiscovery": true
|
|
}`
|
|
c, err := params.NewConfigFromJSON(json)
|
|
require.NoError(t, err)
|
|
require.Equal(t, uint64(3), c.NetworkID)
|
|
require.Equal(t, tmpDir, c.DataDir)
|
|
require.Equal(t, tmpDir, c.KeyStoreDir)
|
|
}
|
|
|
|
func TestConfigWriteRead(t *testing.T) {
|
|
tmpDir, err := ioutil.TempDir(os.TempDir(), "geth-config-tests")
|
|
require.Nil(t, err)
|
|
defer os.RemoveAll(tmpDir) // nolint: errcheck
|
|
|
|
nodeConfig, err := utils.MakeTestNodeConfigWithDataDir("", tmpDir, params.RopstenNetworkID)
|
|
require.Nil(t, err, "cannot create new config object")
|
|
|
|
err = nodeConfig.Save()
|
|
require.Nil(t, err, "cannot persist configuration")
|
|
|
|
loadedConfigData, err := ioutil.ReadFile(filepath.Join(nodeConfig.DataDir, "config.json"))
|
|
require.Nil(t, err, "cannot read configuration from disk")
|
|
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(`"BackupDisabledDataDir": "%s"`, tmpDir))
|
|
}
|
|
|
|
// TestNodeConfigValidate checks validation of individual fields.
|
|
func TestNodeConfigValidate(t *testing.T) {
|
|
testCases := []struct {
|
|
Name string
|
|
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",
|
|
"BackupDisabledDataDir": "/tmp/data",
|
|
"KeyStoreDir": "/tmp/data",
|
|
"NoDiscovery": true
|
|
}`,
|
|
},
|
|
{
|
|
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",
|
|
},
|
|
{
|
|
Name: "Validate all required fields",
|
|
Config: `{}`,
|
|
FieldErrors: map[string]string{
|
|
"NetworkID": "required",
|
|
"DataDir": "required",
|
|
"KeyStoreDir": "required",
|
|
},
|
|
},
|
|
{
|
|
Name: "Validate that Name does not contain slash",
|
|
Config: `{
|
|
"NetworkId": 1,
|
|
"DataDir": "/some/dir",
|
|
"KeyStoreDir": "/some/dir",
|
|
"Name": "invalid/name"
|
|
}`,
|
|
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.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 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,
|
|
"DataDir": "/other/dir",
|
|
"MailserverPassword": "foo"
|
|
}
|
|
}`,
|
|
Error: "WhisperConfig.DataDir must start with DataDir fragment",
|
|
},
|
|
{
|
|
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": "/some/dir",
|
|
"MailserverPassword": "foo"
|
|
}
|
|
}`,
|
|
CheckFunc: func(t *testing.T, config *params.NodeConfig) {
|
|
require.Equal(t, "foo", config.WhisperConfig.MailServerPassword)
|
|
},
|
|
},
|
|
{
|
|
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": "/some/dir"
|
|
}
|
|
}`,
|
|
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": "/some/dir",
|
|
"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",
|
|
},
|
|
{
|
|
Name: "Validate that PFSEnabled & InstallationID are checked for validity",
|
|
Config: `{
|
|
"NetworkId": 1,
|
|
"DataDir": "/some/dir",
|
|
"KeyStoreDir": "/some/dir",
|
|
"NoDiscovery": true,
|
|
"WhisperConfig": {
|
|
"Enabled": true,
|
|
"DataDir": "/foo"
|
|
},
|
|
"ShhextConfig": {
|
|
"BackupDisabledDataDir": "/some/dir",
|
|
"PFSEnabled": true
|
|
}
|
|
}`,
|
|
Error: "PFSEnabled is true, but InstallationID is empty",
|
|
},
|
|
{
|
|
Name: "Default HTTP virtual hosts is localhost and CORS is empty",
|
|
Config: `{
|
|
"NetworkId": 1,
|
|
"DataDir": "/some/dir",
|
|
"KeyStoreDir": "/some/dir"
|
|
}`,
|
|
CheckFunc: func(t *testing.T, config *params.NodeConfig) {
|
|
require.Equal(t, []string{"localhost"}, config.HTTPVirtualHosts)
|
|
require.Nil(t, config.HTTPCors)
|
|
},
|
|
},
|
|
{
|
|
Name: "Set HTTP virtual hosts and CORS",
|
|
Config: `{
|
|
"NetworkId": 1,
|
|
"DataDir": "/some/dir",
|
|
"KeyStoreDir": "/some/dir",
|
|
"HTTPVirtualHosts": ["my.domain.com"],
|
|
"HTTPCors": ["http://my.domain.com:8080"]
|
|
}`,
|
|
CheckFunc: func(t *testing.T, config *params.NodeConfig) {
|
|
require.Equal(t, []string{"my.domain.com"}, config.HTTPVirtualHosts)
|
|
require.Equal(t, []string{"http://my.domain.com:8080"}, config.HTTPCors)
|
|
},
|
|
},
|
|
{
|
|
Name: "ShhextConfig is not required",
|
|
Config: `{
|
|
"NetworkId": 1,
|
|
"DataDir": "/some/dir",
|
|
"KeyStoreDir": "/some/dir"
|
|
}`,
|
|
},
|
|
{
|
|
Name: "BackupDisabledDataDir must be set if PFSEnabled is true",
|
|
Config: `{
|
|
"NetworkId": 1,
|
|
"DataDir": "/some/dir",
|
|
"KeyStoreDir": "/some/dir",
|
|
"ShhextConfig": {
|
|
"PFSEnabled": true
|
|
}
|
|
}`,
|
|
Error: "field BackupDisabledDataDir is required if PFSEnabled is true",
|
|
},
|
|
{
|
|
Name: "Valid JSON config with incentivisation",
|
|
Config: `{
|
|
"NetworkId": 1,
|
|
"DataDir": "/tmp/data",
|
|
"BackupDisabledDataDir": "/tmp/data",
|
|
"KeyStoreDir": "/tmp/data",
|
|
"NoDiscovery": true,
|
|
"IncentivisationConfig": {
|
|
"Enabled": true,
|
|
"IP": "127.0.0.1",
|
|
"Port": 300,
|
|
"RPCEndpoint": "http://test.com",
|
|
"ContractAddress": "0xfffff"
|
|
}
|
|
}`,
|
|
},
|
|
{
|
|
Name: "Missing RPCEndpoint",
|
|
Config: `{
|
|
"NetworkId": 1,
|
|
"DataDir": "/tmp/data",
|
|
"BackupDisabledDataDir": "/tmp/data",
|
|
"KeyStoreDir": "/tmp/data",
|
|
"NoDiscovery": true,
|
|
"IncentivisationConfig": {
|
|
"Enabled": true,
|
|
"IP": "127.0.0.1",
|
|
"Port": 300,
|
|
"ContractAddress": "0xfffff"
|
|
}
|
|
}`,
|
|
FieldErrors: map[string]string{
|
|
"RPCEndpoint": "required",
|
|
},
|
|
Error: "RPCEndpoint is required if incentivisation is enabled",
|
|
},
|
|
{
|
|
Name: "Missing contract address",
|
|
Config: `{
|
|
"NetworkId": 1,
|
|
"DataDir": "/tmp/data",
|
|
"BackupDisabledDataDir": "/tmp/data",
|
|
"KeyStoreDir": "/tmp/data",
|
|
"NoDiscovery": true,
|
|
"IncentivisationConfig": {
|
|
"Enabled": true,
|
|
"IP": "127.0.0.1",
|
|
"Port": 300,
|
|
"RPCEndpoint": "http://test.com"
|
|
}
|
|
}`,
|
|
FieldErrors: map[string]string{
|
|
"ContractAddress": "required",
|
|
},
|
|
Error: "field ContractAddress is required if incentivisation is enabled",
|
|
},
|
|
{
|
|
Name: "Missing ip address",
|
|
Config: `{
|
|
"NetworkId": 1,
|
|
"DataDir": "/tmp/data",
|
|
"BackupDisabledDataDir": "/tmp/data",
|
|
"KeyStoreDir": "/tmp/data",
|
|
"NoDiscovery": true,
|
|
"IncentivisationConfig": {
|
|
"Enabled": true,
|
|
"Port": 300,
|
|
"RPCEndpoint": "http://test.com",
|
|
"ContractAddress": "0xfffff"
|
|
}
|
|
}`,
|
|
FieldErrors: map[string]string{
|
|
"IP": "required",
|
|
},
|
|
Error: "field IP is required if incentivisation is enabled",
|
|
},
|
|
{
|
|
Name: "Missing port",
|
|
Config: `{
|
|
"NetworkId": 1,
|
|
"DataDir": "/tmp/data",
|
|
"BackupDisabledDataDir": "/tmp/data",
|
|
"KeyStoreDir": "/tmp/data",
|
|
"NoDiscovery": true,
|
|
"IncentivisationConfig": {
|
|
"Enabled": true,
|
|
"IP": "127.0.0.1",
|
|
"RPCEndpoint": "http://test.com",
|
|
"ContractAddress": "0xfffff"
|
|
}
|
|
}`,
|
|
FieldErrors: map[string]string{
|
|
"Port": "required",
|
|
},
|
|
Error: "field Port is required if incentivisation is enabled",
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.Name, func(t *testing.T) {
|
|
config, err := params.NewConfigFromJSON(tc.Config)
|
|
switch err := err.(type) {
|
|
case validator.ValidationErrors:
|
|
for _, ve := range err {
|
|
require.Contains(t, tc.FieldErrors, ve.Field())
|
|
require.Equal(t, tc.FieldErrors[ve.Field()], ve.Tag())
|
|
}
|
|
case error:
|
|
if tc.Error == "" {
|
|
require.NoError(t, err)
|
|
} else {
|
|
fmt.Println(tc.Error)
|
|
require.Contains(t, err.Error(), tc.Error)
|
|
}
|
|
case nil:
|
|
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)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|