Skip to content

Commit

Permalink
feat(internal/database): replace chaindb/badgerdb with pebbledb (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
EclesioMeloJunior authored Aug 16, 2023
1 parent d35b128 commit 344461d
Show file tree
Hide file tree
Showing 63 changed files with 1,141 additions and 390 deletions.
15 changes: 10 additions & 5 deletions cmd/gossamer/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ cd gossamer
### Compile

To put the binary in ./bin, run:

```bash
make build
```
Expand Down Expand Up @@ -62,7 +63,7 @@ The node configuration can be modified in the `config.toml` file.
### Start the node

```bash
gossamer --basepath /tmp/gossamer --key alice
gossamer --basepath /tmp/gossamer --key alice
```

**Note: The `init` command is optional. If the node is not initialised, it will be initialised with the default configuration.**
Expand Down Expand Up @@ -108,19 +109,22 @@ This subcommand provides capabilities that are similar to
[Parity's Subkey utility](https://docs.substrate.io/v3/tools/subkey).

The account command supports following arguments:

- `generate` - generates a new key pair; specify `--scheme ed25519`, `--scheme secp256k1`, or `--scheme sr25519` (default)
- `list` - lists the keys in the Gossamer keystore
- `import` - imports a key from a keystore file
- `import-raw` - imports a raw key from a keystore file

Supported flags:

- `keystore-path` - path to the Gossamer keystore
- `keystore-file` - path to the keystore file
- `chain` - path to the human-readable chain-spec file
- `--scheme` - `ed25519`, `secp256k1`, or `sr25519` (default)
- `--password` - allows the user to provide a password to either encrypt a generated key or unlock the Gossamer keystore

Examples:

- `gossamer account generate --scheme ed25519` - generates an `ed25519` key pair
- `gossamer account list` - lists the keys in the Gossamer keystore
- `gossamer account import --keystore-file keystore.json` - imports a key from a keystore file
Expand All @@ -145,6 +149,7 @@ represent the Gossamer default configuration.
- `--output-path` - path to the file where the compiled chain-spec should be written

Examples:

- `gossamer build-spec --chain chain-spec.json --output-path compiled-chain-spec.json` - compiles a human-readable
chain-spec into a format that Gossamer can consume
- `gossamer build-spec --chain chain-spec.json --raw --output-path compiled-chain-spec.json` - compiles a human-readable
Expand All @@ -166,6 +171,7 @@ of a JSON file. The input for this subcommand can be retrieved from
- `--chain` - path to the human-readable chain-spec file

Examples:

- `gossamer import-state --first-slot 1 --header header.json --state state.json --chain chain-spec.json` - seeds Gossamer
storage with key-value pairs from a JSON file

Expand All @@ -185,9 +191,8 @@ What follows is a list that describes the services and capabilities that inform

#### State

This service is a wrapper around an instance of [`chaindb`](https://github.com/ChainSafe/chaindb), a key-value database
that is built on top of [BadgerDB](https://github.com/dgraph-io/badger) from [Dgraph](https://dgraph.io/). The state
service provides storage capabilities for the other Gossamer services - each service is assigned a prefix that is added
This service is a wrapper around an instance of [`pebble`](https://github.com/cockroachdb/pebble), a LevelDB/RocksDB inspired key-value database.
The state service provides storage capabilities for the other Gossamer services - each service is assigned a prefix that is added
to its storage keys. The state service is defined in [dot/state/service.go](../../dot/state/service.go).

#### Network
Expand Down Expand Up @@ -271,4 +276,4 @@ capabilities are defined in the [dot/telemetry](../../dot/telemetry) package and
The default listening address for Prometheus metrics is `localhost:9876`, and Gossamer allows the user to configure this parameter with the
`--metrics-address` command-line parameter. The Gossamer telemetry server publishes telemetry data that is compatible with
[Polkadot Telemetry](https://github.com/paritytech/substrate-telemetry) and
[its helpful UI](https://telemetry.polkadot.io/).
[its helpful UI](https://telemetry.polkadot.io/).
7 changes: 6 additions & 1 deletion cmd/gossamer/commands/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,12 @@ func execInit(cmd *cobra.Command) error {
return fmt.Errorf("failed to get --force: %s", err)
}

if dot.IsNodeInitialised(config.BasePath) {
isInitialised, err := dot.IsNodeInitialised(config.BasePath)
if err != nil {
return fmt.Errorf("checking if node is initialised: %w", err)
}

if isInitialised {
// prompt user to confirm reinitialization
if force || confirmMessage("Are you sure you want to reinitialise the node? [Y/n]") {
logger.Info("reinitialising node at base path " + config.BasePath + "...")
Expand Down
7 changes: 6 additions & 1 deletion cmd/gossamer/commands/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -601,8 +601,13 @@ func execRoot(cmd *cobra.Command) error {
return fmt.Errorf("failed to ensure root: %s", err)
}

isInitialised, err := dot.IsNodeInitialised(config.BasePath)
if err != nil {
return fmt.Errorf("failed to check is not is initialised: %w", err)
}

// if the node is not initialised, initialise it
if !dot.IsNodeInitialised(config.BasePath) {
if !isInitialised {
if err := dot.InitNode(config); err != nil {
return fmt.Errorf("failed to initialise node: %s", err)
}
Expand Down
2 changes: 1 addition & 1 deletion dot/build_spec_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ func TestBuildFromDB(t *testing.T) {
},
}}},
{name: "invalid_db_path", path: t.TempDir(),
err: errors.New("cannot start state service: failed to create block state: cannot get block 0: Key not found")},
err: errors.New("cannot start state service: failed to create block state: cannot get block 0: pebble: not found")},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand Down
3 changes: 2 additions & 1 deletion dot/core/helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/ChainSafe/gossamer/dot/network"
"github.com/ChainSafe/gossamer/dot/state"
"github.com/ChainSafe/gossamer/dot/types"
"github.com/ChainSafe/gossamer/internal/database"
"github.com/ChainSafe/gossamer/internal/log"
"github.com/ChainSafe/gossamer/lib/common"
"github.com/ChainSafe/gossamer/lib/crypto/sr25519"
Expand Down Expand Up @@ -225,7 +226,7 @@ func NewTestService(t *testing.T, cfg *Config) *Service {
if stateSrvc != nil {
nodeStorage.BaseDB = stateSrvc.Base
} else {
nodeStorage.BaseDB, err = utils.SetupDatabase(filepath.Join(testDatadirPath, "offline_storage"), false)
nodeStorage.BaseDB, err = database.LoadDatabase(filepath.Join(testDatadirPath, "offline_storage"), false)
require.NoError(t, err)
}

Expand Down
7 changes: 4 additions & 3 deletions dot/mock_node_builder_test.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions dot/network/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,12 @@ func (s *Service) publishNetworkTelemetry(done <-chan struct{}) {

func (s *Service) sentBlockIntervalTelemetry() {
for {
select {
case <-s.ctx.Done():
return
default:
}

best, err := s.blockState.BestBlockHeader()
if err != nil {
continue
Expand Down
50 changes: 33 additions & 17 deletions dot/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"github.com/ChainSafe/gossamer/dot/system"
"github.com/ChainSafe/gossamer/dot/telemetry"
"github.com/ChainSafe/gossamer/dot/types"
"github.com/ChainSafe/gossamer/internal/database"
"github.com/ChainSafe/gossamer/internal/log"
"github.com/ChainSafe/gossamer/internal/metrics"
"github.com/ChainSafe/gossamer/lib/babe"
Expand All @@ -34,7 +35,6 @@ import (
"github.com/ChainSafe/gossamer/lib/keystore"
"github.com/ChainSafe/gossamer/lib/runtime"
"github.com/ChainSafe/gossamer/lib/services"
"github.com/ChainSafe/gossamer/lib/utils"
)

var logger = log.NewFromGlobal(log.AddContext("pkg", "dot"))
Expand All @@ -49,7 +49,7 @@ type Node struct {
}

type nodeBuilderIface interface {
isNodeInitialised(basepath string) error
isNodeInitialised(basepath string) (bool, error)
initNode(config *cfg.Config) error
createStateService(config *cfg.Config) (*state.Service, error)
createNetworkService(config *cfg.Config, stateSrvc *state.Service, telemetryMailer Telemetry) (*network.Service,
Expand Down Expand Up @@ -78,26 +78,37 @@ type nodeBuilder struct{}

// IsNodeInitialised returns true if, within the configured data directory for the
// node, the state database has been created and the genesis data can been loaded
func IsNodeInitialised(basepath string) bool {
func IsNodeInitialised(basepath string) (bool, error) {
nodeInstance := nodeBuilder{}
err := nodeInstance.isNodeInitialised(basepath)
return err == nil
return nodeInstance.isNodeInitialised(basepath)
}

// isNodeInitialised returns nil if the node is successfully initialised
// and an error otherwise.
func (*nodeBuilder) isNodeInitialised(basepath string) error {
func (*nodeBuilder) isNodeInitialised(basepath string) (bool, error) {
// check if key registry exists
registry := filepath.Join(basepath, utils.DefaultDatabaseDir, "KEYREGISTRY")
nodeDatabaseDir := filepath.Join(basepath, database.DefaultDatabaseDir)

_, err := os.Stat(registry)
if os.IsNotExist(err) {
return fmt.Errorf("cannot find key registry in database directory: %w", err)
_, err := os.Stat(nodeDatabaseDir)
if err != nil {
if os.IsNotExist(err) {
return false, nil
}
return false, err
}

db, err := utils.SetupDatabase(basepath, false)
entries, err := os.ReadDir(nodeDatabaseDir)
if err != nil {
return fmt.Errorf("cannot setup database: %w", err)
return false, fmt.Errorf("failed to read dir %s: %w", nodeDatabaseDir, err)
}

if len(entries) == 0 {
return false, nil
}

db, err := database.LoadDatabase(basepath, false)
if err != nil {
return false, fmt.Errorf("cannot setup database: %w", err)
}

defer func() {
Expand All @@ -109,10 +120,10 @@ func (*nodeBuilder) isNodeInitialised(basepath string) error {

_, err = state.NewBaseState(db).LoadGenesisData()
if err != nil {
return fmt.Errorf("cannot load genesis data in base state: %w", err)
return false, fmt.Errorf("cannot load genesis data in base state: %w", err)
}

return nil
return true, nil
}

// InitNode initialise the node with the given Config
Expand Down Expand Up @@ -204,7 +215,7 @@ func (*nodeBuilder) initNode(config *cfg.Config) error {
// LoadGlobalNodeName returns the stored global node name from database
func LoadGlobalNodeName(basepath string) (nodename string, err error) {
// initialise database using data directory
db, err := utils.SetupDatabase(basepath, false)
db, err := database.LoadDatabase(basepath, false)
if err != nil {
return "", err
}
Expand Down Expand Up @@ -242,7 +253,12 @@ func newNode(config *cfg.Config,
debug.SetGCPercent(prev)
}

if builder.isNodeInitialised(config.BasePath) != nil {
isInitialised, err := builder.isNodeInitialised(config.BasePath)
if err != nil {
return nil, fmt.Errorf("checking if node is initialised: %w", err)
}

if isInitialised {
err := builder.initNode(config)
if err != nil {
return nil, fmt.Errorf("cannot initialise node: %w", err)
Expand Down Expand Up @@ -450,7 +466,7 @@ func setupTelemetry(config *cfg.Config, genesisData *genesis.Data) (mailer Telem

// stores the global node name to reuse
func storeGlobalNodeName(name, basepath string) (err error) {
db, err := utils.SetupDatabase(basepath, false)
db, err := database.LoadDatabase(basepath, false)
if err != nil {
return err
}
Expand Down
22 changes: 15 additions & 7 deletions dot/node_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
system "github.com/ChainSafe/gossamer/dot/system"
"github.com/ChainSafe/gossamer/dot/telemetry"
"github.com/ChainSafe/gossamer/dot/types"
"github.com/ChainSafe/gossamer/internal/database"
"github.com/ChainSafe/gossamer/internal/log"
"github.com/ChainSafe/gossamer/lib/babe"
"github.com/ChainSafe/gossamer/lib/common"
Expand All @@ -35,7 +36,6 @@ import (
"github.com/ChainSafe/gossamer/lib/runtime"
wazero_runtime "github.com/ChainSafe/gossamer/lib/runtime/wazero"
"github.com/ChainSafe/gossamer/lib/trie"
"github.com/ChainSafe/gossamer/lib/utils"
gomock "github.com/golang/mock/gomock"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
Expand Down Expand Up @@ -88,7 +88,7 @@ func TestNewNode(t *testing.T) {
mockServiceRegistry.EXPECT().RegisterService(gomock.Any()).Times(8)

m := NewMocknodeBuilderIface(ctrl)
m.EXPECT().isNodeInitialised(initConfig.BasePath).Return(nil)
m.EXPECT().isNodeInitialised(initConfig.BasePath).Return(false, nil)
m.EXPECT().createStateService(initConfig).DoAndReturn(func(config *cfg.Config) (*state.Service, error) {
stateSrvc := state.NewService(stateConfig)
// create genesis from configuration file
Expand Down Expand Up @@ -214,9 +214,12 @@ func TestInitNode_Integration(t *testing.T) {
require.NoError(t, err)

// confirm database was setup
db, err := utils.SetupDatabase(config.BasePath, false)

db, err := database.LoadDatabase(config.BasePath, false)
require.NoError(t, err)
require.NotNil(t, db)
err = db.Close()
require.NoError(t, err)
}

func TestInitNode_GenesisSpec(t *testing.T) {
Expand All @@ -229,9 +232,12 @@ func TestInitNode_GenesisSpec(t *testing.T) {
err := InitNode(config)
require.NoError(t, err)
// confirm database was setup
db, err := utils.SetupDatabase(config.BasePath, false)
db, err := database.LoadDatabase(config.BasePath, false)
require.NoError(t, err)
require.NotNil(t, db)

err = db.Close()
require.NoError(t, err)
}

func TestNodeInitializedIntegration(t *testing.T) {
Expand All @@ -241,13 +247,15 @@ func TestNodeInitializedIntegration(t *testing.T) {

config.ChainSpec = genFile

result := IsNodeInitialised(config.BasePath)
result, err := IsNodeInitialised(config.BasePath)
require.NoError(t, err)
require.False(t, result)

err := InitNode(config)
err = InitNode(config)
require.NoError(t, err)

result = IsNodeInitialised(config.BasePath)
result, err = IsNodeInitialised(config.BasePath)
require.NoError(t, err)
require.True(t, result)
}

Expand Down
Loading

0 comments on commit 344461d

Please sign in to comment.