diff --git a/dot/build_spec_integration_test.go b/dot/build_spec_integration_test.go new file mode 100644 index 0000000000..3eeb948c4d --- /dev/null +++ b/dot/build_spec_integration_test.go @@ -0,0 +1,157 @@ +//go:build integration +// +build integration + +// Copyright 2020 ChainSafe Systems (ON) Corp. +// This file is part of gossamer. +// +// The gossamer library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The gossamer library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the gossamer library. If not, see . + +package dot + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "os" + "testing" + + "github.com/ChainSafe/gossamer/lib/genesis" + "github.com/stretchr/testify/require" +) + +const genesisLocation = "../chain/gssmr/genesis.json" + +func TestBuildFromGenesis_Integration(t *testing.T) { + file, err := genesis.CreateTestGenesisJSONFile(false) + defer os.Remove(file) + require.NoError(t, err) + bs, err := BuildFromGenesis(file, 0) + + expectedChainType := "TESTCHAINTYPE" + expectedProperties := map[string]interface{}{ + "ss58Format": 0.0, + "tokenDecimals": 0.0, + "tokenSymbol": "TEST", + } + + bs.genesis.ChainType = expectedChainType + bs.genesis.Properties = expectedProperties + + require.NoError(t, err) + + // confirm human-readable fields + hr, err := bs.ToJSON() + require.NoError(t, err) + jGen := genesis.Genesis{} + err = json.Unmarshal(hr, &jGen) + require.NoError(t, err) + genesis.TestGenesis.Genesis = genesis.TestFieldsHR + require.Equal(t, genesis.TestGenesis.Genesis.Runtime, jGen.Genesis.Runtime) + require.Equal(t, expectedChainType, jGen.ChainType) + require.Equal(t, expectedProperties, jGen.Properties) + + // confirm raw fields + raw, err := bs.ToJSONRaw() + require.NoError(t, err) + jGenRaw := genesis.Genesis{} + err = json.Unmarshal(raw, &jGenRaw) + require.NoError(t, err) + genesis.TestGenesis.Genesis = genesis.TestFieldsRaw + require.Equal(t, genesis.TestGenesis.Genesis.Raw, jGenRaw.Genesis.Raw) + require.Equal(t, expectedChainType, jGenRaw.ChainType) + require.Equal(t, expectedProperties, jGenRaw.Properties) +} + +func TestBuildFromGenesis_WhenGenesisDoesNotExists(t *testing.T) { + bs, err := BuildFromGenesis("/not/exists/genesis.json", 0) + require.Nil(t, bs) + require.Error(t, err, os.ErrNotExist) +} + +func TestWriteGenesisSpecFileWhenFileAlreadyExists(t *testing.T) { + f, err := ioutil.TempFile("", "existing file data") + require.NoError(t, err) + defer os.Remove(f.Name()) + + someBytes := []byte("Testing some bytes") + err = WriteGenesisSpecFile(someBytes, f.Name()) + + require.Error(t, err, + fmt.Sprintf("file %s already exists, rename to avoid overwritten", f.Name())) +} + +func TestWriteGenesisSpecFile_Integration(t *testing.T) { + cfg := NewTestConfig(t) + cfg.Init.Genesis = genesisLocation + + expected, err := genesis.NewGenesisFromJSONRaw(cfg.Init.Genesis) + require.NoError(t, err) + + err = InitNode(cfg) + require.NoError(t, err) + + bs, err := BuildFromGenesis(cfg.Init.Genesis, 0) + require.NoError(t, err) + + data, err := bs.ToJSONRaw() + require.NoError(t, err) + + tmpFiles := []string{ + "/tmp/unique-raw-genesis.json", + "./unique-raw-genesis.json", + } + + for _, tmpFile := range tmpFiles { + err = WriteGenesisSpecFile(data, tmpFile) + require.NoError(t, err) + require.FileExists(t, tmpFile) + + defer os.Remove(tmpFile) + + file, err := os.Open(tmpFile) + require.NoError(t, err) + defer file.Close() + + genesisBytes, err := ioutil.ReadAll(file) + require.NoError(t, err) + + gen := new(genesis.Genesis) + err = json.Unmarshal(genesisBytes, gen) + require.NoError(t, err) + + require.Equal(t, expected.ChainType, gen.ChainType) + require.Equal(t, expected.Properties, gen.Properties) + } +} + +func TestBuildFromDB_Integration(t *testing.T) { + // setup expected + cfg := NewTestConfig(t) + cfg.Init.Genesis = genesisLocation + expected, err := genesis.NewGenesisFromJSONRaw(cfg.Init.Genesis) + require.NoError(t, err) + // initialise node (initialise state database and load genesis data) + err = InitNode(cfg) + require.NoError(t, err) + + bs, err := BuildFromDB(cfg.Global.BasePath) + require.NoError(t, err) + res, err := bs.ToJSON() + require.NoError(t, err) + jGen := genesis.Genesis{} + err = json.Unmarshal(res, &jGen) + require.NoError(t, err) + + require.Equal(t, expected.Genesis.Raw["top"]["0x3a636f6465"], jGen.Genesis.Runtime["system"]["code"]) +} diff --git a/dot/build_spec_test.go b/dot/build_spec_test.go index b00683ccea..2dbedd5a2f 100644 --- a/dot/build_spec_test.go +++ b/dot/build_spec_test.go @@ -4,136 +4,345 @@ package dot import ( - "encoding/json" - "fmt" - "io" + "errors" "os" "testing" "github.com/ChainSafe/gossamer/lib/genesis" - "github.com/stretchr/testify/require" + "github.com/stretchr/testify/assert" ) -func TestBuildFromGenesis(t *testing.T) { - file, err := genesis.CreateTestGenesisJSONFile(false) - defer os.Remove(file) - require.NoError(t, err) - bs, err := BuildFromGenesis(file, 0) - - expectedChainType := "TESTCHAINTYPE" - expectedProperties := map[string]interface{}{ - "ss58Format": 0.0, - "tokenDecimals": 0.0, - "tokenSymbol": "TEST", +func TestBuildSpec_ToJSON(t *testing.T) { + type fields struct { + genesis *genesis.Genesis } - - bs.genesis.ChainType = expectedChainType - bs.genesis.Properties = expectedProperties - - require.NoError(t, err) - - // confirm human-readable fields - hr, err := bs.ToJSON() - require.NoError(t, err) - jGen := genesis.Genesis{} - err = json.Unmarshal(hr, &jGen) - require.NoError(t, err) - genesis.TestGenesis.Genesis = genesis.TestFieldsHR - require.Equal(t, genesis.TestGenesis.Genesis.Runtime, jGen.Genesis.Runtime) - require.Equal(t, expectedChainType, jGen.ChainType) - require.Equal(t, expectedProperties, jGen.Properties) - - // confirm raw fields - raw, err := bs.ToJSONRaw() - require.NoError(t, err) - jGenRaw := genesis.Genesis{} - err = json.Unmarshal(raw, &jGenRaw) - require.NoError(t, err) - genesis.TestGenesis.Genesis = genesis.TestFieldsRaw - require.Equal(t, genesis.TestGenesis.Genesis.Raw, jGenRaw.Genesis.Raw) - require.Equal(t, expectedChainType, jGenRaw.ChainType) - require.Equal(t, expectedProperties, jGenRaw.Properties) -} - -func TestBuildFromGenesis_WhenGenesisDoesNotExists(t *testing.T) { - bs, err := BuildFromGenesis("/not/exists/genesis.json", 0) - require.Nil(t, bs) - require.Error(t, err, os.ErrNotExist) -} - -func TestWriteGenesisSpecFileWhenFileAlreadyExists(t *testing.T) { - f, err := os.CreateTemp("", "existing file data") - require.NoError(t, err) - defer os.Remove(f.Name()) - - someBytes := []byte("Testing some bytes") - err = WriteGenesisSpecFile(someBytes, f.Name()) - - require.Error(t, err, - fmt.Sprintf("file %s already exists, rename to avoid overwritten", f.Name())) + tests := []struct { + name string + fields fields + want []byte + err error + }{ + { + name: "name test", + fields: fields{genesis: &genesis.Genesis{Name: "test"}}, + want: []byte{123, 10, 32, 32, 32, 32, 34, 110, 97, 109, 101, 34, 58, 32, 34, 116, 101, 115, 116, 34, + 44, 10, 32, 32, 32, 32, 34, 105, 100, 34, 58, 32, 34, 34, 44, 10, 32, 32, 32, 32, 34, 99, 104, 97, + 105, 110, 84, 121, 112, 101, 34, 58, 32, 34, 34, 44, 10, 32, 32, 32, 32, 34, 98, 111, 111, 116, 78, + 111, 100, 101, 115, 34, 58, 32, 110, 117, 108, 108, 44, 10, 32, 32, 32, 32, 34, 116, 101, 108, 101, + 109, 101, 116, 114, 121, 69, 110, 100, 112, 111, 105, 110, 116, 115, 34, 58, 32, 110, 117, 108, 108, + 44, 10, 32, 32, 32, 32, 34, 112, 114, 111, 116, 111, 99, 111, 108, 73, 100, 34, 58, 32, 34, 34, 44, + 10, 32, 32, 32, 32, 34, 103, 101, 110, 101, 115, 105, 115, 34, 58, 32, 123, 125, 44, 10, 32, 32, 32, + 32, 34, 112, 114, 111, 112, 101, 114, 116, 105, 101, 115, 34, 58, 32, 110, 117, 108, 108, 44, 10, 32, + 32, 32, 32, 34, 102, 111, 114, 107, 66, 108, 111, 99, 107, 115, 34, 58, 32, 110, 117, 108, 108, 44, + 10, 32, 32, 32, 32, 34, 98, 97, 100, 66, 108, 111, 99, 107, 115, 34, 58, 32, 110, 117, 108, 108, 44, + 10, 32, 32, 32, 32, 34, 99, 111, 110, 115, 101, 110, 115, 117, 115, 69, 110, 103, 105, 110, 101, 34, + 58, 32, 34, 34, 44, 10, 32, 32, 32, 32, 34, 99, 111, 100, 101, 83, 117, 98, 115, 116, 105, 116, 117, + 116, 101, 115, 34, 58, 32, 110, 117, 108, 108, 10, 125}, + }, + { + name: "additional parameters test", + fields: fields{genesis: &genesis.Genesis{ + Name: "test", + ID: "ID", + ChainType: "chainType", + ProtocolID: "protocol", + ConsensusEngine: "babe", + }}, + want: []byte{123, 10, 32, 32, 32, 32, 34, 110, 97, 109, 101, 34, 58, 32, 34, 116, 101, 115, 116, 34, 44, + 10, 32, 32, 32, 32, 34, 105, 100, 34, 58, 32, 34, 73, 68, 34, 44, 10, 32, 32, 32, 32, 34, 99, 104, + 97, 105, 110, 84, 121, 112, 101, 34, 58, 32, 34, 99, 104, 97, 105, 110, 84, 121, 112, 101, 34, 44, + 10, 32, 32, 32, 32, 34, 98, 111, 111, 116, 78, 111, 100, 101, 115, 34, 58, 32, 110, 117, 108, 108, + 44, 10, 32, 32, 32, 32, 34, 116, 101, 108, 101, 109, 101, 116, 114, 121, 69, 110, 100, 112, 111, 105, + 110, 116, 115, 34, 58, 32, 110, 117, 108, 108, 44, 10, 32, 32, 32, 32, 34, 112, 114, 111, 116, 111, + 99, 111, 108, 73, 100, 34, 58, 32, 34, 112, 114, 111, 116, 111, 99, 111, 108, 34, 44, 10, 32, 32, 32, + 32, 34, 103, 101, 110, 101, 115, 105, 115, 34, 58, 32, 123, 125, 44, 10, 32, 32, 32, 32, 34, 112, + 114, 111, 112, 101, 114, 116, 105, 101, 115, 34, 58, 32, 110, 117, 108, 108, 44, 10, 32, 32, 32, 32, + 34, 102, 111, 114, 107, 66, 108, 111, 99, 107, 115, 34, 58, 32, 110, 117, 108, 108, 44, 10, 32, 32, + 32, 32, 34, 98, 97, 100, 66, 108, 111, 99, 107, 115, 34, 58, 32, 110, 117, 108, 108, 44, 10, 32, 32, + 32, 32, 34, 99, 111, 110, 115, 101, 110, 115, 117, 115, 69, 110, 103, 105, 110, 101, 34, 58, 32, 34, + 34, 44, 10, 32, 32, 32, 32, 34, 99, 111, 100, 101, 83, 117, 98, 115, 116, 105, 116, 117, 116, 101, + 115, 34, 58, 32, 110, 117, 108, 108, 10, 125}, + }, + { + name: "normal conditions", + fields: fields{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: []byte{123, 10, 32, 32, 32, 32, 34, 110, 97, 109, 101, 34, 58, 32, 34, 116, 101, 115, 116, 34, 44, + 10, 32, 32, 32, 32, 34, 105, 100, 34, 58, 32, 34, 73, 68, 34, 44, 10, 32, 32, 32, 32, 34, 99, 104, + 97, 105, 110, 84, 121, 112, 101, 34, 58, 32, 34, 99, 104, 97, 105, 110, 84, 121, 112, 101, 34, 44, + 10, 32, 32, 32, 32, 34, 98, 111, 111, 116, 78, 111, 100, 101, 115, 34, 58, 32, 91, 10, 32, 32, 32, + 32, 32, 32, 32, 32, 34, 110, 111, 100, 101, 49, 34, 44, 10, 32, 32, 32, 32, 32, 32, 32, 32, 34, 110, + 111, 100, 101, 50, 34, 10, 32, 32, 32, 32, 93, 44, 10, 32, 32, 32, 32, 34, 116, 101, 108, 101, 109, + 101, 116, 114, 121, 69, 110, 100, 112, 111, 105, 110, 116, 115, 34, 58, 32, 110, 117, 108, 108, 44, + 10, 32, 32, 32, 32, 34, 112, 114, 111, 116, 111, 99, 111, 108, 73, 100, 34, 58, 32, 34, 112, 114, + 111, 116, 111, 99, 111, 108, 34, 44, 10, 32, 32, 32, 32, 34, 103, 101, 110, 101, 115, 105, 115, 34, + 58, 32, 123, 125, 44, 10, 32, 32, 32, 32, 34, 112, 114, 111, 112, 101, 114, 116, 105, 101, 115, 34, + 58, 32, 123, 10, 32, 32, 32, 32, 32, 32, 32, 32, 34, 107, 101, 121, 34, 58, 32, 34, 118, 97, 108, + 117, 101, 34, 10, 32, 32, 32, 32, 125, 44, 10, 32, 32, 32, 32, 34, 102, 111, 114, 107, 66, 108, 111, + 99, 107, 115, 34, 58, 32, 110, 117, 108, 108, 44, 10, 32, 32, 32, 32, 34, 98, 97, 100, 66, 108, 111, + 99, 107, 115, 34, 58, 32, 110, 117, 108, 108, 44, 10, 32, 32, 32, 32, 34, 99, 111, 110, 115, 101, + 110, 115, 117, 115, 69, 110, 103, 105, 110, 101, 34, 58, 32, 34, 34, 44, 10, 32, 32, 32, 32, 34, 99, + 111, 100, 101, 83, 117, 98, 115, 116, 105, 116, 117, 116, 101, 115, 34, 58, 32, 110, 117, 108, 108, + 10, 125}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + b := &BuildSpec{ + genesis: tt.fields.genesis, + } + got, err := b.ToJSON() + if tt.err != nil { + assert.EqualError(t, err, tt.err.Error()) + } else { + assert.NoError(t, err) + } + assert.Equal(t, tt.want, got) + }) + } + //<<<<<<< HEAD + //======= + // + // bs.genesis.ChainType = expectedChainType + // bs.genesis.Properties = expectedProperties + // + // require.NoError(t, err) + // + // // confirm human-readable fields + // hr, err := bs.ToJSON() + // require.NoError(t, err) + // jGen := genesis.Genesis{} + // err = json.Unmarshal(hr, &jGen) + // require.NoError(t, err) + // genesis.TestGenesis.Genesis = genesis.TestFieldsHR + // require.Equal(t, genesis.TestGenesis.Genesis.Runtime, jGen.Genesis.Runtime) + // require.Equal(t, expectedChainType, jGen.ChainType) + // require.Equal(t, expectedProperties, jGen.Properties) + // + // // confirm raw fields + // raw, err := bs.ToJSONRaw() + // require.NoError(t, err) + // jGenRaw := genesis.Genesis{} + // err = json.Unmarshal(raw, &jGenRaw) + // require.NoError(t, err) + // genesis.TestGenesis.Genesis = genesis.TestFieldsRaw + // require.Equal(t, genesis.TestGenesis.Genesis.Raw, jGenRaw.Genesis.Raw) + // require.Equal(t, expectedChainType, jGenRaw.ChainType) + // require.Equal(t, expectedProperties, jGenRaw.Properties) + //} + // + //func TestBuildFromGenesis_WhenGenesisDoesNotExists(t *testing.T) { + // bs, err := BuildFromGenesis("/not/exists/genesis.json", 0) + // require.Nil(t, bs) + // require.Error(t, err, os.ErrNotExist) + //} + // + //func TestWriteGenesisSpecFileWhenFileAlreadyExists(t *testing.T) { + // f, err := os.CreateTemp("", "existing file data") + // require.NoError(t, err) + // defer os.Remove(f.Name()) + // + // someBytes := []byte("Testing some bytes") + // err = WriteGenesisSpecFile(someBytes, f.Name()) + // + // require.Error(t, err, + // fmt.Sprintf("file %s already exists, rename to avoid overwritten", f.Name())) + //>>>>>>> development } -func TestWriteGenesisSpecFile(t *testing.T) { +func TestBuildFromDB(t *testing.T) { + // initialise node (initialise state database and load genesis data) cfg := NewTestConfig(t) cfg.Init.Genesis = "../chain/gssmr/genesis.json" + ni := nodeInterface{} + err := ni.initNode(cfg) + assert.NoError(t, err) - expected, err := genesis.NewGenesisFromJSONRaw(cfg.Init.Genesis) - require.NoError(t, err) - - err = InitNode(cfg) - require.NoError(t, err) - - bs, err := BuildFromGenesis(cfg.Init.Genesis, 0) - require.NoError(t, err) - - data, err := bs.ToJSONRaw() - require.NoError(t, err) - - tmpFiles := []string{ - "/tmp/unique-raw-genesis.json", - "./unique-raw-genesis.json", + type args struct { + path string } + tests := []struct { + name string + args args + want *BuildSpec + err error + }{ + {name: "normal conditions", args: args{path: "test_data/TestBuildFromDB"}, + want: &BuildSpec{genesis: &genesis.Genesis{Name: "Gossamer"}}}, + {name: "invalid db path", args: args{path: "foo/bar"}, + err: errors.New("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.args.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) + } + // remove files created for tests + err = os.RemoveAll(tt.args.path) + assert.NoError(t, err) + }) + } +} - for _, tmpFile := range tmpFiles { - err = WriteGenesisSpecFile(data, tmpFile) - require.NoError(t, err) - require.FileExists(t, tmpFile) - - defer os.Remove(tmpFile) - - file, err := os.Open(tmpFile) - require.NoError(t, err) - defer file.Close() - - genesisBytes, err := io.ReadAll(file) - require.NoError(t, err) - - gen := new(genesis.Genesis) - err = json.Unmarshal(genesisBytes, gen) - require.NoError(t, err) +func TestBuildFromGenesis(t *testing.T) { + // setup test file + file, err := genesis.CreateTestGenesisJSONFile(false) + assert.NoError(t, err) + defer os.Remove(file) + //======= + // for _, tmpFile := range tmpFiles { + // err = WriteGenesisSpecFile(data, tmpFile) + // require.NoError(t, err) + // require.FileExists(t, tmpFile) + // + // defer os.Remove(tmpFile) + // + // file, err := os.Open(tmpFile) + // require.NoError(t, err) + // defer file.Close() + // + // genesisBytes, err := io.ReadAll(file) + // require.NoError(t, err) + // + // gen := new(genesis.Genesis) + // err = json.Unmarshal(genesisBytes, gen) + // require.NoError(t, err) + //>>>>>>> development - require.Equal(t, expected.ChainType, gen.ChainType) - require.Equal(t, expected.Properties, gen.Properties) + 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: file, + }, + 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 TestBuildFromDB(t *testing.T) { - // setup expected - cfg := NewTestConfig(t) - cfg.Init.Genesis = "../chain/gssmr/genesis.json" - expected, err := genesis.NewGenesisFromJSONRaw(cfg.Init.Genesis) - require.NoError(t, err) - // initialise node (initialise state database and load genesis data) - err = InitNode(cfg) - require.NoError(t, err) +func TestBuildSpec_ToJSONRaw(t *testing.T) { + type fields struct { + genesis *genesis.Genesis + } + tests := []struct { + name string + fields fields + want []byte + err error + }{ + { + name: "normal conditions", + fields: fields{genesis: &genesis.Genesis{Name: "test"}}, + want: []byte{123, 10, 32, 32, 32, 32, 34, 110, 97, 109, 101, 34, 58, 32, 34, 116, 101, 115, 116, 34, + 44, 10, 32, 32, 32, 32, 34, 105, 100, 34, 58, 32, 34, 34, 44, 10, 32, 32, 32, 32, 34, 99, 104, 97, + 105, 110, 84, 121, 112, 101, 34, 58, 32, 34, 34, 44, 10, 32, 32, 32, 32, 34, 98, 111, 111, 116, 78, + 111, 100, 101, 115, 34, 58, 32, 110, 117, 108, 108, 44, 10, 32, 32, 32, 32, 34, 116, 101, 108, 101, + 109, 101, 116, 114, 121, 69, 110, 100, 112, 111, 105, 110, 116, 115, 34, 58, 32, 110, 117, 108, 108, + 44, 10, 32, 32, 32, 32, 34, 112, 114, 111, 116, 111, 99, 111, 108, 73, 100, 34, 58, 32, 34, 34, 44, + 10, 32, 32, 32, 32, 34, 103, 101, 110, 101, 115, 105, 115, 34, 58, 32, 123, 125, 44, 10, 32, 32, 32, + 32, 34, 112, 114, 111, 112, 101, 114, 116, 105, 101, 115, 34, 58, 32, 110, 117, 108, 108, 44, 10, 32, + 32, 32, 32, 34, 102, 111, 114, 107, 66, 108, 111, 99, 107, 115, 34, 58, 32, 110, 117, 108, 108, 44, + 10, 32, 32, 32, 32, 34, 98, 97, 100, 66, 108, 111, 99, 107, 115, 34, 58, 32, 110, 117, 108, 108, 44, + 10, 32, 32, 32, 32, 34, 99, 111, 110, 115, 101, 110, 115, 117, 115, 69, 110, 103, 105, 110, 101, 34, + 58, 32, 34, 34, 44, 10, 32, 32, 32, 32, 34, 99, 111, 100, 101, 83, 117, 98, 115, 116, 105, 116, 117, + 116, 101, 115, 34, 58, 32, 110, 117, 108, 108, 10, 125}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + b := &BuildSpec{ + genesis: tt.fields.genesis, + } + got, err := b.ToJSONRaw() + if tt.err != nil { + assert.EqualError(t, err, tt.err.Error()) + } else { + assert.NoError(t, err) + } + assert.Equal(t, tt.want, got) + }) + } +} - bs, err := BuildFromDB(cfg.Global.BasePath) - require.NoError(t, err) - res, err := bs.ToJSON() - require.NoError(t, err) - jGen := genesis.Genesis{} - err = json.Unmarshal(res, &jGen) - require.NoError(t, err) +func TestWriteGenesisSpecFile(t *testing.T) { + file, err := os.Create("test.txt") + assert.NoError(t, err) + defer os.Remove(file.Name()) - require.Equal(t, expected.Genesis.Raw["top"]["0x3a636f6465"], jGen.Genesis.Runtime["system"]["code"]) + type args struct { + data []byte + fp string + } + tests := []struct { + name string + args args + err error + }{ + {name: "normal conditions", args: args{ + data: []byte{1}, + fp: "test.file", + }}, + {name: "existing file", args: args{ + data: []byte{1}, + fp: file.Name(), + }, err: errors.New("file test.txt already exists, rename to avoid overwriting")}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := WriteGenesisSpecFile(tt.args.data, tt.args.fp) + if tt.err != nil { + assert.EqualError(t, err, tt.err.Error()) + } else { + assert.NoError(t, err) + } + os.Remove(tt.args.fp) + }) + } } diff --git a/dot/config_integration_test.go b/dot/config_integration_test.go new file mode 100644 index 0000000000..8beceafe9d --- /dev/null +++ b/dot/config_integration_test.go @@ -0,0 +1,47 @@ +//go:build integration +// +build integration + +// Copyright 2020 ChainSafe Systems (ON) Corp. +// This file is part of gossamer. +// +// The gossamer library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The gossamer library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the gossamer library. If not, see . + +package dot + +import ( + "testing" + + "github.com/ChainSafe/gossamer/lib/utils" + + "github.com/stretchr/testify/require" +) + +// TestExportConfig tests exporting a toml configuration file +func TestExportConfig_Integration(t *testing.T) { + cfg, cfgFile := NewTestConfigWithFile(t) + require.NotNil(t, cfg) + + genFile := NewTestGenesisRawFile(t, cfg) + require.NotNil(t, genFile) + + defer utils.RemoveTestDir(t) + + cfg.Init.Genesis = genFile.Name() + + err := InitNode(cfg) + require.Nil(t, err) + + file := ExportConfig(cfg, cfgFile.Name()) + require.NotNil(t, file) +} diff --git a/dot/config_test.go b/dot/config_test.go index e0631f2683..a255f1b35a 100644 --- a/dot/config_test.go +++ b/dot/config_test.go @@ -6,26 +6,287 @@ package dot import ( "testing" - "github.com/ChainSafe/gossamer/lib/utils" - - "github.com/stretchr/testify/require" + "github.com/stretchr/testify/assert" ) -// TestExportConfig tests exporting a toml configuration file -func TestExportConfig(t *testing.T) { - cfg, cfgFile := NewTestConfigWithFile(t) - require.NotNil(t, cfg) +func TestDevConfig(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + want *Config + }{ + { + name: "dev default", + want: &Config{ + Global: GlobalConfig{ + ID: "dev", + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := DevConfig() + assert.Equal(t, tt.want.Global.ID, got.Global.ID) + }) + } +} + +func TestGssmrConfig(t *testing.T) { + t.Parallel() - genFile := NewTestGenesisRawFile(t, cfg) - require.NotNil(t, genFile) + tests := []struct { + name string + want *Config + }{ + { + name: "gossamer default", + want: &Config{ + Global: GlobalConfig{ + ID: "gssmr", + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := GssmrConfig() + assert.Equal(t, tt.want.Global.ID, got.Global.ID) + }) + } +} + +func TestKusamaConfig(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + want *Config + }{ + { + name: "kusama default", + want: &Config{ + Global: GlobalConfig{ + ID: "ksmcc3", + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := KusamaConfig() + assert.Equal(t, tt.want.Global.ID, got.Global.ID) + }) + } +} - defer utils.RemoveTestDir(t) +func TestPolkadotConfig(t *testing.T) { + t.Parallel() - cfg.Init.Genesis = genFile.Name() + tests := []struct { + name string + want *Config + }{ + { + name: "polkadot default", + want: &Config{ + Global: GlobalConfig{ + ID: "polkadot", + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := PolkadotConfig() + assert.Equal(t, tt.want.Global.ID, got.Global.ID) + }) + } +} + +func TestRPCConfig_isRPCEnabled(t *testing.T) { + t.Parallel() + + type fields struct { + Enabled bool + External bool + Unsafe bool + UnsafeExternal bool + Port uint32 + Host string + Modules []string + WSPort uint32 + WS bool + WSExternal bool + WSUnsafe bool + WSUnsafeExternal bool + } + tests := []struct { + name string + fields fields + want bool + }{ + { + name: "default", + want: false, + }, + { + name: "enabled true", + fields: fields{Enabled: true}, + want: true, + }, + { + name: "external true", + fields: fields{External: true}, + want: true, + }, + { + name: "unsafe true", + fields: fields{Unsafe: true}, + want: true, + }, + { + name: "unsafe external true", + fields: fields{UnsafeExternal: true}, + want: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r := &RPCConfig{ + Enabled: tt.fields.Enabled, + External: tt.fields.External, + Unsafe: tt.fields.Unsafe, + UnsafeExternal: tt.fields.UnsafeExternal, + Port: tt.fields.Port, + Host: tt.fields.Host, + Modules: tt.fields.Modules, + WSPort: tt.fields.WSPort, + WS: tt.fields.WS, + WSExternal: tt.fields.WSExternal, + WSUnsafe: tt.fields.WSUnsafe, + WSUnsafeExternal: tt.fields.WSUnsafeExternal, + } + got := r.isRPCEnabled() + assert.Equal(t, tt.want, got) + }) + } +} + +func TestRPCConfig_isWSEnabled(t *testing.T) { + t.Parallel() + + type fields struct { + Enabled bool + External bool + Unsafe bool + UnsafeExternal bool + Port uint32 + Host string + Modules []string + WSPort uint32 + WS bool + WSExternal bool + WSUnsafe bool + WSUnsafeExternal bool + } + tests := []struct { + name string + fields fields + want bool + }{ + { + name: "default", + want: false, + }, + { + name: "ws true", + fields: fields{WS: true}, + want: true, + }, + { + name: "ws external true", + fields: fields{WSExternal: true}, + want: true, + }, + { + name: "ws unsafe true", + fields: fields{WSUnsafe: true}, + want: true, + }, + { + name: "ws unsafe external true", + fields: fields{WSUnsafeExternal: true}, + want: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r := &RPCConfig{ + Enabled: tt.fields.Enabled, + External: tt.fields.External, + Unsafe: tt.fields.Unsafe, + UnsafeExternal: tt.fields.UnsafeExternal, + Port: tt.fields.Port, + Host: tt.fields.Host, + Modules: tt.fields.Modules, + WSPort: tt.fields.WSPort, + WS: tt.fields.WS, + WSExternal: tt.fields.WSExternal, + WSUnsafe: tt.fields.WSUnsafe, + WSUnsafeExternal: tt.fields.WSUnsafeExternal, + } + got := r.isWSEnabled() + assert.Equal(t, tt.want, got) + }) + } +} - err := InitNode(cfg) - require.Nil(t, err) +func Test_networkServiceEnabled(t *testing.T) { + t.Parallel() - file := ExportConfig(cfg, cfgFile.Name()) - require.NotNil(t, file) + type args struct { + cfg *Config + } + tests := []struct { + name string + args args + want bool + }{ + { + name: "dev config", + args: args{cfg: DevConfig()}, + want: true, + }, + { + name: "empty config", + args: args{cfg: &Config{}}, + want: false, + }, + { + name: "core roles 0", + args: args{cfg: &Config{ + Core: CoreConfig{ + Roles: 0, + }, + }}, + want: false, + }, + { + name: "core roles 1", + args: args{cfg: &Config{ + Core: CoreConfig{ + Roles: 1, + }, + }}, + want: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := networkServiceEnabled(tt.args.cfg) + assert.Equal(t, tt.want, got) + }) + } } diff --git a/dot/core/service.go b/dot/core/service.go index 91b97aff49..b1d7088ee7 100644 --- a/dot/core/service.go +++ b/dot/core/service.go @@ -7,6 +7,7 @@ import ( "context" "fmt" "math/big" + "reflect" "sync" "github.com/ChainSafe/gossamer/dot/network" @@ -89,7 +90,14 @@ func NewService(cfg *Config) (*Service, error) { return nil, ErrNilStorageState } - if cfg.Network == nil { + var isNilNetwork bool + switch cfg.Network.(type) { + case Network: + isNilNetwork = cfg.Network == (*network.Service)(nil) + default: + isNilNetwork = reflect.ValueOf(cfg.Network).IsNil() + } + if isNilNetwork { return nil, ErrNilNetwork } diff --git a/dot/import_integration_test.go b/dot/import_integration_test.go new file mode 100644 index 0000000000..c390a8017d --- /dev/null +++ b/dot/import_integration_test.go @@ -0,0 +1,93 @@ +//go:build integration +// +build integration + +// Copyright 2020 ChainSafe Systems (ON) Corp. +// This file is part of gossamer. +// +// The gossamer library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The gossamer library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the gossamer library. If not, see . + +package dot + +import ( + "io/ioutil" + "math/big" + "testing" + + "github.com/ChainSafe/gossamer/dot/types" + "github.com/ChainSafe/gossamer/lib/common" + "github.com/ChainSafe/gossamer/lib/utils" + "github.com/ChainSafe/gossamer/pkg/scale" + + "github.com/stretchr/testify/require" +) + +func TestNewTrieFromPairs(t *testing.T) { + fp := setupStateFile(t) + tr, err := newTrieFromPairs(fp) + require.NoError(t, err) + + expectedRoot := common.MustHexToHash("0x09f9ca28df0560c2291aa16b56e15e07d1e1927088f51356d522722aa90ca7cb") + require.Equal(t, expectedRoot, tr.MustHash()) +} + +func TestNewHeaderFromFile(t *testing.T) { + fp := setupHeaderFile(t) + header, err := newHeaderFromFile(fp) + require.NoError(t, err) + + digestBytes := common.MustHexToBytes("0x080642414245b501013c0000009659bd0f0000000070edad1c9064fff78cb18435223d8" + + "adaf5ea04c24b1a8766e3dc01eb03cc6a0c11b79793d4e31cc0990838229c44fed1669a7c7c79e1e6d0a96374d6496728069d1ef739e2" + + "90497a0e3b728fa88fcbdd3a5504e0efde0242e7a806dd4fa9260c054241424501019e7f28dddcf27c1e6b328d5694c368d5b2ec5dbe0e" + + "412ae1c98f88d53be4d8502fac571f3f19c9caaf281a673319241e0c5095a683ad34316204088a36a4bd86") + digest := types.NewDigest() + err = scale.Unmarshal(digestBytes, &digest) + require.NoError(t, err) + require.Equal(t, 2, len(digest.Types)) + + expected := &types.Header{ + ParentHash: common.MustHexToHash("0x3b45c9c22dcece75a30acc9c2968cb311e6b0557350f83b430f47559db786975"), + Number: big.NewInt(1482002), + StateRoot: common.MustHexToHash("0x09f9ca28df0560c2291aa16b56e15e07d1e1927088f51356d522722aa90ca7cb"), + ExtrinsicsRoot: common.MustHexToHash("0xda26dc8c1455f8f81cae12e4fc59e23ce961b2c837f6d3f664283af906d344e0"), + Digest: digest, + } + + require.Equal(t, expected, header) +} + +func TestImportState_Integration(t *testing.T) { + basepath, err := ioutil.TempDir("", "gossamer-test-*") + require.NoError(t, err) + + cfg := NewTestConfig(t) + require.NotNil(t, cfg) + + genFile := NewTestGenesisRawFile(t, cfg) + require.NotNil(t, genFile) + + defer utils.RemoveTestDir(t) + + cfg.Init.Genesis = genFile.Name() + + cfg.Global.BasePath = basepath + err = InitNode(cfg) + require.NoError(t, err) + + stateFP := setupStateFile(t) + headerFP := setupHeaderFile(t) + + firstSlot := uint64(262493679) + err = ImportState(basepath, stateFP, headerFP, firstSlot) + require.NoError(t, err) +} diff --git a/dot/import_test.go b/dot/import_test.go index 94448eaa17..8aa9ba295e 100644 --- a/dot/import_test.go +++ b/dot/import_test.go @@ -5,6 +5,8 @@ package dot import ( "encoding/json" + "errors" + "io/ioutil" "math/big" "os" "testing" @@ -12,11 +14,157 @@ import ( "github.com/ChainSafe/gossamer/dot/types" "github.com/ChainSafe/gossamer/lib/common" "github.com/ChainSafe/gossamer/lib/utils" - "github.com/ChainSafe/gossamer/pkg/scale" - + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) +func TestImportState(t *testing.T) { + t.Parallel() + + // setup node for test + basepath, err := ioutil.TempDir("", "gossamer-test-*") + require.NoError(t, err) + + cfg := NewTestConfig(t) + require.NotNil(t, cfg) + + genFile := NewTestGenesisRawFile(t, cfg) + require.NotNil(t, genFile) + + defer utils.RemoveTestDir(t) + + cfg.Init.Genesis = genFile.Name() + + cfg.Global.BasePath = basepath + ni := nodeInterface{} + err = ni.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() + + type args struct { + filename string + } + tests := []struct { + name string + args args + want *types.Header + err error + }{ + { + name: "working example", + args: args{filename: setupHeaderFile(t)}, + want: &types.Header{ + ParentHash: common.MustHexToHash("0x3b45c9c22dcece75a30acc9c2968cb311e6b0557350f83b430f47559db786975"), + Number: big.NewInt(1482002), + StateRoot: common.MustHexToHash("0x09f9ca28df0560c2291aa16b56e15e07d1e1927088f51356d522722aa90ca7cb"), + ExtrinsicsRoot: common.MustHexToHash("0xda26dc8c1455f8f81cae12e4fc59e23ce961b2c837f6d3f664283af906d344e0"), + }, + }, + { + name: "no arguments", + err: errors.New("read .: is a directory"), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := newHeaderFromFile(tt.args.filename) + 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.ParentHash, got.ParentHash) + assert.Equal(t, tt.want.Number, got.Number) + assert.Equal(t, tt.want.StateRoot, got.StateRoot) + assert.Equal(t, tt.want.ExtrinsicsRoot, got.ExtrinsicsRoot) + } + }) + } +} + +func Test_newTrieFromPairs(t *testing.T) { + t.Parallel() + + type args struct { + filename string + } + tests := []struct { + name string + args args + want common.Hash + err error + }{ + { + name: "no arguments", + err: errors.New("read .: is a directory"), + want: common.Hash{}, + }, + { + name: "working example", + args: args{filename: setupStateFile(t)}, + want: common.MustHexToHash("0x09f9ca28df0560c2291aa16b56e15e07d1e1927088f51356d522722aa90ca7cb"), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := newTrieFromPairs(tt.args.filename) + if tt.err != nil { + assert.EqualError(t, err, tt.err.Error()) + } else { + assert.NoError(t, err) + } + + if !tt.want.IsEmpty() { + assert.Equal(t, tt.want, got.MustHash()) + } + }) + } +} + func setupStateFile(t *testing.T) string { filename := "../lib/runtime/test_data/kusama/block1482002.out" @@ -53,59 +201,3 @@ func setupHeaderFile(t *testing.T) string { require.NoError(t, err) return fp } - -func TestNewTrieFromPairs(t *testing.T) { - fp := setupStateFile(t) - tr, err := newTrieFromPairs(fp) - require.NoError(t, err) - - expectedRoot := common.MustHexToHash("0x09f9ca28df0560c2291aa16b56e15e07d1e1927088f51356d522722aa90ca7cb") - require.Equal(t, expectedRoot, tr.MustHash()) -} - -func TestNewHeaderFromFile(t *testing.T) { - fp := setupHeaderFile(t) - header, err := newHeaderFromFile(fp) - require.NoError(t, err) - - digestBytes := common.MustHexToBytes("0x080642414245b501013c0000009659bd0f0000000070edad1c9064fff78cb18435223d8adaf5ea04c24b1a8766e3dc01eb03cc6a0c11b79793d4e31cc0990838229c44fed1669a7c7c79e1e6d0a96374d6496728069d1ef739e290497a0e3b728fa88fcbdd3a5504e0efde0242e7a806dd4fa9260c054241424501019e7f28dddcf27c1e6b328d5694c368d5b2ec5dbe0e412ae1c98f88d53be4d8502fac571f3f19c9caaf281a673319241e0c5095a683ad34316204088a36a4bd86") //nolint:lll - digest := types.NewDigest() - err = scale.Unmarshal(digestBytes, &digest) - require.NoError(t, err) - require.Equal(t, 2, len(digest.Types)) - - expected := &types.Header{ - ParentHash: common.MustHexToHash("0x3b45c9c22dcece75a30acc9c2968cb311e6b0557350f83b430f47559db786975"), - Number: big.NewInt(1482002), - StateRoot: common.MustHexToHash("0x09f9ca28df0560c2291aa16b56e15e07d1e1927088f51356d522722aa90ca7cb"), - ExtrinsicsRoot: common.MustHexToHash("0xda26dc8c1455f8f81cae12e4fc59e23ce961b2c837f6d3f664283af906d344e0"), - Digest: digest, - } - - require.Equal(t, expected, header) -} - -func TestImportState(t *testing.T) { - basepath := t.TempDir() - - cfg := NewTestConfig(t) - require.NotNil(t, cfg) - - genFile := NewTestGenesisRawFile(t, cfg) - require.NotNil(t, genFile) - - defer utils.RemoveTestDir(t) - - cfg.Init.Genesis = genFile.Name() - - cfg.Global.BasePath = basepath - err := InitNode(cfg) - require.NoError(t, err) - - stateFP := setupStateFile(t) - headerFP := setupHeaderFile(t) - - firstSlot := uint64(262493679) - err = ImportState(basepath, stateFP, headerFP, firstSlot) - require.NoError(t, err) -} diff --git a/dot/mock_node_test.go b/dot/mock_node_test.go new file mode 100644 index 0000000000..76e56ba222 --- /dev/null +++ b/dot/mock_node_test.go @@ -0,0 +1,266 @@ +// 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" + modules "github.com/ChainSafe/gossamer/dot/rpc/modules" + state "github.com/ChainSafe/gossamer/dot/state" + sync "github.com/ChainSafe/gossamer/dot/sync" + system "github.com/ChainSafe/gossamer/dot/system" + types "github.com/ChainSafe/gossamer/dot/types" + 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" +) + +// MocknewNodeIface is a mock of newNodeIface interface. +type MocknewNodeIface struct { + ctrl *gomock.Controller + recorder *MocknewNodeIfaceMockRecorder +} + +// MocknewNodeIfaceMockRecorder is the mock recorder for MocknewNodeIface. +type MocknewNodeIfaceMockRecorder struct { + mock *MocknewNodeIface +} + +// NewMocknewNodeIface creates a new mock instance. +func NewMocknewNodeIface(ctrl *gomock.Controller) *MocknewNodeIface { + mock := &MocknewNodeIface{ctrl: ctrl} + mock.recorder = &MocknewNodeIfaceMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MocknewNodeIface) EXPECT() *MocknewNodeIfaceMockRecorder { + return m.recorder +} + +// createBABEService mocks base method. +func (m *MocknewNodeIface) createBABEService(cfg *Config, st *state.Service, ks keystore.Keystore, cs *core.Service) (*babe.Service, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "createBABEService", cfg, st, ks, cs) + ret0, _ := ret[0].(*babe.Service) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// createBABEService indicates an expected call of createBABEService. +func (mr *MocknewNodeIfaceMockRecorder) createBABEService(cfg, st, ks, cs interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "createBABEService", reflect.TypeOf((*MocknewNodeIface)(nil).createBABEService), cfg, st, ks, cs) +} + +// createBlockVerifier mocks base method. +func (m *MocknewNodeIface) 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 *MocknewNodeIfaceMockRecorder) createBlockVerifier(st interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "createBlockVerifier", reflect.TypeOf((*MocknewNodeIface)(nil).createBlockVerifier), st) +} + +// createCoreService mocks base method. +func (m *MocknewNodeIface) 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 *MocknewNodeIfaceMockRecorder) createCoreService(cfg, ks, st, net, dh interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "createCoreService", reflect.TypeOf((*MocknewNodeIface)(nil).createCoreService), cfg, ks, st, net, dh) +} + +// createDigestHandler mocks base method. +func (m *MocknewNodeIface) createDigestHandler(st *state.Service) (*digest.Handler, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "createDigestHandler", st) + ret0, _ := ret[0].(*digest.Handler) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// createDigestHandler indicates an expected call of createDigestHandler. +func (mr *MocknewNodeIfaceMockRecorder) createDigestHandler(st interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "createDigestHandler", reflect.TypeOf((*MocknewNodeIface)(nil).createDigestHandler), st) +} + +// createGRANDPAService mocks base method. +func (m *MocknewNodeIface) createGRANDPAService(cfg *Config, st *state.Service, dh *digest.Handler, ks keystore.Keystore, net *network.Service) (*grandpa.Service, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "createGRANDPAService", cfg, st, dh, ks, net) + ret0, _ := ret[0].(*grandpa.Service) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// createGRANDPAService indicates an expected call of createGRANDPAService. +func (mr *MocknewNodeIfaceMockRecorder) createGRANDPAService(cfg, st, dh, ks, net interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "createGRANDPAService", reflect.TypeOf((*MocknewNodeIface)(nil).createGRANDPAService), cfg, st, dh, ks, net) +} + +// createNetworkService mocks base method. +func (m *MocknewNodeIface) createNetworkService(cfg *Config, stateSrvc *state.Service) (*network.Service, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "createNetworkService", cfg, stateSrvc) + ret0, _ := ret[0].(*network.Service) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// createNetworkService indicates an expected call of createNetworkService. +func (mr *MocknewNodeIfaceMockRecorder) createNetworkService(cfg, stateSrvc interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "createNetworkService", reflect.TypeOf((*MocknewNodeIface)(nil).createNetworkService), cfg, stateSrvc) +} + +// createRPCService mocks base method. +func (m *MocknewNodeIface) createRPCService(cfg *Config, ns *runtime.NodeStorage, stateSrvc *state.Service, coreSrvc *core.Service, networkSrvc *network.Service, bp modules.BlockProducerAPI, sysSrvc *system.Service, finSrvc *grandpa.Service) (*rpc.HTTPServer, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "createRPCService", cfg, ns, stateSrvc, coreSrvc, networkSrvc, bp, sysSrvc, finSrvc) + ret0, _ := ret[0].(*rpc.HTTPServer) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// createRPCService indicates an expected call of createRPCService. +func (mr *MocknewNodeIfaceMockRecorder) createRPCService(cfg, ns, stateSrvc, coreSrvc, networkSrvc, bp, sysSrvc, finSrvc interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "createRPCService", reflect.TypeOf((*MocknewNodeIface)(nil).createRPCService), cfg, ns, stateSrvc, coreSrvc, networkSrvc, bp, sysSrvc, finSrvc) +} + +// createRuntimeStorage mocks base method. +func (m *MocknewNodeIface) 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 *MocknewNodeIfaceMockRecorder) createRuntimeStorage(st interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "createRuntimeStorage", reflect.TypeOf((*MocknewNodeIface)(nil).createRuntimeStorage), st) +} + +// createStateService mocks base method. +func (m *MocknewNodeIface) 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 *MocknewNodeIfaceMockRecorder) createStateService(config interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "createStateService", reflect.TypeOf((*MocknewNodeIface)(nil).createStateService), config) +} + +// createSystemService mocks base method. +func (m *MocknewNodeIface) 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 *MocknewNodeIfaceMockRecorder) createSystemService(cfg, stateSrvc interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "createSystemService", reflect.TypeOf((*MocknewNodeIface)(nil).createSystemService), cfg, stateSrvc) +} + +// initNode mocks base method. +func (m *MocknewNodeIface) 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 *MocknewNodeIfaceMockRecorder) initNode(config interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "initNode", reflect.TypeOf((*MocknewNodeIface)(nil).initNode), config) +} + +// initialiseTelemetry mocks base method. +func (m *MocknewNodeIface) initialiseTelemetry(cfg *Config, stateSrvc *state.Service, networkSrvc *network.Service, sysSrvc *system.Service) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "initialiseTelemetry", cfg, stateSrvc, networkSrvc, sysSrvc) +} + +// initialiseTelemetry indicates an expected call of initialiseTelemetry. +func (mr *MocknewNodeIfaceMockRecorder) initialiseTelemetry(cfg, stateSrvc, networkSrvc, sysSrvc interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "initialiseTelemetry", reflect.TypeOf((*MocknewNodeIface)(nil).initialiseTelemetry), cfg, stateSrvc, networkSrvc, sysSrvc) +} + +// loadRuntime mocks base method. +func (m *MocknewNodeIface) 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 *MocknewNodeIfaceMockRecorder) loadRuntime(cfg, ns, stateSrvc, ks, net interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "loadRuntime", reflect.TypeOf((*MocknewNodeIface)(nil).loadRuntime), cfg, ns, stateSrvc, ks, net) +} + +// newSyncService mocks base method. +func (m *MocknewNodeIface) newSyncService(cfg *Config, st *state.Service, fg sync.FinalityGadget, verifier *babe.VerificationManager, cs *core.Service, net *network.Service) (*sync.Service, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "newSyncService", cfg, st, fg, verifier, cs, net) + ret0, _ := ret[0].(*sync.Service) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// newSyncService indicates an expected call of newSyncService. +func (mr *MocknewNodeIfaceMockRecorder) newSyncService(cfg, st, fg, verifier, cs, net interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "newSyncService", reflect.TypeOf((*MocknewNodeIface)(nil).newSyncService), cfg, st, fg, verifier, cs, net) +} + +// nodeInitialised mocks base method. +func (m *MocknewNodeIface) nodeInitialised(arg0 string) bool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "nodeInitialised", arg0) + ret0, _ := ret[0].(bool) + return ret0 +} + +// nodeInitialised indicates an expected call of nodeInitialised. +func (mr *MocknewNodeIfaceMockRecorder) nodeInitialised(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "nodeInitialised", reflect.TypeOf((*MocknewNodeIface)(nil).nodeInitialised), arg0) +} diff --git a/dot/node.go b/dot/node.go index 057aad7794..95dc27d01a 100644 --- a/dot/node.go +++ b/dot/node.go @@ -15,16 +15,23 @@ import ( "syscall" "time" + "github.com/ChainSafe/gossamer/dot/core" + "github.com/ChainSafe/gossamer/dot/digest" "github.com/ChainSafe/gossamer/dot/metrics" "github.com/ChainSafe/gossamer/dot/network" "github.com/ChainSafe/gossamer/dot/rpc" + "github.com/ChainSafe/gossamer/dot/rpc/modules" "github.com/ChainSafe/gossamer/dot/state" "github.com/ChainSafe/gossamer/dot/state/pruner" + gsync "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/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" @@ -41,9 +48,87 @@ type Node struct { started chan struct{} } +//go:generate mockgen -source=node.go -destination=mock_node_test.go -package=$GOPACKAGE + +type newNodeIface interface { + nodeInitialised(string) bool + initNode(config *Config) error + createStateService(config *Config) (*state.Service, error) + createNetworkService(cfg *Config, stateSrvc *state.Service) (*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(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) (*grandpa.Service, error) + newSyncService(cfg *Config, st *state.Service, fg gsync.FinalityGadget, verifier *babe.VerificationManager, + cs *core.Service, net *network.Service) (*gsync.Service, error) + createBABEService(cfg *Config, st *state.Service, ks keystore.Keystore, cs *core.Service) (*babe.Service, error) + createSystemService(cfg *types.SystemInfo, stateSrvc *state.Service) (*system.Service, error) + createRPCService(cfg *Config, ns *runtime.NodeStorage, stateSrvc *state.Service, coreSrvc *core.Service, + networkSrvc *network.Service, bp modules.BlockProducerAPI, sysSrvc *system.Service, + finSrvc *grandpa.Service) (*rpc.HTTPServer, error) + initialiseTelemetry(cfg *Config, stateSrvc *state.Service, networkSrvc *network.Service, sysSrvc *system.Service) +} + +type nodeInterface 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 { + nodeI := nodeInterface{} + return nodeI.nodeInitialised(basepath) +} + +func (nodeInterface) nodeInitialised(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 +} + +// InitNode initialise the node with given Config +func InitNode(cfg *Config) error { + nodeI := nodeInterface{} + return nodeI.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 (nodeInterface) 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...", @@ -105,45 +190,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 @@ -153,9 +199,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 } }() @@ -164,14 +210,16 @@ 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 to create node func NewNode(cfg *Config, ks *keystore.GlobalKeystore) (*Node, error) { + return newNode(cfg, ks, nodeInterface{}) +} + +func newNode(cfg *Config, ks *keystore.GlobalKeystore, nn newNodeIface) (*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) @@ -179,13 +227,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 !nn.nodeInitialised(cfg.Global.BasePath) { + err := nn.initNode(cfg) + if err != nil { + return nil, 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) @@ -195,11 +245,7 @@ func NewNode(cfg *Config, ks *keystore.GlobalKeystore) (*Node, error) { networkSrvc *network.Service ) - if cfg.Pprof.Enabled { - nodeSrvcs = append(nodeSrvcs, createPprofService(cfg.Pprof.Settings)) - } - - stateSrvc, err := createStateService(cfg) + stateSrvc, err := nn.createStateService(cfg) if err != nil { return nil, fmt.Errorf("failed to create state service: %s", err) } @@ -207,7 +253,7 @@ 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) + networkSrvc, err = nn.createNetworkService(cfg, stateSrvc) if err != nil { return nil, fmt.Errorf("failed to create network service: %s", err) } @@ -218,40 +264,40 @@ func NewNode(cfg *Config, ks *keystore.GlobalKeystore) (*Node, error) { } // create runtime - ns, err := createRuntimeStorage(stateSrvc) + ns, err := nn.createRuntimeStorage(stateSrvc) if err != nil { return nil, err } - err = loadRuntime(cfg, ns, stateSrvc, ks, networkSrvc) + err = nn.loadRuntime(cfg, ns, stateSrvc, ks, networkSrvc) if err != nil { return nil, err } - ver, err := createBlockVerifier(stateSrvc) + ver, err := nn.createBlockVerifier(stateSrvc) if err != nil { return nil, err } - dh, err := createDigestHandler(stateSrvc) + dh, err := nn.createDigestHandler(stateSrvc) if err != nil { return nil, err } nodeSrvcs = append(nodeSrvcs, dh) - coreSrvc, err := createCoreService(cfg, ks, stateSrvc, networkSrvc, dh) + coreSrvc, err := nn.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) + fg, err := nn.createGRANDPAService(cfg, stateSrvc, dh, ks.Gran, networkSrvc) if err != nil { return nil, err } nodeSrvcs = append(nodeSrvcs, fg) - syncer, err := newSyncService(cfg, stateSrvc, fg, ver, coreSrvc, networkSrvc) + syncer, err := nn.newSyncService(cfg, stateSrvc, fg, ver, coreSrvc, networkSrvc) if err != nil { return nil, err } @@ -262,13 +308,13 @@ func NewNode(cfg *Config, ks *keystore.GlobalKeystore) (*Node, error) { } nodeSrvcs = append(nodeSrvcs, syncer) - bp, err := createBABEService(cfg, stateSrvc, ks.Babe, coreSrvc) + bp, err := nn.createBABEService(cfg, stateSrvc, ks.Babe, coreSrvc) if err != nil { return nil, err } nodeSrvcs = append(nodeSrvcs, bp) - sysSrvc, err := createSystemService(&cfg.System, stateSrvc) + sysSrvc, err := nn.createSystemService(&cfg.System, stateSrvc) if err != nil { return nil, fmt.Errorf("failed to create system service: %s", err) } @@ -277,7 +323,7 @@ func NewNode(cfg *Config, ks *keystore.GlobalKeystore) (*Node, error) { // check if rpc service is enabled if enabled := cfg.RPC.isRPCEnabled() || cfg.RPC.isWSEnabled(); enabled { var rpcSrvc *rpc.HTTPServer - rpcSrvc, err = createRPCService(cfg, ns, stateSrvc, coreSrvc, networkSrvc, bp, sysSrvc, fg) + rpcSrvc, err = nn.createRPCService(cfg, ns, stateSrvc, coreSrvc, networkSrvc, bp, sysSrvc, fg) if err != nil { return nil, fmt.Errorf("failed to create rpc service: %s", err) } @@ -313,9 +359,16 @@ func NewNode(cfg *Config, ks *keystore.GlobalKeystore) (*Node, error) { metrics.PublishMetrics(address) } + nn.initialiseTelemetry(cfg, stateSrvc, networkSrvc, sysSrvc) + + return node, nil +} + +func (nodeInterface) initialiseTelemetry(cfg *Config, stateSrvc *state.Service, networkSrvc *network.Service, + sysSrvc *system.Service) { gd, err := stateSrvc.Base.LoadGenesisData() if err != nil { - return nil, err + logger.Debugf("problem initialising telemetry: %s", err) } telemetry.GetInstance().Initialise(!cfg.Global.NoTelemetry) @@ -333,19 +386,52 @@ func NewNode(cfg *Config, ks *keystore.GlobalKeystore) (*Node, error) { telemetry.GetInstance().AddConnections(telemetryEndpoints) genesisHash := stateSrvc.Block.GenesisHash() + peerID := "" + if networkSrvc != nil { + peerID = networkSrvc.NetworkState().PeerID + } err = telemetry.GetInstance().SendMessage(telemetry.NewSystemConnectedTM( cfg.Core.GrandpaAuthority, sysSrvc.ChainName(), &genesisHash, sysSrvc.SystemName(), cfg.Global.Name, - networkSrvc.NetworkState().PeerID, + peerID, strconv.FormatInt(time.Now().UnixNano(), 10), sysSrvc.SystemVersion())) if err != nil { logger.Debugf("problem sending system.connected telemetry message: %s", err) } - return node, nil +} + +//InitKeystore to initialize node keystore +func InitKeystore(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) + if err != nil { + logger.Errorf("failed to load account keystore: %s", err) + return nil, err + } + + err = keystore.LoadKeystore(cfg.Account.Key, ks.Babe) + if err != nil { + logger.Errorf("failed to load BABE keystore: %s", err) + return nil, err + } + + err = keystore.LoadKeystore(cfg.Account.Key, ks.Gran) + if err != nil { + logger.Errorf("failed to load grandpa keystore: %s", err) + return nil, 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 } // stores the global node name to reuse @@ -402,7 +488,7 @@ func (n *Node) Stop() { n.wg.Done() } -func loadRuntime(cfg *Config, ns *runtime.NodeStorage, +func (nodeInterface) 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 new file mode 100644 index 0000000000..1356db02d9 --- /dev/null +++ b/dot/node_integration_test.go @@ -0,0 +1,415 @@ +//go:build integration +// +build integration + +// Copyright 2020 ChainSafe Systems (ON) Corp. +// This file is part of gossamer. +// +// The gossamer library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The gossamer library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the gossamer library. If not, see . + +package dot + +import ( + "math/big" + "reflect" + "sync" + "testing" + + "github.com/ChainSafe/gossamer/dot/core" + "github.com/ChainSafe/gossamer/dot/state" + "github.com/ChainSafe/gossamer/dot/types" + "github.com/ChainSafe/gossamer/lib/babe" + "github.com/ChainSafe/gossamer/lib/common" + "github.com/ChainSafe/gossamer/lib/crypto/sr25519" + "github.com/ChainSafe/gossamer/lib/genesis" + "github.com/ChainSafe/gossamer/lib/grandpa" + "github.com/ChainSafe/gossamer/lib/keystore" + "github.com/ChainSafe/gossamer/lib/services" + "github.com/ChainSafe/gossamer/lib/trie" + "github.com/ChainSafe/gossamer/lib/utils" + + log "github.com/ChainSafe/gossamer/internal/log" + "github.com/stretchr/testify/require" +) + +func TestInitNode_Integration(t *testing.T) { + cfg := NewTestConfig(t) + require.NotNil(t, cfg) + + genFile := NewTestGenesisRawFile(t, cfg) + require.NotNil(t, genFile) + + defer utils.RemoveTestDir(t) + + cfg.Init.Genesis = genFile.Name() + + err := InitNode(cfg) + require.NoError(t, err) +} + +func TestInitNode_GenesisSpec(t *testing.T) { + cfg := NewTestConfig(t) + require.NotNil(t, cfg) + + genFile := NewTestGenesisFile(t, cfg) + require.NotNil(t, genFile) + + defer utils.RemoveTestDir(t) + + cfg.Init.Genesis = genFile.Name() + + err := InitNode(cfg) + require.NoError(t, err) +} + +// TestNodeInitialized +func TestNodeInitialized_Integration(t *testing.T) { + cfg := NewTestConfig(t) + require.NotNil(t, cfg) + + genFile := NewTestGenesisRawFile(t, cfg) + require.NotNil(t, genFile) + + defer utils.RemoveTestDir(t) + + cfg.Init.Genesis = genFile.Name() + + expected := NodeInitialized(cfg.Global.BasePath) + require.Equal(t, expected, false) + + err := InitNode(cfg) + require.NoError(t, err) + + expected = NodeInitialized(cfg.Global.BasePath) + require.Equal(t, expected, true) +} + +// TestNewNode +func TestNewNode_Integration(t *testing.T) { + cfg := NewTestConfig(t) + require.NotNil(t, cfg) + + genFile := NewTestGenesisRawFile(t, cfg) + require.NotNil(t, genFile) + + defer utils.RemoveTestDir(t) + + cfg.Init.Genesis = genFile.Name() + + err := InitNode(cfg) + require.NoError(t, err) + + ks := keystore.NewGlobalKeystore() + err = keystore.LoadKeystore("alice", ks.Gran) + require.NoError(t, err) + err = keystore.LoadKeystore("alice", ks.Babe) + require.NoError(t, err) + + cfg.Core.Roles = types.FullNodeRole + + node, err := NewNode(cfg, ks) + require.NoError(t, err) + + bp := node.Services.Get(&babe.Service{}) + require.NotNil(t, bp) + fg := node.Services.Get(&grandpa.Service{}) + require.NotNil(t, fg) +} + +func TestNewNode_Authority(t *testing.T) { + cfg := NewTestConfig(t) + require.NotNil(t, cfg) + + genFile := NewTestGenesisRawFile(t, cfg) + require.NotNil(t, genFile) + + defer utils.RemoveTestDir(t) + + cfg.Init.Genesis = genFile.Name() + + err := InitNode(cfg) + require.NoError(t, err) + + ks := keystore.NewGlobalKeystore() + err = keystore.LoadKeystore("alice", ks.Gran) + require.NoError(t, err) + require.Equal(t, 1, ks.Gran.Size()) + err = keystore.LoadKeystore("alice", ks.Babe) + require.NoError(t, err) + require.Equal(t, 1, ks.Babe.Size()) + + cfg.Core.Roles = types.AuthorityRole + + node, err := NewNode(cfg, ks) + require.NoError(t, err) + + bp := node.Services.Get(&babe.Service{}) + require.NotNil(t, bp) + fg := node.Services.Get(&grandpa.Service{}) + require.NotNil(t, fg) +} + +// TestInitNode_LoadGenesisData +func TestInitNode_LoadGenesisData(t *testing.T) { + cfg := NewTestConfig(t) + require.NotNil(t, cfg) + + genPath := NewTestGenesisAndRuntime(t) + require.NotNil(t, genPath) + + defer utils.RemoveTestDir(t) + + cfg.Init.Genesis = genPath + cfg.Core.GrandpaAuthority = false + + err := InitNode(cfg) + require.NoError(t, err) + + config := state.Config{ + Path: cfg.Global.BasePath, + LogLevel: log.Info, + } + 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, + big.NewInt(0), types.NewDigest()) + require.NoError(t, err) + + err = stateSrvc.Initialise(gen, genesisHeader, genTrie) + require.NoError(t, err) + + err = stateSrvc.Start() + require.NoError(t, err) + + defer 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, big.NewInt(0), + types.NewDigest()) + require.NoError(t, err) + require.Equal(t, expectedHeader.Hash(), genesisHeader.Hash()) +} + +// TestInitNode_LoadStorageRoot +func TestInitNode_LoadStorageRoot(t *testing.T) { + cfg := NewTestConfig(t) + require.NotNil(t, cfg) + + genPath := NewTestGenesisAndRuntime(t) + require.NotNil(t, genPath) + + defer utils.RemoveTestDir(t) + + cfg.Core.Roles = types.FullNodeRole + cfg.Core.BabeAuthority = false + cfg.Core.GrandpaAuthority = false + cfg.Init.Genesis = genPath + + gen, err := genesis.NewGenesisFromJSONRaw(genPath) + require.NoError(t, err) + + err = InitNode(cfg) + require.NoError(t, err) + + ks := keystore.NewGlobalKeystore() + ed25519Keyring, _ := keystore.NewEd25519Keyring() + ks.Gran.Insert(ed25519Keyring.Alice()) + sr25519Keyring, _ := keystore.NewSr25519Keyring() + ks.Babe.Insert(sr25519Keyring.Alice()) + node, err := NewNode(cfg, ks) + require.NoError(t, err) + + if reflect.TypeOf(node) != reflect.TypeOf(&Node{}) { + t.Fatalf("failed to return correct type: got %v expected %v", reflect.TypeOf(node), reflect.TypeOf(&Node{})) + } + + expected := &trie.Trie{} + err = expected.LoadFromMap(gen.GenesisFields().Raw["top"]) + require.NoError(t, err) + + expectedRoot, err := expected.Hash() + require.NoError(t, err) + + mgr := node.Services.Get(&core.Service{}) + + var coreSrvc *core.Service + var ok bool + + if coreSrvc, ok = mgr.(*core.Service); !ok { + t.Fatal("could not find core service") + } + require.NotNil(t, coreSrvc) + + stateRoot, err := coreSrvc.StorageRoot() + require.NoError(t, err) + require.Equal(t, expectedRoot, stateRoot) +} + +// balanceKey returns the storage trie key for the balance of the account with the given public key +func balanceKey(t *testing.T, key [32]byte) []byte { + accKey := append([]byte("balance:"), key[:]...) + hash, err := common.Blake2bHash(accKey) + require.NoError(t, err) + return hash[:] +} + +func TestInitNode_LoadBalances(t *testing.T) { + cfg := NewTestConfig(t) + require.NotNil(t, cfg) + + genPath := NewTestGenesisAndRuntime(t) + require.NotNil(t, genPath) + + defer utils.RemoveTestDir(t) + + cfg.Core.Roles = types.FullNodeRole + cfg.Core.BabeAuthority = false + cfg.Core.GrandpaAuthority = false + cfg.Init.Genesis = genPath + + err := InitNode(cfg) + require.NoError(t, err) + + ks := keystore.NewGlobalKeystore() + ed25519Keyring, _ := keystore.NewEd25519Keyring() + ks.Gran.Insert(ed25519Keyring.Alice()) + + node, err := NewNode(cfg, ks) + require.NoError(t, err) + + if reflect.TypeOf(node) != reflect.TypeOf(&Node{}) { + t.Fatalf("failed to return correct type: got %v expected %v", reflect.TypeOf(node), reflect.TypeOf(&Node{})) + } + + mgr := node.Services.Get(&state.Service{}) + + var stateSrv *state.Service + var ok bool + + if stateSrv, ok = mgr.(*state.Service); !ok { + t.Fatal("could not find core service") + } + require.NotNil(t, stateSrv) + + kr, _ := keystore.NewSr25519Keyring() + alice := kr.Alice().Public().(*sr25519.PublicKey).AsBytes() + + bal, err := stateSrv.Storage.GetStorage(nil, balanceKey(t, alice)) + require.NoError(t, err) + + genbal := "0x0000000000000001" + expected, _ := common.HexToBytes(genbal) + require.Equal(t, expected, bal) +} + +// TestStartNode +func TestStartNode(t *testing.T) { + cfg := NewTestConfig(t) + require.NotNil(t, cfg) + + genFile := NewTestGenesisRawFile(t, cfg) + require.NotNil(t, genFile) + + defer utils.RemoveTestDir(t) + + cfg.Init.Genesis = genFile.Name() + cfg.Core.GrandpaAuthority = false + + err := InitNode(cfg) + require.NoError(t, err) + + ks := keystore.NewGlobalKeystore() + err = keystore.LoadKeystore("alice", ks.Gran) + require.NoError(t, err) + err = keystore.LoadKeystore("alice", ks.Babe) + require.NoError(t, err) + + cfg.Core.Roles = types.FullNodeRole + + node, err := NewNode(cfg, ks) + require.NoError(t, err) + + go func() { + <-node.started + node.Stop() + }() + + err = node.Start() + require.NoError(t, err) +} + +func TestNode_StopFunc(t *testing.T) { + testvar := "before" + //stopFunc := func() { + // testvar = "after" + //} + + node := &Node{ + Services: &services.ServiceRegistry{}, + wg: sync.WaitGroup{}, + } + node.wg.Add(1) + + node.Stop() + require.Equal(t, testvar, "after") +} + +func TestNode_PersistGlobalName_WhenInitialize(t *testing.T) { + globalName := RandomNodeName() + + cfg := NewTestConfig(t) + cfg.Global.Name = globalName + require.NotNil(t, cfg) + + genPath := NewTestGenesisAndRuntime(t) + require.NotNil(t, genPath) + + defer utils.RemoveTestDir(t) + + cfg.Core.Roles = types.FullNodeRole + cfg.Core.BabeAuthority = false + cfg.Core.GrandpaAuthority = false + cfg.Init.Genesis = genPath + + err := InitNode(cfg) + require.NoError(t, err) + + storedName, err := LoadGlobalNodeName(cfg.Global.BasePath) + require.Nil(t, err) + require.Equal(t, globalName, storedName) +} diff --git a/dot/node_test.go b/dot/node_test.go index 1d589486a9..e5ba149cf7 100644 --- a/dot/node_test.go +++ b/dot/node_test.go @@ -4,30 +4,30 @@ package dot import ( - "math/big" - "reflect" + "errors" + "os" "testing" "github.com/ChainSafe/gossamer/dot/core" + "github.com/ChainSafe/gossamer/dot/digest" + "github.com/ChainSafe/gossamer/dot/network" "github.com/ChainSafe/gossamer/dot/state" + gsync "github.com/ChainSafe/gossamer/dot/sync" + "github.com/ChainSafe/gossamer/dot/system" "github.com/ChainSafe/gossamer/dot/types" "github.com/ChainSafe/gossamer/lib/babe" "github.com/ChainSafe/gossamer/lib/common" - "github.com/ChainSafe/gossamer/lib/crypto/sr25519" - "github.com/ChainSafe/gossamer/lib/genesis" "github.com/ChainSafe/gossamer/lib/grandpa" - "github.com/ChainSafe/gossamer/lib/keystore" - "github.com/ChainSafe/gossamer/lib/trie" + "github.com/ChainSafe/gossamer/lib/runtime" + "github.com/ChainSafe/gossamer/lib/runtime/wasmer" "github.com/ChainSafe/gossamer/lib/utils" - - "github.com/ChainSafe/gossamer/internal/log" + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestInitNode(t *testing.T) { cfg := NewTestConfig(t) - require.NotNil(t, cfg) - genFile := NewTestGenesisRawFile(t, cfg) require.NotNil(t, genFile) @@ -35,348 +35,250 @@ func TestInitNode(t *testing.T) { cfg.Init.Genesis = genFile.Name() - err := InitNode(cfg) - require.NoError(t, err) -} - -func TestInitNode_GenesisSpec(t *testing.T) { - cfg := NewTestConfig(t) - require.NotNil(t, cfg) - - genFile := NewTestGenesisFile(t, cfg) - require.NotNil(t, genFile) - - defer utils.RemoveTestDir(t) - - cfg.Init.Genesis = genFile.Name() - - err := InitNode(cfg) - require.NoError(t, err) + type args struct { + cfg *Config + } + tests := []struct { + name string + args args + err error + }{ + { + name: "test config", + args: args{cfg: cfg}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + ni := nodeInterface{} + err := ni.initNode(tt.args.cfg) + + if tt.err != nil { + assert.EqualError(t, err, tt.err.Error()) + } else { + assert.NoError(t, err) + } + }) + } } -// TestNodeInitialized -func TestNodeInitialized(t *testing.T) { - cfg := NewTestConfig(t) - require.NotNil(t, cfg) - - genFile := NewTestGenesisRawFile(t, cfg) - require.NotNil(t, genFile) - - defer utils.RemoveTestDir(t) +func TestLoadGlobalNodeName(t *testing.T) { + t.Parallel() - cfg.Init.Genesis = genFile.Name() - - expected := NodeInitialized(cfg.Global.BasePath) - require.Equal(t, expected, false) - - err := InitNode(cfg) + // initialise database using data directory + basePath := utils.NewTestBasePath(t, "tmpBase") + db, err := utils.SetupDatabase(basePath, false) require.NoError(t, err) - expected = NodeInitialized(cfg.Global.BasePath) - require.Equal(t, expected, true) -} + basestate := state.NewBaseState(db) + basestate.Put(common.NodeNameKey, []byte(`nodeName`)) -// TestNewNode -func TestNewNode(t *testing.T) { - cfg := NewTestConfig(t) - require.NotNil(t, cfg) - - genFile := NewTestGenesisRawFile(t, cfg) - require.NotNil(t, genFile) - - defer utils.RemoveTestDir(t) - - cfg.Init.Genesis = genFile.Name() - - err := InitNode(cfg) + err = db.Close() require.NoError(t, err) - ks := keystore.NewGlobalKeystore() - err = keystore.LoadKeystore("alice", ks.Gran) - require.NoError(t, err) - err = keystore.LoadKeystore("alice", ks.Babe) - require.NoError(t, err) - - cfg.Core.Roles = types.FullNodeRole - - node, err := NewNode(cfg, ks) - require.NoError(t, err) - - bp := node.Services.Get(&babe.Service{}) - require.NotNil(t, bp) - fg := node.Services.Get(&grandpa.Service{}) - require.NotNil(t, fg) + type args struct { + basepath string + } + tests := []struct { + name string + args args + wantNodename string + err error + }{ + { + name: "working example", + args: args{basepath: basePath}, + wantNodename: "nodeName", + }, + { + name: "wrong basepath test", + args: args{basepath: "wrong_path"}, + err: errors.New("Key not found"), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + gotNodename, err := LoadGlobalNodeName(tt.args.basepath) + if tt.err != nil { + assert.EqualError(t, err, tt.err.Error()) + err := os.RemoveAll(tt.args.basepath) + require.NoError(t, err) + } else { + assert.NoError(t, err) + } + + assert.Equal(t, tt.wantNodename, gotNodename) + }) + } } -func TestNewNode_Authority(t *testing.T) { +func TestNewNodeC(t *testing.T) { cfg := NewTestConfig(t) require.NotNil(t, cfg) - - genFile := NewTestGenesisRawFile(t, cfg) - require.NotNil(t, genFile) - defer utils.RemoveTestDir(t) - cfg.Init.Genesis = genFile.Name() - - err := InitNode(cfg) - require.NoError(t, err) - - ks := keystore.NewGlobalKeystore() - err = keystore.LoadKeystore("alice", ks.Gran) - require.NoError(t, err) - require.Equal(t, 1, ks.Gran.Size()) - err = keystore.LoadKeystore("alice", ks.Babe) - require.NoError(t, err) - require.Equal(t, 1, ks.Babe.Size()) - - cfg.Core.Roles = types.AuthorityRole - - node, err := NewNode(cfg, ks) - require.NoError(t, err) - - bp := node.Services.Get(&babe.Service{}) - require.NotNil(t, bp) - fg := node.Services.Get(&grandpa.Service{}) - require.NotNil(t, fg) -} - -// TestStartNode -func TestStartNode(t *testing.T) { - cfg := NewTestConfig(t) - require.NotNil(t, cfg) - genFile := NewTestGenesisRawFile(t, cfg) require.NotNil(t, genFile) - defer utils.RemoveTestDir(t) - - cfg.Init.Genesis = genFile.Name() - cfg.Core.GrandpaAuthority = false - cfg.Core.BABELead = true - - err := InitNode(cfg) - require.NoError(t, err) - - ks := keystore.NewGlobalKeystore() - err = keystore.LoadKeystore("alice", ks.Gran) - require.NoError(t, err) - err = keystore.LoadKeystore("alice", ks.Babe) - require.NoError(t, err) - - cfg.Core.Roles = types.FullNodeRole - - node, err := NewNode(cfg, ks) - require.NoError(t, err) - - go func() { - <-node.started - node.Stop() - }() - - err = node.Start() - require.NoError(t, err) -} - -// TestInitNode_LoadGenesisData -func TestInitNode_LoadGenesisData(t *testing.T) { - cfg := NewTestConfig(t) - require.NotNil(t, cfg) - - genPath := NewTestGenesisAndRuntime(t) - require.NotNil(t, genPath) - - defer utils.RemoveTestDir(t) - - cfg.Init.Genesis = genPath - cfg.Core.GrandpaAuthority = false - - err := InitNode(cfg) - require.NoError(t, err) - - config := state.Config{ - Path: cfg.Global.BasePath, - LogLevel: log.Info, + type args struct { + cfg *Config } - 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, big.NewInt(0), types.NewDigest()) - require.NoError(t, err) - - err = stateSrvc.Initialise(gen, genesisHeader, genTrie) - require.NoError(t, err) - - err = stateSrvc.Start() - require.NoError(t, err) - - defer 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, big.NewInt(0), types.NewDigest()) - require.NoError(t, err) - require.Equal(t, expectedHeader.Hash(), genesisHeader.Hash()) -} - -// TestInitNode_LoadStorageRoot -func TestInitNode_LoadStorageRoot(t *testing.T) { - cfg := NewTestConfig(t) - require.NotNil(t, cfg) - - genPath := NewTestGenesisAndRuntime(t) - require.NotNil(t, genPath) - - defer utils.RemoveTestDir(t) - - cfg.Core.Roles = types.FullNodeRole - cfg.Core.BabeAuthority = false - cfg.Core.GrandpaAuthority = false - cfg.Init.Genesis = genPath - - gen, err := genesis.NewGenesisFromJSONRaw(genPath) - require.NoError(t, err) - - err = InitNode(cfg) - require.NoError(t, err) - - ks := keystore.NewGlobalKeystore() - ed25519Keyring, _ := keystore.NewEd25519Keyring() - ks.Gran.Insert(ed25519Keyring.Alice()) - sr25519Keyring, _ := keystore.NewSr25519Keyring() - ks.Babe.Insert(sr25519Keyring.Alice()) - node, err := NewNode(cfg, ks) - require.NoError(t, err) - - if reflect.TypeOf(node) != reflect.TypeOf(&Node{}) { - t.Fatalf("failed to return correct type: got %v expected %v", reflect.TypeOf(node), reflect.TypeOf(&Node{})) + tests := []struct { + name string + args args + want *Node + err error + }{ + { + name: "minimal config", + args: args{ + cfg: &Config{ + Global: GlobalConfig{BasePath: cfg.Global.BasePath}, + Init: InitConfig{Genesis: genFile.Name()}, + Account: AccountConfig{Key: "alice"}, + Core: CoreConfig{ + Roles: types.FullNodeRole, + WasmInterpreter: wasmer.Name, + }, + }, + }, + }, } - - expected := &trie.Trie{} - err = expected.LoadFromMap(gen.GenesisFields().Raw["top"]) - require.NoError(t, err) - - expectedRoot, err := expected.Hash() - require.NoError(t, err) - - mgr := node.Services.Get(&core.Service{}) - - var coreSrvc *core.Service - var ok bool - - if coreSrvc, ok = mgr.(*core.Service); !ok { - t.Fatal("could not find core service") + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + ks, err := InitKeystore(tt.args.cfg) + assert.NoError(t, err) + got, err := NewNode(tt.args.cfg, ks) + if tt.err != nil { + assert.EqualError(t, err, tt.err.Error()) + utils.RemoveTestDir(t) + } else { + assert.NoError(t, err) + } + + if tt.want != nil { + assert.Equal(t, tt.want.Name, got.Name) + } + }) } - require.NotNil(t, coreSrvc) - - stateRoot, err := coreSrvc.StorageRoot() - require.NoError(t, err) - require.Equal(t, expectedRoot, stateRoot) } -// balanceKey returns the storage trie key for the balance of the account with the given public key -func balanceKey(t *testing.T, key [32]byte) []byte { - accKey := append([]byte("balance:"), key[:]...) - hash, err := common.Blake2bHash(accKey) - require.NoError(t, err) - return hash[:] -} +func TestNewNodeMock(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() -func TestInitNode_LoadBalances(t *testing.T) { cfg := NewTestConfig(t) require.NotNil(t, cfg) - - genPath := NewTestGenesisAndRuntime(t) - require.NotNil(t, genPath) - defer utils.RemoveTestDir(t) - cfg.Core.Roles = types.FullNodeRole - cfg.Core.BabeAuthority = false - cfg.Core.GrandpaAuthority = false - cfg.Init.Genesis = genPath - - err := InitNode(cfg) - require.NoError(t, err) - - ks := keystore.NewGlobalKeystore() - ed25519Keyring, _ := keystore.NewEd25519Keyring() - ks.Gran.Insert(ed25519Keyring.Alice()) - - node, err := NewNode(cfg, ks) - require.NoError(t, err) + genFile := NewTestGenesisRawFile(t, cfg) + require.NotNil(t, genFile) - if reflect.TypeOf(node) != reflect.TypeOf(&Node{}) { - t.Fatalf("failed to return correct type: got %v expected %v", reflect.TypeOf(node), reflect.TypeOf(&Node{})) + type args struct { + cfg *Config } - - mgr := node.Services.Get(&state.Service{}) - - var stateSrv *state.Service - var ok bool - - if stateSrv, ok = mgr.(*state.Service); !ok { - t.Fatal("could not find core service") + tests := []struct { + name string + args args + want *Node + err error + }{ + { + name: "minimal config", + args: args{ + cfg: &Config{ + Global: GlobalConfig{BasePath: cfg.Global.BasePath}, + Init: InitConfig{Genesis: genFile.Name()}, + Account: AccountConfig{Key: "alice"}, + Core: CoreConfig{ + Roles: types.FullNodeRole, + WasmInterpreter: wasmer.Name, + }, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + ks, err := InitKeystore(tt.args.cfg) + assert.NoError(t, err) + m := NewMocknewNodeIface(ctrl) + m.EXPECT().nodeInitialised(tt.args.cfg.Global.BasePath).Return(true) + m.EXPECT().createStateService(tt.args.cfg).Return(&state.Service{}, nil) + m.EXPECT().createRuntimeStorage(&state.Service{}).Return(&runtime.NodeStorage{}, nil) + m.EXPECT().loadRuntime(tt.args.cfg, &runtime.NodeStorage{}, &state.Service{}, ks, &network.Service{}).Return(nil) + m.EXPECT().createBlockVerifier(&state.Service{}).Return(&babe.VerificationManager{}, nil) + m.EXPECT().createDigestHandler(&state.Service{}).Return(&digest.Handler{}, nil) + m.EXPECT().createCoreService(tt.args.cfg, ks, &state.Service{}, &network.Service{}, + &digest.Handler{}).Return(&core.Service{}, nil) + m.EXPECT().createGRANDPAService(tt.args.cfg, &state.Service{}, &digest.Handler{}, ks.Gran, + &network.Service{}).Return(&grandpa.Service{}, nil) + m.EXPECT().newSyncService(tt.args.cfg, &state.Service{}, &grandpa.Service{}, &babe.VerificationManager{}, + &core.Service{}, &network.Service{}).Return(&gsync.Service{}, nil) + m.EXPECT().createBABEService(tt.args.cfg, &state.Service{}, ks.Babe, + &core.Service{}).Return(&babe.Service{}, nil) + m.EXPECT().createSystemService(&tt.args.cfg.System, &state.Service{}).Return(&system.Service{}, nil) + netA := &network.Service{} + netA.SetTransactionHandler(&core.Service{}) + netA.SetSyncer(&gsync.Service{}) + m.EXPECT().initialiseTelemetry(tt.args.cfg, &state.Service{}, netA, + &system.Service{}) + m.EXPECT().createNetworkService(tt.args.cfg, &state.Service{}).Return(&network.Service{}, nil) + + got, err := newNode(tt.args.cfg, ks, m) + if tt.err != nil { + assert.EqualError(t, err, tt.err.Error()) + utils.RemoveTestDir(t) + } else { + assert.NoError(t, err) + } + + if tt.want != nil { + assert.Equal(t, tt.want, got) + } + }) } - require.NotNil(t, stateSrv) - - kr, _ := keystore.NewSr25519Keyring() - alice := kr.Alice().Public().(*sr25519.PublicKey).AsBytes() - - bal, err := stateSrv.Storage.GetStorage(nil, balanceKey(t, alice)) - require.NoError(t, err) - - genbal := "0x0000000000000001" - expected, _ := common.HexToBytes(genbal) - require.Equal(t, expected, bal) } -func TestNode_PersistGlobalName_WhenInitialize(t *testing.T) { - globalName := RandomNodeName() - +func TestNodeInitialized(t *testing.T) { cfg := NewTestConfig(t) - cfg.Global.Name = globalName require.NotNil(t, cfg) - genPath := NewTestGenesisAndRuntime(t) - require.NotNil(t, genPath) + genFile := NewTestGenesisRawFile(t, cfg) + require.NotNil(t, genFile) defer utils.RemoveTestDir(t) - cfg.Core.Roles = types.FullNodeRole - cfg.Core.BabeAuthority = false - cfg.Core.GrandpaAuthority = false - cfg.Init.Genesis = genPath + cfg.Init.Genesis = genFile.Name() - err := InitNode(cfg) + ni := nodeInterface{} + err := ni.initNode(cfg) require.NoError(t, err) - storedName, err := LoadGlobalNodeName(cfg.Global.BasePath) - require.Nil(t, err) - require.Equal(t, globalName, storedName) + type args struct { + basepath string + } + tests := []struct { + name string + args args + want bool + }{ + { + name: "blank base path", + args: args{basepath: ""}, + want: false, + }, + { + name: "working example", + args: args{basepath: cfg.Global.BasePath}, + want: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := NodeInitialized(tt.args.basepath); got != tt.want { + t.Errorf("NodeInitialized() = %v, want %v", got, tt.want) + } + }) + } } diff --git a/dot/services.go b/dot/services.go index aacf37f279..e0173861f7 100644 --- a/dot/services.go +++ b/dot/services.go @@ -42,7 +42,7 @@ func newInMemoryDB(path string) (chaindb.Database, error) { // State Service // createStateService creates the state service and initialise state database -func createStateService(cfg *Config) (*state.Service, error) { +func (nodeInterface) createStateService(cfg *Config) (*state.Service, error) { logger.Debug("creating state service...") config := state.Config{ @@ -68,7 +68,7 @@ func createStateService(cfg *Config) (*state.Service, error) { return stateSrvc, nil } -func createRuntimeStorage(st *state.Service) (*runtime.NodeStorage, error) { +func (nodeInterface) createRuntimeStorage(st *state.Service) (*runtime.NodeStorage, error) { localStorage, err := newInMemoryDB(st.DB().Path()) if err != nil { return nil, err @@ -112,6 +112,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, @@ -159,7 +160,8 @@ func asAuthority(authority bool) string { return "" } -func createBABEService(cfg *Config, st *state.Service, ks keystore.Keystore, cs *core.Service) (*babe.Service, error) { +func (nodeInterface) createBABEService(cfg *Config, st *state.Service, ks keystore.Keystore, + cs *core.Service) (*babe.Service, error) { logger.Info("creating BABE service" + asAuthority(cfg.Core.BabeAuthority) + "...") @@ -202,7 +204,7 @@ func createBABEService(cfg *Config, st *state.Service, ks keystore.Keystore, cs // Core Service // createCoreService creates the core service from the provided core configuration -func createCoreService(cfg *Config, ks *keystore.GlobalKeystore, +func (nodeInterface) createCoreService(cfg *Config, ks *keystore.GlobalKeystore, st *state.Service, net *network.Service, dh *digest.Handler) ( *core.Service, error) { logger.Debug("creating core service" + @@ -246,7 +248,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) (*network.Service, error) { +func (nodeInterface) createNetworkService(cfg *Config, stateSrvc *state.Service) (*network.Service, error) { logger.Debugf( "creating network service with roles %d, port %d, bootnodes %s, protocol ID %s, nobootstrap=%t and noMDNS=%t...", cfg.Core.Roles, cfg.Network.Port, strings.Join(cfg.Network.Bootnodes, ","), cfg.Network.ProtocolID, @@ -289,7 +291,7 @@ func createNetworkService(cfg *Config, stateSrvc *state.Service) (*network.Servi // RPC Service // createRPCService creates the RPC service from the provided core configuration -func createRPCService(cfg *Config, ns *runtime.NodeStorage, stateSrvc *state.Service, +func (nodeInterface) createRPCService(cfg *Config, ns *runtime.NodeStorage, stateSrvc *state.Service, coreSrvc *core.Service, networkSrvc *network.Service, bp modules.BlockProducerAPI, sysSrvc *system.Service, finSrvc *grandpa.Service) (*rpc.HTTPServer, error) { logger.Infof( @@ -340,7 +342,7 @@ func createRPCService(cfg *Config, ns *runtime.NodeStorage, stateSrvc *state.Ser } // createSystemService creates a systemService for providing system related information -func createSystemService(cfg *types.SystemInfo, stateSrvc *state.Service) (*system.Service, error) { +func (nodeInterface) createSystemService(cfg *types.SystemInfo, stateSrvc *state.Service) (*system.Service, error) { genesisData, err := stateSrvc.Base.LoadGenesisData() if err != nil { return nil, err @@ -350,7 +352,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 (nodeInterface) createGRANDPAService(cfg *Config, st *state.Service, dh *digest.Handler, ks keystore.Keystore, net *network.Service) (*grandpa.Service, error) { rt, err := st.Block.GetRuntime(nil) if err != nil { @@ -391,7 +393,7 @@ func createGRANDPAService(cfg *Config, st *state.Service, dh *digest.Handler, return grandpa.NewService(gsCfg) } -func createBlockVerifier(st *state.Service) (*babe.VerificationManager, error) { +func (nodeInterface) createBlockVerifier(st *state.Service) (*babe.VerificationManager, error) { ver, err := babe.NewVerificationManager(st.Block, st.Epoch) if err != nil { return nil, err @@ -400,7 +402,7 @@ func createBlockVerifier(st *state.Service) (*babe.VerificationManager, error) { return ver, nil } -func newSyncService(cfg *Config, st *state.Service, fg sync.FinalityGadget, +func (nodeInterface) newSyncService(cfg *Config, st *state.Service, fg sync.FinalityGadget, verifier *babe.VerificationManager, cs *core.Service, net *network.Service) ( *sync.Service, error) { slotDuration, err := st.Epoch.GetSlotDuration() @@ -425,7 +427,7 @@ func newSyncService(cfg *Config, st *state.Service, fg sync.FinalityGadget, return sync.NewService(syncCfg) } -func createDigestHandler(st *state.Service) (*digest.Handler, error) { +func (nodeInterface) createDigestHandler(st *state.Service) (*digest.Handler, error) { return digest.NewHandler(st.Block, st.Epoch, st.Grandpa) } diff --git a/dot/services_integration_test.go b/dot/services_integration_test.go new file mode 100644 index 0000000000..e7687ad749 --- /dev/null +++ b/dot/services_integration_test.go @@ -0,0 +1,384 @@ +//go:build integration +// +build integration + +// Copyright 2020 ChainSafe Systems (ON) Corp. +// This file is part of gossamer. +// +// The gossamer library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The gossamer library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the gossamer library. If not, see . + +package dot + +import ( + "flag" + "net/url" + "testing" + "time" + + "github.com/ChainSafe/gossamer/dot/network" + "github.com/ChainSafe/gossamer/dot/types" + "github.com/ChainSafe/gossamer/lib/grandpa" + "github.com/ChainSafe/gossamer/lib/keystore" + "github.com/ChainSafe/gossamer/lib/utils" + + "github.com/gorilla/websocket" + "github.com/stretchr/testify/require" +) + +// TestCreateStateService tests the createStateService method +func TestCreateStateService(t *testing.T) { + cfg := NewTestConfig(t) + require.NotNil(t, cfg) + + genFile := NewTestGenesisRawFile(t, cfg) + require.NotNil(t, genFile) + + defer utils.RemoveTestDir(t) + + cfg.Init.Genesis = genFile.Name() + + err := InitNode(cfg) + require.Nil(t, err) + + nodeI := nodeInterface{} + stateSrvc, err := nodeI.createStateService(cfg) + require.Nil(t, err) + require.NotNil(t, stateSrvc) +} + +// TestCreateCoreService tests the createCoreService method +func TestCreateCoreService(t *testing.T) { + cfg := NewTestConfig(t) + require.NotNil(t, cfg) + + genFile := NewTestGenesisRawFile(t, cfg) + require.NotNil(t, genFile) + + defer utils.RemoveTestDir(t) + + cfg.Core.Roles = types.FullNodeRole + cfg.Core.BabeAuthority = false + cfg.Core.GrandpaAuthority = false + cfg.Init.Genesis = genFile.Name() + + err := InitNode(cfg) + require.NoError(t, err) + + nodeI := nodeInterface{} + stateSrvc, err := nodeI.createStateService(cfg) + require.NoError(t, err) + + ks := keystore.NewGlobalKeystore() + require.NotNil(t, ks) + ed25519Keyring, _ := keystore.NewEd25519Keyring() + ks.Gran.Insert(ed25519Keyring.Alice()) + + networkSrvc := &network.Service{} + + dh, err := nodeI.createDigestHandler(stateSrvc) + require.NoError(t, err) + + coreSrvc, err := nodeI.createCoreService(cfg, ks, stateSrvc, networkSrvc, dh) + require.NoError(t, err) + require.NotNil(t, coreSrvc) +} + +func TestCreateBlockVerifier(t *testing.T) { + cfg := NewTestConfig(t) + require.NotNil(t, cfg) + + genFile := NewTestGenesisFile(t, cfg) + require.NotNil(t, genFile) + + defer utils.RemoveTestDir(t) + + cfg.Init.Genesis = genFile.Name() + + err := InitNode(cfg) + require.NoError(t, err) + + nodeI := nodeInterface{} + stateSrvc, err := nodeI.createStateService(cfg) + require.NoError(t, err) + + _, err = nodeI.createBlockVerifier(stateSrvc) + require.NoError(t, err) +} + +func TestCreateSyncService(t *testing.T) { + cfg := NewTestConfig(t) + require.NotNil(t, cfg) + + genFile := NewTestGenesisFile(t, cfg) + require.NotNil(t, genFile) + + defer utils.RemoveTestDir(t) + + cfg.Init.Genesis = genFile.Name() + + err := InitNode(cfg) + require.NoError(t, err) + + nodeI := nodeInterface{} + stateSrvc, err := nodeI.createStateService(cfg) + require.NoError(t, err) + + ks := keystore.NewGlobalKeystore() + require.NotNil(t, ks) + + ver, err := nodeI.createBlockVerifier(stateSrvc) + require.NoError(t, err) + + dh, err := nodeI.createDigestHandler(stateSrvc) + require.NoError(t, err) + + coreSrvc, err := nodeI.createCoreService(cfg, ks, stateSrvc, &network.Service{}, dh) + require.NoError(t, err) + + _, err = nodeI.newSyncService(cfg, stateSrvc, &grandpa.Service{}, ver, coreSrvc, &network.Service{}) + require.NoError(t, err) +} + +// TestCreateNetworkService tests the createNetworkService method +func TestCreateNetworkService(t *testing.T) { + cfg := NewTestConfig(t) + require.NotNil(t, cfg) + + genFile := NewTestGenesisRawFile(t, cfg) + require.NotNil(t, genFile) + + defer utils.RemoveTestDir(t) + + cfg.Init.Genesis = genFile.Name() + + err := InitNode(cfg) + require.NoError(t, err) + + nodeI := nodeInterface{} + stateSrvc, err := nodeI.createStateService(cfg) + require.NoError(t, err) + + networkSrvc, err := nodeI.createNetworkService(cfg, stateSrvc) + require.NoError(t, err) + require.NotNil(t, networkSrvc) +} + +// TestCreateRPCService tests the createRPCService method +func TestCreateRPCService(t *testing.T) { + cfg := NewTestConfig(t) + require.NotNil(t, cfg) + + genFile := NewTestGenesisRawFile(t, cfg) + require.NotNil(t, genFile) + + defer utils.RemoveTestDir(t) + + cfg.Core.Roles = types.FullNodeRole + cfg.Core.BabeAuthority = false + cfg.Core.GrandpaAuthority = false + cfg.Init.Genesis = genFile.Name() + + err := InitNode(cfg) + require.NoError(t, err) + + nodeI := nodeInterface{} + stateSrvc, err := nodeI.createStateService(cfg) + require.NoError(t, err) + + networkSrvc := &network.Service{} + + ks := keystore.NewGlobalKeystore() + ed25519Keyring, _ := keystore.NewEd25519Keyring() + ks.Gran.Insert(ed25519Keyring.Alice()) + + ns, err := nodeI.createRuntimeStorage(stateSrvc) + require.NoError(t, err) + err = nodeI.loadRuntime(cfg, ns, stateSrvc, ks, networkSrvc) + require.NoError(t, err) + + dh, err := nodeI.createDigestHandler(stateSrvc) + require.NoError(t, err) + + coreSrvc, err := nodeI.createCoreService(cfg, ks, stateSrvc, networkSrvc, dh) + require.NoError(t, err) + + sysSrvc, err := nodeI.createSystemService(&cfg.System, stateSrvc) + require.NoError(t, err) + + rpcSrvc, err := nodeI.createRPCService(cfg, ns, stateSrvc, coreSrvc, networkSrvc, nil, sysSrvc, nil) + require.NoError(t, err) + require.NotNil(t, rpcSrvc) +} + +func TestCreateBABEService(t *testing.T) { + cfg := NewTestConfig(t) + require.NotNil(t, cfg) + + genFile := NewTestGenesisRawFile(t, cfg) + require.NotNil(t, genFile) + + defer utils.RemoveTestDir(t) + + cfg.Core.Roles = types.FullNodeRole + cfg.Init.Genesis = genFile.Name() + + err := InitNode(cfg) + require.NoError(t, err) + + nodeI := nodeInterface{} + stateSrvc, err := nodeI.createStateService(cfg) + require.NoError(t, err) + + ks := keystore.NewGlobalKeystore() + kr, err := keystore.NewSr25519Keyring() + require.NoError(t, err) + ks.Babe.Insert(kr.Alice()) + + ns, err := nodeI.createRuntimeStorage(stateSrvc) + require.NoError(t, err) + err = nodeI.loadRuntime(cfg, ns, stateSrvc, ks, &network.Service{}) + require.NoError(t, err) + + dh, err := nodeI.createDigestHandler(stateSrvc) + require.NoError(t, err) + + coreSrvc, err := nodeI.createCoreService(cfg, ks, stateSrvc, &network.Service{}, dh) + require.NoError(t, err) + + bs, err := nodeI.createBABEService(cfg, stateSrvc, ks.Babe, coreSrvc) + require.NoError(t, err) + require.NotNil(t, bs) +} + +func TestCreateGrandpaService(t *testing.T) { + cfg := NewTestConfig(t) + require.NotNil(t, cfg) + + genFile := NewTestGenesisRawFile(t, cfg) + require.NotNil(t, genFile) + + defer utils.RemoveTestDir(t) + + cfg.Core.Roles = types.AuthorityRole + cfg.Init.Genesis = genFile.Name() + + err := InitNode(cfg) + require.NoError(t, err) + + nodeI := nodeInterface{} + stateSrvc, err := nodeI.createStateService(cfg) + require.NoError(t, err) + + ks := keystore.NewGlobalKeystore() + kr, err := keystore.NewEd25519Keyring() + require.NoError(t, err) + ks.Gran.Insert(kr.Alice()) + + ns, err := nodeI.createRuntimeStorage(stateSrvc) + require.NoError(t, err) + + err = nodeI.loadRuntime(cfg, ns, stateSrvc, ks, &network.Service{}) + require.NoError(t, err) + + dh, err := nodeI.createDigestHandler(stateSrvc) + require.NoError(t, err) + + gs, err := nodeI.createGRANDPAService(cfg, stateSrvc, dh, ks.Gran, &network.Service{}) + require.NoError(t, err) + require.NotNil(t, gs) +} + +var addr = flag.String("addr", "localhost:8546", "http service address") +var testCalls = []struct { + call []byte + expected []byte +}{ + {[]byte(`{"jsonrpc":"2.0","method":"system_name","params":[],"id":1}`), []byte(`{"id":1,"jsonrpc":"2.0", +"result":"gossamer"}` + "\n")}, // working request + {[]byte(`{"jsonrpc":"2.0","method":"unknown","params":[],"id":2}`), []byte(`{"error":{"code":-32000,"data":null, +"message":"rpc error method unknown not found"},"id":2,"jsonrpc":"2.0"}` + "\n")}, // unknown method + {[]byte{}, []byte(`{"jsonrpc":"2.0","error":{"code":-32600,"message":"Invalid request"},"id":0}` + "\n")}, + // empty request + {[]byte(`{"jsonrpc":"2.0","method":"chain_subscribeNewHeads","params":[],"id":3}`), []byte(`{"jsonrpc":"2.0", +"result":1,"id":3}` + "\n")}, + {[]byte(`{"jsonrpc":"2.0","method":"state_subscribeStorage","params":[],"id":4}`), []byte(`{"jsonrpc":"2.0", +"result":2,"id":4}` + "\n")}, +} + +func TestNewWebSocketServer(t *testing.T) { + cfg := NewTestConfig(t) + require.NotNil(t, cfg) + + genFile := NewTestGenesisRawFile(t, cfg) + require.NotNil(t, genFile) + + defer utils.RemoveTestDir(t) + + cfg.Core.Roles = types.FullNodeRole + cfg.Core.BabeAuthority = false + cfg.Core.GrandpaAuthority = false + cfg.Init.Genesis = genFile.Name() + cfg.RPC.External = false + cfg.RPC.WS = true + cfg.RPC.WSExternal = false + cfg.System.SystemName = "gossamer" + + err := InitNode(cfg) + require.Nil(t, err) + + nodeI := nodeInterface{} + stateSrvc, err := nodeI.createStateService(cfg) + require.Nil(t, err) + + networkSrvc := &network.Service{} + + ks := keystore.NewGlobalKeystore() + ed25519Keyring, _ := keystore.NewEd25519Keyring() + ks.Gran.Insert(ed25519Keyring.Alice()) + + ns, err := nodeI.createRuntimeStorage(stateSrvc) + require.NoError(t, err) + err = nodeI.loadRuntime(cfg, ns, stateSrvc, ks, networkSrvc) + require.NoError(t, err) + + dh, err := nodeI.createDigestHandler(stateSrvc) + require.NoError(t, err) + + coreSrvc, err := nodeI.createCoreService(cfg, ks, stateSrvc, networkSrvc, dh) + require.Nil(t, err) + + sysSrvc, err := nodeI.createSystemService(&cfg.System, stateSrvc) + require.NoError(t, err) + + rpcSrvc, err := nodeI.createRPCService(cfg, ns, stateSrvc, coreSrvc, networkSrvc, nil, sysSrvc, nil) + require.NoError(t, err) + err = rpcSrvc.Start() + require.Nil(t, err) + + time.Sleep(time.Second) // give server a second to start + + u := url.URL{Scheme: "ws", Host: *addr, Path: "/"} + + c, _, err := websocket.DefaultDialer.Dial(u.String(), nil) + require.NoError(t, err) + defer c.Close() + + for _, item := range testCalls { + err = c.WriteMessage(websocket.TextMessage, item.call) + require.Nil(t, err) + + _, message, err := c.ReadMessage() + require.Nil(t, err) + require.Equal(t, item.expected, message) + } +} diff --git a/dot/services_test.go b/dot/services_test.go index b1a939a38a..7f225c8c36 100644 --- a/dot/services_test.go +++ b/dot/services_test.go @@ -4,371 +4,823 @@ package dot import ( - "flag" - "net/url" + "errors" + "io/ioutil" + "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/rpc" + "github.com/ChainSafe/gossamer/dot/rpc/modules" + "github.com/ChainSafe/gossamer/dot/state" + "github.com/ChainSafe/gossamer/dot/sync" + "github.com/ChainSafe/gossamer/dot/system" "github.com/ChainSafe/gossamer/dot/types" "github.com/ChainSafe/gossamer/internal/pprof" + "github.com/ChainSafe/gossamer/lib/babe" + "github.com/ChainSafe/gossamer/lib/crypto" "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/utils" - - "github.com/gorilla/websocket" + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) -// TestCreateStateService tests the createStateService method -func TestCreateStateService(t *testing.T) { - cfg := NewTestConfig(t) - require.NotNil(t, cfg) - - genFile := NewTestGenesisRawFile(t, cfg) - require.NotNil(t, genFile) - - defer utils.RemoveTestDir(t) - - cfg.Init.Genesis = genFile.Name() - - err := InitNode(cfg) - require.Nil(t, err) - - stateSrvc, err := createStateService(cfg) - require.Nil(t, err) - require.NotNil(t, stateSrvc) -} +func Test_createBABEService(t *testing.T) { + t.Parallel() -// TestCreateCoreService tests the createCoreService method -func TestCreateCoreService(t *testing.T) { cfg := NewTestConfig(t) - require.NotNil(t, cfg) genFile := NewTestGenesisRawFile(t, cfg) - require.NotNil(t, genFile) defer utils.RemoveTestDir(t) cfg.Core.Roles = types.FullNodeRole - cfg.Core.BabeAuthority = false - cfg.Core.GrandpaAuthority = false cfg.Init.Genesis = genFile.Name() - err := InitNode(cfg) + ni := nodeInterface{} + err := ni.initNode(cfg) require.NoError(t, err) - stateSrvc, err := createStateService(cfg) + stateSrvc, err := ni.createStateService(cfg) require.NoError(t, err) ks := keystore.NewGlobalKeystore() - require.NotNil(t, ks) - ed25519Keyring, _ := keystore.NewEd25519Keyring() - ks.Gran.Insert(ed25519Keyring.Alice()) + kr, err := keystore.NewSr25519Keyring() + require.NoError(t, err) + ks.Babe.Insert(kr.Alice()) - networkSrvc := &network.Service{} + ns, err := ni.createRuntimeStorage(stateSrvc) + require.NoError(t, err) + err = ni.loadRuntime(cfg, ns, stateSrvc, ks, &network.Service{}) + require.NoError(t, err) - dh, err := createDigestHandler(stateSrvc) + dh, err := ni.createDigestHandler(stateSrvc) require.NoError(t, err) - coreSrvc, err := createCoreService(cfg, ks, stateSrvc, networkSrvc, dh) + coreSrvc, err := ni.createCoreService(cfg, ks, stateSrvc, &network.Service{}, dh) require.NoError(t, err) - require.NotNil(t, coreSrvc) + + type args struct { + cfg *Config + st *state.Service + ks keystore.Keystore + cs *core.Service + } + tests := []struct { + name string + args args + want *babe.Service + err error + }{ + { + name: "invalid keystore type test", + args: args{ + cfg: cfg, + st: stateSrvc, + ks: ks.Gran, + cs: coreSrvc, + }, + err: errors.New("invalid keystore type"), + }, + { + name: "working example", + args: args{ + cfg: cfg, + st: stateSrvc, + ks: ks.Babe, + cs: coreSrvc, + }, + want: &babe.Service{}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := ni.createBABEService(tt.args.cfg, tt.args.st, tt.args.ks, tt.args.cs) + if tt.err != nil { + assert.EqualError(t, err, tt.err.Error()) + } else { + assert.NoError(t, err) + } + + if tt.want != nil { + assert.NotNil(t, got) + } else { + assert.Nil(t, got) + } + }) + } } -func TestCreateBlockVerifier(t *testing.T) { +func Test_createBlockVerifier(t *testing.T) { cfg := NewTestConfig(t) - require.NotNil(t, cfg) genFile := NewTestGenesisFile(t, cfg) - require.NotNil(t, genFile) defer utils.RemoveTestDir(t) cfg.Init.Genesis = genFile.Name() - err := InitNode(cfg) + ni := nodeInterface{} + err := ni.initNode(cfg) require.NoError(t, err) - stateSrvc, err := createStateService(cfg) + stateSrvc, err := ni.createStateService(cfg) require.NoError(t, err) - _, err = createBlockVerifier(stateSrvc) - require.NoError(t, err) + type args struct { + st *state.Service + } + tests := []struct { + name string + args args + want *babe.VerificationManager + err error + }{ + { + name: "nil BlockState test", + args: args{st: &state.Service{}}, + err: errors.New("cannot have nil BlockState"), + }, + { + name: "working example", + args: args{st: stateSrvc}, + want: &babe.VerificationManager{}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := ni.createBlockVerifier(tt.args.st) + if tt.err != nil { + assert.EqualError(t, err, tt.err.Error()) + } else { + assert.NoError(t, err) + } + + if tt.want != nil { + assert.NotNil(t, got) + } else { + assert.Nil(t, got) + } + }) + } } -func TestCreateSyncService(t *testing.T) { +func Test_createCoreService(t *testing.T) { cfg := NewTestConfig(t) - require.NotNil(t, cfg) - genFile := NewTestGenesisFile(t, cfg) - require.NotNil(t, genFile) + genFile := NewTestGenesisRawFile(t, cfg) defer utils.RemoveTestDir(t) + cfg.Core.Roles = types.FullNodeRole + cfg.Core.BabeAuthority = false + cfg.Core.GrandpaAuthority = false cfg.Init.Genesis = genFile.Name() - err := InitNode(cfg) + ni := nodeInterface{} + err := ni.initNode(cfg) require.NoError(t, err) - stateSrvc, err := createStateService(cfg) - require.NoError(t, err) - - ks := keystore.NewGlobalKeystore() - require.NotNil(t, ks) - - ver, err := createBlockVerifier(stateSrvc) + stateSrvc, err := ni.createStateService(cfg) require.NoError(t, err) - dh, err := createDigestHandler(stateSrvc) + networkSrvc, err := ni.createNetworkService(cfg, stateSrvc) require.NoError(t, err) - coreSrvc, err := createCoreService(cfg, ks, stateSrvc, &network.Service{}, dh) - require.NoError(t, err) + ks := keystore.NewGlobalKeystore() + require.NotNil(t, ks) + ed25519Keyring, _ := keystore.NewEd25519Keyring() + ks.Gran.Insert(ed25519Keyring.Alice()) - _, err = newSyncService(cfg, stateSrvc, &grandpa.Service{}, ver, coreSrvc, &network.Service{}) - require.NoError(t, err) + type args struct { + cfg *Config + ks *keystore.GlobalKeystore + st *state.Service + net *network.Service + dh *digest.Handler + } + tests := []struct { + name string + args args + want *core.Service + err error + }{ + { + name: "missing keystore test", + args: args{ + cfg: cfg, + st: stateSrvc, + }, + err: errors.New("cannot have nil keystore"), + }, + { + name: "working example", + args: args{ + cfg: cfg, + ks: ks, + st: stateSrvc, + net: networkSrvc, + }, + want: &core.Service{}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := ni.createCoreService(tt.args.cfg, tt.args.ks, tt.args.st, tt.args.net, tt.args.dh) + if tt.err != nil { + assert.EqualError(t, err, tt.err.Error()) + } else { + assert.NoError(t, err) + } + if tt.want != nil { + assert.NotNil(t, got) + } else { + assert.Nil(t, got) + } + }) + } } -// TestCreateNetworkService tests the createNetworkService method -func TestCreateNetworkService(t *testing.T) { +func Test_createDigestHandler(t *testing.T) { cfg := NewTestConfig(t) - require.NotNil(t, cfg) - genFile := NewTestGenesisRawFile(t, cfg) - require.NotNil(t, genFile) + genFile := NewTestGenesisFile(t, cfg) defer utils.RemoveTestDir(t) cfg.Init.Genesis = genFile.Name() - err := InitNode(cfg) + ni := nodeInterface{} + err := ni.initNode(cfg) require.NoError(t, err) - stateSrvc, err := createStateService(cfg) + stateSrvc, err := ni.createStateService(cfg) require.NoError(t, err) - networkSrvc, err := createNetworkService(cfg, stateSrvc) - require.NoError(t, err) - require.NotNil(t, networkSrvc) + type args struct { + st *state.Service + } + tests := []struct { + name string + args args + want *digest.Handler + err error + }{ + { + name: "working example", + args: args{st: stateSrvc}, + want: &digest.Handler{}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := ni.createDigestHandler(tt.args.st) + if tt.err != nil { + assert.EqualError(t, err, tt.err.Error()) + } else { + assert.NoError(t, err) + } + + if tt.want != nil { + assert.NotNil(t, got) + } else { + assert.Nil(t, got) + } + }) + } } -// TestCreateRPCService tests the createRPCService method -func TestCreateRPCService(t *testing.T) { +func Test_createGRANDPAService(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + cfg := NewTestConfig(t) - require.NotNil(t, cfg) genFile := NewTestGenesisRawFile(t, cfg) - require.NotNil(t, genFile) defer utils.RemoveTestDir(t) - cfg.Core.Roles = types.FullNodeRole - cfg.Core.BabeAuthority = false - cfg.Core.GrandpaAuthority = false + cfg.Core.Roles = types.AuthorityRole cfg.Init.Genesis = genFile.Name() - err := InitNode(cfg) + ks := keystore.NewGlobalKeystore() + kr, err := keystore.NewEd25519Keyring() require.NoError(t, err) + ks.Gran.Insert(kr.Alice()) - stateSrvc, err := createStateService(cfg) - require.NoError(t, err) + type args struct { + cfg *Config + st *state.Service + dh *digest.Handler + ks keystore.Keystore + net *network.Service + } + tests := []struct { + name string + args args + want *grandpa.Service + err error + }{ + { + name: "invalid key type test", + args: args{ + cfg: cfg, + ks: ks.Babe, + }, + err: errors.New("invalid keystore type"), + }, + { + name: "working example", + args: args{ + cfg: cfg, + ks: ks.Gran, + }, + want: &grandpa.Service{}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mockNodeIface := NewMocknewNodeIface(ctrl) + mockNodeIface.EXPECT().createGRANDPAService(tt.args.cfg, nil, nil, tt.args.ks, + nil).DoAndReturn(func(cfg *Config, st *state.Service, dh *digest.Handler, ks keystore.Keystore, + net *network.Service) (*grandpa.Service, error) { + if ks.Name() != "gran" || ks.Type() != crypto.Ed25519Type { + return nil, ErrInvalidKeystoreType + } + return &grandpa.Service{}, nil + }) + got, err := mockNodeIface.createGRANDPAService(tt.args.cfg, tt.args.st, tt.args.dh, tt.args.ks, tt.args.net) + if tt.err != nil { + assert.EqualError(t, err, tt.err.Error()) + } else { + assert.NoError(t, err) + } + + if tt.want != nil { + assert.NotNil(t, got) + } else { + assert.Nil(t, got) + } + }) + } +} - networkSrvc := &network.Service{} +func Test_createNetworkService(t *testing.T) { + cfg := NewTestConfig(t) - ks := keystore.NewGlobalKeystore() - ed25519Keyring, _ := keystore.NewEd25519Keyring() - ks.Gran.Insert(ed25519Keyring.Alice()) + genFile := NewTestGenesisRawFile(t, cfg) - ns, err := createRuntimeStorage(stateSrvc) - require.NoError(t, err) - err = loadRuntime(cfg, ns, stateSrvc, ks, networkSrvc) - require.NoError(t, err) + defer utils.RemoveTestDir(t) - dh, err := createDigestHandler(stateSrvc) - require.NoError(t, err) + cfg.Init.Genesis = genFile.Name() - coreSrvc, err := createCoreService(cfg, ks, stateSrvc, networkSrvc, dh) + ni := nodeInterface{} + err := ni.initNode(cfg) require.NoError(t, err) - sysSrvc, err := createSystemService(&cfg.System, stateSrvc) + stateSrvc, err := ni.createStateService(cfg) require.NoError(t, err) - rpcSrvc, err := createRPCService(cfg, ns, stateSrvc, coreSrvc, networkSrvc, nil, sysSrvc, nil) - require.NoError(t, err) - require.NotNil(t, rpcSrvc) + type args struct { + cfg *Config + stateSrvc *state.Service + } + tests := []struct { + name string + args args + want *network.Service + err error + }{ + { + name: "working example", + args: args{ + cfg: cfg, + stateSrvc: stateSrvc, + }, + want: &network.Service{}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := ni.createNetworkService(tt.args.cfg, tt.args.stateSrvc) + if tt.err != nil { + assert.EqualError(t, err, tt.err.Error()) + } else { + assert.NoError(t, err) + } + + if tt.want != nil { + assert.NotNil(t, got) + } else { + assert.Nil(t, got) + } + }) + } } -func TestCreateBABEService(t *testing.T) { +func Test_createRPCService(t *testing.T) { cfg := NewTestConfig(t) - require.NotNil(t, cfg) genFile := NewTestGenesisRawFile(t, cfg) - require.NotNil(t, genFile) defer utils.RemoveTestDir(t) cfg.Core.Roles = types.FullNodeRole + cfg.Core.BabeAuthority = false + cfg.Core.GrandpaAuthority = false cfg.Init.Genesis = genFile.Name() - err := InitNode(cfg) + ni := nodeInterface{} + err := ni.initNode(cfg) require.NoError(t, err) - stateSrvc, err := createStateService(cfg) + stateSrvc, err := ni.createStateService(cfg) require.NoError(t, err) - ks := keystore.NewGlobalKeystore() - kr, err := keystore.NewSr25519Keyring() - require.NoError(t, err) - ks.Babe.Insert(kr.Alice()) + type args struct { + cfg *Config + ns *runtime.NodeStorage + stateSrvc *state.Service + coreSrvc *core.Service + networkSrvc *network.Service + bp modules.BlockProducerAPI + sysSrvc *system.Service + finSrvc *grandpa.Service + } + tests := []struct { + name string + args args + want *rpc.HTTPServer + err error + }{ + { + name: "working example", + args: args{ + cfg: cfg, + stateSrvc: stateSrvc, + }, + want: &rpc.HTTPServer{}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := ni.createRPCService(tt.args.cfg, tt.args.ns, tt.args.stateSrvc, tt.args.coreSrvc, + tt.args.networkSrvc, tt.args.bp, tt.args.sysSrvc, tt.args.finSrvc) + if tt.err != nil { + assert.EqualError(t, err, tt.err.Error()) + } else { + assert.NoError(t, err) + } + + if tt.want != nil { + assert.NotNil(t, got) + } else { + assert.Nil(t, got) + } + }) + } +} - ns, err := createRuntimeStorage(stateSrvc) - require.NoError(t, err) - err = loadRuntime(cfg, ns, stateSrvc, ks, &network.Service{}) - require.NoError(t, err) +func Test_createRuntime(t *testing.T) { + cfg := NewTestConfig(t) - dh, err := createDigestHandler(stateSrvc) - require.NoError(t, err) + genFile := NewTestGenesisRawFile(t, cfg) - coreSrvc, err := createCoreService(cfg, ks, stateSrvc, &network.Service{}, dh) + defer utils.RemoveTestDir(t) + + cfg.Init.Genesis = genFile.Name() + + ni := nodeInterface{} + err := ni.initNode(cfg) require.NoError(t, err) - bs, err := createBABEService(cfg, stateSrvc, ks.Babe, coreSrvc) + stateSrvc, err := ni.createStateService(cfg) require.NoError(t, err) - require.NotNil(t, bs) + + _ = wasmer.NewTestInstance(t, runtime.NODE_RUNTIME) + runtimeFilePath := runtime.GetAbsolutePath(runtime.NODE_RUNTIME_FP) + + runtimeData, err := ioutil.ReadFile(filepath.Clean(runtimeFilePath)) + require.Nil(t, err) + + type args struct { + cfg *Config + ns runtime.NodeStorage + st *state.Service + ks *keystore.GlobalKeystore + net *network.Service + code []byte + } + tests := []struct { + name string + args args + want bool + err error + }{ + { + name: "empty code test", + args: args{ + cfg: cfg, + st: stateSrvc, + }, + err: errors.New("failed to create runtime executor: code is empty"), + }, + { + name: "bad code test", + args: args{ + cfg: cfg, + st: stateSrvc, + code: []byte(`fake code`), + }, + err: errors.New("failed to create runtime executor: Failed to instantiate the module:\n compile error" + + ": Validation error \"Bad magic number\""), + }, + { + name: "working example", + args: args{ + cfg: cfg, + st: stateSrvc, + code: runtimeData, + }, + want: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := createRuntime(tt.args.cfg, tt.args.ns, tt.args.st, tt.args.ks, tt.args.net, tt.args.code) + if tt.err != nil { + assert.EqualError(t, err, tt.err.Error()) + } else { + assert.NoError(t, err) + } + + if tt.want { + assert.NotNil(t, got) + } else { + assert.Nil(t, got) + } + }) + } } -func TestCreateGrandpaService(t *testing.T) { +func Test_createRuntimeStorage(t *testing.T) { cfg := NewTestConfig(t) - require.NotNil(t, cfg) genFile := NewTestGenesisRawFile(t, cfg) - require.NotNil(t, genFile) defer utils.RemoveTestDir(t) - cfg.Core.Roles = types.AuthorityRole cfg.Init.Genesis = genFile.Name() - err := InitNode(cfg) + ni := nodeInterface{} + err := ni.initNode(cfg) require.NoError(t, err) - stateSrvc, err := createStateService(cfg) + stateSrvc, err := ni.createStateService(cfg) require.NoError(t, err) - ks := keystore.NewGlobalKeystore() - kr, err := keystore.NewEd25519Keyring() - require.NoError(t, err) - ks.Gran.Insert(kr.Alice()) + type args struct { + st *state.Service + } + tests := []struct { + name string + args args + want *runtime.NodeStorage + err error + }{ + { + name: "working example", + args: args{st: stateSrvc}, + want: &runtime.NodeStorage{}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := ni.createRuntimeStorage(tt.args.st) + if tt.err != nil { + assert.EqualError(t, err, tt.err.Error()) + } else { + assert.NoError(t, err) + } + + if tt.want != nil { + assert.NotNil(t, got) + } else { + assert.Nil(t, got) + } + }) + } +} - ns, err := createRuntimeStorage(stateSrvc) - require.NoError(t, err) +func Test_createStateService(t *testing.T) { + cfg := NewTestConfig(t) - err = loadRuntime(cfg, ns, stateSrvc, ks, &network.Service{}) - require.NoError(t, err) + genFile := NewTestGenesisRawFile(t, cfg) - dh, err := createDigestHandler(stateSrvc) - require.NoError(t, err) + defer utils.RemoveTestDir(t) - networkSrvc, err := createNetworkService(cfg, stateSrvc) - require.NoError(t, err) + cfg.Init.Genesis = genFile.Name() - gs, err := createGRANDPAService(cfg, stateSrvc, dh, ks.Gran, networkSrvc) + ni := nodeInterface{} + err := ni.initNode(cfg) require.NoError(t, err) - require.NotNil(t, gs) -} -var addr = flag.String("addr", "localhost:8546", "http service address") -var testCalls = []struct { - call []byte - expected []byte -}{ - { - call: []byte(`{"jsonrpc":"2.0","method":"system_name","params":[],"id":1}`), - expected: []byte(`{"id":1,"jsonrpc":"2.0","result":"gossamer"}` + "\n")}, // working request - { - call: []byte(`{"jsonrpc":"2.0","method":"unknown","params":[],"id":2}`), - // unknown method - expected: []byte(`{"error":{"code":-32000,"data":null,` + - `"message":"rpc error method unknown not found"},"id":2,` + - `"jsonrpc":"2.0"}` + "\n")}, - { - call: []byte{}, - // empty request - expected: []byte(`{"jsonrpc":"2.0","error":{"code":-32600,` + - `"message":"Invalid request"},"id":0}` + "\n")}, - { - call: []byte(`{"jsonrpc":"2.0","method":"chain_subscribeNewHeads","params":[],"id":3}`), - expected: []byte(`{"jsonrpc":"2.0","result":1,"id":3}` + "\n")}, - { - call: []byte(`{"jsonrpc":"2.0","method":"state_subscribeStorage","params":[],"id":4}`), - expected: []byte(`{"jsonrpc":"2.0","result":2,"id":4}` + "\n")}, + cfg2 := NewTestConfig(t) + cfg2.Global.BasePath = "test_data" + + type args struct { + cfg *Config + } + tests := []struct { + name string + args args + want *state.Service + err error + }{ + { + name: "working example", + args: args{cfg: cfg}, + want: &state.Service{}, + }, + { + name: "broken config test", + args: args{cfg: cfg2}, + err: errors.New("failed to 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 := ni.createStateService(tt.args.cfg) + if tt.err != nil { + assert.EqualError(t, err, tt.err.Error()) + } else { + assert.NoError(t, err) + } + + if tt.want != nil { + assert.NotNil(t, got) + } else { + assert.Nil(t, got) + } + }) + } } -func TestNewWebSocketServer(t *testing.T) { +func Test_createSystemService(t *testing.T) { cfg := NewTestConfig(t) - require.NotNil(t, cfg) genFile := NewTestGenesisRawFile(t, cfg) - require.NotNil(t, genFile) defer utils.RemoveTestDir(t) - cfg.Core.Roles = types.FullNodeRole - cfg.Core.BabeAuthority = false - cfg.Core.GrandpaAuthority = false cfg.Init.Genesis = genFile.Name() - cfg.RPC.External = false - cfg.RPC.WS = true - cfg.RPC.WSExternal = false - cfg.System.SystemName = "gossamer" - err := InitNode(cfg) - require.Nil(t, err) - - stateSrvc, err := createStateService(cfg) - require.Nil(t, err) - - networkSrvc := &network.Service{} - - ks := keystore.NewGlobalKeystore() - ed25519Keyring, _ := keystore.NewEd25519Keyring() - ks.Gran.Insert(ed25519Keyring.Alice()) - - ns, err := createRuntimeStorage(stateSrvc) - require.NoError(t, err) - err = loadRuntime(cfg, ns, stateSrvc, ks, networkSrvc) + ni := nodeInterface{} + err := ni.initNode(cfg) require.NoError(t, err) - dh, err := createDigestHandler(stateSrvc) + stateSrvc, err := ni.createStateService(cfg) require.NoError(t, err) - coreSrvc, err := createCoreService(cfg, ks, stateSrvc, networkSrvc, dh) - require.Nil(t, err) + type args struct { + cfg *types.SystemInfo + stateSrvc *state.Service + } + tests := []struct { + name string + args args + want *system.Service + err error + }{ + { + name: "working example", + args: args{ + stateSrvc: stateSrvc, + }, + want: &system.Service{}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := ni.createSystemService(tt.args.cfg, tt.args.stateSrvc) + if tt.err != nil { + assert.EqualError(t, err, tt.err.Error()) + } else { + assert.NoError(t, err) + } + + if tt.want != nil { + assert.NotNil(t, got) + } else { + assert.Nil(t, got) + } + }) + } +} - sysSrvc, err := createSystemService(&cfg.System, stateSrvc) - require.NoError(t, err) +func Test_newInMemoryDB(t *testing.T) { + type args struct { + path string + } + tests := []struct { + name string + args args + want bool + err error + }{ + { + name: "working example", + args: args{path: "test_data"}, + want: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := newInMemoryDB(tt.args.path) + if tt.err != nil { + assert.EqualError(t, err, tt.err.Error()) + } else { + assert.NoError(t, err) + } + + if tt.want { + assert.NotNil(t, got) + } else { + assert.Nil(t, got) + } + }) + } +} - rpcSrvc, err := createRPCService(cfg, ns, stateSrvc, coreSrvc, networkSrvc, nil, sysSrvc, nil) - require.NoError(t, err) - err = rpcSrvc.Start() - require.Nil(t, err) +func Test_newSyncService(t *testing.T) { + cfg := NewTestConfig(t) + + genFile := NewTestGenesisFile(t, cfg) - time.Sleep(time.Second) // give server a second to start + defer utils.RemoveTestDir(t) - u := url.URL{Scheme: "ws", Host: *addr, Path: "/"} + cfg.Init.Genesis = genFile.Name() - c, _, err := websocket.DefaultDialer.Dial(u.String(), nil) + ni := nodeInterface{} + err := ni.initNode(cfg) require.NoError(t, err) - defer c.Close() - for _, item := range testCalls { - err = c.WriteMessage(websocket.TextMessage, item.call) - require.Nil(t, err) + stateSrvc, err := ni.createStateService(cfg) + require.NoError(t, err) - _, message, err := c.ReadMessage() - require.Nil(t, err) - require.Equal(t, item.expected, message) + type args struct { + cfg *Config + st *state.Service + fg sync.FinalityGadget + verifier *babe.VerificationManager + cs *core.Service + net *network.Service + } + tests := []struct { + name string + args args + want *sync.Service + err error + }{ + { + name: "missing FinalityGadget test", + args: args{ + cfg: cfg, + st: stateSrvc, + }, + err: errors.New("cannot have nil FinalityGadget"), + }, + { + name: "working example", + args: args{ + cfg: cfg, + st: stateSrvc, + fg: &grandpa.Service{}, + }, + want: &sync.Service{}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := ni.newSyncService(tt.args.cfg, tt.args.st, tt.args.fg, tt.args.verifier, tt.args.cs, tt.args.net) + if tt.err != nil { + assert.EqualError(t, err, tt.err.Error()) + } else { + assert.NoError(t, err) + } + + if tt.want != nil { + assert.NotNil(t, got) + } else { + assert.Nil(t, got) + } + }) } } diff --git a/dot/utils_integration_test.go b/dot/utils_integration_test.go new file mode 100644 index 0000000000..baa78183ab --- /dev/null +++ b/dot/utils_integration_test.go @@ -0,0 +1,198 @@ +//go:build integration +// +build integration + +// Copyright 2020 ChainSafe Systems (ON) Corp. +// This file is part of gossamer. +// +// The gossamer library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The gossamer library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the gossamer library. If not, see . + +package dot + +import ( + "log" + "os" + "runtime" + "testing" + "time" + + "github.com/ChainSafe/gossamer/lib/genesis" + "github.com/ChainSafe/gossamer/lib/trie" + "github.com/ChainSafe/gossamer/lib/utils" + "github.com/stretchr/testify/require" +) + +// TestNewConfig tests the NewTestConfig method +func TestNewConfig(t *testing.T) { + cfg := NewTestConfig(t) + defer utils.RemoveTestDir(t) + require.NotNil(t, cfg) +} + +// TestNewConfigAndFile tests the NewTestConfigWithFile method +func TestNewConfigAndFile(t *testing.T) { + testCfg, testCfgFile := NewTestConfigWithFile(t) + defer utils.RemoveTestDir(t) + require.NotNil(t, testCfg) + require.NotNil(t, testCfgFile) +} + +// TestInitNode +func TestNewTestGenesis_Integration(t *testing.T) { + cfg := NewTestConfig(t) + require.NotNil(t, cfg) + + genFile := NewTestGenesisRawFile(t, cfg) + require.NotNil(t, genFile) + + defer utils.RemoveTestDir(t) + + cfg.Init.Genesis = genFile.Name() +} + +func TestNewTestGenesisFile_Integration(t *testing.T) { + cfg := NewTestConfig(t) + require.NotNil(t, cfg) + + genHRFile := NewTestGenesisFile(t, cfg) + require.NotNil(t, genHRFile) + defer os.Remove(genHRFile.Name()) + + genRawFile := NewTestGenesisRawFile(t, cfg) + require.NotNil(t, genRawFile) + defer os.Remove(genRawFile.Name()) + + genHR, err := genesis.NewGenesisFromJSON(genHRFile.Name(), 0) + require.NoError(t, err) + genRaw, err := genesis.NewGenesisFromJSONRaw(genRawFile.Name()) + require.NoError(t, err) + + // values from raw genesis file should equal values generated from human readable genesis file + require.Equal(t, genRaw.Genesis.Raw["top"], genHR.Genesis.Raw["top"]) +} + +func TestDeepCopyVsSnapshot(t *testing.T) { + cfg := NewTestConfig(t) + require.NotNil(t, cfg) + + genRawFile := NewTestGenesisRawFile(t, cfg) + require.NotNil(t, genRawFile) + + defer os.Remove(genRawFile.Name()) + + genRaw, err := genesis.NewGenesisFromJSONRaw(genRawFile.Name()) + require.NoError(t, err) + + tri := trie.NewEmptyTrie() + var ttlLenght int + for k, v := range genRaw.Genesis.Raw["top"] { + val := []byte(v) + ttlLenght += len(val) + tri.Put([]byte(k), val) + } + + testCases := []struct { + name string + fn func(tri *trie.Trie) (*trie.Trie, error) + }{ + {"DeepCopy", func(tri *trie.Trie) (*trie.Trie, error) { + return tri.DeepCopy() + }}, + {"Snapshot", func(tri *trie.Trie) (*trie.Trie, error) { + return tri.Snapshot(), nil + }}, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + trieMap := make(map[int]*trie.Trie) + start := time.Now() + var m runtime.MemStats + for i := 0; i <= 200; i++ { + newTrie, err := tc.fn(tri) + require.NoError(t, err) + + runtime.ReadMemStats(&m) + trieMap[i] = newTrie + } + + log.Printf("\nAlloc = %v MB \nTotalAlloc = %v MB \nSys = %v MB \nNumGC = %v \n\n", m.Alloc/(1024*1024), + m.TotalAlloc/(1024*1024), m.Sys/(1024*1024), m.NumGC) + elapsed := time.Since(start) + log.Printf("DeepCopy to trie took %s", elapsed) + runtime.GC() + }) + } +} + +func TestTrieSnapshot(t *testing.T) { + cfg := NewTestConfig(t) + require.NotNil(t, cfg) + + genRawFile := NewTestGenesisRawFile(t, cfg) + require.NotNil(t, genRawFile) + + defer os.Remove(genRawFile.Name()) + + genRaw, err := genesis.NewGenesisFromJSONRaw(genRawFile.Name()) + require.NoError(t, err) + + tri := trie.NewEmptyTrie() + key := []byte("key") + value := []byte("value") + + for k, v := range genRaw.Genesis.Raw["top"] { + val := []byte(v) + tri.Put([]byte(k), val) + } + + // DeepCopy the trie. + dcTrie, err := tri.DeepCopy() + require.NoError(t, err) + + // Take Snapshot of the trie. + newTrie := tri.Snapshot() + + // Get the Trie root hash for all the 3 tries. + tHash, err := tri.Hash() + require.NoError(t, err) + + dcTrieHash, err := dcTrie.Hash() + require.NoError(t, err) + + newTrieHash, err := newTrie.Hash() + require.NoError(t, err) + + // Root hash for the 3 tries should be equal. + require.Equal(t, tHash, dcTrieHash) + require.Equal(t, tHash, newTrieHash) + + // Modify the current trie. + value[0] = 'w' + newTrie.Put(key, value) + + // Get the updated root hash of all tries. + tHash, err = tri.Hash() + require.NoError(t, err) + + dcTrieHash, err = dcTrie.Hash() + require.NoError(t, err) + + newTrieHash, err = newTrie.Hash() + require.NoError(t, err) + + // Only the current trie should have a different root hash since it is updated. + require.NotEqual(t, newTrieHash, dcTrieHash) + require.NotEqual(t, newTrieHash, tHash) + require.Equal(t, dcTrieHash, tHash) +} diff --git a/dot/utils_test.go b/dot/utils_test.go index 1d66a46efc..0ce7bd8f24 100644 --- a/dot/utils_test.go +++ b/dot/utils_test.go @@ -4,179 +4,426 @@ package dot import ( - "log" "os" - "runtime" + "strings" "testing" - "time" + ctoml "github.com/ChainSafe/gossamer/dot/config/toml" + "github.com/ChainSafe/gossamer/dot/types" "github.com/ChainSafe/gossamer/lib/genesis" - "github.com/ChainSafe/gossamer/lib/trie" - "github.com/ChainSafe/gossamer/lib/utils" - "github.com/stretchr/testify/require" + "github.com/stretchr/testify/assert" ) -// TestNewConfig tests the NewTestConfig method -func TestNewConfig(t *testing.T) { - cfg := NewTestConfig(t) - defer utils.RemoveTestDir(t) - require.NotNil(t, cfg) +func TestCreateJSONRawFile(t *testing.T) { + type args struct { + bs *BuildSpec + fp string + } + tests := []struct { + name string + args args + want *os.File + }{ + { + name: "working example", + args: args{ + bs: &BuildSpec{genesis: NewTestGenesis(t)}, + fp: "test_data/test.json", + }, + want: &os.File{}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := CreateJSONRawFile(tt.args.bs, tt.args.fp) + if tt.want != nil { + assert.NotNil(t, got) + } + }) + } } -// TestNewConfigAndFile tests the NewTestConfigWithFile method -func TestNewConfigAndFile(t *testing.T) { - testCfg, testCfgFile := NewTestConfigWithFile(t) - defer utils.RemoveTestDir(t) - require.NotNil(t, testCfg) - require.NotNil(t, testCfgFile) +func TestExportConfig(t *testing.T) { + type args struct { + cfg *Config + fp string + } + tests := []struct { + name string + args args + want *os.File + }{ + { + name: "working example", + args: args{ + cfg: &Config{}, + fp: "test_data/test.json", + }, + want: &os.File{}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := ExportConfig(tt.args.cfg, tt.args.fp) + if tt.want != nil { + assert.NotNil(t, got) + } + }) + } } -// TestInitNode -func TestNewTestGenesis(t *testing.T) { - cfg := NewTestConfig(t) - require.NotNil(t, cfg) - - genFile := NewTestGenesisRawFile(t, cfg) - require.NotNil(t, genFile) - - defer utils.RemoveTestDir(t) - - cfg.Init.Genesis = genFile.Name() +func TestExportTomlConfig(t *testing.T) { + type args struct { + cfg *ctoml.Config + fp string + } + tests := []struct { + name string + args args + want *os.File + }{ + { + name: "working example", + args: args{ + cfg: &ctoml.Config{}, + fp: "test_data/test.json", + }, + want: &os.File{}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := ExportTomlConfig(tt.args.cfg, tt.args.fp) + if tt.want != nil { + assert.NotNil(t, got) + } + }) + } } -func TestNewTestGenesisFile(t *testing.T) { - cfg := NewTestConfig(t) - require.NotNil(t, cfg) - - genHRFile := NewTestGenesisFile(t, cfg) - require.NotNil(t, genHRFile) - defer os.Remove(genHRFile.Name()) - - genRawFile := NewTestGenesisRawFile(t, cfg) - require.NotNil(t, genRawFile) - defer os.Remove(genRawFile.Name()) - - genHR, err := genesis.NewGenesisFromJSON(genHRFile.Name(), 0) - require.NoError(t, err) - genRaw, err := genesis.NewGenesisFromJSONRaw(genRawFile.Name()) - require.NoError(t, err) - - // values from raw genesis file should equal values generated from human readable genesis file - require.Equal(t, genRaw.Genesis.Raw["top"], genHR.Genesis.Raw["top"]) +func TestNewTestConfig(t *testing.T) { + 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: "test_data/TestNewTestConfig", + LogLvl: 3, + PublishMetrics: false, + MetricsPort: 0, + NoTelemetry: false, + TelemetryURLs: nil, + RetainBlocks: 0, + Pruning: "", + }, + Log: LogConfig{ + CoreLvl: 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) + if tt.want != nil { + assert.Equal(t, tt.want, got) + assert.NotNil(t, got) + } + }) + } } -func TestDeepCopyVsSnapshot(t *testing.T) { - cfg := NewTestConfig(t) - require.NotNil(t, cfg) - - genRawFile := NewTestGenesisRawFile(t, cfg) - require.NotNil(t, genRawFile) - - defer os.Remove(genRawFile.Name()) - - genRaw, err := genesis.NewGenesisFromJSONRaw(genRawFile.Name()) - require.NoError(t, err) +func TestNewTestConfigWithFile(t *testing.T) { + type args struct { + t *testing.T + } + tests := []struct { + name string + args args + want *Config + want1 *os.File + }{ + { + name: "working example", + args: args{t: t}, + want: &Config{ + Global: GlobalConfig{ + Name: "Gossamer", + ID: "gssmr", + BasePath: "test_data/TestNewTestConfigWithFile", + LogLvl: 3, + PublishMetrics: false, + MetricsPort: 0, + NoTelemetry: false, + TelemetryURLs: nil, + RetainBlocks: 0, + Pruning: "", + }, + Log: LogConfig{ + CoreLvl: 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{}, + }, + want1: &os.File{}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, got1 := NewTestConfigWithFile(tt.args.t) - tri := trie.NewEmptyTrie() - var ttlLenght int - for k, v := range genRaw.Genesis.Raw["top"] { - val := []byte(v) - ttlLenght += len(val) - tri.Put([]byte(k), val) + assert.Equal(t, tt.want, got) + if tt.want1 != nil { + assert.NotNil(t, got1) + } + }) } +} - testCases := []struct { +func TestNewTestGenesis(t *testing.T) { + type args struct { + t *testing.T + } + tests := []struct { name string - fn func(tri *trie.Trie) (*trie.Trie, error) + args args + want *genesis.Genesis }{ - {"DeepCopy", func(tri *trie.Trie) (*trie.Trie, error) { - return tri.DeepCopy() - }}, - {"Snapshot", func(tri *trie.Trie) (*trie.Trie, error) { - return tri.Snapshot(), nil - }}, + { + name: "working example", + args: args{t: t}, + want: &genesis.Genesis{}, + }, } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - trieMap := make(map[int]*trie.Trie) - start := time.Now() - var m runtime.MemStats - for i := 0; i <= 200; i++ { - newTrie, err := tc.fn(tri) - require.NoError(t, err) - - runtime.ReadMemStats(&m) - trieMap[i] = newTrie + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := NewTestGenesis(tt.args.t) + if tt.want != nil { + assert.NotNil(t, got) } - - log.Printf("\nAlloc = %v MB \nTotalAlloc = %v MB \nSys = %v MB \nNumGC = %v \n\n", - m.Alloc/(1024*1024), m.TotalAlloc/(1024*1024), m.Sys/(1024*1024), m.NumGC) - elapsed := time.Since(start) - log.Printf("DeepCopy to trie took %s", elapsed) - runtime.GC() }) } } -func TestTrieSnapshot(t *testing.T) { - cfg := NewTestConfig(t) - require.NotNil(t, cfg) - - genRawFile := NewTestGenesisRawFile(t, cfg) - require.NotNil(t, genRawFile) - - defer os.Remove(genRawFile.Name()) - - genRaw, err := genesis.NewGenesisFromJSONRaw(genRawFile.Name()) - require.NoError(t, err) - - tri := trie.NewEmptyTrie() - key := []byte("key") - value := []byte("value") - - for k, v := range genRaw.Genesis.Raw["top"] { - val := []byte(v) - tri.Put([]byte(k), val) +func TestNewTestGenesisAndRuntime(t *testing.T) { + type args struct { + t *testing.T } + tests := []struct { + name string + args args + want string + }{ + { + name: "working example", + args: args{t: t}, + want: "", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := NewTestGenesisAndRuntime(tt.args.t) + assert.True(t, strings.HasPrefix(got, "test_data/TestNewTestGenesisAndRuntime/genesis")) + }) + } +} - // DeepCopy the trie. - dcTrie, err := tri.DeepCopy() - require.NoError(t, err) - - // Take Snapshot of the trie. - newTrie := tri.Snapshot() - - // Get the Trie root hash for all the 3 tries. - tHash, err := tri.Hash() - require.NoError(t, err) - - dcTrieHash, err := dcTrie.Hash() - require.NoError(t, err) - - newTrieHash, err := newTrie.Hash() - require.NoError(t, err) - - // Root hash for the 3 tries should be equal. - require.Equal(t, tHash, dcTrieHash) - require.Equal(t, tHash, newTrieHash) - - // Modify the current trie. - value[0] = 'w' - newTrie.Put(key, value) - - // Get the updated root hash of all tries. - tHash, err = tri.Hash() - require.NoError(t, err) +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) + } + }) + } +} - dcTrieHash, err = dcTrie.Hash() - require.NoError(t, err) +func TestNewTestGenesisRawFile(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 := NewTestGenesisRawFile(tt.args.t, tt.args.cfg) + if tt.want != nil { + assert.NotNil(t, got) + } + }) + } +} - newTrieHash, err = newTrie.Hash() - require.NoError(t, err) +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.Greater(t, len(got), 3) + }) + } +} - // Only the current trie should have a different root hash since it is updated. - require.NotEqual(t, newTrieHash, dcTrieHash) - require.NotEqual(t, newTrieHash, tHash) - require.Equal(t, dcTrieHash, tHash) +func TestWriteConfig(t *testing.T) { + type args struct { + data []byte + fp string + } + tests := []struct { + name string + args args + want *os.File + }{ + { + name: "working example", + args: args{ + data: nil, + fp: "test_data/test.json", + }, + want: &os.File{}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := WriteConfig(tt.args.data, tt.args.fp) + if tt.want != nil { + assert.NotNil(t, got) + } + }) + } } diff --git a/lib/babe/verify.go b/lib/babe/verify.go index 15fd8c8591..426bf4c6fc 100644 --- a/lib/babe/verify.go +++ b/lib/babe/verify.go @@ -7,8 +7,10 @@ import ( "errors" "fmt" "math/big" + "reflect" "sync" + "github.com/ChainSafe/gossamer/dot/state" "github.com/ChainSafe/gossamer/dot/types" "github.com/ChainSafe/gossamer/lib/common" "github.com/ChainSafe/gossamer/lib/crypto/sr25519" @@ -47,11 +49,25 @@ type VerificationManager struct { // NewVerificationManager returns a new NewVerificationManager func NewVerificationManager(blockState BlockState, epochState EpochState) (*VerificationManager, error) { - if blockState == nil { + var isNilBlock bool + switch blockState.(type) { + case *state.BlockState: + isNilBlock = blockState == (*state.BlockState)(nil) + default: + isNilBlock = reflect.ValueOf(blockState).IsNil() + } + if isNilBlock { return nil, errNilBlockState } - if epochState == nil { + var isNilEpoch bool + switch epochState.(type) { + case *state.EpochState: + isNilEpoch = epochState == (*state.EpochState)(nil) + default: + isNilEpoch = reflect.ValueOf(epochState).IsNil() + } + if isNilEpoch { return nil, errNilEpochState } diff --git a/lib/blocktree/blocktree.go b/lib/blocktree/blocktree.go index f3aaf88da7..73f20f1587 100644 --- a/lib/blocktree/blocktree.go +++ b/lib/blocktree/blocktree.go @@ -403,7 +403,7 @@ func (bt *BlockTree) StoreRuntime(hash common.Hash, in runtime.Instance) { // GetBlockRuntime returns block runtime for corresponding block hash. func (bt *BlockTree) GetBlockRuntime(hash common.Hash) (runtime.Instance, error) { ins, ok := bt.runtime.Load(hash) - if !ok { + if !ok || ins == nil { return nil, ErrFailedToGetRuntime } return ins.(runtime.Instance), nil