diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index b608341cb0..c102f9bd5e 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -23,6 +23,7 @@ jobs: matrix: packages: [ + github.com/ChainSafe/gossamer/dot, github.com/ChainSafe/gossamer/dot/core, github.com/ChainSafe/gossamer/dot/rpc/modules, github.com/ChainSafe/gossamer/lib/babe, diff --git a/cmd/gossamer/config_test.go b/cmd/gossamer/config_test.go index c3d6e6aace..910ee770f1 100644 --- a/cmd/gossamer/config_test.go +++ b/cmd/gossamer/config_test.go @@ -4,8 +4,12 @@ package main import ( + "encoding/hex" + "encoding/json" "errors" "io" + "os" + "path/filepath" "testing" "time" @@ -17,8 +21,8 @@ import ( "github.com/ChainSafe/gossamer/dot/types" "github.com/ChainSafe/gossamer/internal/log" "github.com/ChainSafe/gossamer/lib/genesis" + "github.com/ChainSafe/gossamer/lib/runtime" "github.com/ChainSafe/gossamer/lib/utils" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/urfave/cli" @@ -767,7 +771,7 @@ func TestRPCConfigFromFlags(t *testing.T) { // TestUpdateConfigFromGenesisJSON tests updateDotConfigFromGenesisJSON func TestUpdateConfigFromGenesisJSON(t *testing.T) { testCfg, testCfgFile := newTestConfigWithFile(t) - genFile := dot.NewTestGenesisRawFile(t, testCfg) + genFile := newTestGenesisRawFile(t, testCfg) ctx, err := newTestContext( t.Name(), @@ -871,7 +875,7 @@ func TestUpdateConfigFromGenesisJSON_Default(t *testing.T) { func TestUpdateConfigFromGenesisData(t *testing.T) { testCfg, testCfgFile := newTestConfigWithFile(t) - genFile := dot.NewTestGenesisRawFile(t, testCfg) + genFile := newTestGenesisRawFile(t, testCfg) ctx, err := newTestContext( t.Name(), @@ -948,19 +952,50 @@ func TestGlobalNodeName_WhenNodeAlreadyHasStoredName(t *testing.T) { // Initialise a node with a random name globalName := dot.RandomNodeName() - cfg := dot.NewTestConfig(t) + cfg := newTestConfig(t) cfg.Global.Name = globalName require.NotNil(t, cfg) - genPath := dot.NewTestGenesisAndRuntime(t) - require.NotNil(t, genPath) + runtimeFilePath := filepath.Join(t.TempDir(), "runtime") + _, testRuntimeURL := runtime.GetRuntimeVars(runtime.NODE_RUNTIME) + err := runtime.GetRuntimeBlob(runtimeFilePath, testRuntimeURL) + require.NoError(t, err) + runtimeData, err := os.ReadFile(runtimeFilePath) + require.NoError(t, err) + + fp := utils.GetGssmrGenesisRawPathTest(t) + + gssmrGen, err := genesis.NewGenesisFromJSONRaw(fp) + require.NoError(t, err) + + gen := &genesis.Genesis{ + Name: "test", + ID: "test", + Bootnodes: []string(nil), + ProtocolID: "/gossamer/test/0", + Genesis: gssmrGen.GenesisFields(), + } + + gen.Genesis.Raw = map[string]map[string]string{ + "top": { + "0x3a636f6465": "0x" + hex.EncodeToString(runtimeData), + "0xcf722c0832b5231d35e29f319ff27389f5032bfc7bfc3ba5ed7839f2042fb99f": "0x0000000000000001", + }, + } + + genData, err := json.Marshal(gen) + require.NoError(t, err) + + genPath := filepath.Join(t.TempDir(), "genesis.json") + err = os.WriteFile(genPath, genData, os.ModePerm) + require.NoError(t, err) cfg.Core.Roles = types.FullNodeRole cfg.Core.BabeAuthority = false cfg.Core.GrandpaAuthority = false cfg.Init.Genesis = genPath - err := dot.InitNode(cfg) + err = dot.InitNode(cfg) require.NoError(t, err) // call another command and test the name @@ -1302,3 +1337,31 @@ func Test_setLogConfig(t *testing.T) { }) } } + +// newTestGenesisRawFile returns a test genesis file using "gssmr" raw data +func newTestGenesisRawFile(t *testing.T, cfg *dot.Config) (filename string) { + t.Helper() + + filename = filepath.Join(t.TempDir(), "genesis.json") + + fp := utils.GetGssmrGenesisRawPathTest(t) + + gssmrGen, err := genesis.NewGenesisFromJSONRaw(fp) + require.NoError(t, err) + + gen := &genesis.Genesis{ + Name: cfg.Global.Name, + ID: cfg.Global.ID, + Bootnodes: cfg.Network.Bootnodes, + ProtocolID: cfg.Network.ProtocolID, + Genesis: gssmrGen.GenesisFields(), + } + + b, err := json.Marshal(gen) + require.NoError(t, err) + + err = os.WriteFile(filename, b, os.ModePerm) + require.NoError(t, err) + + return filename +} diff --git a/cmd/gossamer/export_test.go b/cmd/gossamer/export_test.go index 5f36e045aa..29b6cb7c66 100644 --- a/cmd/gossamer/export_test.go +++ b/cmd/gossamer/export_test.go @@ -20,7 +20,7 @@ import ( func TestExportCommand(t *testing.T) { testCfg, testConfigFile := newTestConfigWithFile(t) testDir := testCfg.Global.BasePath - genFile := dot.NewTestGenesisRawFile(t, testCfg) + genFile := newTestGenesisRawFile(t, testCfg) testApp := cli.NewApp() testApp.Writer = io.Discard diff --git a/cmd/gossamer/flags_test.go b/cmd/gossamer/flags_test.go index 2234c82470..0e649f8a68 100644 --- a/cmd/gossamer/flags_test.go +++ b/cmd/gossamer/flags_test.go @@ -8,8 +8,6 @@ import ( "testing" "github.com/ChainSafe/gossamer/chain/dev" - "github.com/ChainSafe/gossamer/dot" - "github.com/stretchr/testify/require" "github.com/urfave/cli" ) @@ -17,7 +15,7 @@ import ( // TestFixFlagOrder tests the FixFlagOrder method func TestFixFlagOrder(t *testing.T) { testCfg, testConfig := newTestConfigWithFile(t) - genFile := dot.NewTestGenesisRawFile(t, testCfg) + genFile := newTestGenesisRawFile(t, testCfg) testApp := cli.NewApp() testApp.Writer = io.Discard diff --git a/cmd/gossamer/toml_config_test.go b/cmd/gossamer/toml_config_test.go index 21fad0cee9..9d2ce625e3 100644 --- a/cmd/gossamer/toml_config_test.go +++ b/cmd/gossamer/toml_config_test.go @@ -18,7 +18,7 @@ func TestLoadConfig(t *testing.T) { cfg, cfgFile := newTestConfigWithFile(t) require.NotNil(t, cfg) - genFile := dot.NewTestGenesisRawFile(t, cfg) + genFile := newTestGenesisRawFile(t, cfg) cfg.Init.Genesis = genFile diff --git a/dot/build_spec.go b/dot/build_spec.go index 9f539ccd6e..361fb151ad 100644 --- a/dot/build_spec.go +++ b/dot/build_spec.go @@ -78,8 +78,7 @@ func WriteGenesisSpecFile(data []byte, fp string) error { return err } - writeConfig(data, fp) - return nil + return os.WriteFile(fp, data, 0600) } // BuildFromDB builds a BuildSpec from the DB located at path diff --git a/dot/build_spec_integration_test.go b/dot/build_spec_integration_test.go index db0256e80a..e70e916a6e 100644 --- a/dot/build_spec_integration_test.go +++ b/dot/build_spec_integration_test.go @@ -7,7 +7,6 @@ package dot import ( "encoding/json" - "fmt" "os" "path/filepath" "testing" @@ -20,7 +19,7 @@ import ( // hex encoding for ":code", used as key for code is raw genesis files. const codeHex = "0x3a636f6465" -func TestBuildFromGenesis(t *testing.T) { +func TestBuildFromGenesis_Integration(t *testing.T) { t.Parallel() file := genesis.CreateTestGenesisJSONFile(t, false) @@ -69,20 +68,7 @@ func TestBuildFromGenesis_WhenGenesisDoesNotExists(t *testing.T) { require.ErrorIs(t, err, os.ErrNotExist) } -func TestWriteGenesisSpecFileWhenFileAlreadyExists(t *testing.T) { - t.Parallel() - - filePath := filepath.Join(t.TempDir(), "genesis.raw") - someBytes := []byte("Testing some bytes") - err := WriteGenesisSpecFile(someBytes, filePath) - - require.EqualError(t, err, - fmt.Sprintf("file %s already exists, rename to avoid overwriting", filePath)) -} - -func TestWriteGenesisSpecFile(t *testing.T) { - t.Parallel() - +func TestWriteGenesisSpecFile_Integration(t *testing.T) { cfg := NewTestConfig(t) cfg.Init.Genesis = utils.GetGssmrGenesisRawPathTest(t) @@ -101,11 +87,13 @@ func TestWriteGenesisSpecFile(t *testing.T) { tmpFile := filepath.Join(t.TempDir(), "unique-raw-genesis.json") err = WriteGenesisSpecFile(data, tmpFile) require.NoError(t, err) - require.FileExists(t, tmpFile) file, err := os.Open(tmpFile) require.NoError(t, err) - defer file.Close() + t.Cleanup(func() { + err := file.Close() + require.NoError(t, err) + }) gen := new(genesis.Genesis) @@ -118,9 +106,7 @@ func TestWriteGenesisSpecFile(t *testing.T) { } -func TestBuildFromDB(t *testing.T) { - t.Parallel() - +func TestBuildFromDB_Integration(t *testing.T) { // setup expected cfg := NewTestConfig(t) cfg.Init.Genesis = utils.GetGssmrGenesisRawPathTest(t) diff --git a/dot/build_spec_test.go b/dot/build_spec_test.go new file mode 100644 index 0000000000..9144f45f4d --- /dev/null +++ b/dot/build_spec_test.go @@ -0,0 +1,281 @@ +// Copyright 2021 ChainSafe Systems (ON) +// SPDX-License-Identifier: LGPL-3.0-only + +package dot + +import ( + "errors" + "os" + "path/filepath" + "strings" + "testing" + + "github.com/ChainSafe/gossamer/lib/genesis" + "github.com/ChainSafe/gossamer/lib/utils" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestBuildSpec_ToJSON(t *testing.T) { + tests := []struct { + name string + buildSpec *BuildSpec + want string + err error + }{ + { + name: "name test", + buildSpec: &BuildSpec{ + genesis: &genesis.Genesis{Name: "test"}, + }, + want: ` +{ + "name": "test", + "id": "", + "chainType": "", + "bootNodes": null, + "telemetryEndpoints": null, + "protocolId": "", + "genesis": {}, + "properties": null, + "forkBlocks": null, + "badBlocks": null, + "consensusEngine": "", + "codeSubstitutes": null +}`, + }, + { + name: "additional parameters test", + buildSpec: &BuildSpec{ + genesis: &genesis.Genesis{ + Name: "test", + ID: "ID", + ChainType: "chainType", + ProtocolID: "protocol", + ConsensusEngine: "babe", + }, + }, + want: ` +{ + "name": "test", + "id": "ID", + "chainType": "chainType", + "bootNodes": null, + "telemetryEndpoints": null, + "protocolId": "protocol", + "genesis": {}, + "properties": null, + "forkBlocks": null, + "badBlocks": null, + "consensusEngine": "", + "codeSubstitutes": null +}`, + }, + { + name: "normal conditions", + buildSpec: &BuildSpec{ + genesis: &genesis.Genesis{ + Name: "test", + ID: "ID", + ChainType: "chainType", + Bootnodes: []string{"node1", "node2"}, + TelemetryEndpoints: []interface{}{"endpoint"}, + ProtocolID: "protocol", + Genesis: genesis.Fields{}, + Properties: map[string]interface{}{"key": "value"}, + ForkBlocks: []string{"1", "2"}, + BadBlocks: []string{"3", "4"}, + ConsensusEngine: "babe", + CodeSubstitutes: map[string]string{"key": "value"}, + }, + }, + want: ` +{ + "name": "test", + "id": "ID", + "chainType": "chainType", + "bootNodes": [ + "node1", + "node2" + ], + "telemetryEndpoints": null, + "protocolId": "protocol", + "genesis": {}, + "properties": { + "key": "value" + }, + "forkBlocks": null, + "badBlocks": null, + "consensusEngine": "", + "codeSubstitutes": null +}`, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := tt.buildSpec.ToJSON() + assert.ErrorIs(t, err, tt.err) + assert.Equal(t, strings.TrimSpace(tt.want), string(got)) + }) + } +} + +func TestBuildFromDB(t *testing.T) { + // initialise node (initialise state database and load genesis data) + cfg := NewTestConfig(t) + cfg.Init.Genesis = utils.GetGssmrGenesisRawPathTest(t) + builder := nodeBuilder{} + err := builder.initNode(cfg) + require.NoError(t, err) + + tests := []struct { + name string + path string + want *BuildSpec + err error + }{ + {name: "normal conditions", path: cfg.Global.BasePath, + want: &BuildSpec{genesis: &genesis.Genesis{Name: "Gossamer"}}}, + {name: "invalid db path", path: t.TempDir(), + err: errors.New("cannot start state service: failed to create block state: cannot get block 0: Key not found")}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := BuildFromDB(tt.path) + if tt.err != nil { + assert.EqualError(t, err, tt.err.Error()) + } else { + assert.NoError(t, err) + } + if tt.want != nil { + assert.Equal(t, tt.want.genesis.Name, got.genesis.Name) + } + }) + } +} + +func TestBuildFromGenesis(t *testing.T) { + testGenesisPath := genesis.CreateTestGenesisJSONFile(t, false) + + type args struct { + path string + authCount int + } + tests := []struct { + name string + args args + want *BuildSpec + err error + }{ + { + name: "invalid file path", + args: args{ + path: "/invalid/path", + }, + err: errors.New("open /invalid/path: no such file or directory"), + }, + { + name: "normal conditions", + args: args{ + path: testGenesisPath, + }, + want: &BuildSpec{genesis: &genesis.Genesis{Name: "test"}}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := BuildFromGenesis(tt.args.path, tt.args.authCount) + if tt.err != nil { + assert.EqualError(t, err, tt.err.Error()) + } else { + assert.NoError(t, err) + } + if tt.want != nil { + assert.Equal(t, tt.want.genesis.Name, got.genesis.Name) + } + }) + } +} + +func TestBuildSpec_ToJSONRaw(t *testing.T) { + tests := []struct { + name string + genesis *genesis.Genesis + want string + err error + }{ + { + name: "normal conditions", + genesis: &genesis.Genesis{Name: "test"}, + want: `{ + "name": "test", + "id": "", + "chainType": "", + "bootNodes": null, + "telemetryEndpoints": null, + "protocolId": "", + "genesis": {}, + "properties": null, + "forkBlocks": null, + "badBlocks": null, + "consensusEngine": "", + "codeSubstitutes": null +}`, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + b := &BuildSpec{ + genesis: tt.genesis, + } + got, err := b.ToJSONRaw() + assert.ErrorIs(t, err, tt.err) + assert.Equal(t, tt.want, string(got)) + }) + } +} + +func TestWriteGenesisSpecFile(t *testing.T) { + type args struct { + data []byte + fp string + } + tests := []struct { + name string + args args + touchFile bool + }{ + { + name: "normal conditions", + args: args{ + data: []byte{1}, + fp: filepath.Join(t.TempDir(), "test.file"), + }, + }, + { + name: "existing file", + args: args{ + data: []byte{1}, + }, + touchFile: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var expectedErrMessage error + if tt.touchFile { + path := filepath.Join(t.TempDir(), "test.txt") + err := os.WriteFile(path, nil, os.ModePerm) + require.NoError(t, err) + tt.args.fp = path + expectedErrMessage = errors.New("file " + path + " already exists, rename to avoid overwriting") + } + err := WriteGenesisSpecFile(tt.args.data, tt.args.fp) + if expectedErrMessage != nil { + assert.EqualError(t, err, expectedErrMessage.Error()) + } else { + assert.NoError(t, err) + } + }) + } +} diff --git a/dot/config_integration_test.go b/dot/config_integration_test.go deleted file mode 100644 index b2dbd00311..0000000000 --- a/dot/config_integration_test.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2021 ChainSafe Systems (ON) -// SPDX-License-Identifier: LGPL-3.0-only - -//go:build integration - -package dot - -import ( - "os" - "testing" - - "github.com/stretchr/testify/require" -) - -func TestExportConfig(t *testing.T) { - cfg, cfgFile := newTestConfigWithFile(t) - - genFile := NewTestGenesisRawFile(t, cfg) - - cfg.Init.Genesis = genFile - - err := InitNode(cfg) - require.NoError(t, err) - - file := exportConfig(cfg, cfgFile.Name()) - require.NotNil(t, file) - os.Remove(file.Name()) -} diff --git a/dot/config_test.go b/dot/config_test.go new file mode 100644 index 0000000000..81a3e644e5 --- /dev/null +++ b/dot/config_test.go @@ -0,0 +1,473 @@ +// Copyright 2021 ChainSafe Systems (ON) +// SPDX-License-Identifier: LGPL-3.0-only + +package dot + +import ( + "testing" + "time" + + "github.com/ChainSafe/gossamer/internal/log" + "github.com/ChainSafe/gossamer/internal/pprof" + "github.com/stretchr/testify/assert" +) + +func TestConfig(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + want *Config + configMaker func() *Config + }{ + { + name: "dev default", + want: &Config{ + Global: GlobalConfig{ + Name: "Gossamer", + ID: "dev", + BasePath: "~/.gossamer/dev", + LogLvl: log.Info, + MetricsAddress: ":9876", + RetainBlocks: 512, + Pruning: "archive", + }, + Log: LogConfig{ + CoreLvl: log.Info, + DigestLvl: log.Info, + SyncLvl: log.Info, + NetworkLvl: log.Info, + RPCLvl: log.Info, + StateLvl: log.Info, + RuntimeLvl: log.Info, + BlockProducerLvl: log.Info, + FinalityGadgetLvl: log.Info, + }, + Init: InitConfig{ + Genesis: "./chain/dev/genesis-spec.json", + }, + Account: AccountConfig{ + Key: "alice", + }, + Core: CoreConfig{ + Roles: byte(4), + BabeAuthority: true, + BABELead: true, + GrandpaAuthority: true, + WasmInterpreter: "wasmer", + GrandpaInterval: 0, + }, + Network: NetworkConfig{ + Port: 7001, + }, + RPC: RPCConfig{ + Enabled: true, + External: false, + Unsafe: false, + UnsafeExternal: false, + Port: 8545, + Host: "localhost", + Modules: []string{"system", "author", "chain", "state", "rpc", "grandpa", "offchain", + "childstate", "syncstate", "payment"}, + WSPort: 8546, + WS: true, + }, + Pprof: PprofConfig{ + Enabled: true, + Settings: pprof.Settings{ + ListeningAddress: "localhost:6060", + }, + }, + }, + configMaker: DevConfig, + }, + { + name: "gossamer default", + want: &Config{ + Global: GlobalConfig{ + Name: "Gossamer", + ID: "gssmr", + BasePath: "~/.gossamer/gssmr", + LogLvl: log.Info, + MetricsAddress: "localhost:9876", + RetainBlocks: 512, + Pruning: "archive", + }, + Log: LogConfig{ + CoreLvl: log.Info, + DigestLvl: log.Info, + SyncLvl: log.Info, + NetworkLvl: log.Info, + RPCLvl: log.Info, + StateLvl: log.Info, + RuntimeLvl: log.Info, + BlockProducerLvl: log.Info, + FinalityGadgetLvl: log.Info, + }, + Init: InitConfig{ + Genesis: "./chain/gssmr/genesis-spec.json", + }, + Account: AccountConfig{}, + Core: CoreConfig{ + Roles: byte(4), + BabeAuthority: true, + GrandpaAuthority: true, + WasmInterpreter: "wasmer", + GrandpaInterval: time.Second, + }, + Network: NetworkConfig{ + Port: 7001, + MinPeers: 1, + MaxPeers: 50, + DiscoveryInterval: time.Second * 10, + }, + RPC: RPCConfig{ + Port: 8545, + Host: "localhost", + Modules: []string{"system", "author", "chain", "state", "rpc", "grandpa", "offchain", + "childstate", "syncstate", "payment"}, + WSPort: 8546, + WS: false, + WSExternal: false, + WSUnsafe: false, + WSUnsafeExternal: false, + }, + Pprof: PprofConfig{ + Enabled: true, + Settings: pprof.Settings{ + ListeningAddress: "localhost:6060", + BlockProfileRate: 0, + }, + }, + }, + configMaker: GssmrConfig, + }, + { + name: "kusama default", + want: &Config{ + Global: GlobalConfig{ + Name: "Kusama", + ID: "ksmcc3", + BasePath: "~/.gossamer/kusama", + LogLvl: log.Info, + MetricsAddress: "localhost:9876", + RetainBlocks: 512, + Pruning: "archive", + }, + Log: LogConfig{ + CoreLvl: log.Info, + DigestLvl: log.Info, + SyncLvl: log.Info, + NetworkLvl: log.Info, + RPCLvl: log.Info, + StateLvl: log.Info, + RuntimeLvl: log.Info, + BlockProducerLvl: log.Info, + FinalityGadgetLvl: log.Info, + }, + Init: InitConfig{ + Genesis: "./chain/kusama/genesis.json", + }, + Account: AccountConfig{}, + Core: CoreConfig{ + Roles: byte(1), + WasmInterpreter: "wasmer", + GrandpaInterval: 0, + }, + Network: NetworkConfig{ + Port: 7001, + Bootnodes: nil, + ProtocolID: "", + NoBootstrap: false, + NoMDNS: false, + MinPeers: 0, + MaxPeers: 0, + PersistentPeers: nil, + DiscoveryInterval: 0, + PublicIP: "", + PublicDNS: "", + }, + RPC: RPCConfig{ + Port: 8545, + Host: "localhost", + Modules: []string{"system", "author", "chain", "state", "rpc", "grandpa", "offchain", + "childstate", "syncstate", "payment"}, + WSPort: 8546, + }, + Pprof: PprofConfig{ + Settings: pprof.Settings{ + ListeningAddress: "localhost:6060", + }, + }, + }, + configMaker: KusamaConfig, + }, + { + name: "polkadot default", + want: &Config{ + Global: GlobalConfig{ + Name: "Polkadot", + ID: "polkadot", + BasePath: "~/.gossamer/polkadot", + LogLvl: log.Info, + MetricsAddress: "localhost:9876", + RetainBlocks: 512, + Pruning: "archive", + }, + Log: LogConfig{ + CoreLvl: log.Info, + DigestLvl: log.Info, + SyncLvl: log.Info, + NetworkLvl: log.Info, + RPCLvl: log.Info, + StateLvl: log.Info, + RuntimeLvl: log.Info, + BlockProducerLvl: log.Info, + FinalityGadgetLvl: log.Info, + }, + Init: InitConfig{Genesis: "./chain/polkadot/genesis.json"}, + Core: CoreConfig{ + Roles: byte(1), + WasmInterpreter: "wasmer", + }, + Network: NetworkConfig{ + Port: 7001, + }, + RPC: RPCConfig{ + Port: 8545, + Host: "localhost", + Modules: []string{"system", "author", "chain", "state", "rpc", "grandpa", "offchain", + "childstate", "syncstate", "payment"}, + WSPort: 8546, + }, + Pprof: PprofConfig{ + Settings: pprof.Settings{ + ListeningAddress: "localhost:6060", + }, + }, + }, + configMaker: PolkadotConfig, + }, + } + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + got := tt.configMaker() + assert.Equal(t, tt.want, got) + }) + } +} + +func TestRPCConfig_isRPCEnabled(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + rpcConfig *RPCConfig + want bool + }{ + { + name: "default", + rpcConfig: &RPCConfig{}, + want: false, + }, + { + name: "enabled true", + rpcConfig: &RPCConfig{Enabled: true}, + want: true, + }, + { + name: "external true", + rpcConfig: &RPCConfig{External: true}, + want: true, + }, + { + name: "unsafe true", + rpcConfig: &RPCConfig{Unsafe: true}, + want: true, + }, + { + name: "unsafe external true", + rpcConfig: &RPCConfig{UnsafeExternal: true}, + want: true, + }, + } + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + got := tt.rpcConfig.isRPCEnabled() + assert.Equal(t, tt.want, got) + }) + } +} + +func TestRPCConfig_isWSEnabled(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + rpcConfig *RPCConfig + want bool + }{ + { + name: "default", + rpcConfig: &RPCConfig{}, + want: false, + }, + { + name: "ws true", + rpcConfig: &RPCConfig{WS: true}, + want: true, + }, + { + name: "ws external true", + rpcConfig: &RPCConfig{WSExternal: true}, + want: true, + }, + { + name: "ws unsafe true", + rpcConfig: &RPCConfig{WSUnsafe: true}, + want: true, + }, + { + name: "ws unsafe external true", + rpcConfig: &RPCConfig{WSUnsafeExternal: true}, + want: true, + }, + } + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + got := tt.rpcConfig.isWSEnabled() + assert.Equal(t, tt.want, got) + }) + } +} + +func Test_networkServiceEnabled(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + config *Config + want bool + }{ + { + name: "dev config", + config: DevConfig(), + want: true, + }, + { + name: "empty config", + config: &Config{}, + want: false, + }, + { + name: "core roles 0", + config: &Config{ + Core: CoreConfig{ + Roles: 0, + }, + }, + want: false, + }, + { + name: "core roles 1", + config: &Config{ + Core: CoreConfig{ + Roles: 1, + }, + }, + want: true, + }, + } + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + got := networkServiceEnabled(tt.config) + assert.Equal(t, tt.want, got) + }) + } +} + +func TestRPCConfig_String(t *testing.T) { + tests := []struct { + name string + rpcConfig RPCConfig + want string + }{ + { + name: "default base case", + rpcConfig: RPCConfig{}, + want: "enabled=false external=false unsafe=false unsafeexternal=false port=0 host= modules= wsport=0 ws" + + "=false wsexternal=false wsunsafe=false wsunsafeexternal=false", + }, + { + name: "fields changed", + rpcConfig: RPCConfig{ + Enabled: true, + External: true, + Unsafe: true, + UnsafeExternal: true, + Port: 1234, + Host: "5678", + Modules: nil, + WSPort: 2345, + WS: true, + WSExternal: true, + WSUnsafe: true, + WSUnsafeExternal: true, + }, + want: "enabled=true external=true unsafe=true unsafeexternal=true port=1234 host=5678 modules= wsport" + + "=2345 ws=true wsexternal=true wsunsafe=true wsunsafeexternal=true", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equal(t, tt.want, tt.rpcConfig.String()) + }) + } +} + +func TestLogConfig_String(t *testing.T) { + tests := []struct { + name string + logConfig LogConfig + want string + }{ + { + name: "default case", + logConfig: LogConfig{}, + want: "core: CRIT, digest: CRIT, sync: CRIT, network: CRIT, rpc: CRIT, state: CRIT, runtime: CRIT, " + + "block producer: CRIT, finality gadget: CRIT", + }, + { + name: "change fields case", + logConfig: LogConfig{ + CoreLvl: log.Debug, + DigestLvl: log.Info, + SyncLvl: log.Warn, + NetworkLvl: log.Error, + RPCLvl: log.Critical, + StateLvl: log.Debug, + RuntimeLvl: log.Info, + BlockProducerLvl: log.Warn, + FinalityGadgetLvl: log.Error, + }, + want: "core: DBUG, digest: INFO, sync: WARN, network: EROR, rpc: CRIT, state: DBUG, runtime: INFO, " + + "block producer: WARN, finality gadget: EROR", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equal(t, tt.want, tt.logConfig.String()) + }) + } +} diff --git a/dot/core/service.go b/dot/core/service.go index e267b5a051..241cf978a0 100644 --- a/dot/core/service.go +++ b/dot/core/service.go @@ -7,6 +7,7 @@ import ( "bytes" "context" "errors" + "reflect" "sync" "github.com/ChainSafe/gossamer/dot/network" @@ -92,7 +93,7 @@ func NewService(cfg *Config) (*Service, error) { return nil, ErrNilStorageState } - if cfg.Network == nil { + if reflect.ValueOf(cfg.Network).IsNil() { return nil, ErrNilNetwork } diff --git a/dot/import_integration_test.go b/dot/import_integration_test.go index 5699cf4440..52054b1bea 100644 --- a/dot/import_integration_test.go +++ b/dot/import_integration_test.go @@ -6,10 +6,7 @@ package dot import ( - "encoding/json" - "io/ioutil" "os" - "path/filepath" "testing" "github.com/ChainSafe/gossamer/dot/state" @@ -20,47 +17,6 @@ import ( "github.com/stretchr/testify/require" ) -func setupStateFile(t *testing.T) string { - t.Helper() - - const filename = "../lib/runtime/test_data/kusama/block1482002.out" - - data, err := ioutil.ReadFile(filename) - require.NoError(t, err) - - rpcPairs := make(map[string]interface{}) - err = json.Unmarshal(data, &rpcPairs) - require.NoError(t, err) - pairs := rpcPairs["result"].([]interface{}) - - bz, err := json.Marshal(pairs) - require.NoError(t, err) - - fp := filepath.Join(t.TempDir(), "state.json") - err = ioutil.WriteFile(fp, bz, 0777) - require.NoError(t, err) - - return fp -} - -func setupHeaderFile(t *testing.T) string { - t.Helper() - - const headerStr = `{` + - `"digest":{"logs":[` + - `"0x0642414245b501013c0000009659bd0f0000000070edad1c9064fff78cb18435223d8adaf5ea04c24b1a8766e3dc01eb03cc6a0c11b79793d4e31cc0990838229c44fed1669a7c7c79e1e6d0a96374d6496728069d1ef739e290497a0e3b728fa88fcbdd3a5504e0efde0242e7a806dd4fa9260c",` + //nolint:lll - `"0x054241424501019e7f28dddcf27c1e6b328d5694c368d5b2ec5dbe0e412ae1c98f88d53be4d8502fac571f3f19c9caaf281a673319241e0c5095a683ad34316204088a36a4bd86"` + //nolint:lll - `]},` + - `"extrinsicsRoot":"0xda26dc8c1455f8f81cae12e4fc59e23ce961b2c837f6d3f664283af906d344e0",` + - `"number":"0x169d12",` + - `"parentHash":"0x3b45c9c22dcece75a30acc9c2968cb311e6b0557350f83b430f47559db786975",` + - `"stateRoot":"0x09f9ca28df0560c2291aa16b56e15e07d1e1927088f51356d522722aa90ca7cb"}` - fp := filepath.Join(t.TempDir(), "header.json") - err := ioutil.WriteFile(fp, []byte(headerStr), 0777) - require.NoError(t, err) - return fp -} - func TestNewTrieFromPairs(t *testing.T) { fp := setupStateFile(t) trie, err := newTrieFromPairs(fp) @@ -92,13 +48,13 @@ func TestNewHeaderFromFile(t *testing.T) { require.Equal(t, expected, header) } -func TestImportState(t *testing.T) { +func TestImportState_Integration(t *testing.T) { basepath := os.TempDir() cfg := NewTestConfig(t) require.NotNil(t, cfg) - genFile := NewTestGenesisRawFile(t, cfg) + genFile := newTestGenesisRawFile(t, cfg) cfg.Init.Genesis = genFile diff --git a/dot/import_test.go b/dot/import_test.go new file mode 100644 index 0000000000..59f825cbd8 --- /dev/null +++ b/dot/import_test.go @@ -0,0 +1,225 @@ +// Copyright 2021 ChainSafe Systems (ON) +// SPDX-License-Identifier: LGPL-3.0-only + +package dot + +import ( + "encoding/json" + "errors" + "os" + "path/filepath" + "testing" + + "github.com/ChainSafe/gossamer/dot/types" + "github.com/ChainSafe/gossamer/lib/common" + "github.com/ChainSafe/gossamer/lib/utils" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func setupStateFile(t *testing.T) string { + t.Helper() + + rootPath, err := utils.GetProjectRootPath() + require.NoError(t, err) + filename := filepath.Join(rootPath, "lib/runtime/test_data/kusama/block1482002.out") + data, err := os.ReadFile(filename) + require.NoError(t, err) + + rpcPairs := make(map[string]interface{}) + err = json.Unmarshal(data, &rpcPairs) + require.NoError(t, err) + pairs := rpcPairs["result"].([]interface{}) + + bz, err := json.Marshal(pairs) + require.NoError(t, err) + + fp := filepath.Join(t.TempDir(), "state.json") + err = os.WriteFile(fp, bz, 0777) + require.NoError(t, err) + + return fp +} + +func setupHeaderFile(t *testing.T) string { + t.Helper() + + //nolint:lll + const headerStr = `{ + "digest":{ + "logs":[ + "0x0642414245b501013c0000009659bd0f0000000070edad1c9064fff78cb18435223d8adaf5ea04c24b1a8766e3dc01eb03cc6a0c11b79793d4e31cc0990838229c44fed1669a7c7c79e1e6d0a96374d6496728069d1ef739e290497a0e3b728fa88fcbdd3a5504e0efde0242e7a806dd4fa9260c", + "0x054241424501019e7f28dddcf27c1e6b328d5694c368d5b2ec5dbe0e412ae1c98f88d53be4d8502fac571f3f19c9caaf281a673319241e0c5095a683ad34316204088a36a4bd86" + ] + }, + "extrinsicsRoot":"0xda26dc8c1455f8f81cae12e4fc59e23ce961b2c837f6d3f664283af906d344e0", + "number":"0x169d12", + "parentHash":"0x3b45c9c22dcece75a30acc9c2968cb311e6b0557350f83b430f47559db786975", + "stateRoot":"0x09f9ca28df0560c2291aa16b56e15e07d1e1927088f51356d522722aa90ca7cb" +}` + + fp := filepath.Join(t.TempDir(), "header.json") + err := os.WriteFile(fp, []byte(headerStr), 0777) + require.NoError(t, err) + return fp +} + +func TestImportState(t *testing.T) { + t.Parallel() + + basepath := t.TempDir() + + cfg := NewTestConfig(t) + + cfg.Init.Genesis = newTestGenesisRawFile(t, cfg) + + cfg.Global.BasePath = basepath + nodeInstance := nodeBuilder{} + err := nodeInstance.initNode(cfg) + require.NoError(t, err) + + stateFP := setupStateFile(t) + headerFP := setupHeaderFile(t) + + type args struct { + basepath string + stateFP string + headerFP string + firstSlot uint64 + } + tests := []struct { + name string + args args + err error + }{ + { + name: "no arguments", + err: errors.New("read .: is a directory"), + }, + { + name: "working example", + args: args{ + basepath: basepath, + stateFP: stateFP, + headerFP: headerFP, + firstSlot: 262493679, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := ImportState(tt.args.basepath, tt.args.stateFP, tt.args.headerFP, tt.args.firstSlot) + if tt.err != nil { + assert.EqualError(t, err, tt.err.Error()) + } else { + assert.NoError(t, err) + } + }) + } +} + +func Test_newHeaderFromFile(t *testing.T) { + t.Parallel() + + digest := types.NewDigest() + preRuntimeDigest := types.PreRuntimeDigest{ + ConsensusEngineID: types.BabeEngineID, + // bytes for PreRuntimeDigest that was created in setupHeaderFile function + Data: []byte{1, 60, 0, 0, 0, 150, 89, 189, 15, 0, 0, 0, 0, 112, 237, 173, 28, 144, 100, 255, + 247, 140, 177, 132, 53, 34, 61, 138, 218, 245, 234, 4, 194, 75, 26, 135, 102, 227, 220, 1, 235, 3, 204, + 106, 12, 17, 183, 151, 147, 212, 227, 28, 192, 153, 8, 56, 34, 156, 68, 254, 209, 102, 154, 124, 124, + 121, 225, 230, 208, 169, 99, 116, 214, 73, 103, 40, 6, 157, 30, 247, 57, 226, 144, 73, 122, 14, 59, 114, + 143, 168, 143, 203, 221, 58, 85, 4, 224, 239, 222, 2, 66, 231, 168, 6, 221, 79, 169, 38, 12}, + } + + preRuntimeDigestItem := types.NewDigestItem() + err := preRuntimeDigestItem.Set(preRuntimeDigest) + require.NoError(t, err) + digest.Add(preRuntimeDigestItem.Value()) + + sealDigest := types.SealDigest{ + ConsensusEngineID: types.BabeEngineID, + // bytes for SealDigest that was created in setupHeaderFile function + Data: []byte{158, 127, 40, 221, 220, 242, 124, 30, 107, 50, 141, 86, 148, 195, 104, 213, 178, 236, 93, 190, + 14, 65, 42, 225, 201, 143, 136, 213, 59, 228, 216, 80, 47, 172, 87, 31, 63, 25, 201, 202, 175, 40, 26, + 103, 51, 25, 36, 30, 12, 80, 149, 166, 131, 173, 52, 49, 98, 4, 8, 138, 54, 164, 189, 134}, + } + + sealDigestItem := types.NewDigestItem() + err = sealDigestItem.Set(sealDigest) + require.NoError(t, err) + digest.Add(sealDigestItem.Value()) + + expectedHeader := &types.Header{ + ParentHash: common.MustHexToHash("0x3b45c9c22dcece75a30acc9c2968cb311e6b0557350f83b430f47559db786975"), + Number: 1482002, + StateRoot: common.MustHexToHash("0x09f9ca28df0560c2291aa16b56e15e07d1e1927088f51356d522722aa90ca7cb"), + ExtrinsicsRoot: common.MustHexToHash("0xda26dc8c1455f8f81cae12e4fc59e23ce961b2c837f6d3f664283af906d344e0"), + Digest: digest, + } + + tests := []struct { + name string + filename string + want *types.Header + err error + }{ + { + name: "no arguments", + err: errors.New("read .: is a directory"), + }, + { + name: "working example", + filename: setupHeaderFile(t), + want: expectedHeader, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := newHeaderFromFile(tt.filename) + if tt.err != nil { + assert.EqualError(t, err, tt.err.Error()) + } else { + assert.NoError(t, err) + } + assert.Equal(t, tt.want, got) + }) + } +} + +func Test_newTrieFromPairs(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + filename string + want common.Hash + err error + }{ + { + name: "no arguments", + err: errors.New("read .: is a directory"), + want: common.Hash{}, + }, + { + name: "working example", + filename: setupStateFile(t), + want: common.MustHexToHash("0x09f9ca28df0560c2291aa16b56e15e07d1e1927088f51356d522722aa90ca7cb"), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := newTrieFromPairs(tt.filename) + if tt.err != nil { + assert.EqualError(t, err, tt.err.Error()) + } else { + assert.NoError(t, err) + } + if tt.want.IsEmpty() { + assert.Nil(t, got) + } else { + assert.Equal(t, tt.want, got.MustHash()) + } + }) + } +} diff --git a/dot/mock_babe_builder_test.go b/dot/mock_babe_builder_test.go new file mode 100644 index 0000000000..12842d0828 --- /dev/null +++ b/dot/mock_babe_builder_test.go @@ -0,0 +1,171 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/ChainSafe/gossamer/lib/babe (interfaces: ServiceIFace,ServiceBuilder) + +// Package dot is a generated GoMock package. +package dot + +import ( + reflect "reflect" + + babe "github.com/ChainSafe/gossamer/lib/babe" + gomock "github.com/golang/mock/gomock" +) + +// MockServiceIFace is a mock of ServiceIFace interface. +type MockServiceIFace struct { + ctrl *gomock.Controller + recorder *MockServiceIFaceMockRecorder +} + +// MockServiceIFaceMockRecorder is the mock recorder for MockServiceIFace. +type MockServiceIFaceMockRecorder struct { + mock *MockServiceIFace +} + +// NewMockServiceIFace creates a new mock instance. +func NewMockServiceIFace(ctrl *gomock.Controller) *MockServiceIFace { + mock := &MockServiceIFace{ctrl: ctrl} + mock.recorder = &MockServiceIFaceMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockServiceIFace) EXPECT() *MockServiceIFaceMockRecorder { + return m.recorder +} + +// EpochLength mocks base method. +func (m *MockServiceIFace) EpochLength() uint64 { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "EpochLength") + ret0, _ := ret[0].(uint64) + return ret0 +} + +// EpochLength indicates an expected call of EpochLength. +func (mr *MockServiceIFaceMockRecorder) EpochLength() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EpochLength", reflect.TypeOf((*MockServiceIFace)(nil).EpochLength)) +} + +// IsPaused mocks base method. +func (m *MockServiceIFace) IsPaused() bool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IsPaused") + ret0, _ := ret[0].(bool) + return ret0 +} + +// IsPaused indicates an expected call of IsPaused. +func (mr *MockServiceIFaceMockRecorder) IsPaused() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsPaused", reflect.TypeOf((*MockServiceIFace)(nil).IsPaused)) +} + +// Pause mocks base method. +func (m *MockServiceIFace) Pause() error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Pause") + ret0, _ := ret[0].(error) + return ret0 +} + +// Pause indicates an expected call of Pause. +func (mr *MockServiceIFaceMockRecorder) Pause() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Pause", reflect.TypeOf((*MockServiceIFace)(nil).Pause)) +} + +// Resume mocks base method. +func (m *MockServiceIFace) Resume() error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Resume") + ret0, _ := ret[0].(error) + return ret0 +} + +// Resume indicates an expected call of Resume. +func (mr *MockServiceIFaceMockRecorder) Resume() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Resume", reflect.TypeOf((*MockServiceIFace)(nil).Resume)) +} + +// SlotDuration mocks base method. +func (m *MockServiceIFace) SlotDuration() uint64 { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SlotDuration") + ret0, _ := ret[0].(uint64) + return ret0 +} + +// SlotDuration indicates an expected call of SlotDuration. +func (mr *MockServiceIFaceMockRecorder) SlotDuration() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SlotDuration", reflect.TypeOf((*MockServiceIFace)(nil).SlotDuration)) +} + +// Start mocks base method. +func (m *MockServiceIFace) Start() error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Start") + ret0, _ := ret[0].(error) + return ret0 +} + +// Start indicates an expected call of Start. +func (mr *MockServiceIFaceMockRecorder) Start() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*MockServiceIFace)(nil).Start)) +} + +// Stop mocks base method. +func (m *MockServiceIFace) Stop() error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Stop") + ret0, _ := ret[0].(error) + return ret0 +} + +// Stop indicates an expected call of Stop. +func (mr *MockServiceIFaceMockRecorder) Stop() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stop", reflect.TypeOf((*MockServiceIFace)(nil).Stop)) +} + +// MockServiceBuilder is a mock of ServiceBuilder interface. +type MockServiceBuilder struct { + ctrl *gomock.Controller + recorder *MockServiceBuilderMockRecorder +} + +// MockServiceBuilderMockRecorder is the mock recorder for MockServiceBuilder. +type MockServiceBuilderMockRecorder struct { + mock *MockServiceBuilder +} + +// NewMockServiceBuilder creates a new mock instance. +func NewMockServiceBuilder(ctrl *gomock.Controller) *MockServiceBuilder { + mock := &MockServiceBuilder{ctrl: ctrl} + mock.recorder = &MockServiceBuilderMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockServiceBuilder) EXPECT() *MockServiceBuilderMockRecorder { + return m.recorder +} + +// NewServiceIFace mocks base method. +func (m *MockServiceBuilder) NewServiceIFace(arg0 *babe.ServiceConfig) (babe.ServiceIFace, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "NewServiceIFace", arg0) + ret0, _ := ret[0].(babe.ServiceIFace) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// NewServiceIFace indicates an expected call of NewServiceIFace. +func (mr *MockServiceBuilderMockRecorder) NewServiceIFace(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewServiceIFace", reflect.TypeOf((*MockServiceBuilder)(nil).NewServiceIFace), arg0) +} diff --git a/dot/mock_block_state_test.go b/dot/mock_block_state_test.go new file mode 100644 index 0000000000..432e6210b4 --- /dev/null +++ b/dot/mock_block_state_test.go @@ -0,0 +1,125 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/ChainSafe/gossamer/dot/network (interfaces: BlockState) + +// Package dot is a generated GoMock package. +package dot + +import ( + reflect "reflect" + + types "github.com/ChainSafe/gossamer/dot/types" + common "github.com/ChainSafe/gossamer/lib/common" + gomock "github.com/golang/mock/gomock" +) + +// MockBlockState is a mock of BlockState interface. +type MockBlockState struct { + ctrl *gomock.Controller + recorder *MockBlockStateMockRecorder +} + +// MockBlockStateMockRecorder is the mock recorder for MockBlockState. +type MockBlockStateMockRecorder struct { + mock *MockBlockState +} + +// NewMockBlockState creates a new mock instance. +func NewMockBlockState(ctrl *gomock.Controller) *MockBlockState { + mock := &MockBlockState{ctrl: ctrl} + mock.recorder = &MockBlockStateMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockBlockState) EXPECT() *MockBlockStateMockRecorder { + return m.recorder +} + +// BestBlockHeader mocks base method. +func (m *MockBlockState) BestBlockHeader() (*types.Header, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "BestBlockHeader") + ret0, _ := ret[0].(*types.Header) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// BestBlockHeader indicates an expected call of BestBlockHeader. +func (mr *MockBlockStateMockRecorder) BestBlockHeader() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BestBlockHeader", reflect.TypeOf((*MockBlockState)(nil).BestBlockHeader)) +} + +// BestBlockNumber mocks base method. +func (m *MockBlockState) BestBlockNumber() (uint, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "BestBlockNumber") + ret0, _ := ret[0].(uint) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// BestBlockNumber indicates an expected call of BestBlockNumber. +func (mr *MockBlockStateMockRecorder) BestBlockNumber() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BestBlockNumber", reflect.TypeOf((*MockBlockState)(nil).BestBlockNumber)) +} + +// GenesisHash mocks base method. +func (m *MockBlockState) GenesisHash() common.Hash { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GenesisHash") + ret0, _ := ret[0].(common.Hash) + return ret0 +} + +// GenesisHash indicates an expected call of GenesisHash. +func (mr *MockBlockStateMockRecorder) GenesisHash() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GenesisHash", reflect.TypeOf((*MockBlockState)(nil).GenesisHash)) +} + +// GetHashByNumber mocks base method. +func (m *MockBlockState) GetHashByNumber(arg0 uint) (common.Hash, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetHashByNumber", arg0) + ret0, _ := ret[0].(common.Hash) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetHashByNumber indicates an expected call of GetHashByNumber. +func (mr *MockBlockStateMockRecorder) GetHashByNumber(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetHashByNumber", reflect.TypeOf((*MockBlockState)(nil).GetHashByNumber), arg0) +} + +// GetHighestFinalisedHeader mocks base method. +func (m *MockBlockState) GetHighestFinalisedHeader() (*types.Header, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetHighestFinalisedHeader") + ret0, _ := ret[0].(*types.Header) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetHighestFinalisedHeader indicates an expected call of GetHighestFinalisedHeader. +func (mr *MockBlockStateMockRecorder) GetHighestFinalisedHeader() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetHighestFinalisedHeader", reflect.TypeOf((*MockBlockState)(nil).GetHighestFinalisedHeader)) +} + +// HasBlockBody mocks base method. +func (m *MockBlockState) HasBlockBody(arg0 common.Hash) (bool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "HasBlockBody", arg0) + ret0, _ := ret[0].(bool) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// HasBlockBody indicates an expected call of HasBlockBody. +func (mr *MockBlockStateMockRecorder) HasBlockBody(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HasBlockBody", reflect.TypeOf((*MockBlockState)(nil).HasBlockBody), arg0) +} diff --git a/dot/mock_node_builder_test.go b/dot/mock_node_builder_test.go new file mode 100644 index 0000000000..586bbdac57 --- /dev/null +++ b/dot/mock_node_builder_test.go @@ -0,0 +1,255 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: node.go + +// Package dot is a generated GoMock package. +package dot + +import ( + reflect "reflect" + + core "github.com/ChainSafe/gossamer/dot/core" + digest "github.com/ChainSafe/gossamer/dot/digest" + network "github.com/ChainSafe/gossamer/dot/network" + rpc "github.com/ChainSafe/gossamer/dot/rpc" + state "github.com/ChainSafe/gossamer/dot/state" + sync "github.com/ChainSafe/gossamer/dot/sync" + system "github.com/ChainSafe/gossamer/dot/system" + telemetry "github.com/ChainSafe/gossamer/dot/telemetry" + types "github.com/ChainSafe/gossamer/dot/types" + log "github.com/ChainSafe/gossamer/internal/log" + babe "github.com/ChainSafe/gossamer/lib/babe" + grandpa "github.com/ChainSafe/gossamer/lib/grandpa" + keystore "github.com/ChainSafe/gossamer/lib/keystore" + runtime "github.com/ChainSafe/gossamer/lib/runtime" + gomock "github.com/golang/mock/gomock" +) + +// MocknodeBuilderIface is a mock of nodeBuilderIface interface. +type MocknodeBuilderIface struct { + ctrl *gomock.Controller + recorder *MocknodeBuilderIfaceMockRecorder +} + +// MocknodeBuilderIfaceMockRecorder is the mock recorder for MocknodeBuilderIface. +type MocknodeBuilderIfaceMockRecorder struct { + mock *MocknodeBuilderIface +} + +// NewMocknodeBuilderIface creates a new mock instance. +func NewMocknodeBuilderIface(ctrl *gomock.Controller) *MocknodeBuilderIface { + mock := &MocknodeBuilderIface{ctrl: ctrl} + mock.recorder = &MocknodeBuilderIfaceMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MocknodeBuilderIface) EXPECT() *MocknodeBuilderIfaceMockRecorder { + return m.recorder +} + +// createBABEService mocks base method. +func (m *MocknodeBuilderIface) createBABEService(cfg *Config, st *state.Service, ks keystore.Keystore, cs *core.Service, telemetryMailer telemetry.Client) (babe.ServiceIFace, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "createBABEService", cfg, st, ks, cs, telemetryMailer) + ret0, _ := ret[0].(babe.ServiceIFace) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// createBABEService indicates an expected call of createBABEService. +func (mr *MocknodeBuilderIfaceMockRecorder) createBABEService(cfg, st, ks, cs, telemetryMailer interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "createBABEService", reflect.TypeOf((*MocknodeBuilderIface)(nil).createBABEService), cfg, st, ks, cs, telemetryMailer) +} + +// createBlockVerifier mocks base method. +func (m *MocknodeBuilderIface) createBlockVerifier(st *state.Service) (*babe.VerificationManager, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "createBlockVerifier", st) + ret0, _ := ret[0].(*babe.VerificationManager) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// createBlockVerifier indicates an expected call of createBlockVerifier. +func (mr *MocknodeBuilderIfaceMockRecorder) createBlockVerifier(st interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "createBlockVerifier", reflect.TypeOf((*MocknodeBuilderIface)(nil).createBlockVerifier), st) +} + +// createCoreService mocks base method. +func (m *MocknodeBuilderIface) createCoreService(cfg *Config, ks *keystore.GlobalKeystore, st *state.Service, net *network.Service, dh *digest.Handler) (*core.Service, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "createCoreService", cfg, ks, st, net, dh) + ret0, _ := ret[0].(*core.Service) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// createCoreService indicates an expected call of createCoreService. +func (mr *MocknodeBuilderIfaceMockRecorder) createCoreService(cfg, ks, st, net, dh interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "createCoreService", reflect.TypeOf((*MocknodeBuilderIface)(nil).createCoreService), cfg, ks, st, net, dh) +} + +// createDigestHandler mocks base method. +func (m *MocknodeBuilderIface) createDigestHandler(lvl log.Level, st *state.Service) (*digest.Handler, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "createDigestHandler", lvl, st) + ret0, _ := ret[0].(*digest.Handler) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// createDigestHandler indicates an expected call of createDigestHandler. +func (mr *MocknodeBuilderIfaceMockRecorder) createDigestHandler(lvl, st interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "createDigestHandler", reflect.TypeOf((*MocknodeBuilderIface)(nil).createDigestHandler), lvl, st) +} + +// createGRANDPAService mocks base method. +func (m *MocknodeBuilderIface) createGRANDPAService(cfg *Config, st *state.Service, dh *digest.Handler, ks keystore.Keystore, net *network.Service, telemetryMailer telemetry.Client) (*grandpa.Service, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "createGRANDPAService", cfg, st, dh, ks, net, telemetryMailer) + ret0, _ := ret[0].(*grandpa.Service) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// createGRANDPAService indicates an expected call of createGRANDPAService. +func (mr *MocknodeBuilderIfaceMockRecorder) createGRANDPAService(cfg, st, dh, ks, net, telemetryMailer interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "createGRANDPAService", reflect.TypeOf((*MocknodeBuilderIface)(nil).createGRANDPAService), cfg, st, dh, ks, net, telemetryMailer) +} + +// createNetworkService mocks base method. +func (m *MocknodeBuilderIface) createNetworkService(cfg *Config, stateSrvc *state.Service, telemetryMailer telemetry.Client) (*network.Service, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "createNetworkService", cfg, stateSrvc, telemetryMailer) + ret0, _ := ret[0].(*network.Service) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// createNetworkService indicates an expected call of createNetworkService. +func (mr *MocknodeBuilderIfaceMockRecorder) createNetworkService(cfg, stateSrvc, telemetryMailer interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "createNetworkService", reflect.TypeOf((*MocknodeBuilderIface)(nil).createNetworkService), cfg, stateSrvc, telemetryMailer) +} + +// createRPCService mocks base method. +func (m *MocknodeBuilderIface) createRPCService(params rpcServiceSettings) (*rpc.HTTPServer, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "createRPCService", params) + ret0, _ := ret[0].(*rpc.HTTPServer) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// createRPCService indicates an expected call of createRPCService. +func (mr *MocknodeBuilderIfaceMockRecorder) createRPCService(params interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "createRPCService", reflect.TypeOf((*MocknodeBuilderIface)(nil).createRPCService), params) +} + +// createRuntimeStorage mocks base method. +func (m *MocknodeBuilderIface) createRuntimeStorage(st *state.Service) (*runtime.NodeStorage, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "createRuntimeStorage", st) + ret0, _ := ret[0].(*runtime.NodeStorage) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// createRuntimeStorage indicates an expected call of createRuntimeStorage. +func (mr *MocknodeBuilderIfaceMockRecorder) createRuntimeStorage(st interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "createRuntimeStorage", reflect.TypeOf((*MocknodeBuilderIface)(nil).createRuntimeStorage), st) +} + +// createStateService mocks base method. +func (m *MocknodeBuilderIface) createStateService(config *Config) (*state.Service, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "createStateService", config) + ret0, _ := ret[0].(*state.Service) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// createStateService indicates an expected call of createStateService. +func (mr *MocknodeBuilderIfaceMockRecorder) createStateService(config interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "createStateService", reflect.TypeOf((*MocknodeBuilderIface)(nil).createStateService), config) +} + +// createSystemService mocks base method. +func (m *MocknodeBuilderIface) createSystemService(cfg *types.SystemInfo, stateSrvc *state.Service) (*system.Service, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "createSystemService", cfg, stateSrvc) + ret0, _ := ret[0].(*system.Service) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// createSystemService indicates an expected call of createSystemService. +func (mr *MocknodeBuilderIfaceMockRecorder) createSystemService(cfg, stateSrvc interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "createSystemService", reflect.TypeOf((*MocknodeBuilderIface)(nil).createSystemService), cfg, stateSrvc) +} + +// initNode mocks base method. +func (m *MocknodeBuilderIface) initNode(config *Config) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "initNode", config) + ret0, _ := ret[0].(error) + return ret0 +} + +// initNode indicates an expected call of initNode. +func (mr *MocknodeBuilderIfaceMockRecorder) initNode(config interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "initNode", reflect.TypeOf((*MocknodeBuilderIface)(nil).initNode), config) +} + +// loadRuntime mocks base method. +func (m *MocknodeBuilderIface) loadRuntime(cfg *Config, ns *runtime.NodeStorage, stateSrvc *state.Service, ks *keystore.GlobalKeystore, net *network.Service) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "loadRuntime", cfg, ns, stateSrvc, ks, net) + ret0, _ := ret[0].(error) + return ret0 +} + +// loadRuntime indicates an expected call of loadRuntime. +func (mr *MocknodeBuilderIfaceMockRecorder) loadRuntime(cfg, ns, stateSrvc, ks, net interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "loadRuntime", reflect.TypeOf((*MocknodeBuilderIface)(nil).loadRuntime), cfg, ns, stateSrvc, ks, net) +} + +// newSyncService mocks base method. +func (m *MocknodeBuilderIface) newSyncService(cfg *Config, st *state.Service, fg sync.FinalityGadget, verifier *babe.VerificationManager, cs *core.Service, net *network.Service, telemetryMailer telemetry.Client) (*sync.Service, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "newSyncService", cfg, st, fg, verifier, cs, net, telemetryMailer) + ret0, _ := ret[0].(*sync.Service) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// newSyncService indicates an expected call of newSyncService. +func (mr *MocknodeBuilderIfaceMockRecorder) newSyncService(cfg, st, fg, verifier, cs, net, telemetryMailer interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "newSyncService", reflect.TypeOf((*MocknodeBuilderIface)(nil).newSyncService), cfg, st, fg, verifier, cs, net, telemetryMailer) +} + +// nodeInitialised mocks base method. +func (m *MocknodeBuilderIface) nodeInitialised(arg0 string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "nodeInitialised", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// nodeInitialised indicates an expected call of nodeInitialised. +func (mr *MocknodeBuilderIfaceMockRecorder) nodeInitialised(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "nodeInitialised", reflect.TypeOf((*MocknodeBuilderIface)(nil).nodeInitialised), arg0) +} diff --git a/dot/mock_service_registry_test.go b/dot/mock_service_registry_test.go new file mode 100644 index 0000000000..e5a3c82b46 --- /dev/null +++ b/dot/mock_service_registry_test.go @@ -0,0 +1,136 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/ChainSafe/gossamer/lib/services (interfaces: Service,ServiceRegisterer) + +// Package dot is a generated GoMock package. +package dot + +import ( + reflect "reflect" + + services "github.com/ChainSafe/gossamer/lib/services" + gomock "github.com/golang/mock/gomock" +) + +// MockService is a mock of Service interface. +type MockService struct { + ctrl *gomock.Controller + recorder *MockServiceMockRecorder +} + +// MockServiceMockRecorder is the mock recorder for MockService. +type MockServiceMockRecorder struct { + mock *MockService +} + +// NewMockService creates a new mock instance. +func NewMockService(ctrl *gomock.Controller) *MockService { + mock := &MockService{ctrl: ctrl} + mock.recorder = &MockServiceMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockService) EXPECT() *MockServiceMockRecorder { + return m.recorder +} + +// Start mocks base method. +func (m *MockService) Start() error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Start") + ret0, _ := ret[0].(error) + return ret0 +} + +// Start indicates an expected call of Start. +func (mr *MockServiceMockRecorder) Start() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*MockService)(nil).Start)) +} + +// Stop mocks base method. +func (m *MockService) Stop() error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Stop") + ret0, _ := ret[0].(error) + return ret0 +} + +// Stop indicates an expected call of Stop. +func (mr *MockServiceMockRecorder) Stop() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stop", reflect.TypeOf((*MockService)(nil).Stop)) +} + +// MockServiceRegisterer is a mock of ServiceRegisterer interface. +type MockServiceRegisterer struct { + ctrl *gomock.Controller + recorder *MockServiceRegistererMockRecorder +} + +// MockServiceRegistererMockRecorder is the mock recorder for MockServiceRegisterer. +type MockServiceRegistererMockRecorder struct { + mock *MockServiceRegisterer +} + +// NewMockServiceRegisterer creates a new mock instance. +func NewMockServiceRegisterer(ctrl *gomock.Controller) *MockServiceRegisterer { + mock := &MockServiceRegisterer{ctrl: ctrl} + mock.recorder = &MockServiceRegistererMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockServiceRegisterer) EXPECT() *MockServiceRegistererMockRecorder { + return m.recorder +} + +// Get mocks base method. +func (m *MockServiceRegisterer) Get(arg0 interface{}) services.Service { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Get", arg0) + ret0, _ := ret[0].(services.Service) + return ret0 +} + +// Get indicates an expected call of Get. +func (mr *MockServiceRegistererMockRecorder) Get(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockServiceRegisterer)(nil).Get), arg0) +} + +// RegisterService mocks base method. +func (m *MockServiceRegisterer) RegisterService(arg0 services.Service) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "RegisterService", arg0) +} + +// RegisterService indicates an expected call of RegisterService. +func (mr *MockServiceRegistererMockRecorder) RegisterService(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RegisterService", reflect.TypeOf((*MockServiceRegisterer)(nil).RegisterService), arg0) +} + +// StartAll mocks base method. +func (m *MockServiceRegisterer) StartAll() { + m.ctrl.T.Helper() + m.ctrl.Call(m, "StartAll") +} + +// StartAll indicates an expected call of StartAll. +func (mr *MockServiceRegistererMockRecorder) StartAll() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StartAll", reflect.TypeOf((*MockServiceRegisterer)(nil).StartAll)) +} + +// StopAll mocks base method. +func (m *MockServiceRegisterer) StopAll() { + m.ctrl.T.Helper() + m.ctrl.Call(m, "StopAll") +} + +// StopAll indicates an expected call of StopAll. +func (mr *MockServiceRegistererMockRecorder) StopAll() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StopAll", reflect.TypeOf((*MockServiceRegisterer)(nil).StopAll)) +} diff --git a/dot/network/service_test.go b/dot/network/service_test.go index ea6e925398..ffb57489c5 100644 --- a/dot/network/service_test.go +++ b/dot/network/service_test.go @@ -73,8 +73,8 @@ func newTestBlockResponseMessage(t *testing.T) *BlockResponseMessage { return msg } -//go:generate mockgen -destination=mock_block_state_test.go -package $GOPACKAGE . BlockState //go:generate mockgen -destination=mock_syncer_test.go -package $GOPACKAGE . Syncer +//go:generate mockgen -destination=mock_block_state_test.go -package $GOPACKAGE . BlockState // helper method to create and start a new network service func createTestService(t *testing.T, cfg *Config) (srvc *Service) { diff --git a/dot/node.go b/dot/node.go index 0d3a626087..53dd9dcdaa 100644 --- a/dot/node.go +++ b/dot/node.go @@ -8,22 +8,28 @@ import ( "fmt" "os" "os/signal" - "path" + "path/filepath" "runtime/debug" "sync" "syscall" "time" + "github.com/ChainSafe/gossamer/dot/core" + "github.com/ChainSafe/gossamer/dot/digest" "github.com/ChainSafe/gossamer/dot/network" "github.com/ChainSafe/gossamer/dot/rpc" "github.com/ChainSafe/gossamer/dot/state" "github.com/ChainSafe/gossamer/dot/state/pruner" + dotsync "github.com/ChainSafe/gossamer/dot/sync" + "github.com/ChainSafe/gossamer/dot/system" "github.com/ChainSafe/gossamer/dot/telemetry" "github.com/ChainSafe/gossamer/dot/types" "github.com/ChainSafe/gossamer/internal/log" "github.com/ChainSafe/gossamer/internal/metrics" + "github.com/ChainSafe/gossamer/lib/babe" "github.com/ChainSafe/gossamer/lib/common" "github.com/ChainSafe/gossamer/lib/genesis" + "github.com/ChainSafe/gossamer/lib/grandpa" "github.com/ChainSafe/gossamer/lib/keystore" "github.com/ChainSafe/gossamer/lib/runtime" "github.com/ChainSafe/gossamer/lib/services" @@ -34,16 +40,92 @@ var logger = log.NewFromGlobal(log.AddContext("pkg", "dot")) // Node is a container for all the components of a node. type Node struct { - Name string - Services *services.ServiceRegistry // registry of all node services - wg sync.WaitGroup - started chan struct{} - metricsServer *metrics.Server + Name string + ServiceRegistry services.ServiceRegisterer // registry of all node services + wg sync.WaitGroup + started chan struct{} + metricsServer *metrics.Server +} + +//go:generate mockgen -source=node.go -destination=mock_node_builder_test.go -package=$GOPACKAGE + +type nodeBuilderIface interface { + nodeInitialised(string) error + initNode(config *Config) error + createStateService(config *Config) (*state.Service, error) + createNetworkService(cfg *Config, stateSrvc *state.Service, telemetryMailer telemetry.Client) (*network.Service, + error) + createRuntimeStorage(st *state.Service) (*runtime.NodeStorage, error) + loadRuntime(cfg *Config, ns *runtime.NodeStorage, stateSrvc *state.Service, ks *keystore.GlobalKeystore, + net *network.Service) error + createBlockVerifier(st *state.Service) (*babe.VerificationManager, error) + createDigestHandler(lvl log.Level, st *state.Service) (*digest.Handler, error) + createCoreService(cfg *Config, ks *keystore.GlobalKeystore, st *state.Service, net *network.Service, + dh *digest.Handler) (*core.Service, error) + createGRANDPAService(cfg *Config, st *state.Service, dh *digest.Handler, ks keystore.Keystore, + net *network.Service, telemetryMailer telemetry.Client) (*grandpa.Service, error) + newSyncService(cfg *Config, st *state.Service, fg dotsync.FinalityGadget, verifier *babe.VerificationManager, + cs *core.Service, net *network.Service, telemetryMailer telemetry.Client) (*dotsync.Service, error) + createBABEService(cfg *Config, st *state.Service, ks keystore.Keystore, cs *core.Service, + telemetryMailer telemetry.Client) (babe.ServiceIFace, error) + createSystemService(cfg *types.SystemInfo, stateSrvc *state.Service) (*system.Service, error) + createRPCService(params rpcServiceSettings) (*rpc.HTTPServer, error) +} + +var _ nodeBuilderIface = (*nodeBuilder)(nil) + +type nodeBuilder struct{} + +// 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(basepath string) bool { + nodeInstance := nodeBuilder{} + err := nodeInstance.nodeInitialised(basepath) + if err != nil { + logger.Errorf("failed to initialise node from base path %s: %s", basepath, err) + return false + } + return true +} + +func (*nodeBuilder) nodeInitialised(basepath string) error { + // check if key registry exists + registry := filepath.Join(basepath, utils.DefaultDatabaseDir, "KEYREGISTRY") + + _, err := os.Stat(registry) + if os.IsNotExist(err) { + return fmt.Errorf("cannot find key registry in database directory: %w", err) + } + + db, err := utils.SetupDatabase(basepath, false) + if err != nil { + return fmt.Errorf("cannot setup database: %w", err) + } + + defer func() { + closeErr := db.Close() + if err != nil { + logger.Errorf("failed to close database: %s", closeErr) + } + }() + + _, err = state.NewBaseState(db).LoadGenesisData() + if err != nil { + return fmt.Errorf("cannot load genesis data in base state: %w", err) + } + + return nil +} + +// InitNode initialise the node with the given Config +func InitNode(cfg *Config) error { + nodeInstance := nodeBuilder{} + return nodeInstance.initNode(cfg) } // InitNode initialises a new dot node from the provided dot node configuration // and JSON formatted genesis file. -func InitNode(cfg *Config) error { +func (*nodeBuilder) initNode(cfg *Config) error { logger.Patch(log.SetLevel(cfg.Global.LogLvl)) logger.Infof( "🕸️ initialising node with name %s, id %s, base path %s and genesis %s...", @@ -112,45 +194,6 @@ func InitNode(cfg *Config) error { return nil } -// 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(basepath string) bool { - // check if key registry exists - registry := path.Join(basepath, utils.DefaultDatabaseDir, "KEYREGISTRY") - - _, err := os.Stat(registry) - if os.IsNotExist(err) { - logger.Debug("node has not been initialised from base path " + basepath + - ": failed to locate KEYREGISTRY file in data directory") - - return false - } - - // initialise database using data directory - db, err := utils.SetupDatabase(basepath, false) - if err != nil { - logger.Debugf("failed to create database from base path %s: %s", basepath, err) - return false - } - - defer func() { - // close database - err = db.Close() - if err != nil { - logger.Errorf("failed to close database: %s", err) - } - }() - - // load genesis data from initialised node database - _, err = state.NewBaseState(db).LoadGenesisData() - if err != nil { - logger.Errorf("node has not been initialised from base path %s: %s", basepath, err) - return false - } - - return true -} - // LoadGlobalNodeName returns the stored global node name from database func LoadGlobalNodeName(basepath string) (nodename string, err error) { // initialise database using data directory @@ -160,9 +203,9 @@ func LoadGlobalNodeName(basepath string) (nodename string, err error) { } defer func() { - err = db.Close() - if err != nil { - logger.Errorf("failed to close database: %s", err) + closeErr := db.Close() + if closeErr != nil { + logger.Errorf("failed to close database: %s", closeErr) return } }() @@ -171,14 +214,20 @@ func LoadGlobalNodeName(basepath string) (nodename string, err error) { nodename, err = basestate.LoadNodeGlobalName() if err != nil { logger.Warnf("failed to load global node name from base path %s: %s", basepath, err) - return "", err } - return nodename, err } -// NewNode creates a new dot node from a dot node configuration +// NewNode creates a node based on the given Config and key store. func NewNode(cfg *Config, ks *keystore.GlobalKeystore) (*Node, error) { + serviceRegistryLogger := logger.New(log.AddContext("pkg", "services")) + return newNode(cfg, ks, &nodeBuilder{}, services.NewServiceRegistry(serviceRegistryLogger)) +} + +func newNode(cfg *Config, + ks *keystore.GlobalKeystore, + builder nodeBuilderIface, + serviceRegistry services.ServiceRegisterer) (*Node, error) { // set garbage collection percent to 10% // can be overwritten by setting the GOGC env variable, which defaults to 100 prev := debug.SetGCPercent(10) @@ -186,13 +235,15 @@ func NewNode(cfg *Config, ks *keystore.GlobalKeystore) (*Node, error) { debug.SetGCPercent(prev) } - logger.Patch(log.SetLevel(cfg.Global.LogLvl)) - - // if authority node, should have at least 1 key in keystore - if cfg.Core.Roles == types.AuthorityRole && (ks.Babe.Size() == 0 || ks.Gran.Size() == 0) { - return nil, ErrNoKeysProvided + if builder.nodeInitialised(cfg.Global.BasePath) != nil { + err := builder.initNode(cfg) + if err != nil { + return nil, fmt.Errorf("cannot initialise node: %w", err) + } } + logger.Patch(log.SetLevel(cfg.Global.LogLvl)) + logger.Infof( "🕸️ initialising node services with global configuration name %s, id %s and base path %s...", cfg.Global.Name, cfg.Global.ID, cfg.Global.BasePath) @@ -206,7 +257,7 @@ func NewNode(cfg *Config, ks *keystore.GlobalKeystore) (*Node, error) { nodeSrvcs = append(nodeSrvcs, createPprofService(cfg.Pprof.Settings)) } - stateSrvc, err := createStateService(cfg) + stateSrvc, err := builder.createStateService(cfg) if err != nil { return nil, fmt.Errorf("failed to create state service: %s", err) } @@ -228,7 +279,7 @@ func NewNode(cfg *Config, ks *keystore.GlobalKeystore) (*Node, error) { return nil, fmt.Errorf("cannot start state service: %w", err) } - sysSrvc, err := createSystemService(&cfg.System, stateSrvc) + sysSrvc, err := builder.createSystemService(&cfg.System, stateSrvc) if err != nil { return nil, fmt.Errorf("failed to create system service: %s", err) } @@ -238,12 +289,11 @@ func NewNode(cfg *Config, ks *keystore.GlobalKeystore) (*Node, error) { // check if network service is enabled if enabled := networkServiceEnabled(cfg); enabled { // create network service and append network service to node services - networkSrvc, err = createNetworkService(cfg, stateSrvc, telemetryMailer) + networkSrvc, err = builder.createNetworkService(cfg, stateSrvc, telemetryMailer) if err != nil { return nil, fmt.Errorf("failed to create network service: %s", err) } nodeSrvcs = append(nodeSrvcs, networkSrvc) - startupTime := fmt.Sprint(time.Now().UnixNano()) genesisHash := stateSrvc.Block.GenesisHash() netstate := networkSrvc.NetworkState() @@ -266,40 +316,40 @@ func NewNode(cfg *Config, ks *keystore.GlobalKeystore) (*Node, error) { } // create runtime - ns, err := createRuntimeStorage(stateSrvc) + ns, err := builder.createRuntimeStorage(stateSrvc) if err != nil { return nil, err } - err = loadRuntime(cfg, ns, stateSrvc, ks, networkSrvc) + err = builder.loadRuntime(cfg, ns, stateSrvc, ks, networkSrvc) if err != nil { return nil, err } - ver, err := createBlockVerifier(stateSrvc) + ver, err := builder.createBlockVerifier(stateSrvc) if err != nil { return nil, err } - dh, err := createDigestHandler(cfg.Log.DigestLvl, stateSrvc) + dh, err := builder.createDigestHandler(cfg.Log.DigestLvl, stateSrvc) if err != nil { return nil, err } nodeSrvcs = append(nodeSrvcs, dh) - coreSrvc, err := createCoreService(cfg, ks, stateSrvc, networkSrvc, dh) + coreSrvc, err := builder.createCoreService(cfg, ks, stateSrvc, networkSrvc, dh) if err != nil { return nil, fmt.Errorf("failed to create core service: %s", err) } nodeSrvcs = append(nodeSrvcs, coreSrvc) - fg, err := createGRANDPAService(cfg, stateSrvc, dh, ks.Gran, networkSrvc, telemetryMailer) + fg, err := builder.createGRANDPAService(cfg, stateSrvc, dh, ks.Gran, networkSrvc, telemetryMailer) if err != nil { return nil, err } nodeSrvcs = append(nodeSrvcs, fg) - syncer, err := newSyncService(cfg, stateSrvc, fg, ver, coreSrvc, networkSrvc, telemetryMailer) + syncer, err := builder.newSyncService(cfg, stateSrvc, fg, ver, coreSrvc, networkSrvc, telemetryMailer) if err != nil { return nil, err } @@ -310,7 +360,7 @@ func NewNode(cfg *Config, ks *keystore.GlobalKeystore) (*Node, error) { } nodeSrvcs = append(nodeSrvcs, syncer) - bp, err := createBABEService(cfg, stateSrvc, ks.Babe, coreSrvc, telemetryMailer) + bp, err := builder.createBABEService(cfg, stateSrvc, ks.Babe, coreSrvc, telemetryMailer) if err != nil { return nil, err } @@ -330,7 +380,7 @@ func NewNode(cfg *Config, ks *keystore.GlobalKeystore) (*Node, error) { blockFinality: fg, syncer: syncer, } - rpcSrvc, err = createRPCService(cRPCParams) + rpcSrvc, err = builder.createRPCService(cRPCParams) if err != nil { return nil, fmt.Errorf("failed to create rpc service: %s", err) } @@ -342,15 +392,14 @@ func NewNode(cfg *Config, ks *keystore.GlobalKeystore) (*Node, error) { // close state service last nodeSrvcs = append(nodeSrvcs, stateSrvc) - serviceRegistryLogger := logger.New(log.AddContext("pkg", "services")) node := &Node{ - Name: cfg.Global.Name, - Services: services.NewServiceRegistry(serviceRegistryLogger), - started: make(chan struct{}), + Name: cfg.Global.Name, + ServiceRegistry: serviceRegistry, + started: make(chan struct{}), } for _, srvc := range nodeSrvcs { - node.Services.RegisterService(srvc) + node.ServiceRegistry.RegisterService(srvc) } if cfg.Global.PublishMetrics { @@ -410,7 +459,7 @@ func (n *Node) Start() error { logger.Info("🕸️ starting node services...") // start all dot node services - n.Services.StartAll() + n.ServiceRegistry.StartAll() n.wg.Add(1) go func() { @@ -430,7 +479,7 @@ func (n *Node) Start() error { // Stop stops all dot node services func (n *Node) Stop() { // stop all node services - n.Services.StopAll() + n.ServiceRegistry.StopAll() n.wg.Done() if n.metricsServer != nil { err := n.metricsServer.Stop() @@ -440,7 +489,7 @@ func (n *Node) Stop() { } } -func loadRuntime(cfg *Config, ns *runtime.NodeStorage, +func (n *nodeBuilder) loadRuntime(cfg *Config, ns *runtime.NodeStorage, stateSrvc *state.Service, ks *keystore.GlobalKeystore, net *network.Service) error { blocks := stateSrvc.Block.GetNonFinalisedBlocks() diff --git a/dot/node_integration_test.go b/dot/node_integration_test.go index ab90b7e028..42f7aef69d 100644 --- a/dot/node_integration_test.go +++ b/dot/node_integration_test.go @@ -6,6 +6,10 @@ package dot import ( + "encoding/hex" + "encoding/json" + "os" + "path/filepath" "testing" "github.com/ChainSafe/gossamer/dot/core" @@ -17,16 +21,17 @@ import ( "github.com/ChainSafe/gossamer/lib/genesis" "github.com/ChainSafe/gossamer/lib/grandpa" "github.com/ChainSafe/gossamer/lib/keystore" + "github.com/ChainSafe/gossamer/lib/runtime" "github.com/ChainSafe/gossamer/lib/trie" "github.com/ChainSafe/gossamer/lib/utils" "github.com/stretchr/testify/require" ) -func TestInitNode(t *testing.T) { +func TestInitNode_Integration(t *testing.T) { cfg := NewTestConfig(t) require.NotNil(t, cfg) - genFile := NewTestGenesisRawFile(t, cfg) + genFile := newTestGenesisRawFile(t, cfg) cfg.Init.Genesis = genFile @@ -55,11 +60,11 @@ func TestInitNode_GenesisSpec(t *testing.T) { require.NotNil(t, db) } -func TestNodeInitialized(t *testing.T) { +func TestNodeInitializedIntegration(t *testing.T) { cfg := NewTestConfig(t) require.NotNil(t, cfg) - genFile := NewTestGenesisRawFile(t, cfg) + genFile := newTestGenesisRawFile(t, cfg) cfg.Init.Genesis = genFile @@ -73,11 +78,11 @@ func TestNodeInitialized(t *testing.T) { require.True(t, result) } -func TestNewNode(t *testing.T) { +func TestNewNodeIntegration(t *testing.T) { cfg := NewTestConfig(t) require.NotNil(t, cfg) - genFile := NewTestGenesisRawFile(t, cfg) + genFile := newTestGenesisRawFile(t, cfg) cfg.Init.Genesis = genFile @@ -95,9 +100,9 @@ func TestNewNode(t *testing.T) { node, err := NewNode(cfg, ks) require.NoError(t, err) - bp := node.Services.Get(&babe.Service{}) + bp := node.ServiceRegistry.Get(&babe.Service{}) require.IsType(t, &babe.Service{}, bp) - fg := node.Services.Get(&grandpa.Service{}) + fg := node.ServiceRegistry.Get(&grandpa.Service{}) require.IsType(t, &grandpa.Service{}, fg) } @@ -105,7 +110,7 @@ func TestNewNode_Authority(t *testing.T) { cfg := NewTestConfig(t) require.NotNil(t, cfg) - genFile := NewTestGenesisRawFile(t, cfg) + genFile := newTestGenesisRawFile(t, cfg) cfg.Init.Genesis = genFile @@ -125,20 +130,21 @@ func TestNewNode_Authority(t *testing.T) { node, err := NewNode(cfg, ks) require.NoError(t, err) - bp := node.Services.Get(&babe.Service{}) + bp := node.ServiceRegistry.Get(&babe.Service{}) require.NotNil(t, bp) - fg := node.Services.Get(&grandpa.Service{}) + fg := node.ServiceRegistry.Get(&grandpa.Service{}) require.NotNil(t, fg) } -func TestStartNode(t *testing.T) { +func TestStartStopNode(t *testing.T) { cfg := NewTestConfig(t) require.NotNil(t, cfg) - genFile := NewTestGenesisRawFile(t, cfg) + genFile := newTestGenesisRawFile(t, cfg) cfg.Init.Genesis = genFile cfg.Core.GrandpaAuthority = false + cfg.Core.BabeAuthority = false err := InitNode(cfg) require.NoError(t, err) @@ -154,78 +160,19 @@ func TestStartNode(t *testing.T) { node, err := NewNode(cfg, ks) require.NoError(t, err) + go func() { + <-node.started + node.Stop() + }() err = node.Start() require.NoError(t, err) - <-node.started - node.Stop() -} - -func TestInitNode_LoadGenesisData(t *testing.T) { - cfg := NewTestConfig(t) - require.NotNil(t, cfg) - - genPath := NewTestGenesisAndRuntime(t) - - cfg.Init.Genesis = genPath - cfg.Core.GrandpaAuthority = false - - err := InitNode(cfg) - require.NoError(t, err) - - config := state.Config{ - Path: cfg.Global.BasePath, - } - stateSrvc := state.NewService(config) - - gen, err := genesis.NewGenesisFromJSONRaw(genPath) - require.NoError(t, err) - - genTrie, err := genesis.NewTrieFromGenesis(gen) - require.NoError(t, err) - - genesisHeader, err := types.NewHeader(common.NewHash([]byte{0}), - genTrie.MustHash(), trie.EmptyHash, 0, types.NewDigest()) - require.NoError(t, err) - - err = stateSrvc.Initialise(gen, genesisHeader, genTrie) - require.NoError(t, err) - - err = stateSrvc.Start() - require.NoError(t, err) - - t.Cleanup(func() { - err = stateSrvc.Stop() - require.NoError(t, err) - }) - - gendata, err := stateSrvc.Base.LoadGenesisData() - require.NoError(t, err) - - testGenesis := newTestGenesis(t) - - expected := &genesis.Data{ - Name: testGenesis.Name, - ID: testGenesis.ID, - Bootnodes: common.StringArrayToBytes(testGenesis.Bootnodes), - ProtocolID: testGenesis.ProtocolID, - } - require.Equal(t, expected, gendata) - - genesisHeader, err = stateSrvc.Block.BestBlockHeader() - require.NoError(t, err) - - stateRoot := genesisHeader.StateRoot - expectedHeader, err := types.NewHeader(common.NewHash([]byte{0}), - stateRoot, trie.EmptyHash, 0, types.NewDigest()) - require.NoError(t, err) - require.Equal(t, expectedHeader.Hash(), genesisHeader.Hash()) } func TestInitNode_LoadStorageRoot(t *testing.T) { cfg := NewTestConfig(t) require.NotNil(t, cfg) - genPath := NewTestGenesisAndRuntime(t) + genPath := newTestGenesisAndRuntime(t) cfg.Core.Roles = types.FullNodeRole cfg.Core.BabeAuthority = false @@ -253,7 +200,7 @@ func TestInitNode_LoadStorageRoot(t *testing.T) { expectedRoot, err := expected.Hash() require.NoError(t, err) - coreServiceInterface := node.Services.Get(&core.Service{}) + coreServiceInterface := node.ServiceRegistry.Get(&core.Service{}) coreSrvc, ok := coreServiceInterface.(*core.Service) require.True(t, ok, "could not find core service") @@ -275,7 +222,7 @@ func TestInitNode_LoadBalances(t *testing.T) { cfg := NewTestConfig(t) require.NotNil(t, cfg) - genPath := NewTestGenesisAndRuntime(t) + genPath := newTestGenesisAndRuntime(t) cfg.Core.Roles = types.FullNodeRole cfg.Core.BabeAuthority = false @@ -292,7 +239,7 @@ func TestInitNode_LoadBalances(t *testing.T) { node, err := NewNode(cfg, ks) require.NoError(t, err) - mgr := node.Services.Get(&state.Service{}) + mgr := node.ServiceRegistry.Get(&state.Service{}) stateSrv, ok := mgr.(*state.Service) require.True(t, ok, "could not find core service") @@ -319,7 +266,7 @@ func TestNode_PersistGlobalName_WhenInitialize(t *testing.T) { cfg.Core.Roles = types.FullNodeRole cfg.Core.BabeAuthority = false cfg.Core.GrandpaAuthority = false - cfg.Init.Genesis = NewTestGenesisAndRuntime(t) + cfg.Init.Genesis = newTestGenesisAndRuntime(t) err := InitNode(cfg) require.NoError(t, err) @@ -328,3 +275,33 @@ func TestNode_PersistGlobalName_WhenInitialize(t *testing.T) { require.NoError(t, err) require.Equal(t, globalName, storedName) } + +// newTestGenesisAndRuntime create a new test runtime and a new test genesis +// file with the test runtime stored in raw data and returns the genesis file +func newTestGenesisAndRuntime(t *testing.T) (filename string) { + runtimeFilePath := filepath.Join(t.TempDir(), "runtime") + _, testRuntimeURL := runtime.GetRuntimeVars(runtime.NODE_RUNTIME) + err := runtime.GetRuntimeBlob(runtimeFilePath, testRuntimeURL) + require.NoError(t, err) + runtimeData, err := os.ReadFile(runtimeFilePath) + require.NoError(t, err) + + gen := NewTestGenesis(t) + hex := hex.EncodeToString(runtimeData) + + gen.Genesis.Raw = map[string]map[string]string{ + "top": { + "0x3a636f6465": "0x" + hex, + "0xcf722c0832b5231d35e29f319ff27389f5032bfc7bfc3ba5ed7839f2042fb99f": "0x0000000000000001", + }, + } + + genData, err := json.Marshal(gen) + require.NoError(t, err) + + filename = filepath.Join(t.TempDir(), "genesis.json") + err = os.WriteFile(filename, genData, os.ModePerm) + require.NoError(t, err) + + return filename +} diff --git a/dot/node_test.go b/dot/node_test.go new file mode 100644 index 0000000000..886f125960 --- /dev/null +++ b/dot/node_test.go @@ -0,0 +1,402 @@ +// Copyright 2021 ChainSafe Systems (ON) +// SPDX-License-Identifier: LGPL-3.0-only + +package dot + +import ( + "errors" + "fmt" + "os" + "path/filepath" + "testing" + "time" + + "github.com/ChainSafe/gossamer/dot/core" + "github.com/ChainSafe/gossamer/dot/digest" + "github.com/ChainSafe/gossamer/dot/network" + "github.com/ChainSafe/gossamer/dot/state" + dotsync "github.com/ChainSafe/gossamer/dot/sync" + "github.com/ChainSafe/gossamer/dot/system" + "github.com/ChainSafe/gossamer/dot/telemetry" + "github.com/ChainSafe/gossamer/dot/types" + "github.com/ChainSafe/gossamer/internal/log" + "github.com/ChainSafe/gossamer/internal/metrics" + "github.com/ChainSafe/gossamer/lib/babe" + "github.com/ChainSafe/gossamer/lib/common" + "github.com/ChainSafe/gossamer/lib/genesis" + "github.com/ChainSafe/gossamer/lib/grandpa" + "github.com/ChainSafe/gossamer/lib/keystore" + "github.com/ChainSafe/gossamer/lib/runtime" + "github.com/ChainSafe/gossamer/lib/runtime/wasmer" + "github.com/ChainSafe/gossamer/lib/services" + "github.com/ChainSafe/gossamer/lib/utils" + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestInitNode(t *testing.T) { + cfg := NewTestConfig(t) + cfg.Init.Genesis = newTestGenesisRawFile(t, cfg) + tests := []struct { + name string + config *Config + err error + }{ + { + name: "test config", + config: cfg, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := InitNode(tt.config) + assert.ErrorIs(t, err, tt.err) + // confirm InitNode has created database dir + registry := filepath.Join(tt.config.Global.BasePath, utils.DefaultDatabaseDir, "KEYREGISTRY") + _, err = os.Stat(registry) + require.NoError(t, err) + }) + } +} + +func TestLoadGlobalNodeName(t *testing.T) { + t.Parallel() + + basePath := t.TempDir() + db, err := utils.SetupDatabase(basePath, false) + require.NoError(t, err) + + basestate := state.NewBaseState(db) + basestate.Put(common.NodeNameKey, []byte(`nodeName`)) + + err = db.Close() + require.NoError(t, err) + + tests := []struct { + name string + basepath string + wantNodename string + err error + }{ + { + name: "working example", + basepath: basePath, + wantNodename: "nodeName", + }, + { + name: "wrong basepath test", + basepath: t.TempDir(), + err: errors.New("Key not found"), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + gotNodename, err := LoadGlobalNodeName(tt.basepath) + if tt.err != nil { + assert.EqualError(t, err, tt.err.Error()) + } else { + assert.NoError(t, err) + } + + assert.Equal(t, tt.wantNodename, gotNodename) + }) + } +} + +//go:generate mockgen -destination=mock_service_registry_test.go -package=$GOPACKAGE github.com/ChainSafe/gossamer/lib/services Service,ServiceRegisterer + +func TestNewNode(t *testing.T) { + ctrl := gomock.NewController(t) + + mockTelemetryClient := NewMockClient(ctrl) + mockTelemetryClient.EXPECT().SendMessage(gomock.Any()) + + initConfig := NewTestConfig(t) + + genFile := newTestGenesisRawFile(t, initConfig) + + networkConfig := &network.Config{ + BasePath: t.TempDir(), + NoBootstrap: true, + NoMDNS: true, + } + testNetworkService := createTestService(t, networkConfig) + + config := state.Config{ + Path: initConfig.Global.BasePath, + LogLevel: initConfig.Log.StateLvl, + } + + dotConfig := &Config{ + Global: GlobalConfig{BasePath: initConfig.Global.BasePath}, + Init: InitConfig{Genesis: genFile}, + Account: AccountConfig{Key: "alice"}, + Core: CoreConfig{ + Roles: types.FullNodeRole, + WasmInterpreter: wasmer.Name, + }, + } + + dotConfig.Init = InitConfig{Genesis: genFile} + dotConfig.Account = AccountConfig{Key: "alice"} + dotConfig.Core.Roles = types.FullNodeRole + dotConfig.Core.WasmInterpreter = wasmer.Name + dotConfig.Global.Name = "TestNode" + + ks, err := initKeystore(t, dotConfig) + assert.NoError(t, err) + + mockServiceRegistry := NewMockServiceRegisterer(ctrl) + mockServiceRegistry.EXPECT().RegisterService(gomock.Any()).Times(8) + + m := NewMocknodeBuilderIface(ctrl) + m.EXPECT().nodeInitialised(dotConfig.Global.BasePath).Return(nil) + m.EXPECT().createStateService(dotConfig).DoAndReturn(func(cfg *Config) (*state.Service, error) { + stateSrvc := state.NewService(config) + // create genesis from configuration file + gen, err := genesis.NewGenesisFromJSONRaw(cfg.Init.Genesis) + if err != nil { + return nil, fmt.Errorf("failed to load genesis from file: %w", err) + } + // create trie from genesis + trie, err := genesis.NewTrieFromGenesis(gen) + if err != nil { + return nil, fmt.Errorf("failed to create trie from genesis: %w", err) + } + // create genesis block from trie + header, err := genesis.NewGenesisBlockFromTrie(trie) + if err != nil { + return nil, fmt.Errorf("failed to create genesis block from trie: %w", err) + } + stateSrvc.Telemetry = mockTelemetryClient + err = stateSrvc.Initialise(gen, header, trie) + if err != nil { + return nil, fmt.Errorf("failed to initialise state service: %s", err) + } + + err = stateSrvc.SetupBase() + if err != nil { + return nil, fmt.Errorf("cannot setup base: %w", err) + } + return stateSrvc, nil + }) + + m.EXPECT().createRuntimeStorage(gomock.AssignableToTypeOf(&state.Service{})).Return(&runtime. + NodeStorage{}, nil) + m.EXPECT().loadRuntime(dotConfig, &runtime.NodeStorage{}, gomock.AssignableToTypeOf(&state.Service{}), + ks, gomock.AssignableToTypeOf(&network.Service{})).Return(nil) + m.EXPECT().createBlockVerifier(gomock.AssignableToTypeOf(&state.Service{})).Return(&babe. + VerificationManager{}, nil) + m.EXPECT().createDigestHandler(log.Critical, gomock.AssignableToTypeOf(&state.Service{})). + Return(&digest.Handler{}, nil) + m.EXPECT().createCoreService(dotConfig, ks, gomock.AssignableToTypeOf(&state.Service{}), + gomock.AssignableToTypeOf(&network.Service{}), &digest.Handler{}). + Return(&core.Service{}, nil) + m.EXPECT().createGRANDPAService(dotConfig, gomock.AssignableToTypeOf(&state.Service{}), + &digest.Handler{}, ks.Gran, gomock.AssignableToTypeOf(&network.Service{}), + gomock.AssignableToTypeOf(&telemetry.Mailer{})). + Return(&grandpa.Service{}, nil) + m.EXPECT().newSyncService(dotConfig, gomock.AssignableToTypeOf(&state.Service{}), &grandpa.Service{}, + &babe.VerificationManager{}, &core.Service{}, gomock.AssignableToTypeOf(&network.Service{}), + gomock.AssignableToTypeOf(&telemetry.Mailer{})). + Return(&dotsync.Service{}, nil) + m.EXPECT().createBABEService(dotConfig, gomock.AssignableToTypeOf(&state.Service{}), ks.Babe, + &core.Service{}, gomock.AssignableToTypeOf(&telemetry.Mailer{})). + Return(&babe.Service{}, nil) + m.EXPECT().createSystemService(&dotConfig.System, gomock.AssignableToTypeOf(&state.Service{})). + DoAndReturn(func(cfg *types.SystemInfo, stateSrvc *state.Service) (*system.Service, error) { + gd, err := stateSrvc.Base.LoadGenesisData() + systemService := system.NewService(cfg, gd) + return systemService, err + }) + m.EXPECT().createNetworkService(dotConfig, gomock.AssignableToTypeOf(&state.Service{}), + gomock.AssignableToTypeOf(&telemetry.Mailer{})).Return(testNetworkService, nil) + + got, err := newNode(dotConfig, ks, m, mockServiceRegistry) + assert.NoError(t, err) + + expected := &Node{ + Name: "TestNode", + } + + assert.Equal(t, expected.Name, got.Name) +} + +//go:generate mockgen -destination=mock_block_state_test.go -package $GOPACKAGE github.com/ChainSafe/gossamer/dot/network BlockState + +func createTestService(t *testing.T, cfg *network.Config) (srvc *network.Service) { + t.Helper() + ctrl := gomock.NewController(t) + + if cfg == nil { + cfg = &network.Config{ + BasePath: t.TempDir(), + NoBootstrap: true, + NoMDNS: true, + LogLvl: log.Warn, + SlotDuration: time.Second, + } + } + if cfg.BlockState == nil { + blockstate := NewMockBlockState(ctrl) + + cfg.BlockState = blockstate + } + + cfg.SlotDuration = time.Second + + if cfg.Telemetry == nil { + telemetryMock := NewMockClient(ctrl) + telemetryMock.EXPECT().SendMessage(gomock.Any()).AnyTimes() + cfg.Telemetry = telemetryMock + } + + srvc, err := network.NewService(cfg) + require.NoError(t, err) + + return srvc +} + +func TestNodeInitialized(t *testing.T) { + cfg := NewTestConfig(t) + + genFile := newTestGenesisRawFile(t, cfg) + + cfg.Init.Genesis = genFile + + nodeInstance := nodeBuilder{} + err := nodeInstance.initNode(cfg) + require.NoError(t, err) + + tests := []struct { + name string + basepath string + want bool + }{ + { + name: "blank base path", + basepath: "", + want: false, + }, + { + name: "working example", + basepath: cfg.Global.BasePath, + want: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := NodeInitialized(tt.basepath) + assert.Equal(t, tt.want, got) + }) + } +} + +func initKeystore(t *testing.T, cfg *Config) (*keystore.GlobalKeystore, error) { + ks := keystore.NewGlobalKeystore() + // load built-in test keys if specified by `cfg.Account.Key` + err := keystore.LoadKeystore(cfg.Account.Key, ks.Acco) + require.NoError(t, err) + + err = keystore.LoadKeystore(cfg.Account.Key, ks.Babe) + require.NoError(t, err) + + err = keystore.LoadKeystore(cfg.Account.Key, ks.Gran) + require.NoError(t, err) + + // if authority node, should have at least 1 key in keystore + if cfg.Core.Roles == types.AuthorityRole && (ks.Babe.Size() == 0 || ks.Gran.Size() == 0) { + return nil, ErrNoKeysProvided + } + + return ks, nil +} + +func TestNode_StartStop(t *testing.T) { + serviceRegistryLogger := logger.New(log.AddContext("pkg", "services")) + type fields struct { + Name string + Services *services.ServiceRegistry + started chan struct{} + metricsServer *metrics.Server + } + tests := []struct { + name string + fields fields + err error + }{ + { + name: "base case", + fields: fields{ + Name: "Node", + Services: services.NewServiceRegistry(serviceRegistryLogger), + started: make(chan struct{}), + }, + err: nil, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + n := &Node{ + Name: tt.fields.Name, + ServiceRegistry: tt.fields.Services, + started: tt.fields.started, + metricsServer: tt.fields.metricsServer, + } + go func() { + <-n.started + n.Stop() + }() + + err := n.Start() + if tt.err != nil { + assert.EqualError(t, err, tt.err.Error()) + } else { + assert.NoError(t, err) + } + }) + } +} + +func Test_nodeBuilder_loadRuntime(t *testing.T) { + cfg := NewTestConfig(t) + type args struct { + cfg *Config + ns *runtime.NodeStorage + ks *keystore.GlobalKeystore + net *network.Service + } + tests := []struct { + name string + args args + err error + }{ + { + name: "base case", + args: args{ + cfg: cfg, + ns: &runtime.NodeStorage{}, + ks: nil, + net: nil, + }, + err: nil, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + ctrl := gomock.NewController(t) + stateSrvc := newStateService(t, ctrl) + no := nodeBuilder{} + err := no.loadRuntime(tt.args.cfg, tt.args.ns, stateSrvc, tt.args.ks, tt.args.net) + assert.ErrorIs(t, err, tt.err) + blocks := stateSrvc.Block.GetNonFinalisedBlocks() + for i := range blocks { + hash := &blocks[i] + code, err := stateSrvc.Storage.GetStorageByBlockHash(hash, []byte(":code")) + require.NoError(t, err) + require.NotEmpty(t, code) + } + }) + } +} diff --git a/dot/services.go b/dot/services.go index 49c2c1f03a..478fcaecae 100644 --- a/dot/services.go +++ b/dot/services.go @@ -6,11 +6,9 @@ package dot import ( "errors" "fmt" - "path/filepath" "strings" "github.com/ChainSafe/chaindb" - "github.com/ChainSafe/gossamer/dot/core" "github.com/ChainSafe/gossamer/dot/digest" "github.com/ChainSafe/gossamer/dot/network" @@ -49,12 +47,12 @@ type rpcServiceSettings struct { syncer *sync.Service } -func newInMemoryDB(path string) (chaindb.Database, error) { - return utils.SetupDatabase(filepath.Join(path, "local_storage"), true) +func newInMemoryDB() (chaindb.Database, error) { + return utils.SetupDatabase("", true) } // createStateService creates the state service and initialise state database -func createStateService(cfg *Config) (*state.Service, error) { +func (nodeBuilder) createStateService(cfg *Config) (*state.Service, error) { logger.Debug("creating state service...") config := state.Config{ @@ -92,8 +90,8 @@ func startStateService(cfg *Config, stateSrvc *state.Service) error { return nil } -func createRuntimeStorage(st *state.Service) (*runtime.NodeStorage, error) { - localStorage, err := newInMemoryDB(st.DB().Path()) +func (nodeBuilder) createRuntimeStorage(st *state.Service) (*runtime.NodeStorage, error) { + localStorage, err := newInMemoryDB() if err != nil { return nil, err } @@ -136,6 +134,7 @@ func createRuntime(cfg *Config, ns runtime.NodeStorage, st *state.Service, var rt runtime.Instance switch cfg.Core.WasmInterpreter { + // TODO no default case to handle if cfg.Core.WasmInterpreter is not set or set incorrectly case wasmer.Name: rtCfg := &wasmer.Config{ Imports: wasmer.ImportsNodeRuntime, @@ -183,8 +182,13 @@ func asAuthority(authority bool) string { return "" } -func createBABEService(cfg *Config, st *state.Service, ks keystore.Keystore, - cs *core.Service, telemetryMailer telemetry.Client) (*babe.Service, error) { +func (nb nodeBuilder) createBABEService(cfg *Config, st *state.Service, ks keystore.Keystore, + cs *core.Service, telemetryMailer telemetry.Client) (babe.ServiceIFace, error) { + return nb.createBABEServiceWithBuilder(cfg, st, ks, cs, telemetryMailer, babe.Builder{}) +} +func (nodeBuilder) createBABEServiceWithBuilder(cfg *Config, st *state.Service, ks keystore.Keystore, + cs *core.Service, telemetryMailer telemetry.Client, newBabeService babe.ServiceBuilder) (babe. + ServiceIFace, error) { logger.Info("creating BABE service" + asAuthority(cfg.Core.BabeAuthority) + "...") @@ -215,20 +219,18 @@ func createBABEService(cfg *Config, st *state.Service, ks keystore.Keystore, bcfg.Keypair = kps[0].(*sr25519.Keypair) } - // create new BABE service - bs, err := babe.NewService(bcfg) + bs, err := newBabeService.NewServiceIFace(bcfg) if err != nil { logger.Errorf("failed to initialise BABE service: %s", err) return nil, err } - return bs, nil } // Core Service // createCoreService creates the core service from the provided core configuration -func createCoreService(cfg *Config, ks *keystore.GlobalKeystore, +func (nodeBuilder) createCoreService(cfg *Config, ks *keystore.GlobalKeystore, st *state.Service, net *network.Service, dh *digest.Handler) ( *core.Service, error) { logger.Debug("creating core service" + @@ -272,7 +274,7 @@ func createCoreService(cfg *Config, ks *keystore.GlobalKeystore, // Network Service // createNetworkService creates a network service from the command configuration and genesis data -func createNetworkService(cfg *Config, stateSrvc *state.Service, +func (nodeBuilder) createNetworkService(cfg *Config, stateSrvc *state.Service, telemetryMailer telemetry.Client) (*network.Service, error) { logger.Debugf( "creating network service with roles %d, port %d, bootnodes %s, protocol ID %s, nobootstrap=%t and noMDNS=%t...", @@ -318,7 +320,7 @@ func createNetworkService(cfg *Config, stateSrvc *state.Service, // RPC Service // createRPCService creates the RPC service from the provided core configuration -func createRPCService(params rpcServiceSettings) (*rpc.HTTPServer, error) { +func (nodeBuilder) createRPCService(params rpcServiceSettings) (*rpc.HTTPServer, error) { logger.Infof( "creating rpc service with host %s, external=%t, port %d, modules %s, ws=%t, ws port %d and ws external=%t", params.config.RPC.Host, @@ -373,7 +375,7 @@ func createRPCService(params rpcServiceSettings) (*rpc.HTTPServer, error) { } // createSystemService creates a systemService for providing system related information -func createSystemService(cfg *types.SystemInfo, stateSrvc *state.Service) (*system.Service, error) { +func (nodeBuilder) createSystemService(cfg *types.SystemInfo, stateSrvc *state.Service) (*system.Service, error) { genesisData, err := stateSrvc.Base.LoadGenesisData() if err != nil { return nil, err @@ -383,7 +385,7 @@ func createSystemService(cfg *types.SystemInfo, stateSrvc *state.Service) (*syst } // createGRANDPAService creates a new GRANDPA service -func createGRANDPAService(cfg *Config, st *state.Service, dh *digest.Handler, +func (nodeBuilder) createGRANDPAService(cfg *Config, st *state.Service, dh *digest.Handler, ks keystore.Keystore, net *network.Service, telemetryMailer telemetry.Client) (*grandpa.Service, error) { rt, err := st.Block.GetRuntime(nil) if err != nil { @@ -425,7 +427,7 @@ func createGRANDPAService(cfg *Config, st *state.Service, dh *digest.Handler, return grandpa.NewService(gsCfg) } -func createBlockVerifier(st *state.Service) (*babe.VerificationManager, error) { +func (nodeBuilder) createBlockVerifier(st *state.Service) (*babe.VerificationManager, error) { ver, err := babe.NewVerificationManager(st.Block, st.Epoch) if err != nil { return nil, err @@ -434,7 +436,7 @@ func createBlockVerifier(st *state.Service) (*babe.VerificationManager, error) { return ver, nil } -func newSyncService(cfg *Config, st *state.Service, fg sync.FinalityGadget, +func (nodeBuilder) newSyncService(cfg *Config, st *state.Service, fg sync.FinalityGadget, verifier *babe.VerificationManager, cs *core.Service, net *network.Service, telemetryMailer telemetry.Client) ( *sync.Service, error) { slotDuration, err := st.Epoch.GetSlotDuration() @@ -460,7 +462,7 @@ func newSyncService(cfg *Config, st *state.Service, fg sync.FinalityGadget, return sync.NewService(syncCfg) } -func createDigestHandler(lvl log.Level, st *state.Service) (*digest.Handler, error) { +func (nodeBuilder) createDigestHandler(lvl log.Level, st *state.Service) (*digest.Handler, error) { return digest.NewHandler(lvl, st.Block, st.Epoch, st.Grandpa) } diff --git a/dot/services_integration_test.go b/dot/services_integration_test.go index f00bd55ca7..e961af4804 100644 --- a/dot/services_integration_test.go +++ b/dot/services_integration_test.go @@ -11,11 +11,19 @@ import ( "time" "github.com/ChainSafe/gossamer/dot/network" + "github.com/ChainSafe/gossamer/dot/state" + "github.com/ChainSafe/gossamer/dot/telemetry" "github.com/ChainSafe/gossamer/dot/types" + "github.com/ChainSafe/gossamer/internal/log" "github.com/ChainSafe/gossamer/internal/pprof" + "github.com/ChainSafe/gossamer/lib/genesis" "github.com/ChainSafe/gossamer/lib/grandpa" "github.com/ChainSafe/gossamer/lib/keystore" + "github.com/ChainSafe/gossamer/lib/runtime" + rtstorage "github.com/ChainSafe/gossamer/lib/runtime/storage" + "github.com/ChainSafe/gossamer/lib/runtime/wasmer" "github.com/gorilla/websocket" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -23,23 +31,73 @@ func TestCreateStateService(t *testing.T) { cfg := NewTestConfig(t) require.NotNil(t, cfg) - genFile := NewTestGenesisRawFile(t, cfg) + genFile := newTestGenesisRawFile(t, cfg) cfg.Init.Genesis = genFile err := InitNode(cfg) require.NoError(t, err) - stateSrvc, err := createStateService(cfg) + builder := nodeBuilder{} + stateSrvc, err := builder.createStateService(cfg) require.NoError(t, err) require.NotNil(t, stateSrvc) } +func newStateServiceWithoutMock(t *testing.T) *state.Service { + t.Helper() + + stateConfig := state.Config{ + Path: t.TempDir(), + LogLevel: log.Error, + Telemetry: telemetry.NoopClient{}, + } + stateSrvc := state.NewService(stateConfig) + stateSrvc.UseMemDB() + genData, genTrie, genesisHeader := genesis.NewTestGenesisWithTrieAndHeader(t) + err := stateSrvc.Initialise(genData, genesisHeader, genTrie) + require.NoError(t, err) + + err = stateSrvc.SetupBase() + require.NoError(t, err) + + genesisBABEConfig := &types.BabeConfiguration{ + SlotDuration: 1000, + EpochLength: 200, + C1: 1, + C2: 4, + GenesisAuthorities: []types.AuthorityRaw{}, + Randomness: [32]byte{}, + SecondarySlots: 0, + } + epochState, err := state.NewEpochStateFromGenesis(stateSrvc.DB(), stateSrvc.Block, genesisBABEConfig) + require.NoError(t, err) + + stateSrvc.Epoch = epochState + + rtCfg := &wasmer.Config{} + + rtCfg.Storage, err = rtstorage.NewTrieState(genTrie) + require.NoError(t, err) + + rtCfg.CodeHash, err = stateSrvc.Storage.LoadCodeHash(nil) + require.NoError(t, err) + + rtCfg.NodeStorage = runtime.NodeStorage{} + + rt, err := wasmer.NewRuntimeFromGenesis(rtCfg) + require.NoError(t, err) + + stateSrvc.Block.StoreRuntime(stateSrvc.Block.BestBlockHash(), rt) + + return stateSrvc +} + func TestCreateCoreService(t *testing.T) { cfg := NewTestConfig(t) require.NotNil(t, cfg) - genFile := NewTestGenesisRawFile(t, cfg) + genFile := newTestGenesisRawFile(t, cfg) cfg.Core.Roles = types.FullNodeRole cfg.Core.BabeAuthority = false @@ -49,8 +107,7 @@ func TestCreateCoreService(t *testing.T) { err := InitNode(cfg) require.NoError(t, err) - stateSrvc, err := createStateService(cfg) - require.NoError(t, err) + stateSrvc := newStateServiceWithoutMock(t) ks := keystore.NewGlobalKeystore() require.NotNil(t, ks) @@ -59,10 +116,11 @@ func TestCreateCoreService(t *testing.T) { networkSrvc := &network.Service{} - dh, err := createDigestHandler(cfg.Log.DigestLvl, stateSrvc) + builder := nodeBuilder{} + dh, err := builder.createDigestHandler(cfg.Log.DigestLvl, stateSrvc) require.NoError(t, err) - coreSrvc, err := createCoreService(cfg, ks, stateSrvc, networkSrvc, dh) + coreSrvc, err := builder.createCoreService(cfg, ks, stateSrvc, networkSrvc, dh) require.NoError(t, err) require.NotNil(t, coreSrvc) } @@ -78,10 +136,12 @@ func TestCreateBlockVerifier(t *testing.T) { err := InitNode(cfg) require.NoError(t, err) - stateSrvc, err := createStateService(cfg) + builder := nodeBuilder{} + stateSrvc, err := builder.createStateService(cfg) require.NoError(t, err) + stateSrvc.Epoch = &state.EpochState{} - _, err = createBlockVerifier(stateSrvc) + _, err = builder.createBlockVerifier(stateSrvc) require.NoError(t, err) } @@ -96,22 +156,22 @@ func TestCreateSyncService(t *testing.T) { err := InitNode(cfg) require.NoError(t, err) - stateSrvc, err := createStateService(cfg) - require.NoError(t, err) + builder := nodeBuilder{} + stateSrvc := newStateServiceWithoutMock(t) ks := keystore.NewGlobalKeystore() require.NotNil(t, ks) - ver, err := createBlockVerifier(stateSrvc) + ver, err := builder.createBlockVerifier(stateSrvc) require.NoError(t, err) - dh, err := createDigestHandler(cfg.Log.DigestLvl, stateSrvc) + dh, err := builder.createDigestHandler(cfg.Log.DigestLvl, stateSrvc) require.NoError(t, err) - coreSrvc, err := createCoreService(cfg, ks, stateSrvc, &network.Service{}, dh) + coreSrvc, err := builder.createCoreService(cfg, ks, stateSrvc, &network.Service{}, dh) require.NoError(t, err) - _, err = newSyncService(cfg, stateSrvc, &grandpa.Service{}, ver, coreSrvc, &network.Service{}, nil) + _, err = builder.newSyncService(cfg, stateSrvc, &grandpa.Service{}, ver, coreSrvc, &network.Service{}, nil) require.NoError(t, err) } @@ -119,17 +179,17 @@ func TestCreateNetworkService(t *testing.T) { cfg := NewTestConfig(t) require.NotNil(t, cfg) - genFile := NewTestGenesisRawFile(t, cfg) + genFile := newTestGenesisRawFile(t, cfg) cfg.Init.Genesis = genFile err := InitNode(cfg) require.NoError(t, err) - stateSrvc, err := createStateService(cfg) - require.NoError(t, err) + builder := nodeBuilder{} + stateSrvc := newStateServiceWithoutMock(t) - networkSrvc, err := createNetworkService(cfg, stateSrvc, nil) + networkSrvc, err := builder.createNetworkService(cfg, stateSrvc, nil) require.NoError(t, err) require.NotNil(t, networkSrvc) } @@ -138,7 +198,7 @@ func TestCreateRPCService(t *testing.T) { cfg := NewTestConfig(t) require.NotNil(t, cfg) - genFile := NewTestGenesisRawFile(t, cfg) + genFile := newTestGenesisRawFile(t, cfg) cfg.Core.Roles = types.FullNodeRole cfg.Core.BabeAuthority = false @@ -148,8 +208,8 @@ func TestCreateRPCService(t *testing.T) { err := InitNode(cfg) require.NoError(t, err) - stateSrvc, err := createStateService(cfg) - require.NoError(t, err) + builder := nodeBuilder{} + stateSrvc := newStateServiceWithoutMock(t) networkSrvc := &network.Service{} @@ -157,18 +217,18 @@ func TestCreateRPCService(t *testing.T) { ed25519Keyring, _ := keystore.NewEd25519Keyring() ks.Gran.Insert(ed25519Keyring.Alice()) - ns, err := createRuntimeStorage(stateSrvc) + ns, err := builder.createRuntimeStorage(stateSrvc) require.NoError(t, err) - err = loadRuntime(cfg, ns, stateSrvc, ks, networkSrvc) + err = builder.loadRuntime(cfg, ns, stateSrvc, ks, networkSrvc) require.NoError(t, err) - dh, err := createDigestHandler(cfg.Log.DigestLvl, stateSrvc) + dh, err := builder.createDigestHandler(cfg.Log.DigestLvl, stateSrvc) require.NoError(t, err) - coreSrvc, err := createCoreService(cfg, ks, stateSrvc, networkSrvc, dh) + coreSrvc, err := builder.createCoreService(cfg, ks, stateSrvc, networkSrvc, dh) require.NoError(t, err) - sysSrvc, err := createSystemService(&cfg.System, stateSrvc) + sysSrvc, err := builder.createSystemService(&cfg.System, stateSrvc) require.NoError(t, err) rpcSettings := rpcServiceSettings{ @@ -179,15 +239,15 @@ func TestCreateRPCService(t *testing.T) { network: networkSrvc, system: sysSrvc, } - rpcSrvc, err := createRPCService(rpcSettings) + rpcSrvc, err := builder.createRPCService(rpcSettings) require.NoError(t, err) require.NotNil(t, rpcSrvc) } -func TestCreateBABEService(t *testing.T) { +func TestCreateBABEService_Integration(t *testing.T) { cfg := NewTestConfig(t) - genFile := NewTestGenesisRawFile(t, cfg) + genFile := newTestGenesisRawFile(t, cfg) cfg.Core.Roles = types.FullNodeRole cfg.Init.Genesis = genFile @@ -195,26 +255,26 @@ func TestCreateBABEService(t *testing.T) { err := InitNode(cfg) require.NoError(t, err) - stateSrvc, err := createStateService(cfg) - require.NoError(t, err) + builder := nodeBuilder{} + stateSrvc := newStateServiceWithoutMock(t) ks := keystore.NewGlobalKeystore() kr, err := keystore.NewSr25519Keyring() require.NoError(t, err) ks.Babe.Insert(kr.Alice()) - ns, err := createRuntimeStorage(stateSrvc) + ns, err := builder.createRuntimeStorage(stateSrvc) require.NoError(t, err) - err = loadRuntime(cfg, ns, stateSrvc, ks, &network.Service{}) + err = builder.loadRuntime(cfg, ns, stateSrvc, ks, &network.Service{}) require.NoError(t, err) - dh, err := createDigestHandler(cfg.Log.DigestLvl, stateSrvc) + dh, err := builder.createDigestHandler(cfg.Log.DigestLvl, stateSrvc) require.NoError(t, err) - coreSrvc, err := createCoreService(cfg, ks, stateSrvc, &network.Service{}, dh) + coreSrvc, err := builder.createCoreService(cfg, ks, stateSrvc, &network.Service{}, dh) require.NoError(t, err) - bs, err := createBABEService(cfg, stateSrvc, ks.Babe, coreSrvc, nil) + bs, err := builder.createBABEService(cfg, stateSrvc, ks.Babe, coreSrvc, nil) require.NoError(t, err) require.NotNil(t, bs) } @@ -223,7 +283,7 @@ func TestCreateGrandpaService(t *testing.T) { cfg := NewTestConfig(t) require.NotNil(t, cfg) - genFile := NewTestGenesisRawFile(t, cfg) + genFile := newTestGenesisRawFile(t, cfg) cfg.Core.Roles = types.AuthorityRole cfg.Init.Genesis = genFile @@ -231,24 +291,31 @@ func TestCreateGrandpaService(t *testing.T) { err := InitNode(cfg) require.NoError(t, err) - stateSrvc, err := createStateService(cfg) - require.NoError(t, err) + builder := nodeBuilder{} + stateSrvc := newStateServiceWithoutMock(t) ks := keystore.NewGlobalKeystore() kr, err := keystore.NewEd25519Keyring() require.NoError(t, err) ks.Gran.Insert(kr.Alice()) - ns, err := createRuntimeStorage(stateSrvc) + ns, err := builder.createRuntimeStorage(stateSrvc) require.NoError(t, err) - err = loadRuntime(cfg, ns, stateSrvc, ks, &network.Service{}) + err = builder.loadRuntime(cfg, ns, stateSrvc, ks, &network.Service{}) require.NoError(t, err) - dh, err := createDigestHandler(cfg.Log.DigestLvl, stateSrvc) + dh, err := builder.createDigestHandler(cfg.Log.DigestLvl, stateSrvc) require.NoError(t, err) - gs, err := createGRANDPAService(cfg, stateSrvc, dh, ks.Gran, &network.Service{}, nil) + networkConfig := &network.Config{ + BasePath: t.TempDir(), + NoBootstrap: true, + NoMDNS: true, + } + testNetworkService := createTestService(t, networkConfig) + + gs, err := builder.createGRANDPAService(cfg, stateSrvc, dh, ks.Gran, testNetworkService, nil) require.NoError(t, err) require.NotNil(t, gs) } @@ -284,7 +351,7 @@ func TestNewWebSocketServer(t *testing.T) { cfg := NewTestConfig(t) require.NotNil(t, cfg) - genFile := NewTestGenesisRawFile(t, cfg) + genFile := newTestGenesisRawFile(t, cfg) cfg.Core.Roles = types.FullNodeRole cfg.Core.BabeAuthority = false @@ -298,8 +365,8 @@ func TestNewWebSocketServer(t *testing.T) { err := InitNode(cfg) require.NoError(t, err) - stateSrvc, err := createStateService(cfg) - require.NoError(t, err) + builder := nodeBuilder{} + stateSrvc := newStateServiceWithoutMock(t) networkSrvc := &network.Service{} @@ -307,18 +374,18 @@ func TestNewWebSocketServer(t *testing.T) { ed25519Keyring, _ := keystore.NewEd25519Keyring() ks.Gran.Insert(ed25519Keyring.Alice()) - ns, err := createRuntimeStorage(stateSrvc) + ns, err := builder.createRuntimeStorage(stateSrvc) require.NoError(t, err) - err = loadRuntime(cfg, ns, stateSrvc, ks, networkSrvc) + err = builder.loadRuntime(cfg, ns, stateSrvc, ks, networkSrvc) require.NoError(t, err) - dh, err := createDigestHandler(cfg.Log.DigestLvl, stateSrvc) + dh, err := builder.createDigestHandler(cfg.Log.DigestLvl, stateSrvc) require.NoError(t, err) - coreSrvc, err := createCoreService(cfg, ks, stateSrvc, networkSrvc, dh) + coreSrvc, err := builder.createCoreService(cfg, ks, stateSrvc, networkSrvc, dh) require.NoError(t, err) - sysSrvc, err := createSystemService(&cfg.System, stateSrvc) + sysSrvc, err := builder.createSystemService(&cfg.System, stateSrvc) require.NoError(t, err) rpcSettings := rpcServiceSettings{ @@ -329,7 +396,7 @@ func TestNewWebSocketServer(t *testing.T) { network: networkSrvc, system: sysSrvc, } - rpcSrvc, err := createRPCService(rpcSettings) + rpcSrvc, err := builder.createRPCService(rpcSettings) require.NoError(t, err) err = rpcSrvc.Start() require.NoError(t, err) @@ -353,10 +420,26 @@ func TestNewWebSocketServer(t *testing.T) { } func Test_createPprofService(t *testing.T) { - t.Parallel() - settings := pprof.Settings{} - service := createPprofService(settings) - require.NotNil(t, service) + tests := []struct { + name string + settings pprof.Settings + notNil bool + }{ + { + name: "base case", + notNil: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := createPprofService(tt.settings) + if tt.notNil { + assert.NotNil(t, got) + } else { + assert.Nil(t, got) + } + }) + } } func Test_createDigestHandler(t *testing.T) { @@ -371,13 +454,14 @@ func Test_createDigestHandler(t *testing.T) { err := InitNode(cfg) require.NoError(t, err) - stateSrvc, err := createStateService(cfg) + builder := nodeBuilder{} + stateSrvc, err := builder.createStateService(cfg) require.NoError(t, err) err = startStateService(cfg, stateSrvc) require.NoError(t, err) - _, err = createDigestHandler(cfg.Log.DigestLvl, stateSrvc) + _, err = builder.createDigestHandler(cfg.Log.DigestLvl, stateSrvc) require.NoError(t, err) } diff --git a/dot/services_test.go b/dot/services_test.go new file mode 100644 index 0000000000..e949157d11 --- /dev/null +++ b/dot/services_test.go @@ -0,0 +1,617 @@ +// Copyright 2021 ChainSafe Systems (ON) +// SPDX-License-Identifier: LGPL-3.0-only + +package dot + +import ( + "errors" + "reflect" + "testing" + + "github.com/ChainSafe/gossamer/dot/core" + "github.com/ChainSafe/gossamer/dot/digest" + "github.com/ChainSafe/gossamer/dot/network" + "github.com/ChainSafe/gossamer/dot/rpc" + "github.com/ChainSafe/gossamer/dot/state" + "github.com/ChainSafe/gossamer/dot/sync" + "github.com/ChainSafe/gossamer/dot/telemetry" + "github.com/ChainSafe/gossamer/dot/types" + "github.com/ChainSafe/gossamer/internal/log" + "github.com/ChainSafe/gossamer/lib/babe" + "github.com/ChainSafe/gossamer/lib/genesis" + "github.com/ChainSafe/gossamer/lib/grandpa" + "github.com/ChainSafe/gossamer/lib/keystore" + "github.com/ChainSafe/gossamer/lib/runtime" + "github.com/ChainSafe/gossamer/lib/runtime/life" + rtstorage "github.com/ChainSafe/gossamer/lib/runtime/storage" + "github.com/ChainSafe/gossamer/lib/runtime/wasmer" + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func Test_createBlockVerifier(t *testing.T) { + cfg := NewTestConfig(t) + + cfg.Init.Genesis = newTestGenesisRawFile(t, cfg) + + nodeInstance := nodeBuilder{} + err := nodeInstance.initNode(cfg) + require.NoError(t, err) + + stateSrvc, err := nodeInstance.createStateService(cfg) + require.NoError(t, err) + + stateSrvc.Block = &state.BlockState{} + stateSrvc.Epoch = &state.EpochState{} + + tests := []struct { + name string + service *state.Service + expectNil bool + err error + }{ + { + name: "nil BlockState test", + service: &state.Service{}, + expectNil: true, + err: errors.New("cannot have nil EpochState"), + }, + { + name: "working example", + service: stateSrvc, + expectNil: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := nodeInstance.createBlockVerifier(tt.service) + if tt.err != nil { + assert.EqualError(t, err, tt.err.Error()) + } else { + assert.NoError(t, err) + } + + if tt.expectNil { + assert.Nil(t, got) + } else { + assert.NotNil(t, got) + } + }) + } +} + +func Test_createRuntimeStorage(t *testing.T) { + cfg := NewTestConfig(t) + + cfg.Init.Genesis = newTestGenesisRawFile(t, cfg) + + builder := nodeBuilder{} + err := builder.initNode(cfg) + require.NoError(t, err) + + stateSrvc, err := builder.createStateService(cfg) + require.NoError(t, err) + + tests := []struct { + name string + service *state.Service + expectedBaseDB *state.BaseState + err error + }{ + { + name: "working example", + service: stateSrvc, + expectedBaseDB: stateSrvc.Base, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := builder.createRuntimeStorage(tt.service) + assert.ErrorIs(t, err, tt.err) + assert.Equal(t, tt.expectedBaseDB, got.BaseDB) + assert.NotNil(t, got.LocalStorage) + assert.NotNil(t, got.PersistentStorage) + }) + } +} + +func Test_createSystemService(t *testing.T) { + cfg := NewTestConfig(t) + + cfg.Init.Genesis = newTestGenesisRawFile(t, cfg) + + builder := nodeBuilder{} + err := builder.initNode(cfg) + require.NoError(t, err) + + stateSrvc, err := builder.createStateService(cfg) + require.NoError(t, err) + + type args struct { + cfg *types.SystemInfo + service *state.Service + } + tests := []struct { + name string + args args + expectNil bool + err error + }{ + { + name: "working example", + args: args{ + service: stateSrvc, + }, + expectNil: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := builder.createSystemService(tt.args.cfg, tt.args.service) + assert.ErrorIs(t, err, tt.err) + + // TODO: change this check to assert.Equal after state.Service interface is implemented. + if tt.expectNil { + assert.Nil(t, got) + } else { + assert.NotNil(t, got) + } + }) + } +} + +func Test_newInMemoryDB(t *testing.T) { + tests := []struct { + name string + expectNil bool + err error + }{ + { + name: "working example", + expectNil: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := newInMemoryDB() + assert.ErrorIs(t, err, tt.err) + + if tt.expectNil { + assert.Nil(t, got) + } else { + assert.NotNil(t, got) + } + }) + } +} + +//go:generate mockgen -destination=mock_babe_builder_test.go -package $GOPACKAGE github.com/ChainSafe/gossamer/lib/babe ServiceIFace,ServiceBuilder + +func Test_nodeBuilder_createBABEService(t *testing.T) { + ctrl := gomock.NewController(t) + mockBabeIFace := NewMockServiceIFace(ctrl) + t.Parallel() + + cfg := NewTestConfig(t) + + ks := keystore.NewGlobalKeystore() + ks2 := keystore.NewGlobalKeystore() + kr, err := keystore.NewSr25519Keyring() + require.NoError(t, err) + ks2.Babe.Insert(kr.Alice()) + + type args struct { + cfg *Config + initStateService bool + ks keystore.Keystore + cs *core.Service + telemetryMailer telemetry.Client + } + tests := []struct { + name string + args args + expected babe.ServiceIFace + err error + }{ + { + name: "invalid keystore", + args: args{ + cfg: cfg, + initStateService: true, + ks: ks.Gran, + }, + expected: nil, + err: ErrInvalidKeystoreType, + }, + { + name: "empty keystore", + args: args{ + cfg: cfg, + initStateService: true, + ks: ks.Babe, + }, + expected: nil, + err: ErrNoKeysProvided, + }, + { + name: "config error", + args: args{ + cfg: cfg, + initStateService: false, + ks: ks2.Babe, + }, + expected: nil, + err: babe.ErrNilBlockState, + }, + { + name: "base case", + args: args{ + cfg: cfg, + initStateService: true, + ks: ks2.Babe, + }, + expected: mockBabeIFace, + err: nil, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + stateSrvc := newStateService(t, ctrl) + mockBabeBuilder := NewMockServiceBuilder(ctrl) + mockBabeBuilder.EXPECT().NewServiceIFace( + gomock.AssignableToTypeOf(&babe.ServiceConfig{})).DoAndReturn(func(cfg *babe.ServiceConfig) (babe. + ServiceIFace, error) { + if reflect.ValueOf(cfg.BlockState).Kind() == reflect.Ptr && reflect.ValueOf(cfg.BlockState).IsNil() { + return nil, babe.ErrNilBlockState + } + return mockBabeIFace, nil + }).AnyTimes() + builder := nodeBuilder{} + var got babe.ServiceIFace + if tt.args.initStateService { + got, err = builder.createBABEServiceWithBuilder(tt.args.cfg, stateSrvc, tt.args.ks, tt.args.cs, + tt.args.telemetryMailer, mockBabeBuilder) + } else { + got, err = builder.createBABEServiceWithBuilder(tt.args.cfg, &state.Service{}, tt.args.ks, tt.args.cs, + tt.args.telemetryMailer, mockBabeBuilder) + } + + assert.Equal(t, tt.expected, got) + assert.ErrorIs(t, err, tt.err) + }) + } +} + +func newStateService(t *testing.T, ctrl *gomock.Controller) *state.Service { + t.Helper() + + telemetryMock := NewMockClient(ctrl) + telemetryMock.EXPECT().SendMessage(gomock.Any()).AnyTimes() + + stateConfig := state.Config{ + Path: t.TempDir(), + LogLevel: log.Info, + Telemetry: telemetryMock, + } + stateSrvc := state.NewService(stateConfig) + stateSrvc.UseMemDB() + genData, genTrie, genesisHeader := genesis.NewTestGenesisWithTrieAndHeader(t) + err := stateSrvc.Initialise(genData, genesisHeader, genTrie) + require.NoError(t, err) + + err = stateSrvc.SetupBase() + require.NoError(t, err) + + genesisBABEConfig := &types.BabeConfiguration{ + SlotDuration: 1000, + EpochLength: 200, + C1: 1, + C2: 4, + GenesisAuthorities: []types.AuthorityRaw{}, + Randomness: [32]byte{}, + SecondarySlots: 0, + } + epochState, err := state.NewEpochStateFromGenesis(stateSrvc.DB(), stateSrvc.Block, genesisBABEConfig) + require.NoError(t, err) + + stateSrvc.Epoch = epochState + + rtCfg := &wasmer.Config{} + + rtCfg.Storage, err = rtstorage.NewTrieState(genTrie) + require.NoError(t, err) + + rtCfg.CodeHash, err = stateSrvc.Storage.LoadCodeHash(nil) + require.NoError(t, err) + + rtCfg.NodeStorage = runtime.NodeStorage{} + + rt, err := wasmer.NewRuntimeFromGenesis(rtCfg) + require.NoError(t, err) + + stateSrvc.Block.StoreRuntime(stateSrvc.Block.BestBlockHash(), rt) + + return stateSrvc +} + +func Test_nodeBuilder_createCoreService(t *testing.T) { + t.Parallel() + + ks := keystore.NewGlobalKeystore() + kr, err := keystore.NewSr25519Keyring() + require.NoError(t, err) + ks.Babe.Insert(kr.Alice()) + + networkService := &network.Service{} + + type args struct { + ks *keystore.GlobalKeystore + net *network.Service + dh *digest.Handler + } + tests := []struct { + name string + args args + expectNil bool + err error + }{ + { + name: "base case", + args: args{ + ks: ks, + net: networkService, + }, + expectNil: false, + err: nil, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + cfg := NewTestConfig(t) + ctrl := gomock.NewController(t) + stateSrvc := newStateService(t, ctrl) + + builder := nodeBuilder{} + got, err := builder.createCoreService(cfg, tt.args.ks, stateSrvc, tt.args.net, tt.args.dh) + + assert.ErrorIs(t, err, tt.err) + + // TODO: create interface for core.NewService sa that we can assert.Equal the results + if tt.expectNil { + assert.Nil(t, got) + } else { + assert.NotNil(t, got) + assert.IsType(t, &core.Service{}, got) + } + }) + } +} + +func Test_nodeBuilder_createNetworkService(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + cfg *Config + expectNil bool + err error + }{ + { + name: "base case", + expectNil: false, + err: nil, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + cfg := NewTestConfig(t) + ctrl := gomock.NewController(t) + stateSrvc := newStateService(t, ctrl) + no := nodeBuilder{} + got, err := no.createNetworkService(cfg, stateSrvc, nil) + assert.ErrorIs(t, err, tt.err) + // TODO: create interface for network.NewService to handle assert.Equal test + if tt.expectNil { + assert.Nil(t, got) + } else { + assert.NotNil(t, got) + assert.IsType(t, &network.Service{}, got) + } + }) + } +} + +func Test_nodeBuilder_createRPCService(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + expectNil bool + err error + }{ + { + name: "base state", + expectNil: false, + err: nil, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + cfg := NewTestConfig(t) + ctrl := gomock.NewController(t) + stateSrvc := newStateService(t, ctrl) + no := nodeBuilder{} + rpcParams := rpcServiceSettings{ + config: cfg, + state: stateSrvc, + } + got, err := no.createRPCService(rpcParams) + assert.ErrorIs(t, err, tt.err) + + // TODO: create interface for rpc.HTTPServer to handle assert.Equal test + if tt.expectNil { + assert.Nil(t, got) + } else { + assert.NotNil(t, got) + assert.IsType(t, &rpc.HTTPServer{}, got) + } + }) + } +} + +func Test_nodeBuilder_createGRANDPAService(t *testing.T) { + t.Parallel() + ks := keystore.NewGlobalKeystore() + kr, err := keystore.NewEd25519Keyring() + require.NoError(t, err) + ks.Gran.Insert(kr.Alice()) + + require.NoError(t, err) + tests := []struct { + name string + ks keystore.Keystore + expectNil bool + err error + }{ + { + name: "wrong key type", + ks: ks.Babe, + expectNil: true, + err: ErrInvalidKeystoreType, + }, + { + name: "base case", + ks: ks.Gran, + expectNil: false, + err: nil, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + cfg := NewTestConfig(t) + ctrl := gomock.NewController(t) + stateSrvc := newStateService(t, ctrl) + networkConfig := &network.Config{ + BasePath: t.TempDir(), + BlockState: stateSrvc.Block, + RandSeed: 2, + } + networkSrvc, err := network.NewService(networkConfig) + require.NoError(t, err) + builder := nodeBuilder{} + got, err := builder.createGRANDPAService(cfg, stateSrvc, nil, tt.ks, networkSrvc, + nil) + assert.ErrorIs(t, err, tt.err) + // TODO: create interface for grandpa.NewService to enable testing with assert.Equal + if tt.expectNil { + assert.Nil(t, got) + } else { + assert.NotNil(t, got) + assert.IsType(t, &grandpa.Service{}, got) + } + }) + } +} + +func Test_createRuntime(t *testing.T) { + t.Parallel() + cfg := NewTestConfig(t) + + cfgLife := NewTestConfig(t) + cfgLife.Core.WasmInterpreter = life.Name + + type args struct { + cfg *Config + ns runtime.NodeStorage + } + tests := []struct { + name string + args args + expectedType interface{} + err error + }{ + { + name: "wasmer runtime", + args: args{ + cfg: cfg, + ns: runtime.NodeStorage{}, + }, + expectedType: &wasmer.Instance{}, + err: nil, + }, + { + name: "wasmer life", + args: args{ + cfg: cfgLife, + ns: runtime.NodeStorage{}, + }, + expectedType: &life.Instance{}, + err: nil, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + ctrl := gomock.NewController(t) + stateSrvc := newStateService(t, ctrl) + code, err := stateSrvc.Storage.LoadCode(nil) + require.NoError(t, err) + + got, err := createRuntime(tt.args.cfg, tt.args.ns, stateSrvc, nil, nil, code) + assert.ErrorIs(t, err, tt.err) + if tt.expectedType == nil { + assert.Nil(t, got) + } else { + assert.NotNil(t, got) + assert.IsType(t, tt.expectedType, got) + } + }) + } +} + +func Test_nodeBuilder_newSyncService(t *testing.T) { + t.Parallel() + finalityGadget := &grandpa.Service{} + type args struct { + fg sync.FinalityGadget + verifier *babe.VerificationManager + cs *core.Service + net *network.Service + telemetryMailer telemetry.Client + } + tests := []struct { + name string + args args + expectNil bool + err error + }{ + { + name: "base case", + args: args{ + fg: finalityGadget, + verifier: nil, + cs: nil, + net: nil, + telemetryMailer: nil, + }, + expectNil: false, + err: nil, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + cfg := NewTestConfig(t) + ctrl := gomock.NewController(t) + stateSrvc := newStateService(t, ctrl) + no := nodeBuilder{} + got, err := no.newSyncService(cfg, stateSrvc, tt.args.fg, tt.args.verifier, tt.args.cs, + tt.args.net, tt.args.telemetryMailer) + assert.ErrorIs(t, err, tt.err) + if tt.expectNil { + assert.Nil(t, got) + } else { + assert.NotNil(t, got) + } + }) + } +} diff --git a/dot/state/service.go b/dot/state/service.go index 86c5d76e42..aa763b1779 100644 --- a/dot/state/service.go +++ b/dot/state/service.go @@ -272,9 +272,11 @@ func (s *Service) Stop() error { func (s *Service) Import(header *types.Header, t *trie.Trie, firstSlot uint64) error { var err error // initialise database using data directory - s.db, err = utils.SetupDatabase(s.dbPath, s.isMemDB) - if err != nil { - return fmt.Errorf("failed to create database: %s", err) + if !s.isMemDB { + s.db, err = utils.SetupDatabase(s.dbPath, s.isMemDB) + if err != nil { + return fmt.Errorf("failed to create database: %s", err) + } } block := &BlockState{ diff --git a/dot/state/service_test.go b/dot/state/service_test.go index 01507161dd..7782a88047 100644 --- a/dot/state/service_test.go +++ b/dot/state/service_test.go @@ -367,8 +367,6 @@ func TestService_Import(t *testing.T) { genData, genTrie, genesisHeader := genesis.NewTestGenesisWithTrieAndHeader(t) err := serv.Initialise(genData, genesisHeader, genTrie) require.NoError(t, err) - err = serv.db.Close() - require.NoError(t, err) tr := trie.NewEmptyTrie() var testCases = []string{ diff --git a/dot/telemetry/telemetry.go b/dot/telemetry/telemetry.go index f18e05388c..504976ec85 100644 --- a/dot/telemetry/telemetry.go +++ b/dot/telemetry/telemetry.go @@ -32,3 +32,9 @@ type Client interface { type Message interface { MarshalJSON() ([]byte, error) } + +// NoopClient used for minimal implementation of the Client interface +type NoopClient struct{} + +// SendMessage is an empty implementation used for testing +func (NoopClient) SendMessage(msg Message) {} diff --git a/dot/utils.go b/dot/utils.go index 2c67588a5e..ca5dae9dfa 100644 --- a/dot/utils.go +++ b/dot/utils.go @@ -14,7 +14,6 @@ import ( "testing" ctoml "github.com/ChainSafe/gossamer/dot/config/toml" - "github.com/ChainSafe/gossamer/internal/log" "github.com/ChainSafe/gossamer/lib/genesis" "github.com/ChainSafe/gossamer/lib/runtime" "github.com/ChainSafe/gossamer/lib/runtime/wasmer" @@ -66,31 +65,6 @@ func NewTestGenesisRawFile(t *testing.T, cfg *Config) (filename string) { return filename } -// newTestGenesisFile returns a human-readable test genesis file using "gssmr" human readable data -func newTestGenesisFile(t *testing.T, cfg *Config) (filename string) { - fp := utils.GetGssmrGenesisPathTest(t) - - gssmrGen, err := genesis.NewGenesisFromJSON(fp, 0) - require.NoError(t, err) - - gen := &genesis.Genesis{ - Name: cfg.Global.Name, - ID: cfg.Global.ID, - Bootnodes: cfg.Network.Bootnodes, - ProtocolID: cfg.Network.ProtocolID, - Genesis: gssmrGen.GenesisFields(), - } - - b, err := json.Marshal(gen) - require.NoError(t, err) - - filename = filepath.Join(t.TempDir(), "genesis.json") - err = os.WriteFile(filename, b, os.ModePerm) - require.NoError(t, err) - - return filename -} - // NewTestGenesisAndRuntime create a new test runtime and a new test genesis // file with the test runtime stored in raw data and returns the genesis file func NewTestGenesisAndRuntime(t *testing.T) (filename string) { @@ -120,91 +94,44 @@ func NewTestGenesisAndRuntime(t *testing.T) (filename string) { return filename } -// NewTestConfig returns a new test configuration using the provided basepath -func NewTestConfig(t *testing.T) *Config { - dir := t.TempDir() - - cfg := &Config{ - Global: GlobalConfig{ - Name: GssmrConfig().Global.Name, - ID: GssmrConfig().Global.ID, - BasePath: dir, - LogLvl: log.Info, - NoTelemetry: true, - }, - Log: GssmrConfig().Log, - Init: GssmrConfig().Init, - Account: GssmrConfig().Account, - Core: GssmrConfig().Core, - Network: GssmrConfig().Network, - RPC: GssmrConfig().RPC, - } - - return cfg -} - -// newTestConfigWithFile returns a new test configuration and a temporary configuration file -func newTestConfigWithFile(t *testing.T) (*Config, *os.File) { - cfg := NewTestConfig(t) - - configPath := filepath.Join(cfg.Global.BasePath, "config.toml") - err := os.WriteFile(configPath, nil, os.ModePerm) - require.NoError(t, err) - - cfgFile := exportConfig(cfg, configPath) - return cfg, cfgFile -} - // exportConfig exports a dot configuration to a toml configuration file -func exportConfig(cfg *Config, fp string) *os.File { +func exportConfig(cfg *Config, fp string) { raw, err := toml.Marshal(*cfg) if err != nil { logger.Errorf("failed to marshal configuration: %s", err) os.Exit(1) } - return writeConfig(raw, fp) + if err := os.WriteFile(fp, raw, 0600); err != nil { + logger.Errorf("failed to write file: %s", err) + os.Exit(1) + } } // ExportTomlConfig exports a dot configuration to a toml configuration file -func ExportTomlConfig(cfg *ctoml.Config, fp string) *os.File { +func ExportTomlConfig(cfg *ctoml.Config, fp string) { raw, err := toml.Marshal(*cfg) if err != nil { logger.Errorf("failed to marshal configuration: %s", err) os.Exit(1) } - return writeConfig(raw, fp) -} - -// writeConfig writes the config `data` in the file 'fp'. -func writeConfig(data []byte, fp string) *os.File { - newFile, err := os.Create(filepath.Clean(fp)) - if err != nil { - logger.Errorf("failed to create configuration file: %s", err) + if err := os.WriteFile(fp, raw, 0600); err != nil { + logger.Errorf("failed to write file: %s", err) os.Exit(1) } - - _, err = newFile.Write(data) - if err != nil { - logger.Errorf("failed to write to configuration file: %s", err) - os.Exit(1) - } - - if err := newFile.Close(); err != nil { - logger.Errorf("failed to close configuration file: %s", err) - os.Exit(1) - } - - return newFile } // CreateJSONRawFile will generate a JSON genesis file with raw storage -func CreateJSONRawFile(bs *BuildSpec, fp string) *os.File { +func CreateJSONRawFile(bs *BuildSpec, fp string) { data, err := bs.ToJSONRaw() if err != nil { logger.Errorf("failed to convert into raw json: %s", err) os.Exit(1) } - return writeConfig(data, fp) + + if err := os.WriteFile(fp, data, 0600); err != nil { + logger.Errorf("failed to write file: %s", err) + os.Exit(1) + } } // RandomNodeName generates a new random name if there is no name configured for the node diff --git a/dot/utils_integration_test.go b/dot/utils_integration_test.go index 69a4a18aa8..430807de1b 100644 --- a/dot/utils_integration_test.go +++ b/dot/utils_integration_test.go @@ -17,7 +17,7 @@ func TestTrieSnapshot(t *testing.T) { cfg := NewTestConfig(t) require.NotNil(t, cfg) - genRawFile := NewTestGenesisRawFile(t, cfg) + genRawFile := newTestGenesisRawFile(t, cfg) genRaw, err := genesis.NewGenesisFromJSONRaw(genRawFile) require.NoError(t, err) diff --git a/dot/utils_test.go b/dot/utils_test.go new file mode 100644 index 0000000000..bdf39c12d5 --- /dev/null +++ b/dot/utils_test.go @@ -0,0 +1,428 @@ +// Copyright 2021 ChainSafe Systems (ON) +// SPDX-License-Identifier: LGPL-3.0-only + +package dot + +import ( + "crypto/sha256" + "encoding/json" + "fmt" + "io/ioutil" + "os" + "path/filepath" + "testing" + + ctoml "github.com/ChainSafe/gossamer/dot/config/toml" + "github.com/ChainSafe/gossamer/dot/types" + "github.com/ChainSafe/gossamer/internal/log" + "github.com/ChainSafe/gossamer/lib/genesis" + "github.com/ChainSafe/gossamer/lib/utils" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// newTestGenesisFile returns a human-readable test genesis file using "gssmr" human readable data +func newTestGenesisFile(t *testing.T, cfg *Config) (filename string) { + t.Helper() + + fp := utils.GetGssmrGenesisPathTest(t) + + gssmrGen, err := genesis.NewGenesisFromJSON(fp, 0) + require.NoError(t, err) + + gen := &genesis.Genesis{ + Name: cfg.Global.Name, + ID: cfg.Global.ID, + Bootnodes: cfg.Network.Bootnodes, + ProtocolID: cfg.Network.ProtocolID, + Genesis: gssmrGen.GenesisFields(), + } + + b, err := json.Marshal(gen) + require.NoError(t, err) + + filename = filepath.Join(t.TempDir(), "genesis.json") + err = os.WriteFile(filename, b, os.ModePerm) + require.NoError(t, err) + + return filename +} + +func TestCreateJSONRawFile(t *testing.T) { + type args struct { + bs *BuildSpec + fp string + } + tests := []struct { + name string + args args + expectedHash string + }{ + { + name: "working example", + args: args{ + bs: &BuildSpec{genesis: NewTestGenesis(t)}, + fp: filepath.Join(t.TempDir(), "/test.json"), + }, + expectedHash: "23356cdb5d3537d39b735726707216c9e329c7b8a2c8a41b25da0f5f936b3caa", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + CreateJSONRawFile(tt.args.bs, tt.args.fp) + + b, err := ioutil.ReadFile(tt.args.fp) + require.NoError(t, err) + digest := sha256.Sum256(b) + hexDigest := fmt.Sprintf("%x", digest) + require.Equal(t, tt.expectedHash, hexDigest) + }) + } +} + +func TestExportConfig(t *testing.T) { + filepath := t.TempDir() + "/test.json" + type args struct { + cfg *Config + fp string + } + tests := []struct { + name string + args args + want *os.File + wantedContent string + }{ + { + name: "working example", + args: args{ + cfg: &Config{}, + fp: filepath, + }, + want: &os.File{}, + wantedContent: `[global] +name = "" +id = "" +base_path = "" +log_lvl = 0 +publish_metrics = false +metrics_address = "" +no_telemetry = false +telemetry_urls = [] +retain_blocks = 0 +pruning = "" + +[log] +core_lvl = 0 +digest_lvl = 0 +sync_lvl = 0 +network_lvl = 0 +rpc_lvl = 0 +state_lvl = 0 +runtime_lvl = 0 +block_producer_lvl = 0 +finality_gadget_lvl = 0 + +[init] +genesis = "" + +[account] +key = "" +unlock = "" + +[core] +roles = 0 +babe_authority = false +b_a_b_e_lead = false +grandpa_authority = false +wasm_interpreter = "" +grandpa_interval = 0 + +[network] +port = 0 +bootnodes = [] +protocol_id = "" +no_bootstrap = false +no_m_dns = false +min_peers = 0 +max_peers = 0 +persistent_peers = [] +discovery_interval = 0 +public_ip = "" +public_dns = "" + +[rpc] +enabled = false +external = false +unsafe = false +unsafe_external = false +port = 0 +host = "" +modules = [] +w_s_port = 0 +w_s = false +w_s_external = false +w_s_unsafe = false +w_s_unsafe_external = false + +[system] +system_name = "" +system_version = "" + +[state] +rewind = 0 + +[pprof] +enabled = false + +[pprof.settings] +listening_address = "" +block_profile_rate = 0 +mutex_profile_rate = 0 +`, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + exportConfig(tt.args.cfg, tt.args.fp) + + content, err := ioutil.ReadFile(tt.args.fp) + require.NoError(t, err) + require.Equal(t, tt.wantedContent, string(content)) + + }) + } +} + +func TestExportTomlConfig(t *testing.T) { + filepath := t.TempDir() + "/test.json" + type args struct { + cfg *ctoml.Config + fp string + } + tests := []struct { + name string + args args + wantedContent string + }{ + { + name: "working example", + args: args{ + cfg: &ctoml.Config{}, + fp: filepath, + }, + wantedContent: `[core] +babe-authority = false +grandpa-authority = false +`, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + ExportTomlConfig(tt.args.cfg, tt.args.fp) + + content, err := ioutil.ReadFile(tt.args.fp) + require.NoError(t, err) + require.Equal(t, tt.wantedContent, string(content)) + + }) + } +} + +func TestNewTestConfig(t *testing.T) { + basePath := t.TempDir() + incBasePath := basePath[:len(basePath)-1] + "2" + type args struct { + t *testing.T + } + tests := []struct { + name string + args args + want *Config + }{ + { + name: "working example", + args: args{t: t}, + want: &Config{ + Global: GlobalConfig{ + Name: "Gossamer", + ID: "gssmr", + BasePath: incBasePath, + LogLvl: 3, + PublishMetrics: false, + MetricsAddress: "", + NoTelemetry: true, + TelemetryURLs: nil, + RetainBlocks: 0, + Pruning: "", + }, + Log: LogConfig{ + CoreLvl: 3, + DigestLvl: 3, + SyncLvl: 3, + NetworkLvl: 3, + RPCLvl: 3, + StateLvl: 3, + RuntimeLvl: 3, + BlockProducerLvl: 3, + FinalityGadgetLvl: 3, + }, + Init: InitConfig{Genesis: "./chain/gssmr/genesis-spec.json"}, + Core: CoreConfig{ + Roles: 4, + BabeAuthority: true, + BABELead: false, + GrandpaAuthority: true, + WasmInterpreter: "wasmer", + GrandpaInterval: 1000000000, + }, + Network: NetworkConfig{ + Port: 7001, + Bootnodes: nil, + ProtocolID: "", + NoBootstrap: false, + NoMDNS: false, + MinPeers: 1, + MaxPeers: 50, + PersistentPeers: nil, + DiscoveryInterval: 10000000000, + }, + RPC: RPCConfig{ + Enabled: false, + External: false, + Unsafe: false, + UnsafeExternal: false, + Port: 8545, + Host: "localhost", + Modules: []string{"system", "author", "chain", "state", "rpc", "grandpa", "offchain", + "childstate", "syncstate", "payment"}, + WSPort: 8546, + WS: false, + WSExternal: false, + WSUnsafe: false, + WSUnsafeExternal: false, + }, + System: types.SystemInfo{}, + State: StateConfig{}, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := NewTestConfig(tt.args.t) + assert.Equal(t, tt.want, got) + }) + } +} + +func TestNewTestGenesisFile(t *testing.T) { + type args struct { + t *testing.T + cfg *Config + } + tests := []struct { + name string + args args + want *os.File + }{ + { + name: "working example", + args: args{ + t: t, + cfg: &Config{}, + }, + want: &os.File{}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := newTestGenesisFile(tt.args.t, tt.args.cfg) + if tt.want != nil { + assert.NotNil(t, got) + } + }) + } +} + +func TestRandomNodeName(t *testing.T) { + tests := []struct { + name string + want string + }{ + { + name: "working example", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := RandomNodeName() + assert.Regexp(t, "[a-z]*-[a-z]*-[0-9]*", got) + }) + } +} + +// NewTestConfig returns a new test configuration using the provided basepath +func NewTestConfig(t *testing.T) *Config { + dir := t.TempDir() + + cfg := &Config{ + Global: GlobalConfig{ + Name: GssmrConfig().Global.Name, + ID: GssmrConfig().Global.ID, + BasePath: dir, + LogLvl: log.Info, + NoTelemetry: true, + }, + Log: GssmrConfig().Log, + Init: GssmrConfig().Init, + Account: GssmrConfig().Account, + Core: GssmrConfig().Core, + Network: GssmrConfig().Network, + RPC: GssmrConfig().RPC, + } + + return cfg +} + +// NewTestGenesis returns a test genesis instance using "gssmr" raw data +func NewTestGenesis(t *testing.T) *genesis.Genesis { + fp, err := utils.GetGssmrGenesisRawPath() + require.NoError(t, err) + + gssmrGen, err := genesis.NewGenesisFromJSONRaw(fp) + require.NoError(t, err) + + return &genesis.Genesis{ + Name: "test", + ID: "test", + Bootnodes: []string(nil), + ProtocolID: "/gossamer/test/0", + Genesis: gssmrGen.GenesisFields(), + } +} + +// newTestGenesisRawFile returns a test genesis file using "gssmr" raw data +func newTestGenesisRawFile(t *testing.T, cfg *Config) (filename string) { + filename = filepath.Join(t.TempDir(), "genesis.json") + + fp, err := utils.GetGssmrGenesisRawPath() + require.NoError(t, err) + + gssmrGen, err := genesis.NewGenesisFromJSONRaw(fp) + require.NoError(t, err) + + gen := &genesis.Genesis{ + Name: cfg.Global.Name, + ID: cfg.Global.ID, + Bootnodes: cfg.Network.Bootnodes, + ProtocolID: cfg.Network.ProtocolID, + Genesis: gssmrGen.GenesisFields(), + } + + b, err := json.Marshal(gen) + require.NoError(t, err) + + err = os.WriteFile(filename, b, os.ModePerm) + require.NoError(t, err) + + return filename +} diff --git a/go.mod b/go.mod index 04e823cfa7..a2ac897801 100644 --- a/go.mod +++ b/go.mod @@ -1,7 +1,7 @@ module github.com/ChainSafe/gossamer require ( - github.com/ChainSafe/chaindb v0.1.5-0.20210608140454-9606fe8c3985 + github.com/ChainSafe/chaindb v0.1.5-0.20220322154826-c0d431995732 github.com/ChainSafe/go-schnorrkel v0.0.0-20210318173838-ccb5cd955283 github.com/OneOfOne/xxhash v1.2.8 github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce diff --git a/go.sum b/go.sum index f9326a0968..f988f6d5f1 100644 --- a/go.sum +++ b/go.sum @@ -61,8 +61,8 @@ github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6L github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/ChainSafe/chaindb v0.1.5-0.20210608140454-9606fe8c3985 h1:jyFsOjzTMoRwNvmW/OORZpffmItkoLvsbxB8koHX4ns= -github.com/ChainSafe/chaindb v0.1.5-0.20210608140454-9606fe8c3985/go.mod h1:P01m9E6xj6Mps1rtf7SurEX9oOcy1jYEyccZQAEw9+4= +github.com/ChainSafe/chaindb v0.1.5-0.20220322154826-c0d431995732 h1:dOdDfWtgfucQHXder8eSItN03JgQzY1V0BiKJgNERnE= +github.com/ChainSafe/chaindb v0.1.5-0.20220322154826-c0d431995732/go.mod h1:P01m9E6xj6Mps1rtf7SurEX9oOcy1jYEyccZQAEw9+4= github.com/ChainSafe/go-schnorrkel v0.0.0-20201021020641-d3c6d3118d10/go.mod h1:URdX5+vg25ts3aCh8H5IFZybJYKWhJHYMTnf+ULtoC4= github.com/ChainSafe/go-schnorrkel v0.0.0-20210318173838-ccb5cd955283 h1:bCAjrlKrO8Y9biIFMx2ejhXpG1x75mwKqbsL8dx5EOk= github.com/ChainSafe/go-schnorrkel v0.0.0-20210318173838-ccb5cd955283/go.mod h1:URdX5+vg25ts3aCh8H5IFZybJYKWhJHYMTnf+ULtoC4= diff --git a/lib/babe/babe.go b/lib/babe/babe.go index 60eeb6fa7e..1e102d4909 100644 --- a/lib/babe/babe.go +++ b/lib/babe/babe.go @@ -68,7 +68,101 @@ type ServiceConfig struct { Telemetry telemetry.Client } -// NewService returns a new Babe Service using the provided VRF keys and runtime +// Validate returns error if config does not contain required attributes +func (sc *ServiceConfig) Validate() error { + if sc.Keypair == nil && sc.Authority { + return errNoBABEAuthorityKeyProvided + } + + if sc.BlockState == nil { + return ErrNilBlockState + } + + if sc.EpochState == nil { + return errNilEpochState + } + + if sc.BlockImportHandler == nil { + return errNilBlockImportHandler + } + + return nil +} + +// ServiceIFace interface that defines methods available to BabeService +type ServiceIFace interface { + Start() error + Stop() error + EpochLength() uint64 + Pause() error + IsPaused() bool + Resume() error + SlotDuration() uint64 +} + +// ServiceBuilder interface to define the building of babe service +type ServiceBuilder interface { + NewServiceIFace(cfg *ServiceConfig) (ServiceIFace, error) +} + +var _ ServiceBuilder = (*Builder)(nil) + +// Builder struct to hold babe builder functions +type Builder struct{} + +//NewServiceIFace returns a new Babe Service using the provided VRF keys and runtime +func (Builder) NewServiceIFace(cfg *ServiceConfig) (ServiceIFace, error) { + if err := cfg.Validate(); err != nil { + return nil, fmt.Errorf("could not verify service config: %w", err) + } + + logger.Patch(log.SetLevel(cfg.LogLvl)) + + slotDuration, err := cfg.EpochState.GetSlotDuration() + if err != nil { + return nil, fmt.Errorf("cannot get slot duration: %w", err) + } + + epochLength, err := cfg.EpochState.GetEpochLength() + if err != nil { + return nil, fmt.Errorf("cannot get epoch length: %w", err) + } + + ctx, cancel := context.WithCancel(context.Background()) + + babeService := &Service{ + ctx: ctx, + cancel: cancel, + blockState: cfg.BlockState, + storageState: cfg.StorageState, + epochState: cfg.EpochState, + keypair: cfg.Keypair, + transactionState: cfg.TransactionState, + pause: make(chan struct{}), + authority: cfg.Authority, + dev: cfg.IsDev, + blockImportHandler: cfg.BlockImportHandler, + lead: cfg.Lead, + constants: constants{ + slotDuration: slotDuration, + epochLength: epochLength, + }, + telemetry: cfg.Telemetry, + } + + logger.Debugf( + "created service with block producer ID=%v, slot duration %s, epoch length (slots) %d", + cfg.Authority, babeService.constants.slotDuration, babeService.constants.epochLength, + ) + + if cfg.Lead { + logger.Debug("node designated to build block 1") + } + + return babeService, nil +} + +// NewService function to create babe service func NewService(cfg *ServiceConfig) (*Service, error) { if cfg.Keypair == nil && cfg.Authority { return nil, errors.New("cannot create BABE service as authority; no keypair provided") @@ -202,6 +296,11 @@ func (b *Service) EpochLength() uint64 { return b.constants.epochLength } +// GetBlockImportHandler returns the block import handler of the service. +func (b *Service) GetBlockImportHandler() BlockImportHandler { + return b.blockImportHandler +} + // Pause pauses the service ie. halts block production func (b *Service) Pause() error { b.Lock() diff --git a/lib/babe/babe_integration_test.go b/lib/babe/babe_integration_test.go index cc4c7e7f2e..1f14fce7f4 100644 --- a/lib/babe/babe_integration_test.go +++ b/lib/babe/babe_integration_test.go @@ -226,8 +226,11 @@ func TestService_SlotDuration(t *testing.T) { } func TestService_ProducesBlocks(t *testing.T) { - babeService := createTestService(t, nil) - babeService.lead = true + cfg := &ServiceConfig{ + Authority: true, + Lead: true, + } + babeService := createTestService(t, cfg) err := babeService.Start() require.NoError(t, err) @@ -236,7 +239,7 @@ func TestService_ProducesBlocks(t *testing.T) { }() time.Sleep(babeService.constants.slotDuration * 2) - babeService.blockImportHandler.(*mocks.BlockImportHandler). + babeService.GetBlockImportHandler().(*mocks.BlockImportHandler). AssertCalled(t, "HandleBlockProduced", mock.AnythingOfType("*types.Block"), mock.AnythingOfType("*storage.TrieState")) diff --git a/lib/babe/errors.go b/lib/babe/errors.go index 1ff4e8878f..527a0a8915 100644 --- a/lib/babe/errors.go +++ b/lib/babe/errors.go @@ -64,27 +64,28 @@ var ( ErrNilTransactionState = errors.New("cannot create block builder; transaction state is nil") ErrNilVRFProof = errors.New("cannot create block builder; slot VRF proof is nil") - errNilBlockImportHandler = errors.New("cannot have nil BlockImportHandler") - errNilEpochState = errors.New("cannot have nil EpochState") - errNilStorageState = errors.New("storage state is nil") - errNilParentHeader = errors.New("parent header is nil") - errInvalidResult = errors.New("invalid error value") - errNoEpochData = errors.New("no epoch data found for upcoming epoch") - errFirstBlockTimeout = errors.New("timed out waiting for first block") - errChannelClosed = errors.New("block notifier channel was closed") - errOverPrimarySlotThreshold = errors.New("cannot claim slot, over primary threshold") - errNotOurTurnToPropose = errors.New("cannot claim slot, not our turn to propose a block") - errNoConfigData = errors.New("cannot find ConfigData for epoch") - errGetEpochData = errors.New("get epochData error") - errFailedFinalisation = errors.New("failed to check finalisation") - errMissingDigest = errors.New("chain head missing digest") - errSetFirstSlot = errors.New("set first slot error") - errGetEpoch = errors.New("get epoch error") - errSkipVerify = errors.New("skipVerify error") - errMissingDigestItems = errors.New("block header is missing digest items") - errDescendant = errors.New("descendant err") - errServicePaused = errors.New("service paused") - errInvalidSlotTechnique = errors.New("invalid slot claiming technique") + errNilBlockImportHandler = errors.New("cannot have nil BlockImportHandler") + errNilEpochState = errors.New("cannot have nil EpochState") + errNilStorageState = errors.New("storage state is nil") + errNilParentHeader = errors.New("parent header is nil") + errInvalidResult = errors.New("invalid error value") + errNoEpochData = errors.New("no epoch data found for upcoming epoch") + errFirstBlockTimeout = errors.New("timed out waiting for first block") + errChannelClosed = errors.New("block notifier channel was closed") + errOverPrimarySlotThreshold = errors.New("cannot claim slot, over primary threshold") + errNotOurTurnToPropose = errors.New("cannot claim slot, not our turn to propose a block") + errNoConfigData = errors.New("cannot find ConfigData for epoch") + errGetEpochData = errors.New("get epochData error") + errFailedFinalisation = errors.New("failed to check finalisation") + errMissingDigest = errors.New("chain head missing digest") + errSetFirstSlot = errors.New("set first slot error") + errGetEpoch = errors.New("get epoch error") + errSkipVerify = errors.New("skipVerify error") + errMissingDigestItems = errors.New("block header is missing digest items") + errDescendant = errors.New("descendant err") + errServicePaused = errors.New("service paused") + errInvalidSlotTechnique = errors.New("invalid slot claiming technique") + errNoBABEAuthorityKeyProvided = errors.New("cannot create BABE service as authority; no keypair provided") other Other invalidCustom InvalidCustom diff --git a/lib/babe/verify.go b/lib/babe/verify.go index 459b5e57c9..32d0e09663 100644 --- a/lib/babe/verify.go +++ b/lib/babe/verify.go @@ -5,6 +5,7 @@ package babe import ( "fmt" + "reflect" "sync" "github.com/ChainSafe/gossamer/dot/types" @@ -49,7 +50,7 @@ func NewVerificationManager(blockState BlockState, epochState EpochState) (*Veri return nil, ErrNilBlockState } - if epochState == nil { + if reflect.ValueOf(epochState).IsNil() { return nil, errNilEpochState } diff --git a/lib/services/services.go b/lib/services/services.go index 30fe41edc4..0ae54d0e67 100644 --- a/lib/services/services.go +++ b/lib/services/services.go @@ -17,6 +17,15 @@ type Service interface { Stop() error } +// ServiceRegisterer can register a service interface, start or stop all services, +// and get a particular service. +type ServiceRegisterer interface { + RegisterService(service Service) + StartAll() + StopAll() + Get(srvc interface{}) Service +} + // ServiceRegistry is a structure to manage core system services type ServiceRegistry struct { services map[reflect.Type]Service // map of types to service instances diff --git a/tests/utils/gossamer_utils.go b/tests/utils/gossamer_utils.go index ec36d3009d..e043b4a610 100644 --- a/tests/utils/gossamer_utils.go +++ b/tests/utils/gossamer_utils.go @@ -411,7 +411,7 @@ func GenerateGenesisThreeAuth() { Logger.Errorf("genesis file not found: %s", err) os.Exit(1) } - _ = dot.CreateJSONRawFile(bs, GenesisThreeAuths) + dot.CreateJSONRawFile(bs, GenesisThreeAuths) } // GenerateGenesisSixAuth generates Genesis file with six authority. @@ -421,7 +421,7 @@ func GenerateGenesisSixAuth(t *testing.T) { Logger.Errorf("genesis file not found: %s", err) os.Exit(1) } - _ = dot.CreateJSONRawFile(bs, GenesisSixAuths) + dot.CreateJSONRawFile(bs, GenesisSixAuths) } func generateDefaultConfig() *ctoml.Config { @@ -473,7 +473,7 @@ func generateDefaultConfig() *ctoml.Config { // CreateDefaultConfig generates and creates default config file. func CreateDefaultConfig() { cfg := generateDefaultConfig() - _ = dot.ExportTomlConfig(cfg, ConfigDefault) + dot.ExportTomlConfig(cfg, ConfigDefault) } func generateConfigLogGrandpa() *ctoml.Config { @@ -491,7 +491,7 @@ func generateConfigLogGrandpa() *ctoml.Config { // CreateConfigLogGrandpa generates and creates grandpa config file. func CreateConfigLogGrandpa() { cfg := generateConfigLogGrandpa() - _ = dot.ExportTomlConfig(cfg, ConfigLogGrandpa) + dot.ExportTomlConfig(cfg, ConfigLogGrandpa) } func generateConfigNoBabe() *ctoml.Config { @@ -509,7 +509,7 @@ func generateConfigNoBabe() *ctoml.Config { // CreateConfigNoBabe generates and creates no babe config file. func CreateConfigNoBabe() { cfg := generateConfigNoBabe() - _ = dot.ExportTomlConfig(cfg, ConfigNoBABE) + dot.ExportTomlConfig(cfg, ConfigNoBABE) } func generateConfigNoGrandpa() *ctoml.Config { @@ -523,7 +523,7 @@ func generateConfigNoGrandpa() *ctoml.Config { // CreateConfigNoGrandpa generates and creates no grandpa config file. func CreateConfigNoGrandpa() { cfg := generateConfigNoGrandpa() - _ = dot.ExportTomlConfig(cfg, ConfigNoGrandpa) + dot.ExportTomlConfig(cfg, ConfigNoGrandpa) } func generateConfigNotAuthority() *ctoml.Config { @@ -537,5 +537,5 @@ func generateConfigNotAuthority() *ctoml.Config { // CreateConfigNotAuthority generates and creates non-authority config file. func CreateConfigNotAuthority() { cfg := generateConfigNotAuthority() - _ = dot.ExportTomlConfig(cfg, ConfigNotAuthority) + dot.ExportTomlConfig(cfg, ConfigNotAuthority) }