Static peers are now also optionally configurable (#738)

* Rename bootnode to static peers

Also add some groupings between types.

* Remove not needed genesisHash

* More cleanup of bootnodes with static peers

* Add option of cluster configuration file

* New generated bindata.go

* Changes after npm install

* Add argument for cluster configuration file

* Add test for dynamic cluster loading

Not yet sure with name "cluster config".

* Solved conflicts

* Renaming of static peers

* Remove static peers population

* Missing argument for config

* Renaming of static peers to boot nodes for consistency

* Fix of name change

* Cluster config is now cluster data

* Load static nodes from configuration

* Final renaming of var for file content
This commit is contained in:
Frank Mueller 2018-03-19 17:22:09 +01:00 committed by GitHub
parent d84510de92
commit 8e42014671
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 260 additions and 241 deletions

View File

@ -25,6 +25,7 @@ var (
)
var (
clusterConfigFile = flag.String("clusterconfig", "", "Cluster configuration file")
prodMode = flag.Bool("production", false, "Whether production settings should be loaded")
nodeKeyFile = flag.String("nodekey", "", "P2P node key file (private key)")
dataDir = flag.String("datadir", params.DataDir, "Data directory for the databases and keystore")
@ -189,7 +190,7 @@ func startCollectingStats(interruptCh <-chan struct{}, nodeManager common.NodeMa
// makeNodeConfig parses incoming CLI options and returns node configuration object
func makeNodeConfig() (*params.NodeConfig, error) {
devMode := !*prodMode
nodeConfig, err := params.NewNodeConfig(*dataDir, uint64(*networkID), devMode)
nodeConfig, err := params.NewNodeConfig(*dataDir, *clusterConfigFile, uint64(*networkID), devMode)
if err != nil {
return nil, err
}

View File

@ -6,6 +6,8 @@ package common
import (
context "context"
reflect "reflect"
accounts "github.com/ethereum/go-ethereum/accounts"
keystore "github.com/ethereum/go-ethereum/accounts/keystore"
common "github.com/ethereum/go-ethereum/common"
@ -15,7 +17,6 @@ import (
gomock "github.com/golang/mock/gomock"
params "github.com/status-im/status-go/geth/params"
rpc "github.com/status-im/status-go/geth/rpc"
reflect "reflect"
)
// MockNodeManager is a mock of NodeManager interface

View File

@ -98,12 +98,6 @@ func (m *NodeManager) startNode(config *params.NodeConfig) error {
log.Error("Failed to create an RPC client", "error", err)
return RPCClientError(err)
}
// populate static peers exits when node stopped
go func() {
if err := m.PopulateStaticPeers(); err != nil {
log.Error("Static peers population", "error", err)
}
}()
return nil
}
@ -202,7 +196,7 @@ func (m *NodeManager) populateStaticPeers() error {
func (m *NodeManager) removeStaticPeers() error {
if !m.config.BootClusterConfig.Enabled {
log.Info("Boot cluster is disabled")
log.Info("static peers are disabled")
return nil
}
server := m.node.Server()
@ -212,10 +206,10 @@ func (m *NodeManager) removeStaticPeers() error {
for _, enode := range m.config.BootClusterConfig.BootNodes {
err := m.removePeer(enode)
if err != nil {
log.Warn("Boot node deletion failed", "error", err)
log.Warn("static peer deletion failed", "error", err)
return err
}
log.Info("Boot node deleted", "enode", enode)
log.Info("static peer deleted", "enode", enode)
}
return nil
}

View File

@ -116,7 +116,7 @@ func defaultEmbeddedNodeConfig(config *params.NodeConfig) *node.Config {
}
if config.BootClusterConfig.Enabled {
nc.P2P.BootstrapNodes = makeBootstrapNodes(config.BootClusterConfig.BootNodes)
nc.P2P.StaticNodes = makeBootstrapNodes(config.BootClusterConfig.BootNodes)
}
return nc

View File

@ -25,6 +25,10 @@ var (
ErrAuthorizationKeyFileNotSet = errors.New("authorization key file is not set")
)
// ----------
// LightEthConfig
// ----------
// LightEthConfig holds LES-related configuration
// Status nodes are always lightweight clients (due to mobile platform constraints)
type LightEthConfig struct {
@ -38,6 +42,10 @@ type LightEthConfig struct {
DatabaseCache int
}
// ----------
// FirebaseConfig
// ----------
// FirebaseConfig holds FCM-related configuration
type FirebaseConfig struct {
// AuthorizationKeyFile file path that contains FCM authorization key
@ -67,6 +75,10 @@ func (c *FirebaseConfig) ReadAuthorizationKeyFile() ([]byte, error) {
return key, nil
}
// ----------
// WhisperConfig
// ----------
// WhisperConfig holds SHH-related configuration
type WhisperConfig struct {
// Enabled flag specifies whether protocol is enabled
@ -126,6 +138,10 @@ func (c *WhisperConfig) String() string {
return string(data)
}
// ----------
// SwarmConfig
// ----------
// SwarmConfig holds Swarm-related configuration
type SwarmConfig struct {
// Enabled flag specifies whether protocol is enabled
@ -138,14 +154,18 @@ func (c *SwarmConfig) String() string {
return string(data)
}
// BootClusterConfig holds configuration for supporting boot cluster, which is a temporary
// ----------
// BootClusterConfig
// ----------
// BootClusterConfig holds configuration for supporting cluster peers, which is a temporary
// means for mobile devices to get connected to Ethereum network (UDP-based discovery
// may not be available, so we need means to discover the network manually).
type BootClusterConfig struct {
// Enabled flag specifies whether feature is enabled
Enabled bool
// BootNodes list of bootstrap nodes for a given network (Ropsten, Rinkeby, Homestead),
// BootNodes list of cluster peer nodes for a given network (Ropsten, Rinkeby, Homestead),
// for a given mode (production vs development)
BootNodes []string
}
@ -156,7 +176,9 @@ func (c *BootClusterConfig) String() string {
return string(data)
}
//=====================================================================================
// ----------
// UpstreamRPCConfig
// ----------
// UpstreamRPCConfig stores configuration for upstream rpc connection.
type UpstreamRPCConfig struct {
@ -168,7 +190,9 @@ type UpstreamRPCConfig struct {
URL string
}
//=====================================================================================
// ----------
// NodeConfig
// ----------
// NodeConfig stores configuration options for a node
type NodeConfig struct {
@ -187,7 +211,7 @@ type NodeConfig struct {
// If KeyStoreDir is empty, the default location is the "keystore" subdirectory of DataDir.
KeyStoreDir string
// PrivateKeyFile is a filename with node ID (private key)
// NodeKeyFile is a filename with node ID (private key)
// This file should contain a valid secp256k1 private key that will be used for both
// remote peer identification as well as network traffic encryption.
NodeKeyFile string
@ -256,7 +280,11 @@ type NodeConfig struct {
// UpstreamConfig extra config for providing upstream infura server.
UpstreamConfig UpstreamRPCConfig `json:"UpstreamConfig"`
// BootClusterConfig extra configuration for supporting cluster
// ClusterConfigFile contains the file name of the cluster configuration. If
// empty the statical configuration data will be taken.
ClusterConfigFile string `json:"ClusterConfigFile"`
// BootClusterConfig extra configuration for supporting cluster peers.
BootClusterConfig *BootClusterConfig `json:"BootClusterConfig," validate:"structonly"`
// LightEthConfig extra configuration for LES
@ -270,7 +298,7 @@ type NodeConfig struct {
}
// NewNodeConfig creates new node configuration object
func NewNodeConfig(dataDir string, networkID uint64, devMode bool) (*NodeConfig, error) {
func NewNodeConfig(dataDir string, clstrCfgFile string, networkID uint64, devMode bool) (*NodeConfig, error) {
nodeConfig := &NodeConfig{
DevMode: devMode,
NetworkID: networkID,
@ -290,6 +318,7 @@ func NewNodeConfig(dataDir string, networkID uint64, devMode bool) (*NodeConfig,
LogFile: LogFile,
LogLevel: LogLevel,
LogToStderr: LogToStderr,
ClusterConfigFile: clstrCfgFile,
BootClusterConfig: &BootClusterConfig{
Enabled: true,
BootNodes: []string{},
@ -332,7 +361,7 @@ func LoadNodeConfig(configJSON string) (*NodeConfig, error) {
}
func loadNodeConfig(configJSON string) (*NodeConfig, error) {
nodeConfig, err := NewNodeConfig("", 0, true)
nodeConfig, err := NewNodeConfig("", "", 0, true)
if err != nil {
return nil, err
}
@ -433,7 +462,7 @@ func (c *NodeConfig) updateConfig() error {
return err
}
if err := c.updateBootClusterConfig(); err != nil {
if err := c.updateClusterConfig(); err != nil {
return err
}
@ -511,10 +540,10 @@ func (c *NodeConfig) updateUpstreamConfig() error {
return nil
}
// updateBootClusterConfig loads boot nodes and CHT for a given network and mode.
// 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) updateBootClusterConfig() error {
func (c *NodeConfig) updateClusterConfig() error {
if !c.BootClusterConfig.Enabled {
return nil
}
@ -523,27 +552,38 @@ func (c *NodeConfig) updateBootClusterConfig() error {
// Once CHT sync sub-protocol is working in LES, we will rely on it, as it provides
// decentralized solution. For now, in order to avoid forcing users to long sync times
// we use central static resource
type subClusterConfig struct {
type subClusterData struct {
Number int `json:"number"`
Hash string `json:"hash"`
BootNodes []string `json:"bootnodes"`
}
type clusterConfig struct {
type clusterData struct {
NetworkID int `json:"networkID"`
GenesisHash string `json:"genesisHash"`
Prod subClusterConfig `json:"prod"`
Dev subClusterConfig `json:"dev"`
Prod subClusterData `json:"prod"`
Dev subClusterData `json:"dev"`
}
chtFile, err := static.Asset("config/staticpeers.json")
var configFile []byte
var err error
if c.ClusterConfigFile != "" {
// Load cluster configuration from external file.
configFile, err = ioutil.ReadFile(c.ClusterConfigFile)
if err != nil {
return fmt.Errorf("staticpeers.json could not be loaded: %s", err)
return fmt.Errorf("cluster configuration file '%s' could not be loaded: %s", c.ClusterConfigFile, err)
}
} else {
// Fallback to embedded file.
configFile, err = static.Asset("config/cluster.json")
if err != nil {
return fmt.Errorf("cluster.json could not be loaded: %s", err)
}
}
var clusters []clusterConfig
err = json.Unmarshal(chtFile, &clusters)
var clusters []clusterData
err = json.Unmarshal(configFile, &clusters)
if err != nil {
return fmt.Errorf("failed to unmarshal staticpeers.json: %s", err)
return fmt.Errorf("failed to unmarshal cluster configuration file: %s", err)
}
for _, cluster := range clusters {

View File

@ -17,6 +17,24 @@ import (
"github.com/stretchr/testify/require"
)
var clusterConfigData = []byte(`[
{
"networkID": 3,
"prod": {
"bootnodes": [
"enode://7ab298cedc4185a894d21d8a4615262ec6bdce66c9b6783878258e0d5b31013d30c9038932432f70e5b2b6a5cd323bf820554fcb22fbc7b45367889522e9c449@10.1.1.1:30303",
"enode://f59e8701f18c79c5cbc7618dc7bb928d44dc2f5405c7d693dad97da2d8585975942ec6fd36d3fe608bfdc7270a34a4dd00f38cfe96b2baa24f7cd0ac28d382a1@10.1.1.2:30303"
]
},
"dev": {
"bootnodes": [
"enode://7ab298cedc4185a894d21d8a4615262ec6bdce66c9b6783878258e0d5b31013d30c9038932432f70e5b2b6a5cd323bf820554fcb22fbc7b45367889522e9c449@10.1.1.1:30303",
"enode://f59e8701f18c79c5cbc7618dc7bb928d44dc2f5405c7d693dad97da2d8585975942ec6fd36d3fe608bfdc7270a34a4dd00f38cfe96b2baa24f7cd0ac28d382a1@10.1.1.2:30303"
]
}
}
]`)
var loadConfigTestCases = []struct {
name string
configJSON string
@ -215,17 +233,43 @@ var loadConfigTestCases = []struct {
},
},
{
`default boot cluster (Ropsten Dev)`,
`default boot nodes (Ropsten Dev)`,
`{
"NetworkId": 3,
"DataDir": "$TMPDIR"
}`,
func(t *testing.T, dataDir string, nodeConfig *params.NodeConfig, err error) {
require.NoError(t, err)
require.True(t, nodeConfig.BootClusterConfig.Enabled, "boot cluster is expected to be enabled by default")
require.True(t, nodeConfig.BootClusterConfig.Enabled, "boot nodes are expected to be enabled by default")
enodes := nodeConfig.BootClusterConfig.BootNodes
require.Len(t, enodes, 4)
require.Len(t, enodes, 2)
},
},
{
`illegal cluster config file`,
`{
"NetworkId": 3,
"DataDir": "$TMPDIR",
"ClusterConfigFile": "/file/does/not.exist"
}`,
func(t *testing.T, dataDir string, nodeConfig *params.NodeConfig, err error) {
require.Error(t, err, "error is expected, not thrown")
},
},
{
`valid cluster config file`,
`{
"NetworkId": 3,
"DataDir": "$TMPDIR",
"ClusterConfigFile": "$TMPDIR/cluster.json"
}`,
func(t *testing.T, dataDir string, nodeConfig *params.NodeConfig, err error) {
require.NoError(t, err)
require.True(t, nodeConfig.BootClusterConfig.Enabled, "boot cluster is expected to be enabled after loading file")
enodes := nodeConfig.BootClusterConfig.BootNodes
require.True(t, len(enodes) == 2)
},
},
{
@ -240,7 +284,7 @@ var loadConfigTestCases = []struct {
require.True(t, nodeConfig.BootClusterConfig.Enabled, "boot cluster is expected to be enabled by default")
enodes := nodeConfig.BootClusterConfig.BootNodes
require.Len(t, enodes, 4)
require.Len(t, enodes, 2)
},
},
{
@ -364,9 +408,8 @@ func TestLoadNodeConfig(t *testing.T) {
}
defer os.RemoveAll(tmpDir) // nolint: errcheck
// create sample Bootstrap Cluster Config
bootstrapConfig := []byte(`["enode://foobar@41.41.41.41:30300", "enode://foobaz@42.42.42.42:30302"]`)
err = ioutil.WriteFile(filepath.Join(tmpDir, "bootstrap-cluster.json"), bootstrapConfig, os.ModePerm)
// create sample bootnodes config
err = ioutil.WriteFile(filepath.Join(tmpDir, "cluster.json"), clusterConfigData, os.ModePerm)
require.NoError(t, err)
t.Log(tmpDir)
@ -383,7 +426,7 @@ func TestConfigWriteRead(t *testing.T) {
require.Nil(t, err)
defer os.RemoveAll(tmpDir) // nolint: errcheck
nodeConfig, err := params.NewNodeConfig(tmpDir, params.RopstenNetworkID, true)
nodeConfig, err := params.NewNodeConfig(tmpDir, "", params.RopstenNetworkID, true)
require.Nil(t, err, "cannot create new config object")
err = nodeConfig.Save()

View File

@ -57,7 +57,7 @@ func (s *TxQueueTestSuite) SetupTest() {
s.client = gethrpc.DialInProc(s.server)
rpclient, _ := rpc.NewClient(s.client, params.UpstreamRPCConfig{})
s.nodeManagerMock.EXPECT().RPCClient().Return(rpclient)
nodeConfig, err := params.NewNodeConfig("/tmp", params.RopstenNetworkID, true)
nodeConfig, err := params.NewNodeConfig("/tmp", "", params.RopstenNetworkID, true)
s.Require().NoError(err)
s.nodeConfig = nodeConfig

View File

@ -17,7 +17,7 @@ import (
//GenerateConfig for status node
//export GenerateConfig
func GenerateConfig(datadir *C.char, networkID C.int, devMode C.int) *C.char {
config, err := params.NewNodeConfig(C.GoString(datadir), uint64(networkID), devMode == 1)
config, err := params.NewNodeConfig(C.GoString(datadir), "", uint64(networkID), devMode == 1)
if err != nil {
return makeJSONResponse(err)
}

File diff suppressed because one or more lines are too long

View File

@ -1,7 +1,6 @@
[
{
"networkID": 3,
"genesisHash": "0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d",
"prod": {
"bootnodes": [
"enode://dffef3874011709b12d1e540d83ddb19a9db8614ad9151d05bcf813585e45cbebba5aaea223fe315786c401d8cecb1ad2de9f179680c536ea30311fb21fa934b@188.166.100.178:30303",
@ -17,7 +16,6 @@
},
{
"networkID": 4,
"genesisHash": "0x6341fd3daf94b748c72ced5a5b26028f2474f5f00d824504e4fa37a75767e177",
"prod": {
"bootnodes": [
"enode://fda3f6273a0f2da4ac5858d1f52e5afaf9def281121be3d37558c67d4d9ca26c6ad7a0520b2cd7454120fb770e86d5760487c9924b2166e65485f606e56d60fc@51.15.69.144:30303",
@ -35,7 +33,6 @@
},
{
"networkID": 1,
"genesisHash": "0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3",
"prod": {
"bootnodes": [
"enode://71bb01b58165e3262aea2d3b06dbf9abb8d5512d96e5000e7e41ab2138b47be685935d3eb119fc25e1413db00d8db231fd9d59555a1cd75229821559b6a4eb51@51.15.85.243:30303",

View File

@ -306,7 +306,7 @@ func (s *ManagerTestSuite) TestStartNodeWithUpstreamEnabled() {
// },
// func(config *params.NodeConfig) {
// log.Info("PopulateStaticPeers()")
// s.T().Logf("PopulateStaticPeers(), error: %v", s.NodeManager.PopulateStaticPeers())
// s.T().Logf("PopulateBootNodes(), error: %v", s.NodeManager.PopulateStaticPeers())
// progress <- struct{}{}
// },
// // TODO(adam): quarantined until it uses a different datadir