Skip to content

Commit

Permalink
cmd, dot: load genesis data for initialized node (#761)
Browse files Browse the repository at this point in the history
  • Loading branch information
ryanchristo committed Jun 24, 2020
1 parent c08bce2 commit 4b58caa
Show file tree
Hide file tree
Showing 6 changed files with 187 additions and 70 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@
*.wasm

test_data
tmp
71 changes: 67 additions & 4 deletions cmd/gossamer/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ import (
"strings"

"github.com/ChainSafe/gossamer/dot"
"github.com/ChainSafe/gossamer/dot/state"
"github.com/ChainSafe/gossamer/lib/common"
"github.com/ChainSafe/gossamer/lib/database"
"github.com/ChainSafe/gossamer/lib/genesis"

log "github.com/ChainSafe/log15"
Expand Down Expand Up @@ -84,7 +87,7 @@ func createDotConfig(ctx *cli.Context) (cfg *dot.Config, err error) {
setDotGlobalConfig(ctx, &cfg.Global)

// ensure configuration values match genesis and overwrite with genesis
updateDotConfigFromGenesis(ctx, cfg)
updateDotConfigFromGenesisJSON(ctx, cfg)

// set remaining cli configuration values
setDotAccountConfig(ctx, &cfg.Account)
Expand All @@ -110,7 +113,7 @@ func createInitConfig(ctx *cli.Context) (cfg *dot.Config, err error) {
setDotInitConfig(ctx, &cfg.Init)

// ensure configuration values match genesis and overwrite with genesis
updateDotConfigFromGenesis(ctx, cfg)
updateDotConfigFromGenesisJSON(ctx, cfg)

return cfg, nil
}
Expand Down Expand Up @@ -295,8 +298,8 @@ func setDotRPCConfig(ctx *cli.Context, cfg *dot.RPCConfig) {
)
}

// updateDotConfigFromGenesis updates the configuration based on the genesis file values
func updateDotConfigFromGenesis(ctx *cli.Context, cfg *dot.Config) {
// updateDotConfigFromGenesisJSON updates the configuration based on the genesis file values
func updateDotConfigFromGenesisJSON(ctx *cli.Context, cfg *dot.Config) {

// load Genesis from genesis configuration file
gen, err := genesis.NewGenesisFromJSON(cfg.Init.Genesis)
Expand Down Expand Up @@ -340,4 +343,64 @@ func updateDotConfigFromGenesis(ctx *cli.Context, cfg *dot.Config) {
log.Warn("[cmd] genesis mismatch, overwriting protocol", "protocol", gen.ProtocolID)
cfg.Network.ProtocolID = gen.ProtocolID
}

log.Debug(
"[cmd] Configuration after genesis json",
"name", cfg.Global.Name,
"id", cfg.Global.ID,
"bootnodes", cfg.Network.Bootnodes,
"protocol", cfg.Network.ProtocolID,
)
}

// updateDotConfigFromGenesisData updates the configuration from genesis data of an initialized node
func updateDotConfigFromGenesisData(ctx *cli.Context, cfg *dot.Config) error {

// initialize database using data directory
db, err := database.NewBadgerDB(cfg.Global.DataDir)
if err != nil {
return fmt.Errorf("failed to create database: %s", err)
}

// load genesis data from initialized node database
gen, err := state.LoadGenesisData(db)
if err != nil {
return fmt.Errorf("failed to load genesis data: %s", err)
}

// check genesis name and use genesis name if --name flag not set
if !ctx.GlobalIsSet(NameFlag.Name) {
cfg.Global.Name = gen.Name
}

// check genesis id and use genesis id if --node flag not set
if !ctx.GlobalIsSet(NodeFlag.Name) {
cfg.Global.ID = gen.ID
}

// check genesis bootnodes and use genesis --bootnodes if name flag not set
if !ctx.GlobalIsSet(BootnodesFlag.Name) {
cfg.Network.Bootnodes = common.BytesToStringArray(gen.Bootnodes)
}

// check genesis protocol and use genesis --protocol if name flag not set
if !ctx.GlobalIsSet(ProtocolFlag.Name) {
cfg.Network.ProtocolID = gen.ProtocolID
}

// close database
err = db.Close()
if err != nil {
return fmt.Errorf("failed to close database: %s", err)
}

log.Debug(
"[cmd] Configuration after genesis data",
"name", cfg.Global.Name,
"id", cfg.Global.ID,
"bootnodes", cfg.Network.Bootnodes,
"protocol", cfg.Network.ProtocolID,
)

return nil
}
117 changes: 71 additions & 46 deletions cmd/gossamer/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import (
"testing"

"github.com/ChainSafe/gossamer/dot"
"github.com/ChainSafe/gossamer/dot/state"
"github.com/ChainSafe/gossamer/lib/database"
"github.com/ChainSafe/gossamer/lib/genesis"
"github.com/ChainSafe/gossamer/lib/utils"

Expand Down Expand Up @@ -472,61 +474,84 @@ func TestRPCConfigFromFlags(t *testing.T) {
}
}

// TestUpdateConfigFromGenesis tests updateDotConfigFromGenesis
func TestUpdateConfigFromGenesis(t *testing.T) {
// TestUpdateConfigFromGenesisJSON tests updateDotConfigFromGenesisJSON
func TestUpdateConfigFromGenesisJSON(t *testing.T) {
testCfg, testCfgFile := dot.NewTestConfigWithFile(t)
require.NotNil(t, testCfg)
require.NotNil(t, testCfgFile)

testGenFile := dot.NewTestGenesisFile(t, testCfg)

defer utils.RemoveTestDir(t)

testGen, err := genesis.NewGenesisFromJSON(testGenFile.Name())
ctx, err := newTestContext(
t.Name(),
[]string{"config", "name"},
[]interface{}{testCfgFile.Name(), "TESTNODE"},
)
require.Nil(t, err)

testApp := cli.NewApp()
testApp.Writer = ioutil.Discard

testcases := []struct {
description string
flags []string
values []interface{}
expected dot.Config
}{
{
"Test gossamer --genesis",
[]string{"config", "genesis"},
[]interface{}{testCfgFile.Name(), testGenFile.Name()},
dot.Config{
Global: dot.GlobalConfig{
Name: testGen.Name, // genesis name
ID: testGen.ID, // genesis id
DataDir: testCfg.Global.DataDir,
},
Account: testCfg.Account,
Core: testCfg.Core,
Network: dot.NetworkConfig{
Port: testCfg.Network.Port,
Bootnodes: testGen.Bootnodes, // genesis bootnodes
ProtocolID: testGen.ProtocolID, // genesis protocol id
NoBootstrap: testCfg.Network.NoBootstrap,
NoMDNS: testCfg.Network.NoMDNS,
},
RPC: testCfg.RPC,
},
expected := &dot.Config{
Global: dot.GlobalConfig{
Name: "TESTNODE",
ID: testCfg.Global.ID,
DataDir: testCfg.Global.DataDir,
},
Account: testCfg.Account,
Core: testCfg.Core,
Network: testCfg.Network,
RPC: testCfg.RPC,
}

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)
cfg, err := createDotConfig(ctx)
require.Nil(t, err)
cfg, err := createDotConfig(ctx)
require.Nil(t, err)

require.Equal(t, &c.expected, cfg)
})
updateDotConfigFromGenesisJSON(ctx, cfg)

require.Equal(t, expected, cfg)
}

func TestUpdateConfigFromGenesisData(t *testing.T) {
testCfg, testCfgFile := dot.NewTestConfigWithFile(t)

defer utils.RemoveTestDir(t)

ctx, err := newTestContext(
t.Name(),
[]string{"config", "name"},
[]interface{}{testCfgFile.Name(), "TESTNODE"},
)
require.Nil(t, err)

expected := &dot.Config{
Global: dot.GlobalConfig{
Name: "TESTNODE",
ID: testCfg.Global.ID,
DataDir: testCfg.Global.DataDir,
},
Account: testCfg.Account,
Core: testCfg.Core,
Network: testCfg.Network,
RPC: testCfg.RPC,
}

cfg, err := createDotConfig(ctx)
require.Nil(t, err)

db, err := database.NewBadgerDB(cfg.Global.DataDir)
require.Nil(t, err)

genFile := dot.NewTestGenesisFile(t, testCfg)

gen, err := genesis.NewGenesisFromJSON(genFile.Name())
require.Nil(t, err)

gen.Name = "TESTNODE" // simulate initialized node with name

err = state.StoreGenesisData(db, gen.GenesisData())
require.Nil(t, err)

err = db.Close()
require.Nil(t, err)

err = updateDotConfigFromGenesisData(ctx, cfg) // name should not be updated if provided as flag value
require.Nil(t, err)

require.Equal(t, expected, cfg)
}
23 changes: 16 additions & 7 deletions cmd/gossamer/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,19 +119,29 @@ func gossamerAction(ctx *cli.Context) error {
return err
}

// expand data directory and update node configuration (performed separate
// expand data directory and update node configuration (performed separately
// from createDotConfig because dot config should not include expanded path)
cfg.Global.DataDir = utils.ExpandDir(cfg.Global.DataDir)

// check if node has not been initialized
if !dot.NodeInitialized(cfg) {
// check if node has not been initialized (expected true - add warning log)
if !dot.NodeInitialized(cfg, true) {

// initialize node (initialize databases and load genesis data)
err = dot.InitNode(cfg)
if err != nil {
log.Error("[cmd] Failed to initialize node", "error", err)
return err
}
}

// ensure configuration matches genesis data stored during node initialization
// and overwrite with flag values if flag values are provided
err = updateDotConfigFromGenesisData(ctx, cfg)
if err != nil {
log.Error("[cmd] Failed to update config from genesis data", "error", err)
return err
}

ks, err := keystore.LoadKeystore(cfg.Account.Key)
if err != nil {
log.Error("[cmd] Failed to load keystore", "error", err)
Expand Down Expand Up @@ -173,16 +183,15 @@ func initAction(ctx *cli.Context) error {
return err
}

// expand data directory and update node configuration (performed separate
// expand data directory and update node configuration (performed separately
// from createDotConfig because dot config should not include expanded path)
cfg.Global.DataDir = utils.ExpandDir(cfg.Global.DataDir)

// check if node has been initialized
if dot.NodeInitialized(cfg) {
// check if node has been initialized (expected false - no warning log)
if dot.NodeInitialized(cfg, false) {

// TODO: prompt user to confirm when reinitializing a node #760
log.Warn("[cmd] Node has already been initialized, reinitializing node...")

}

// initialize node (initialize databases and load genesis data)
Expand Down
43 changes: 31 additions & 12 deletions dot/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (

"github.com/ChainSafe/gossamer/dot/network"
"github.com/ChainSafe/gossamer/dot/state"
"github.com/ChainSafe/gossamer/lib/common"
"github.com/ChainSafe/gossamer/lib/genesis"
"github.com/ChainSafe/gossamer/lib/keystore"
"github.com/ChainSafe/gossamer/lib/services"
Expand Down Expand Up @@ -74,8 +75,18 @@ func InitNode(cfg *Config) error {
// create new state service
stateSrvc := state.NewService(cfg.Global.DataDir)

// declare genesis data
data := gen.GenesisData()

// set genesis data using configuration values (assumes the genesis values
// have already been loaded into the configuration)
data.Name = cfg.Global.Name
data.ID = cfg.Global.ID
data.Bootnodes = common.StringArrayToBytes(cfg.Network.Bootnodes)
data.ProtocolID = cfg.Network.ProtocolID

// initialize state service with genesis data, block, and trie
err = stateSrvc.Initialize(gen.GenesisData(), header, t)
err = stateSrvc.Initialize(data, header, t)
if err != nil {
return fmt.Errorf("failed to initialize state service: %s", err)
}
Expand All @@ -94,29 +105,33 @@ func InitNode(cfg *Config) error {

// NodeInitialized returns true if, within the configured data directory for the
// node, the state database has been created and the genesis data has been loaded
func NodeInitialized(cfg *Config) bool {
func NodeInitialized(cfg *Config, expected bool) bool {

// check if key registry exists
registry := path.Join(cfg.Global.DataDir, "KEYREGISTRY")
_, err := os.Stat(registry)
if os.IsNotExist(err) {
log.Warn(
"[dot] Node has not been initialized",
"datadir", cfg.Global.DataDir,
"error", "failed to locate KEYREGISTRY file in data directory",
)
if expected {
log.Warn(
"[dot] Node has not been initialized",
"datadir", cfg.Global.DataDir,
"error", "failed to locate KEYREGISTRY file in data directory",
)
}
return false
}

// check if manifest exists
manifest := path.Join(cfg.Global.DataDir, "MANIFEST")
_, err = os.Stat(manifest)
if os.IsNotExist(err) {
log.Warn(
"[dot] Node has not been initialized",
"datadir", cfg.Global.DataDir,
"error", "failed to locate MANIFEST file in data directory",
)
if expected {
log.Warn(
"[dot] Node has not been initialized",
"datadir", cfg.Global.DataDir,
"error", "failed to locate MANIFEST file in data directory",
)
}
return false
}

Expand All @@ -137,7 +152,11 @@ func NewNode(cfg *Config, ks *keystore.Keystore) (*Node, error) {

log.Info(
"[dot] Creating node services...",
"name", cfg.Global.Name,
"id", cfg.Global.ID,
"datadir", cfg.Global.DataDir,
"bootnodes", cfg.Network.Bootnodes,
"protocol", cfg.Network.ProtocolID,
)

var nodeSrvcs []services.Service
Expand Down
2 changes: 1 addition & 1 deletion dot/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ func NewTestConfig(t *testing.T) *Config {
},
Network: NetworkConfig{
Port: uint32(7001),
Bootnodes: []string(nil),
Bootnodes: []string{"/ip4/104.131.131.82/tcp/4001/ipfs/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ"},
ProtocolID: string("/gossamer/test/0"),
NoBootstrap: false,
NoMDNS: false,
Expand Down

0 comments on commit 4b58caa

Please sign in to comment.