Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

refactor(server/v2): clean up storage use and config #22008

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
16 changes: 11 additions & 5 deletions server/v2/cometbft/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,21 @@ type CometBFTServer[T transaction.Tx] struct {

initTxCodec transaction.Codec[T]
logger log.Logger
store types.Store
serverOptions ServerOptions[T]
config Config
cfgOptions []CfgOption
}

func New[T transaction.Tx](txCodec transaction.Codec[T], serverOptions ServerOptions[T], cfgOptions ...CfgOption) *CometBFTServer[T] {
func New[T transaction.Tx](
txCodec transaction.Codec[T],
store types.Store,
serverOptions ServerOptions[T],
cfgOptions ...CfgOption,
) *CometBFTServer[T] {
return &CometBFTServer[T]{
initTxCodec: txCodec,
store: store,
serverOptions: serverOptions,
cfgOptions: cfgOptions,
}
Expand Down Expand Up @@ -98,15 +105,14 @@ func (s *CometBFTServer[T]) Init(appI serverv2.AppI[T], cfg map[string]any, logg
}

s.logger = logger.With(log.ModuleKey, s.Name())
store := appI.GetStore().(types.Store)
consensus := NewConsensus(
s.logger,
appI.Name(),
appI.GetAppManager(),
s.serverOptions.Mempool(cfg),
indexEvents,
appI.GetGPRCMethodsToMessageMap(),
store,
s.store,
s.config,
s.initTxCodec,
chainID,
Expand All @@ -118,8 +124,8 @@ func (s *CometBFTServer[T]) Init(appI serverv2.AppI[T], cfg map[string]any, logg
consensus.addrPeerFilter = s.serverOptions.AddrPeerFilter
consensus.idPeerFilter = s.serverOptions.IdPeerFilter

ss := store.GetStateStorage().(snapshots.StorageSnapshotter)
sc := store.GetStateCommitment().(snapshots.CommitSnapshotter)
ss := s.store.GetStateStorage().(snapshots.StorageSnapshotter)
sc := s.store.GetStateCommitment().(snapshots.CommitSnapshotter)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Handle unchecked type assertions to prevent potential panics

The type assertions on s.store.GetStateStorage() and s.store.GetStateCommitment() are unchecked and could lead to a panic if the assertions fail. It is recommended to perform type assertion checks to handle potential failures gracefully.

Apply the following changes to handle potential type assertion failures:

- ss := s.store.GetStateStorage().(snapshots.StorageSnapshotter)
- sc := s.store.GetStateCommitment().(snapshots.CommitSnapshotter)
+ ssInterface := s.store.GetStateStorage()
+ ss, ok := ssInterface.(snapshots.StorageSnapshotter)
+ if !ok {
+     return fmt.Errorf("failed to assert s.store.GetStateStorage() to snapshots.StorageSnapshotter")
+ }
+ scInterface := s.store.GetStateCommitment()
+ sc, ok := scInterface.(snapshots.CommitSnapshotter)
+ if !ok {
+     return fmt.Errorf("failed to assert s.store.GetStateCommitment() to snapshots.CommitSnapshotter")
+ }

This change ensures that the type assertions are checked, and appropriate error handling is in place to prevent unexpected panics.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
ss := s.store.GetStateStorage().(snapshots.StorageSnapshotter)
sc := s.store.GetStateCommitment().(snapshots.CommitSnapshotter)
ssInterface := s.store.GetStateStorage()
ss, ok := ssInterface.(snapshots.StorageSnapshotter)
if !ok {
return fmt.Errorf("failed to assert s.store.GetStateStorage() to snapshots.StorageSnapshotter")
}
scInterface := s.store.GetStateCommitment()
sc, ok := scInterface.(snapshots.CommitSnapshotter)
if !ok {
return fmt.Errorf("failed to assert s.store.GetStateCommitment() to snapshots.CommitSnapshotter")
}


snapshotStore, err := GetSnapshotStore(s.config.ConfigTomlConfig.RootDir)
if err != nil {
Expand Down
8 changes: 2 additions & 6 deletions server/v2/cometbft/types/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import (
)

type Store interface {
storev2.Backend

kocubinski marked this conversation as resolved.
Show resolved Hide resolved
// GetLatestVersion returns the latest version that consensus has been made on
GetLatestVersion() (uint64, error)
// StateLatest returns a readonly view over the latest
Expand All @@ -30,10 +32,4 @@ type Store interface {

// LastCommitID returns a CommitID pertaining to the last commitment.
LastCommitID() (proof.CommitID, error)

// GetStateStorage returns the SS backend.
GetStateStorage() storev2.VersionedDatabase

// GetStateCommitment returns the SC backend.
GetStateCommitment() storev2.Committer
}
2 changes: 1 addition & 1 deletion server/v2/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ func Execute(rootCmd *cobra.Command, envPrefix, defaultHome string) error {
}

// AddCommands add the server commands to the root command
// It configure the config handling and the logger handling
// It configures the config handling and the logger handling
func AddCommands[T transaction.Tx](
rootCmd *cobra.Command,
newApp AppCreator[T],
Expand Down
2 changes: 1 addition & 1 deletion server/v2/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ func TestServer(t *testing.T) {
err = grpcServer.Init(&mockApp[transaction.Tx]{}, cfg, logger)
require.NoError(t, err)

storeServer := store.New[transaction.Tx](nil /* nil appCreator as not using CLI commands */)
storeServer := store.New[transaction.Tx](nil /* nil store is not using CLI commands */)
err = storeServer.Init(&mockApp[transaction.Tx]{}, cfg, logger)
require.NoError(t, err)

Expand Down
16 changes: 8 additions & 8 deletions server/v2/store/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"cosmossdk.io/core/transaction"
"cosmossdk.io/log"
serverv2 "cosmossdk.io/server/v2"
storev2 "cosmossdk.io/store/v2"
)

var (
Expand All @@ -22,15 +23,14 @@ const ServerName = "store"
// Server manages store config and contains prune & snapshot commands
type Server[T transaction.Tx] struct {
config *Config
// saving appCreator for only RestoreSnapshotCmd
appCreator serverv2.AppCreator[T]
store storev2.Backend
}

func New[T transaction.Tx](appCreator serverv2.AppCreator[T]) *Server[T] {
return &Server[T]{appCreator: appCreator}
func New[T transaction.Tx](store storev2.Backend) *Server[T] {
return &Server[T]{store: store}
}

func (s *Server[T]) Init(appI serverv2.AppI[T], cfg map[string]any, logger log.Logger) error {
func (s *Server[T]) Init(_ serverv2.AppI[T], cfg map[string]any, _ log.Logger) error {
serverCfg := s.Config().(*Config)
if len(cfg) > 0 {
if err := serverv2.UnmarshalSubConfig(cfg, s.Name(), &serverCfg); err != nil {
Expand All @@ -46,11 +46,11 @@ func (s *Server[T]) Name() string {
return ServerName
}

func (s *Server[T]) Start(ctx context.Context) error {
func (s *Server[T]) Start(context.Context) error {
return nil
}

func (s *Server[T]) Stop(ctx context.Context) error {
func (s *Server[T]) Stop(context.Context) error {
return nil
}

Expand All @@ -63,7 +63,7 @@ func (s *Server[T]) CLICommands() serverv2.CLIConfig {
s.ListSnapshotsCmd(),
s.DumpArchiveCmd(),
s.LoadArchiveCmd(),
s.RestoreSnapshotCmd(s.appCreator),
s.RestoreSnapshotCmd(s.store),
},
}
}
Expand Down
14 changes: 9 additions & 5 deletions server/v2/store/snapshot.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ func (s *Server[T]) ExportSnapshotCmd() *cobra.Command {
}

// RestoreSnapshotCmd returns a command to restore a snapshot
func (s *Server[T]) RestoreSnapshotCmd(newApp serverv2.AppCreator[T]) *cobra.Command {
func (s *Server[T]) RestoreSnapshotCmd(rootStore storev2.Backend) *cobra.Command {
cmd := &cobra.Command{
Use: "restore <height> <format>",
Short: "Restore app state from local snapshot",
Expand All @@ -95,8 +95,6 @@ func (s *Server[T]) RestoreSnapshotCmd(newApp serverv2.AppCreator[T]) *cobra.Com
}

logger := log.NewLogger(cmd.OutOrStdout())
app := newApp(logger, v)
rootStore := app.GetStore().(storev2.RootStore)

sm, err := createSnapshotsManager(cmd, v, logger, rootStore)
if err != nil {
Expand Down Expand Up @@ -350,7 +348,9 @@ func (s *Server[T]) LoadArchiveCmd() *cobra.Command {
}
}

func createSnapshotsManager(cmd *cobra.Command, v *viper.Viper, logger log.Logger, store storev2.RootStore) (*snapshots.Manager, error) {
func createSnapshotsManager(
cmd *cobra.Command, v *viper.Viper, logger log.Logger, store storev2.Backend,
) (*snapshots.Manager, error) {
kocubinski marked this conversation as resolved.
Show resolved Hide resolved
home := v.GetString(serverv2.FlagHome)
snapshotStore, err := snapshots.NewStore(filepath.Join(home, "data", "snapshots"))
if err != nil {
Expand All @@ -371,7 +371,11 @@ func createSnapshotsManager(cmd *cobra.Command, v *viper.Viper, logger log.Logge
}
}

sm := snapshots.NewManager(snapshotStore, snapshots.NewSnapshotOptions(interval, uint32(keepRecent)), store.GetStateCommitment().(snapshots.CommitSnapshotter), store.GetStateStorage().(snapshots.StorageSnapshotter), nil, logger)
sm := snapshots.NewManager(
snapshotStore, snapshots.NewSnapshotOptions(interval, uint32(keepRecent)),
store.GetStateCommitment().(snapshots.CommitSnapshotter),
store.GetStateStorage().(snapshots.StorageSnapshotter),
nil, logger)
Comment on lines +374 to +378
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Handle type assertions to prevent potential panics.

At lines 376-377, the code uses unchecked type assertions. The Uber Go Style Guide recommends using the two-value form to handle cases where the assertion might fail, preventing runtime panics.

Modify the code to handle type assertion failures:

commitSnapshotter, ok := store.GetStateCommitment().(snapshots.CommitSnapshotter)
if !ok {
	return nil, fmt.Errorf("store does not implement snapshots.CommitSnapshotter")
}
storageSnapshotter, ok := store.GetStateStorage().(snapshots.StorageSnapshotter)
if !ok {
	return nil, fmt.Errorf("store does not implement snapshots.StorageSnapshotter")
}

sm := snapshots.NewManager(
	snapshotStore,
	snapshots.NewSnapshotOptions(interval, uint32(keepRecent)),
	commitSnapshotter,
	storageSnapshotter,
	nil,
	logger,
)

Comment on lines +374 to +378
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Prevent potential runtime panic due to unchecked type assertions

In the call to snapshots.NewManager, the code performs unchecked type assertions:

store.GetStateCommitment().(snapshots.CommitSnapshotter),
store.GetStateStorage().(snapshots.StorageSnapshotter),

If store.GetStateCommitment() or store.GetStateStorage() do not return types that implement the expected interfaces, this will cause a runtime panic.

Consider using the comma-ok idiom to safely perform the type assertions and handle any errors appropriately. Here's how you can modify the code:

+	commitSnapshotter, ok := store.GetStateCommitment().(snapshots.CommitSnapshotter)
+	if !ok {
+		return nil, fmt.Errorf("store's state commitment does not implement snapshots.CommitSnapshotter")
+	}
+	storageSnapshotter, ok := store.GetStateStorage().(snapshots.StorageSnapshotter)
+	if !ok {
+		return nil, fmt.Errorf("store's state storage does not implement snapshots.StorageSnapshotter")
+	}
-	sm := snapshots.NewManager(
-		snapshotStore, snapshots.NewSnapshotOptions(interval, uint32(keepRecent)),
-		store.GetStateCommitment().(snapshots.CommitSnapshotter),
-		store.GetStateStorage().(snapshots.StorageSnapshotter),
-		nil, logger)
+	sm := snapshots.NewManager(
+		snapshotStore, snapshots.NewSnapshotOptions(interval, uint32(keepRecent)),
+		commitSnapshotter,
+		storageSnapshotter,
+		nil, logger)

This change ensures that if the type assertions fail, a meaningful error is returned instead of causing a panic.

return sm, nil
}

Expand Down
1 change: 0 additions & 1 deletion server/v2/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,4 @@ type AppI[T transaction.Tx] interface {
InterfaceRegistry() server.InterfaceRegistry
GetAppManager() *appmanager.AppManager[T]
GetGPRCMethodsToMessageMap() map[string]func() gogoproto.Message
GetStore() any
}
9 changes: 6 additions & 3 deletions simapp/v2/simdv2/cmd/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ import (
"cosmossdk.io/server/v2/api/grpc"
"cosmossdk.io/server/v2/api/telemetry"
"cosmossdk.io/server/v2/cometbft"
"cosmossdk.io/server/v2/store"
cometbfttypes "cosmossdk.io/server/v2/cometbft/types"
serverstore "cosmossdk.io/server/v2/store"
"cosmossdk.io/simapp/v2"
confixcmd "cosmossdk.io/tools/confix/cmd"

Expand All @@ -43,6 +44,7 @@ func newApp[T transaction.Tx](logger log.Logger, viper *viper.Viper) serverv2.Ap
func initRootCmd[T transaction.Tx](
rootCmd *cobra.Command,
txConfig client.TxConfig,
store cometbfttypes.Store,
kocubinski marked this conversation as resolved.
Show resolved Hide resolved
moduleManager *runtimev2.MM[T],
) {
cfg := sdk.GetConfig()
Expand All @@ -52,7 +54,7 @@ func initRootCmd[T transaction.Tx](
genutilcli.InitCmd(moduleManager),
debug.Cmd(),
confixcmd.ConfigCommand(),
NewTestnetCmd(moduleManager),
NewTestnetCmd(store, moduleManager),
)

logger, err := serverv2.NewLogger(viper.New(), rootCmd.OutOrStdout())
Expand All @@ -77,11 +79,12 @@ func initRootCmd[T transaction.Tx](
initServerConfig(),
cometbft.New(
&genericTxDecoder[T]{txConfig},
store,
initCometOptions[T](),
initCometConfig(),
),
grpc.New[T](),
store.New[T](newApp),
serverstore.New[T](store),
telemetry.New[T](),
); err != nil {
panic(err)
Expand Down
5 changes: 4 additions & 1 deletion simapp/v2/simdv2/cmd/root_di.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ func NewRootCmd[T transaction.Tx]() *cobra.Command {
autoCliOpts autocli.AppOptions
moduleManager *runtime.MM[T]
clientCtx client.Context
storeBuilder runtime.StoreBuilder
)

if err := depinject.Inject(
Expand All @@ -51,6 +52,7 @@ func NewRootCmd[T transaction.Tx]() *cobra.Command {
std.RegisterLegacyAminoCodec,
),
),
&storeBuilder,
&autoCliOpts,
&moduleManager,
&clientCtx,
Expand Down Expand Up @@ -83,7 +85,8 @@ func NewRootCmd[T transaction.Tx]() *cobra.Command {
},
}

initRootCmd(rootCmd, clientCtx.TxConfig, moduleManager)
store := storeBuilder.Get()
initRootCmd(rootCmd, clientCtx.TxConfig, store, moduleManager)

nodeCmds := nodeservice.NewNodeCommands()
autoCliOpts.ModuleOptions = make(map[string]*autocliv1.ModuleOptions)
Expand Down
13 changes: 8 additions & 5 deletions simapp/v2/simdv2/cmd/testnet.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
serverv2 "cosmossdk.io/server/v2"
"cosmossdk.io/server/v2/api/grpc"
"cosmossdk.io/server/v2/cometbft"
cometbfttypes "cosmossdk.io/server/v2/cometbft/types"
"cosmossdk.io/server/v2/store"
banktypes "cosmossdk.io/x/bank/types"
bankv2types "cosmossdk.io/x/bank/v2/types"
Expand Down Expand Up @@ -87,7 +88,7 @@ func addTestnetFlagsToCmd(cmd *cobra.Command) {

// NewTestnetCmd creates a root testnet command with subcommands to run an in-process testnet or initialize
// validator configuration files for running a multi-validator testnet in a separate process
func NewTestnetCmd[T transaction.Tx](mm *runtimev2.MM[T]) *cobra.Command {
func NewTestnetCmd[T transaction.Tx](rs cometbfttypes.Store, mm *runtimev2.MM[T]) *cobra.Command {
testnetCmd := &cobra.Command{
Use: "testnet",
Short: "subcommands for starting or configuring local testnets",
Expand All @@ -96,13 +97,13 @@ func NewTestnetCmd[T transaction.Tx](mm *runtimev2.MM[T]) *cobra.Command {
RunE: client.ValidateCmd,
}

testnetCmd.AddCommand(testnetInitFilesCmd(mm))
testnetCmd.AddCommand(testnetInitFilesCmd(rs, mm))

return testnetCmd
}

// testnetInitFilesCmd returns a cmd to initialize all files for CometBFT testnet and application
func testnetInitFilesCmd[T transaction.Tx](mm *runtimev2.MM[T]) *cobra.Command {
func testnetInitFilesCmd[T transaction.Tx](rs cometbfttypes.Store, mm *runtimev2.MM[T]) *cobra.Command {
cmd := &cobra.Command{
Use: "init-files",
Short: "Initialize config directories & files for a multi-validator testnet running locally via separate processes (e.g. Docker Compose or similar)",
Expand Down Expand Up @@ -143,7 +144,7 @@ Example:
return err
}

return initTestnetFiles(clientCtx, cmd, config, mm, args)
return initTestnetFiles(clientCtx, rs, cmd, config, mm, args)
},
}

Expand All @@ -165,6 +166,7 @@ const nodeDirPerm = 0o755
// initTestnetFiles initializes testnet files for a testnet to be run in a separate process
func initTestnetFiles[T transaction.Tx](
clientCtx client.Context,
rootStore cometbfttypes.Store,
cmd *cobra.Command,
nodeConfig *cmtconfig.Config,
mm *runtimev2.MM[T],
Expand Down Expand Up @@ -339,10 +341,11 @@ func initTestnetFiles[T transaction.Tx](
// Write server config
cometServer := cometbft.New[T](
&genericTxDecoder[T]{clientCtx.TxConfig},
rootStore,
cometbft.ServerOptions[T]{},
cometbft.OverwriteDefaultConfigTomlConfig(nodeConfig),
)
storeServer := store.New[T](newApp)
storeServer := store.New[T](rootStore)
grpcServer := grpc.New[T](grpc.OverwriteDefaultConfig(grpcConfig))
server := serverv2.NewServer(log.NewNopLogger(), serverCfg, cometServer, grpcServer, storeServer)
err = server.WriteConfig(filepath.Join(nodeDir, "config"))
Expand Down
20 changes: 12 additions & 8 deletions store/v2/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ import (
// RootStore defines an abstraction layer containing a State Storage (SS) engine
// and one or more State Commitment (SC) engines.
type RootStore interface {
Pruner
Backend

// StateLatest returns a read-only version of the RootStore at the latest
// height, alongside the associated version.
StateLatest() (uint64, corestore.ReaderMap, error)
Expand All @@ -21,12 +24,6 @@ type RootStore interface {
// an error must be returned.
StateAt(version uint64) (corestore.ReaderMap, error)

// GetStateStorage returns the SS backend.
GetStateStorage() VersionedDatabase

// GetStateCommitment returns the SC backend.
GetStateCommitment() Committer

// Query performs a query on the RootStore for a given store key, version (height),
// and key tuple. Queries should be routed to the underlying SS engine.
Query(storeKey []byte, version uint64, key []byte, prove bool) (QueryResult, error)
Expand Down Expand Up @@ -67,11 +64,18 @@ type RootStore interface {
// SetMetrics sets the telemetry handler on the RootStore.
SetMetrics(m metrics.Metrics)

Prune(version uint64) error

io.Closer
}

// Backend defines the interface for the RootStore backends.
type Backend interface {
// GetStateStorage returns the SS backend.
GetStateStorage() VersionedDatabase

// GetStateCommitment returns the SC backend.
GetStateCommitment() Committer
}

// UpgradeableStore defines the interface for upgrading store keys.
type UpgradeableStore interface {
// LoadVersionAndUpgrade behaves identically to LoadVersion except it also
Expand Down
Loading