From 8f0504068c26c8acdff140a27be35b7ec15ba768 Mon Sep 17 00:00:00 2001 From: Matt Kocubinski Date: Fri, 13 Sep 2024 05:28:04 -0500 Subject: [PATCH 1/3] fix(runtime/v2): provide default factory options if unset in app builder (#21690) (cherry picked from commit 9fc6675885f9667865ec8572f175fb206bd74aad) # Conflicts: # runtime/v2/builder.go --- runtime/v2/builder.go | 229 +++++++++++++++++++++++++++++++ simapp/v2/app_di.go | 8 +- simapp/v2/simdv2/cmd/commands.go | 8 +- 3 files changed, 236 insertions(+), 9 deletions(-) create mode 100644 runtime/v2/builder.go diff --git a/runtime/v2/builder.go b/runtime/v2/builder.go new file mode 100644 index 00000000000..0a1b279330d --- /dev/null +++ b/runtime/v2/builder.go @@ -0,0 +1,229 @@ +package runtime + +import ( + "context" + "encoding/json" + "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/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 + + // the following fields are used to overwrite the default + branch func(state store.ReaderMap) store.WriterMap + txValidator func(ctx context.Context, tx T) error + postTxExec func(ctx context.Context, tx T, success bool) error +} + +// DefaultGenesis returns a default genesis from the registered AppModule's. +func (a *AppBuilder[T]) DefaultGenesis() map[string]json.RawMessage { + return a.app.moduleManager.DefaultGenesis() +} + +// RegisterModules registers the provided modules with the module manager. +// This is the primary hook for integrating with modules which are not registered using the app config. +func (a *AppBuilder[T]) RegisterModules(modules map[string]appmodulev2.AppModule) error { + for name, appModule := range modules { + // if a (legacy) module implements the HasName interface, check that the name matches + if mod, ok := appModule.(interface{ Name() string }); ok { + if name != mod.Name() { + a.app.logger.Warn(fmt.Sprintf("module name %q does not match name returned by HasName: %q", name, mod.Name())) + } + } + + if _, ok := a.app.moduleManager.modules[name]; ok { + return fmt.Errorf("module named %q already exists", name) + } + a.app.moduleManager.modules[name] = appModule + + if mod, ok := appModule.(appmodulev2.HasRegisterInterfaces); ok { + mod.RegisterInterfaces(a.app.interfaceRegistrar) + } + + if mod, ok := appModule.(appmodule.HasAminoCodec); ok { + mod.RegisterLegacyAminoCodec(a.app.amino) + } + } + + return nil +} + +// RegisterStores registers the provided store keys. +// This method should only be used for registering extra stores +// which is necessary for modules that not registered using the app config. +// To be used in combination of RegisterModules. +func (a *AppBuilder[T]) RegisterStores(keys ...string) { + a.app.storeKeys = append(a.app.storeKeys, keys...) +} + +// Build builds an *App instance. +func (a *AppBuilder[T]) Build(opts ...AppBuilderOption[T]) (*App[T], error) { + for _, opt := range opts { + opt(a) + } + + // default branch + if a.branch == nil { + a.branch = branch.DefaultNewWriterMap + } + + // default tx validator + if a.txValidator == nil { + a.txValidator = a.app.moduleManager.TxValidators() + } + + // default post tx exec + if a.postTxExec == nil { + a.postTxExec = func(ctx context.Context, tx T, success bool) error { + return nil + } + } + + if err := a.app.moduleManager.RegisterServices(a.app); err != nil { + return nil, err + } + + endBlocker, valUpdate := a.app.moduleManager.EndBlock() + + stf, err := stf.NewSTF[T]( + a.app.logger.With("module", "stf"), + a.app.msgRouterBuilder, + a.app.queryRouterBuilder, + a.app.moduleManager.PreBlocker(), + a.app.moduleManager.BeginBlock(), + endBlocker, + a.txValidator, + valUpdate, + a.postTxExec, + a.branch, + ) + if err != nil { + return nil, fmt.Errorf("failed to create STF: %w", err) + } + 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, + ValidateTxGasLimit: a.app.config.GasConfig.ValidateTxGasLimit, + QueryGasLimit: a.app.config.GasConfig.QueryGasLimit, + SimulationGasLimit: a.app.config.GasConfig.SimulationGasLimit, + InitGenesis: func(ctx context.Context, src io.Reader, txHandler func(json.RawMessage) error) error { + // this implementation assumes that the state is a JSON object + bz, err := io.ReadAll(src) + if err != nil { + return fmt.Errorf("failed to read import state: %w", err) + } + var genesisState map[string]json.RawMessage + if err = json.Unmarshal(bz, &genesisState); err != nil { + return err + } + if err = a.app.moduleManager.InitGenesisJSON(ctx, genesisState, txHandler); err != nil { + return fmt.Errorf("failed to init genesis: %w", err) + } + return nil + }, + ExportGenesis: func(ctx context.Context, version uint64) ([]byte, error) { + genesisJson, err := a.app.moduleManager.ExportGenesisForModules(ctx) + if err != nil { + return nil, fmt.Errorf("failed to export genesis: %w", err) + } + + bz, err := json.Marshal(genesisJson) + if err != nil { + return nil, fmt.Errorf("failed to marshal genesis: %w", err) + } + + return bz, nil + }, + } + + appManager, err := appManagerBuilder.Build() + if err != nil { + return nil, fmt.Errorf("failed to build app manager: %w", err) + } + a.app.AppManager = appManager + + return a.app, nil +} + +// AppBuilderOption is a function that can be passed to AppBuilder.Build to customize the resulting app. +type AppBuilderOption[T transaction.Tx] func(*AppBuilder[T]) + +// AppBuilderWithBranch sets a custom branch implementation for the app. +func AppBuilderWithBranch[T transaction.Tx](branch func(state store.ReaderMap) store.WriterMap) AppBuilderOption[T] { + return func(a *AppBuilder[T]) { + a.branch = branch + } +} + +// AppBuilderWithTxValidator sets the tx validator for the app. +// It overrides all default tx validators defined by modules. +func AppBuilderWithTxValidator[T transaction.Tx](txValidators func(ctx context.Context, tx T) error) AppBuilderOption[T] { + return func(a *AppBuilder[T]) { + a.txValidator = txValidators + } +} + +// AppBuilderWithPostTxExec sets logic that will be executed after each transaction. +// When not provided, a no-op function will be used. +func AppBuilderWithPostTxExec[T transaction.Tx](postTxExec func(ctx context.Context, tx T, success bool) error) AppBuilderOption[T] { + return func(a *AppBuilder[T]) { + a.postTxExec = postTxExec + } +} + +func AppBuilderWithStoreOptions[T transaction.Tx](opts *rootstore.Options) AppBuilderOption[T] { + return func(a *AppBuilder[T]) { + a.storeOptions = opts + } +} diff --git a/simapp/v2/app_di.go b/simapp/v2/app_di.go index 0f402c47866..f8bed8de600 100644 --- a/simapp/v2/app_di.go +++ b/simapp/v2/app_di.go @@ -98,7 +98,7 @@ func NewSimApp[T transaction.Tx]( app = &SimApp[T]{} appBuilder *runtime.AppBuilder[T] err error - storeOptions = root.DefaultStoreOptions() + storeOptions = &root.Options{} // merge the AppConfig and other configuration in one config appConfig = depinject.Configs( @@ -187,13 +187,15 @@ func NewSimApp[T transaction.Tx]( panic(err) } + var builderOpts []runtime.AppBuilderOption[T] if sub := viper.Sub("store.options"); sub != nil { - err = sub.Unmarshal(&storeOptions) + err = sub.Unmarshal(storeOptions) if err != nil { panic(err) } + builderOpts = append(builderOpts, runtime.AppBuilderWithStoreOptions[T](storeOptions)) } - app.App, err = appBuilder.Build(runtime.AppBuilderWithStoreOptions[T](storeOptions)) + app.App, err = appBuilder.Build(builderOpts...) if err != nil { panic(err) } diff --git a/simapp/v2/simdv2/cmd/commands.go b/simapp/v2/simdv2/cmd/commands.go index 2e315c64a18..d58619170e0 100644 --- a/simapp/v2/simdv2/cmd/commands.go +++ b/simapp/v2/simdv2/cmd/commands.go @@ -34,13 +34,9 @@ import ( v2 "github.com/cosmos/cosmos-sdk/x/genutil/v2/cli" ) -func newApp[T transaction.Tx]( - logger log.Logger, viper *viper.Viper, -) serverv2.AppI[T] { +func newApp[T transaction.Tx](logger log.Logger, viper *viper.Viper) serverv2.AppI[T] { viper.Set(serverv2.FlagHome, simapp.DefaultNodeHome) - - return serverv2.AppI[T]( - simapp.NewSimApp[T](logger, viper)) + return serverv2.AppI[T](simapp.NewSimApp[T](logger, viper)) } func initRootCmd[T transaction.Tx]( From 0fb7289b337647206abeabddd5f77dc6616fb3b3 Mon Sep 17 00:00:00 2001 From: Julien Robert Date: Fri, 13 Sep 2024 12:39:31 +0200 Subject: [PATCH 2/3] rm runtime/v2 --- runtime/v2/builder.go | 229 ------------------------------------------ 1 file changed, 229 deletions(-) delete mode 100644 runtime/v2/builder.go diff --git a/runtime/v2/builder.go b/runtime/v2/builder.go deleted file mode 100644 index 0a1b279330d..00000000000 --- a/runtime/v2/builder.go +++ /dev/null @@ -1,229 +0,0 @@ -package runtime - -import ( - "context" - "encoding/json" - "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/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 - - // the following fields are used to overwrite the default - branch func(state store.ReaderMap) store.WriterMap - txValidator func(ctx context.Context, tx T) error - postTxExec func(ctx context.Context, tx T, success bool) error -} - -// DefaultGenesis returns a default genesis from the registered AppModule's. -func (a *AppBuilder[T]) DefaultGenesis() map[string]json.RawMessage { - return a.app.moduleManager.DefaultGenesis() -} - -// RegisterModules registers the provided modules with the module manager. -// This is the primary hook for integrating with modules which are not registered using the app config. -func (a *AppBuilder[T]) RegisterModules(modules map[string]appmodulev2.AppModule) error { - for name, appModule := range modules { - // if a (legacy) module implements the HasName interface, check that the name matches - if mod, ok := appModule.(interface{ Name() string }); ok { - if name != mod.Name() { - a.app.logger.Warn(fmt.Sprintf("module name %q does not match name returned by HasName: %q", name, mod.Name())) - } - } - - if _, ok := a.app.moduleManager.modules[name]; ok { - return fmt.Errorf("module named %q already exists", name) - } - a.app.moduleManager.modules[name] = appModule - - if mod, ok := appModule.(appmodulev2.HasRegisterInterfaces); ok { - mod.RegisterInterfaces(a.app.interfaceRegistrar) - } - - if mod, ok := appModule.(appmodule.HasAminoCodec); ok { - mod.RegisterLegacyAminoCodec(a.app.amino) - } - } - - return nil -} - -// RegisterStores registers the provided store keys. -// This method should only be used for registering extra stores -// which is necessary for modules that not registered using the app config. -// To be used in combination of RegisterModules. -func (a *AppBuilder[T]) RegisterStores(keys ...string) { - a.app.storeKeys = append(a.app.storeKeys, keys...) -} - -// Build builds an *App instance. -func (a *AppBuilder[T]) Build(opts ...AppBuilderOption[T]) (*App[T], error) { - for _, opt := range opts { - opt(a) - } - - // default branch - if a.branch == nil { - a.branch = branch.DefaultNewWriterMap - } - - // default tx validator - if a.txValidator == nil { - a.txValidator = a.app.moduleManager.TxValidators() - } - - // default post tx exec - if a.postTxExec == nil { - a.postTxExec = func(ctx context.Context, tx T, success bool) error { - return nil - } - } - - if err := a.app.moduleManager.RegisterServices(a.app); err != nil { - return nil, err - } - - endBlocker, valUpdate := a.app.moduleManager.EndBlock() - - stf, err := stf.NewSTF[T]( - a.app.logger.With("module", "stf"), - a.app.msgRouterBuilder, - a.app.queryRouterBuilder, - a.app.moduleManager.PreBlocker(), - a.app.moduleManager.BeginBlock(), - endBlocker, - a.txValidator, - valUpdate, - a.postTxExec, - a.branch, - ) - if err != nil { - return nil, fmt.Errorf("failed to create STF: %w", err) - } - 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, - ValidateTxGasLimit: a.app.config.GasConfig.ValidateTxGasLimit, - QueryGasLimit: a.app.config.GasConfig.QueryGasLimit, - SimulationGasLimit: a.app.config.GasConfig.SimulationGasLimit, - InitGenesis: func(ctx context.Context, src io.Reader, txHandler func(json.RawMessage) error) error { - // this implementation assumes that the state is a JSON object - bz, err := io.ReadAll(src) - if err != nil { - return fmt.Errorf("failed to read import state: %w", err) - } - var genesisState map[string]json.RawMessage - if err = json.Unmarshal(bz, &genesisState); err != nil { - return err - } - if err = a.app.moduleManager.InitGenesisJSON(ctx, genesisState, txHandler); err != nil { - return fmt.Errorf("failed to init genesis: %w", err) - } - return nil - }, - ExportGenesis: func(ctx context.Context, version uint64) ([]byte, error) { - genesisJson, err := a.app.moduleManager.ExportGenesisForModules(ctx) - if err != nil { - return nil, fmt.Errorf("failed to export genesis: %w", err) - } - - bz, err := json.Marshal(genesisJson) - if err != nil { - return nil, fmt.Errorf("failed to marshal genesis: %w", err) - } - - return bz, nil - }, - } - - appManager, err := appManagerBuilder.Build() - if err != nil { - return nil, fmt.Errorf("failed to build app manager: %w", err) - } - a.app.AppManager = appManager - - return a.app, nil -} - -// AppBuilderOption is a function that can be passed to AppBuilder.Build to customize the resulting app. -type AppBuilderOption[T transaction.Tx] func(*AppBuilder[T]) - -// AppBuilderWithBranch sets a custom branch implementation for the app. -func AppBuilderWithBranch[T transaction.Tx](branch func(state store.ReaderMap) store.WriterMap) AppBuilderOption[T] { - return func(a *AppBuilder[T]) { - a.branch = branch - } -} - -// AppBuilderWithTxValidator sets the tx validator for the app. -// It overrides all default tx validators defined by modules. -func AppBuilderWithTxValidator[T transaction.Tx](txValidators func(ctx context.Context, tx T) error) AppBuilderOption[T] { - return func(a *AppBuilder[T]) { - a.txValidator = txValidators - } -} - -// AppBuilderWithPostTxExec sets logic that will be executed after each transaction. -// When not provided, a no-op function will be used. -func AppBuilderWithPostTxExec[T transaction.Tx](postTxExec func(ctx context.Context, tx T, success bool) error) AppBuilderOption[T] { - return func(a *AppBuilder[T]) { - a.postTxExec = postTxExec - } -} - -func AppBuilderWithStoreOptions[T transaction.Tx](opts *rootstore.Options) AppBuilderOption[T] { - return func(a *AppBuilder[T]) { - a.storeOptions = opts - } -} From a57f552b3a334bb97babd4ba019227cf189e53f8 Mon Sep 17 00:00:00 2001 From: Julien Robert Date: Fri, 13 Sep 2024 12:42:10 +0200 Subject: [PATCH 3/3] bump runtime --- simapp/v2/go.mod | 2 +- simapp/v2/go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/simapp/v2/go.mod b/simapp/v2/go.mod index b13b84a68c9..707d3b46f1c 100644 --- a/simapp/v2/go.mod +++ b/simapp/v2/go.mod @@ -10,7 +10,7 @@ require ( cosmossdk.io/depinject v1.0.0 cosmossdk.io/log v1.4.1 cosmossdk.io/math v1.3.0 - cosmossdk.io/runtime/v2 v2.0.0-20240912070812-0fc06f14104b // main + cosmossdk.io/runtime/v2 v2.0.0-20240913102804-9fc6675885f9 // main cosmossdk.io/server/v2 v2.0.0-20240912070812-0fc06f14104b // main cosmossdk.io/server/v2/cometbft v0.0.0-00010101000000-000000000000 cosmossdk.io/store/v2 v2.0.0-20240906090851-36d9b25e8981 // main diff --git a/simapp/v2/go.sum b/simapp/v2/go.sum index b5745744da2..dc3a52245d4 100644 --- a/simapp/v2/go.sum +++ b/simapp/v2/go.sum @@ -210,8 +210,8 @@ cosmossdk.io/log v1.4.1 h1:wKdjfDRbDyZRuWa8M+9nuvpVYxrEOwbD/CA8hvhU8QM= cosmossdk.io/log v1.4.1/go.mod h1:k08v0Pyq+gCP6phvdI6RCGhLf/r425UT6Rk/m+o74rU= cosmossdk.io/math v1.3.0 h1:RC+jryuKeytIiictDslBP9i1fhkVm6ZDmZEoNP316zE= cosmossdk.io/math v1.3.0/go.mod h1:vnRTxewy+M7BtXBNFybkuhSH4WfedVAAnERHgVFhp3k= -cosmossdk.io/runtime/v2 v2.0.0-20240912070812-0fc06f14104b h1:usuxpv4UZLKOXHK+L34KouKdVb0oUTUPoAJTz8ApWzA= -cosmossdk.io/runtime/v2 v2.0.0-20240912070812-0fc06f14104b/go.mod h1:kV5J0FOZvQIH6o23pCmkl5h+UdIAbQ1WU/BgVgrvbKQ= +cosmossdk.io/runtime/v2 v2.0.0-20240913102804-9fc6675885f9 h1:xAPMtcHd8bW8F+Nt/lRmoe3gAD/zghEkzHdXb/5hkwY= +cosmossdk.io/runtime/v2 v2.0.0-20240913102804-9fc6675885f9/go.mod h1:bIUuDyiXe4zAa/QbOhciGdtTsmyytU8kt1FjLVtcZ1Q= cosmossdk.io/schema v0.2.0 h1:UH5CR1DqUq8yP+5Np8PbvG4YX0zAUsTN2Qk6yThmfMk= cosmossdk.io/schema v0.2.0/go.mod h1:RDAhxIeNB4bYqAlF4NBJwRrgtnciMcyyg0DOKnhNZQQ= cosmossdk.io/server/v2 v2.0.0-20240912070812-0fc06f14104b h1:rG8VQF/3yAEJOQewDgrcBWwr1xKzu+VwhrILW5hui/Y=