Skip to content

Commit

Permalink
refactor(runtime/v2): use StoreBuilder (#21989)
Browse files Browse the repository at this point in the history
  • Loading branch information
kocubinski authored Oct 1, 2024
1 parent 73ee336 commit 3a20261
Show file tree
Hide file tree
Showing 5 changed files with 112 additions and 65 deletions.
49 changes: 5 additions & 44 deletions runtime/v2/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,28 +6,22 @@ import (
"errors"
"fmt"
"io"
"path/filepath"

"cosmossdk.io/core/appmodule"
appmodulev2 "cosmossdk.io/core/appmodule/v2"
"cosmossdk.io/core/server"
"cosmossdk.io/core/store"
"cosmossdk.io/core/transaction"
"cosmossdk.io/runtime/v2/services"
"cosmossdk.io/server/v2/appmanager"
"cosmossdk.io/server/v2/stf"
"cosmossdk.io/server/v2/stf/branch"
"cosmossdk.io/store/v2/db"
rootstore "cosmossdk.io/store/v2/root"
)

// AppBuilder is a type that is injected into a container by the runtime/v2 module
// (as *AppBuilder) which can be used to create an app which is compatible with
// the existing app.go initialization conventions.
type AppBuilder[T transaction.Tx] struct {
app *App[T]
config server.DynamicConfig
storeOptions *rootstore.Options
app *App[T]

// the following fields are used to overwrite the default
branch func(state store.ReaderMap) store.WriterMap
Expand Down Expand Up @@ -99,6 +93,10 @@ func (a *AppBuilder[T]) Build(opts ...AppBuilderOption[T]) (*App[T], error) {
}
}

if a.app.db == nil {
return nil, fmt.Errorf("app.db is not set, it is required to build the app")
}

if err := a.app.moduleManager.RegisterServices(a.app); err != nil {
return nil, err
}
Expand All @@ -122,37 +120,6 @@ func (a *AppBuilder[T]) Build(opts ...AppBuilderOption[T]) (*App[T], error) {
}
a.app.stf = stf

home := a.config.GetString(FlagHome)
scRawDb, err := db.NewDB(
db.DBType(a.config.GetString("store.app-db-backend")),
"application",
filepath.Join(home, "data"),
nil,
)
if err != nil {
panic(err)
}

var storeOptions rootstore.Options
if a.storeOptions != nil {
storeOptions = *a.storeOptions
} else {
storeOptions = rootstore.DefaultStoreOptions()
}
factoryOptions := &rootstore.FactoryOptions{
Logger: a.app.logger,
RootDir: home,
Options: storeOptions,
StoreKeys: append(a.app.storeKeys, "stf"),
SCRawDB: scRawDb,
}

rs, err := rootstore.CreateRootStore(factoryOptions)
if err != nil {
return nil, fmt.Errorf("failed to create root store: %w", err)
}
a.app.db = rs

appManagerBuilder := appmanager.Builder[T]{
STF: a.app.stf,
DB: a.app.db,
Expand Down Expand Up @@ -251,9 +218,3 @@ func AppBuilderWithPostTxExec[T transaction.Tx](postTxExec func(ctx context.Cont
a.postTxExec = postTxExec
}
}

func AppBuilderWithStoreOptions[T transaction.Tx](opts *rootstore.Options) AppBuilderOption[T] {
return func(a *AppBuilder[T]) {
a.storeOptions = opts
}
}
39 changes: 32 additions & 7 deletions runtime/v2/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
reflectionv1 "cosmossdk.io/api/cosmos/reflection/v1"
appmodulev2 "cosmossdk.io/core/appmodule/v2"
"cosmossdk.io/core/comet"
"cosmossdk.io/core/event"
"cosmossdk.io/core/header"
"cosmossdk.io/core/registry"
"cosmossdk.io/core/server"
Expand All @@ -26,6 +27,7 @@ import (
"cosmossdk.io/log"
"cosmossdk.io/runtime/v2/services"
"cosmossdk.io/server/v2/stf"
rootstore "cosmossdk.io/store/v2/root"
)

var (
Expand All @@ -40,19 +42,19 @@ type appModule[T transaction.Tx] struct {
func (m appModule[T]) IsOnePerModuleType() {}
func (m appModule[T]) IsAppModule() {}

func (m appModule[T]) RegisterServices(registar grpc.ServiceRegistrar) error {
func (m appModule[T]) RegisterServices(registrar grpc.ServiceRegistrar) error {
autoCliQueryService, err := services.NewAutoCLIQueryService(m.app.moduleManager.modules)
if err != nil {
return err
}

autocliv1.RegisterQueryServer(registar, autoCliQueryService)
autocliv1.RegisterQueryServer(registrar, autoCliQueryService)

reflectionSvc, err := services.NewReflectionService()
if err != nil {
return err
}
reflectionv1.RegisterReflectionServiceServer(registar, reflectionSvc)
reflectionv1.RegisterReflectionServiceServer(registrar, reflectionSvc)

return nil
}
Expand Down Expand Up @@ -97,6 +99,7 @@ func init() {
ProvideAppBuilder[transaction.Tx],
ProvideEnvironment[transaction.Tx],
ProvideModuleManager[transaction.Tx],
ProvideStoreBuilder,
),
appconfig.Invoke(SetupAppBuilder),
)
Expand Down Expand Up @@ -146,7 +149,12 @@ type AppInputs struct {
InterfaceRegistrar registry.InterfaceRegistrar
LegacyAmino registry.AminoRegistrar
Logger log.Logger
DynamicConfig server.DynamicConfig `optional:"true"` // can be nil in client wiring
// StoreBuilder is a builder for a store/v2 RootStore satisfying the Store interface
StoreBuilder *StoreBuilder
// StoreOptions are required as input for the StoreBuilder. If not provided, the default options are used.
StoreOptions *rootstore.Options `optional:"true"`
// DynamicConfig can be nil in client wiring, but is required in server wiring.
DynamicConfig server.DynamicConfig `optional:"true"`
}

func SetupAppBuilder(inputs AppInputs) {
Expand All @@ -157,8 +165,22 @@ func SetupAppBuilder(inputs AppInputs) {
app.moduleManager.RegisterInterfaces(inputs.InterfaceRegistrar)
app.moduleManager.RegisterLegacyAminoCodec(inputs.LegacyAmino)

if inputs.DynamicConfig != nil {
inputs.AppBuilder.config = inputs.DynamicConfig
if inputs.DynamicConfig == nil {
return
}
storeOptions := rootstore.DefaultStoreOptions()
if inputs.StoreOptions != nil {
storeOptions = *inputs.StoreOptions
}
var err error
app.db, err = inputs.StoreBuilder.Build(
inputs.Logger,
app.storeKeys,
inputs.DynamicConfig,
storeOptions,
)
if err != nil {
panic(err)
}
}

Expand All @@ -178,6 +200,7 @@ func ProvideEnvironment[T transaction.Tx](
appBuilder *AppBuilder[T],
kvFactory store.KVStoreServiceFactory,
headerService header.Service,
eventService event.Service,
) (
appmodulev2.Environment,
store.KVStoreService,
Expand Down Expand Up @@ -209,7 +232,7 @@ func ProvideEnvironment[T transaction.Tx](
env := appmodulev2.Environment{
Logger: logger,
BranchService: stf.BranchService{},
EventService: stf.NewEventService(),
EventService: eventService,
GasService: stf.NewGasMeterService(),
HeaderService: headerService,
QueryRouterService: stf.NewQueryRouterService(),
Expand Down Expand Up @@ -254,10 +277,12 @@ func DefaultServiceBindings() depinject.Config {
}
headerService header.Service = services.NewGenesisHeaderService(stf.HeaderService{})
cometService comet.Service = &services.ContextAwareCometInfoService{}
eventService = stf.NewEventService()
)
return depinject.Supply(
kvServiceFactory,
headerService,
cometService,
eventService,
)
}
57 changes: 57 additions & 0 deletions runtime/v2/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,16 @@ package runtime
import (
"errors"
"fmt"
"path/filepath"

"cosmossdk.io/core/server"
"cosmossdk.io/core/store"
"cosmossdk.io/log"
"cosmossdk.io/server/v2/stf"
storev2 "cosmossdk.io/store/v2"
"cosmossdk.io/store/v2/db"
"cosmossdk.io/store/v2/proof"
"cosmossdk.io/store/v2/root"
)

// NewKVStoreService creates a new KVStoreService.
Expand Down Expand Up @@ -59,6 +64,58 @@ type Store interface {
LastCommitID() (proof.CommitID, error)
}

// StoreBuilder is a builder for a store/v2 RootStore satisfying the Store interface.
type StoreBuilder struct {
store Store
}

// Build creates a new store/v2 RootStore.
func (sb *StoreBuilder) Build(
logger log.Logger,
storeKeys []string,
config server.DynamicConfig,
options root.Options,
) (Store, error) {
if sb.store != nil {
return sb.store, nil
}
home := config.GetString(flagHome)
scRawDb, err := db.NewDB(
db.DBType(config.GetString("store.app-db-backend")),
"application",
filepath.Join(home, "data"),
nil,
)
if err != nil {
return nil, fmt.Errorf("failed to create SCRawDB: %w", err)
}

factoryOptions := &root.FactoryOptions{
Logger: logger,
RootDir: home,
Options: options,
// STF needs to store a bit of state
StoreKeys: append(storeKeys, "stf"),
SCRawDB: scRawDb,
}

rs, err := root.CreateRootStore(factoryOptions)
if err != nil {
return nil, fmt.Errorf("failed to create root store: %w", err)
}
sb.store = rs
return sb.store, nil
}

// Get returns the Store. Build must be called before calling Get or the result will be nil.
func (sb *StoreBuilder) Get() Store {
return sb.store
}

func ProvideStoreBuilder() *StoreBuilder {
return &StoreBuilder{}
}

// StoreLoader allows for custom loading of the store, this is useful when upgrading the store from a previous version
type StoreLoader func(store Store) error

Expand Down
2 changes: 1 addition & 1 deletion runtime/v2/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import (

const (
ModuleName = "runtime"
FlagHome = "home"
flagHome = "home"
)

// validateProtoAnnotations validates that the proto annotations are correct.
Expand Down
30 changes: 17 additions & 13 deletions simapp/v2/app_di.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,9 @@ func NewSimApp[T transaction.Tx](
viper *viper.Viper,
) *SimApp[T] {
var (
app = &SimApp[T]{}
appBuilder *runtime.AppBuilder[T]
err error
storeOptions = &root.Options{}
app = &SimApp[T]{}
appBuilder *runtime.AppBuilder[T]
err error

// merge the AppConfig and other configuration in one config
appConfig = depinject.Configs(
Expand Down Expand Up @@ -149,6 +148,19 @@ func NewSimApp[T transaction.Tx](
)
)

// the subsection of config that contains the store options (in app.toml [store.options] header)
// is unmarshaled into a store.Options struct and passed to the store builder.
// future work may move this specification and retrieval into store/v2.
// If these options are not specified then default values will be used.
if sub := viper.Sub("store.options"); sub != nil {
storeOptions := &root.Options{}
err := sub.Unmarshal(storeOptions)
if err != nil {
panic(err)
}
appConfig = depinject.Configs(appConfig, depinject.Supply(storeOptions))
}

if err := depinject.Inject(appConfig,
&appBuilder,
&app.appCodec,
Expand All @@ -160,15 +172,7 @@ func NewSimApp[T transaction.Tx](
panic(err)
}

var builderOpts []runtime.AppBuilderOption[T]
if sub := viper.Sub("store.options"); sub != nil {
err = sub.Unmarshal(storeOptions)
if err != nil {
panic(err)
}
builderOpts = append(builderOpts, runtime.AppBuilderWithStoreOptions[T](storeOptions))
}
app.App, err = appBuilder.Build(builderOpts...)
app.App, err = appBuilder.Build()
if err != nil {
panic(err)
}
Expand Down

0 comments on commit 3a20261

Please sign in to comment.