Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/persist node name #1543

Merged
merged 18 commits into from
Apr 30, 2021
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion chain/gssmr/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ key = ""
unlock = ""

[core]
roles = 4
roles = 2
EclesioMeloJunior marked this conversation as resolved.
Show resolved Hide resolved
babe-authority = true
grandpa-authority = true

Expand Down
103 changes: 79 additions & 24 deletions cmd/gossamer/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
package main

import (
"encoding/binary"
"fmt"
"strconv"
"strings"
Expand All @@ -33,7 +32,7 @@ import (
"github.com/ChainSafe/gossamer/lib/runtime/life"
"github.com/ChainSafe/gossamer/lib/runtime/wasmer"
"github.com/ChainSafe/gossamer/lib/runtime/wasmtime"
"github.com/cosmos/go-bip39"
"github.com/ChainSafe/gossamer/lib/utils"

log "github.com/ChainSafe/log15"
"github.com/urfave/cli"
Expand Down Expand Up @@ -121,7 +120,6 @@ func createDotConfig(ctx *cli.Context) (*dot.Config, error) {
logger.Error("failed to set chain configuration", "error", err)
return nil, err
}

// set log config
err = setLogConfig(ctx, tomlCfg, &cfg.Global, &cfg.Log)
if err != nil {
Expand All @@ -132,7 +130,10 @@ func createDotConfig(ctx *cli.Context) (*dot.Config, error) {
logger.Info("loaded package log configuration", "cfg", cfg.Log)

// set global configuration values
setDotGlobalConfig(ctx, tomlCfg, &cfg.Global)
if err := setDotGlobalConfig(ctx, tomlCfg, &cfg.Global); err != nil {
logger.Error("failed to set global node configuration", "error", err)
return nil, err
}

// set remaining cli configuration values
setDotInitConfig(ctx, tomlCfg.Init, &cfg.Init)
Expand Down Expand Up @@ -160,7 +161,10 @@ func createInitConfig(ctx *cli.Context) (*dot.Config, error) {
}

// set global configuration values
setDotGlobalConfig(ctx, tomlCfg, &cfg.Global)
if err := setDotGlobalConfig(ctx, tomlCfg, &cfg.Global); err != nil {
logger.Error("failed to set global node configuration", "error", err)
return nil, err
}

// set log config
err = setLogConfig(ctx, tomlCfg, &cfg.Global, &cfg.Log)
Expand Down Expand Up @@ -196,7 +200,11 @@ func createImportStateConfig(ctx *cli.Context) (*dot.Config, error) {
}

// set global configuration values
setDotGlobalConfig(ctx, tomlCfg, &cfg.Global)
if err := setDotGlobalConfig(ctx, tomlCfg, &cfg.Global); err != nil {
logger.Error("failed to set global node configuration", "error", err)
return nil, err
}

return cfg, nil
}

Expand All @@ -210,7 +218,11 @@ func createBuildSpecConfig(ctx *cli.Context) (*dot.Config, error) {
}

// set global configuration values
setDotGlobalConfig(ctx, tomlCfg, &cfg.Global)
if err := setDotGlobalConfig(ctx, tomlCfg, &cfg.Global); err != nil {
logger.Error("failed to set global node configuration", "error", err)
return nil, err
}

return cfg, nil
}

Expand All @@ -229,7 +241,10 @@ func createExportConfig(ctx *cli.Context) (*dot.Config, error) {
updateDotConfigFromGenesisJSONRaw(*tomlCfg, cfg)

// set global configuration values
setDotGlobalConfig(ctx, tomlCfg, &cfg.Global)
if err := setDotGlobalConfig(ctx, tomlCfg, &cfg.Global); err != nil {
logger.Error("failed to set global node configuration", "error", err)
return nil, err
}

// set log config
err = setLogConfig(ctx, &ctoml.Config{}, &cfg.Global, &cfg.Log)
Expand Down Expand Up @@ -385,8 +400,26 @@ func setDotInitConfig(ctx *cli.Context, tomlCfg ctoml.InitConfig, cfg *dot.InitC
)
}

// setDotGlobalConfig sets dot.GlobalConfig using flag values from the cli context
func setDotGlobalConfig(ctx *cli.Context, tomlCfg *ctoml.Config, cfg *dot.GlobalConfig) {
func setDotGlobalConfig(ctx *cli.Context, tomlConfig *ctoml.Config, cfg *dot.GlobalConfig) error {
setDotGlobalConfigFromToml(tomlConfig, cfg)
setDotGlobalConfigFromFlags(ctx, cfg)

if err := setDotGlobalConfigName(ctx, tomlConfig, cfg); err != nil {
return fmt.Errorf("could not set global node name: %w", err)
}

logger.Debug(
"global configuration",
"name", cfg.Name,
"id", cfg.ID,
"basepath", cfg.BasePath,
)

return nil
}

// setDotGlobalConfigFromToml will apply the toml configs to dot global config
func setDotGlobalConfigFromToml(tomlCfg *ctoml.Config, cfg *dot.GlobalConfig) {
if tomlCfg != nil {
if tomlCfg.Global.Name != "" {
cfg.Name = tomlCfg.Global.Name
Expand All @@ -406,18 +439,13 @@ func setDotGlobalConfig(ctx *cli.Context, tomlCfg *ctoml.Config, cfg *dot.Global

cfg.MetricsPort = tomlCfg.Global.MetricsPort
}
}

// TODO: generate random name if one is not assigned (see issue #1496)
// setDotGlobalConfigFromFlags sets dot.GlobalConfig using flag values from the cli context
func setDotGlobalConfigFromFlags(ctx *cli.Context, cfg *dot.GlobalConfig) {
// check --name flag and update node configuration
if name := ctx.GlobalString(NameFlag.Name); name != "" {
cfg.Name = name
} else {
// generate random name
entropy, _ := bip39.NewEntropy(128)
randomNamesString, _ := bip39.NewMnemonic(entropy)
randomNames := strings.Split(randomNamesString, " ")
number := binary.BigEndian.Uint16(entropy)
cfg.Name = randomNames[0] + "-" + randomNames[1] + "-" + fmt.Sprint(number)
}

// check --basepath flag and update node configuration
Expand All @@ -429,6 +457,7 @@ func setDotGlobalConfig(ctx *cli.Context, tomlCfg *ctoml.Config, cfg *dot.Global
if cfg.BasePath == "" {
cfg.BasePath = dot.GssmrConfig().Global.BasePath
}

// check --log flag
if lvlToInt, err := strconv.Atoi(ctx.String(LogFlag.Name)); err == nil {
cfg.LogLvl = log.Lvl(lvlToInt)
Expand All @@ -444,13 +473,39 @@ func setDotGlobalConfig(ctx *cli.Context, tomlCfg *ctoml.Config, cfg *dot.Global
}

cfg.NoTelemetry = ctx.Bool("no-telemetry")
}

logger.Debug(
"global configuration",
"name", cfg.Name,
"id", cfg.ID,
"basepath", cfg.BasePath,
)
func setDotGlobalConfigName(ctx *cli.Context, tomlConfig *ctoml.Config, cfg *dot.GlobalConfig) error {
globalBasePath := utils.ExpandDir(cfg.BasePath)
initialized := dot.NodeInitialized(globalBasePath, false)

// if node was previusly initialized then retrieve the node name
// from the current database
if initialized {
var err error
if cfg.Name, err = dot.LoadGlobalNodeName(globalBasePath); err != nil {
return err
}

// if successfull global node name loaded
if cfg.Name != "" {
logger.Debug("load global node name from database", "name", cfg.Name)
return nil
}
}

thereIsNameFlag := ctx.GlobalString(NameFlag.Name) != ""
thereIsNameTomlConfig := tomlConfig.Global.Name != ""

// if the node was not initialized
// and no name flag was defined neither there is a name
// defined at toml config file then create a new
// random name and set at dot globals config
if !initialized && !thereIsNameFlag && !thereIsNameTomlConfig {
cfg.Name = dot.RandonNodeName()
}

return nil
}

// setDotAccountConfig sets dot.AccountConfig using flag values from the cli context
Expand Down
60 changes: 60 additions & 0 deletions cmd/gossamer/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"github.com/ChainSafe/gossamer/chain/gssmr"
"github.com/ChainSafe/gossamer/dot"
"github.com/ChainSafe/gossamer/dot/state"
"github.com/ChainSafe/gossamer/dot/types"
"github.com/ChainSafe/gossamer/lib/genesis"
"github.com/ChainSafe/gossamer/lib/utils"

Expand Down Expand Up @@ -833,3 +834,62 @@ func TestUpdateConfigFromGenesisData(t *testing.T) {

require.Equal(t, expected, cfg)
}

func TestGlobalNodeName_WhenNodeAlreadyHasStoredName(t *testing.T) {
// Initialize a node with a random name
globalName := dot.RandonNodeName()

cfg := dot.NewTestConfig(t)
cfg.Global.Name = globalName
require.NotNil(t, cfg)

genPath := dot.NewTestGenesisAndRuntime(t)
require.NotNil(t, genPath)

defer utils.RemoveTestDir(t)

cfg.Core.Roles = types.FullNodeRole
cfg.Core.BabeAuthority = false
cfg.Core.GrandpaAuthority = false
cfg.Core.BabeThresholdNumerator = 0
cfg.Core.BabeThresholdDenominator = 0
cfg.Init.Genesis = genPath

err := dot.InitNode(cfg)
require.NoError(t, err)

// call another command and test the name
testApp := cli.NewApp()
testApp.Writer = ioutil.Discard

testcases := []struct {
description string
flags []string
values []interface{}
expected string
}{
{
"Test gossamer --roles --basepath",
[]string{"basepath", "roles"},
[]interface{}{cfg.Global.BasePath, "4"},
globalName,
},
{
"Test gossamer --roles",
[]string{"basepath", "roles"},
[]interface{}{cfg.Global.BasePath, "0"},
globalName,
},
}

for _, c := range testcases {
c := c // bypass scopelint false positive
t.Run(c.description, func(t *testing.T) {
ctx, err := newTestContext(c.description, c.flags, c.values)
require.Nil(t, err)
createdCfg, err := createDotConfig(ctx)
require.Nil(t, err)
require.Equal(t, c.expected, createdCfg.Global.Name)
})
}
}
5 changes: 3 additions & 2 deletions cmd/gossamer/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ import (
"golang.org/x/crypto/ssh/terminal" //nolint
)

const confirmCharacter = "Y"

// setupLogger sets up the gossamer logger
func setupLogger(ctx *cli.Context) (log.Lvl, error) {
handler := log.StreamHandler(os.Stdout, log.TerminalFormat())
Expand Down Expand Up @@ -76,7 +78,7 @@ func confirmMessage(msg string) bool {
for {
text, _ := reader.ReadString('\n')
text = strings.ReplaceAll(text, "\n", "")
return strings.Compare("Y", text) == 0
return strings.Compare(confirmCharacter, strings.ToUpper(text)) == 0
}
}

Expand Down Expand Up @@ -124,7 +126,6 @@ func newTestConfigWithFile(t *testing.T) (*dot.Config, *os.File) {
require.NoError(t, err)

tomlCfg := dotConfigToToml(cfg)

cfgFile := exportConfig(tomlCfg, file.Name())
return cfg, cfgFile
}
64 changes: 63 additions & 1 deletion dot/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,13 @@ func InitNode(cfg *Config) error {
return fmt.Errorf("failed to initialise state service: %s", err)
}

err = storeGlobalNodeName(cfg.Global.Name, cfg.Global.BasePath)
if err != nil {
return fmt.Errorf("failed to store global node name: %s", err)
}

logger.Info(
"node initialised",
"node initialised",
EclesioMeloJunior marked this conversation as resolved.
Show resolved Hide resolved
"name", cfg.Global.Name,
"id", cfg.Global.ID,
"basepath", cfg.Global.BasePath,
Expand All @@ -122,6 +127,7 @@ func InitNode(cfg *Config) error {
func NodeInitialized(basepath string, expected bool) bool {
// check if key registry exists
registry := path.Join(basepath, "KEYREGISTRY")

_, err := os.Stat(registry)
if os.IsNotExist(err) {
if expected {
Expand Down Expand Up @@ -167,6 +173,34 @@ func NodeInitialized(basepath string, expected bool) bool {
return true
}

// LoadGlobalNodeName returns the stored global node name from database
func LoadGlobalNodeName(basepath string) (string, error) {
// initialize database using data directory
db, err := state.SetupDatabase(basepath)
if err != nil {
return "", err
}

name, err := state.LoadNodeGlobalName(db)
if err != nil {
logger.Warn(
"failed to load global node name",
"basepath", basepath,
"error", err,
)
return "", nil
}

// close database
err = db.Close()
if err != nil {
logger.Error("failed to close database", "error", err)
return "", err
}

return name, nil
}

// NewNode creates a new dot node from a dot node configuration
func NewNode(cfg *Config, ks *keystore.GlobalKeystore, stopFunc func()) (*Node, error) {
// set garbage collection percent to 10%
Expand Down Expand Up @@ -198,6 +232,7 @@ func NewNode(cfg *Config, ks *keystore.GlobalKeystore, stopFunc func()) (*Node,

// create state service and append state service to node services
stateSrvc, err := createStateService(cfg)

if err != nil {
return nil, fmt.Errorf("failed to create state service: %s", err)
}
Expand Down Expand Up @@ -354,6 +389,33 @@ func setupMetricsServer(address string) {
}()
}

// stores the global node name to reuse
func storeGlobalNodeName(name, basepath string) error {
db, err := state.SetupDatabase(basepath)
if err != nil {
return err
}

err = state.StoreNodeGlobalName(db, name)
if err != nil {
logger.Warn(
"failed to store global node name",
"basepath", basepath,
"error", err,
)
return nil
EclesioMeloJunior marked this conversation as resolved.
Show resolved Hide resolved
}

// close database
err = db.Close()
EclesioMeloJunior marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
logger.Error("failed to close database", "error", err)
return err
}

return nil
}

// Start starts all dot node services
func (n *Node) Start() error {
logger.Info("🕸️ starting node services...")
Expand Down
Loading