refactor(runtime/v2): use StoreBuilder (#21989)
This commit is contained in:
parent
73ee336359
commit
3a20261c78
@ -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
|
||||
@ -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
|
||||
}
|
||||
@ -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,
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@ -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"
|
||||
@ -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 (
|
||||
@ -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
|
||||
}
|
||||
@ -97,6 +99,7 @@ func init() {
|
||||
ProvideAppBuilder[transaction.Tx],
|
||||
ProvideEnvironment[transaction.Tx],
|
||||
ProvideModuleManager[transaction.Tx],
|
||||
ProvideStoreBuilder,
|
||||
),
|
||||
appconfig.Invoke(SetupAppBuilder),
|
||||
)
|
||||
@ -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) {
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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,
|
||||
@ -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(),
|
||||
@ -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,
|
||||
)
|
||||
}
|
||||
|
||||
@ -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.
|
||||
@ -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
|
||||
|
||||
|
||||
@ -14,7 +14,7 @@ import (
|
||||
|
||||
const (
|
||||
ModuleName = "runtime"
|
||||
FlagHome = "home"
|
||||
flagHome = "home"
|
||||
)
|
||||
|
||||
// validateProtoAnnotations validates that the proto annotations are correct.
|
||||
|
||||
@ -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(
|
||||
@ -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,
|
||||
@ -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)
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user