From 0795530bb7884cfacee59035a227460cdbd97014 Mon Sep 17 00:00:00 2001 From: Randy Grok <98407738+randygrok@users.noreply.github.com> Date: Fri, 13 Sep 2024 16:22:14 +0200 Subject: [PATCH 1/3] feat(runtime/v2): store loader on simappv2 (#21704) Co-authored-by: marbar3778 (cherry picked from commit 7d6ff0df56421ea5affce7d5254d0f15b3af1b23) # Conflicts: # CHANGELOG.md # runtime/v2/app.go # runtime/v2/module.go # runtime/v2/store.go --- CHANGELOG.md | 8 ++ runtime/store.go | 21 +++ runtime/v2/app.go | 125 +++++++++++++++++ runtime/v2/module.go | 239 +++++++++++++++++++++++++++++++++ runtime/v2/store.go | 90 +++++++++++++ simapp/v2/upgrades.go | 5 +- x/upgrade/README.md | 3 + x/upgrade/types/storeloader.go | 28 +--- 8 files changed, 491 insertions(+), 28 deletions(-) create mode 100644 runtime/v2/app.go create mode 100644 runtime/v2/module.go create mode 100644 runtime/v2/store.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 423b364e7419..99529f57e299 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,6 +40,14 @@ Ref: https://keepachangelog.com/en/1.0.0/ Every module contains its own CHANGELOG.md. Please refer to the module you are interested in. +<<<<<<< HEAD +======= +### Features + +* (baseapp) [#20291](https://github.com/cosmos/cosmos-sdk/pull/20291) Simulate nested messages. +* (runtime) [#21704](https://github.com/cosmos/cosmos-sdk/pull/21704) Add StoreLoader in simappv2. + +>>>>>>> 7d6ff0df5 (feat(runtime/v2): store loader on simappv2 (#21704)) ### Improvements * (genutil) [#21701](https://github.com/cosmos/cosmos-sdk/pull/21701) Improved error messages for genesis validation. diff --git a/runtime/store.go b/runtime/store.go index 32097fb41bbe..1585bc54af71 100644 --- a/runtime/store.go +++ b/runtime/store.go @@ -9,6 +9,7 @@ import ( "cosmossdk.io/core/store" storetypes "cosmossdk.io/store/types" + "github.com/cosmos/cosmos-sdk/baseapp" sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -181,3 +182,23 @@ func (s kvStoreAdapter) ReverseIterator(start, end []byte) dbm.Iterator { func KVStoreAdapter(store store.KVStore) storetypes.KVStore { return &kvStoreAdapter{store} } + +// UpgradeStoreLoader is used to prepare baseapp with a fixed StoreLoader +// pattern. This is useful for custom upgrade loading logic. +func UpgradeStoreLoader(upgradeHeight int64, storeUpgrades *store.StoreUpgrades) baseapp.StoreLoader { + return func(ms storetypes.CommitMultiStore) error { + if upgradeHeight == ms.LastCommitID().Version+1 { + // Check if the current commit version and upgrade height matches + if len(storeUpgrades.Deleted) > 0 || len(storeUpgrades.Added) > 0 { + stup := &storetypes.StoreUpgrades{ + Added: storeUpgrades.Added, + Deleted: storeUpgrades.Deleted, + } + return ms.LoadLatestVersionAndUpgrade(stup) + } + } + + // Otherwise load default store loader + return baseapp.DefaultStoreLoader(ms) + } +} diff --git a/runtime/v2/app.go b/runtime/v2/app.go new file mode 100644 index 000000000000..b7104f9e4774 --- /dev/null +++ b/runtime/v2/app.go @@ -0,0 +1,125 @@ +package runtime + +import ( + "encoding/json" + "errors" + "slices" + + gogoproto "github.com/cosmos/gogoproto/proto" + + runtimev2 "cosmossdk.io/api/cosmos/app/runtime/v2" + "cosmossdk.io/core/registry" + "cosmossdk.io/core/transaction" + "cosmossdk.io/log" + "cosmossdk.io/server/v2/appmanager" + "cosmossdk.io/server/v2/stf" +) + +// App is a wrapper around AppManager and ModuleManager that can be used in hybrid +// app.go/app config scenarios or directly as a servertypes.Application instance. +// To get an instance of *App, *AppBuilder must be requested as a dependency +// in a container which declares the runtime module and the AppBuilder.Build() +// method must be called. +// +// App can be used to create a hybrid app.go setup where some configuration is +// done declaratively with an app config and the rest of it is done the old way. +// See simapp/app_v2.go for an example of this setup. +type App[T transaction.Tx] struct { + *appmanager.AppManager[T] + + // app manager dependencies + stf *stf.STF[T] + msgRouterBuilder *stf.MsgRouterBuilder + queryRouterBuilder *stf.MsgRouterBuilder + db Store + + // app configuration + logger log.Logger + config *runtimev2.Module + + // modules configuration + storeKeys []string + interfaceRegistrar registry.InterfaceRegistrar + amino registry.AminoRegistrar + moduleManager *MM[T] + + // GRPCMethodsToMessageMap maps gRPC method name to a function that decodes the request + // bytes into a gogoproto.Message, which then can be passed to appmanager. + GRPCMethodsToMessageMap map[string]func() gogoproto.Message + + storeLoader StoreLoader +} + +// Name returns the app name. +func (a *App[T]) Name() string { + return a.config.AppName +} + +// Logger returns the app logger. +func (a *App[T]) Logger() log.Logger { + return a.logger +} + +// ModuleManager returns the module manager. +func (a *App[T]) ModuleManager() *MM[T] { + return a.moduleManager +} + +// DefaultGenesis returns a default genesis from the registered modules. +func (a *App[T]) DefaultGenesis() map[string]json.RawMessage { + return a.moduleManager.DefaultGenesis() +} + +// SetStoreLoader sets the store loader. +func (a *App[T]) SetStoreLoader(loader StoreLoader) { + a.storeLoader = loader +} + +// LoadLatest loads the latest version. +func (a *App[T]) LoadLatest() error { + return a.storeLoader(a.db) +} + +// LoadHeight loads a particular height +func (a *App[T]) LoadHeight(height uint64) error { + return a.db.LoadVersion(height) +} + +// LoadLatestHeight loads the latest height. +func (a *App[T]) LoadLatestHeight() (uint64, error) { + return a.db.GetLatestVersion() +} + +// Close is called in start cmd to gracefully cleanup resources. +func (a *App[T]) Close() error { + return nil +} + +// GetStoreKeys returns all the app store keys. +func (a *App[T]) GetStoreKeys() []string { + return a.storeKeys +} + +// UnsafeFindStoreKey fetches a registered StoreKey from the App in linear time. +// NOTE: This should only be used in testing. +func (a *App[T]) UnsafeFindStoreKey(storeKey string) (string, error) { + i := slices.IndexFunc(a.storeKeys, func(s string) bool { return s == storeKey }) + if i == -1 { + return "", errors.New("store key not found") + } + + return a.storeKeys[i], nil +} + +// GetStore returns the app store. +func (a *App[T]) GetStore() Store { + return a.db +} + +func (a *App[T]) GetAppManager() *appmanager.AppManager[T] { + return a.AppManager +} + +func (a *App[T]) GetGPRCMethodsToMessageMap() map[string]func() gogoproto.Message { + return a.GRPCMethodsToMessageMap +} diff --git a/runtime/v2/module.go b/runtime/v2/module.go new file mode 100644 index 000000000000..8db0f557a57b --- /dev/null +++ b/runtime/v2/module.go @@ -0,0 +1,239 @@ +package runtime + +import ( + "fmt" + "os" + "slices" + + "github.com/cosmos/gogoproto/proto" + "google.golang.org/grpc" + "google.golang.org/protobuf/reflect/protodesc" + "google.golang.org/protobuf/reflect/protoregistry" + + runtimev2 "cosmossdk.io/api/cosmos/app/runtime/v2" + appv1alpha1 "cosmossdk.io/api/cosmos/app/v1alpha1" + autocliv1 "cosmossdk.io/api/cosmos/autocli/v1" + reflectionv1 "cosmossdk.io/api/cosmos/reflection/v1" + appmodulev2 "cosmossdk.io/core/appmodule/v2" + "cosmossdk.io/core/comet" + "cosmossdk.io/core/registry" + "cosmossdk.io/core/server" + "cosmossdk.io/core/store" + "cosmossdk.io/core/transaction" + "cosmossdk.io/depinject" + "cosmossdk.io/depinject/appconfig" + "cosmossdk.io/log" + "cosmossdk.io/runtime/v2/services" + "cosmossdk.io/server/v2/stf" +) + +var ( + _ appmodulev2.AppModule = appModule[transaction.Tx]{} + _ hasServicesV1 = appModule[transaction.Tx]{} +) + +type appModule[T transaction.Tx] struct { + app *App[T] +} + +func (m appModule[T]) IsOnePerModuleType() {} +func (m appModule[T]) IsAppModule() {} + +func (m appModule[T]) RegisterServices(registar grpc.ServiceRegistrar) error { + autoCliQueryService, err := services.NewAutoCLIQueryService(m.app.moduleManager.modules) + if err != nil { + return err + } + + autocliv1.RegisterQueryServer(registar, autoCliQueryService) + + reflectionSvc, err := services.NewReflectionService() + if err != nil { + return err + } + reflectionv1.RegisterReflectionServiceServer(registar, reflectionSvc) + + return nil +} + +func (m appModule[T]) AutoCLIOptions() *autocliv1.ModuleOptions { + return &autocliv1.ModuleOptions{ + Query: &autocliv1.ServiceCommandDescriptor{ + Service: appv1alpha1.Query_ServiceDesc.ServiceName, + RpcCommandOptions: []*autocliv1.RpcCommandOptions{ + { + RpcMethod: "Config", + Short: "Query the current app config", + }, + }, + SubCommands: map[string]*autocliv1.ServiceCommandDescriptor{ + "autocli": { + Service: autocliv1.Query_ServiceDesc.ServiceName, + RpcCommandOptions: []*autocliv1.RpcCommandOptions{ + { + RpcMethod: "AppOptions", + Short: "Query the custom autocli options", + }, + }, + }, + "reflection": { + Service: reflectionv1.ReflectionService_ServiceDesc.ServiceName, + RpcCommandOptions: []*autocliv1.RpcCommandOptions{ + { + RpcMethod: "FileDescriptors", + Short: "Query the app's protobuf file descriptors", + }, + }, + }, + }, + }, + } +} + +func init() { + appconfig.Register(&runtimev2.Module{}, + appconfig.Provide( + ProvideAppBuilder[transaction.Tx], + ProvideEnvironment[transaction.Tx], + ProvideModuleManager[transaction.Tx], + ProvideCometService, + ), + appconfig.Invoke(SetupAppBuilder), + ) +} + +func ProvideAppBuilder[T transaction.Tx]( + interfaceRegistrar registry.InterfaceRegistrar, + amino registry.AminoRegistrar, +) ( + *AppBuilder[T], + *stf.MsgRouterBuilder, + appmodulev2.AppModule, + protodesc.Resolver, + protoregistry.MessageTypeResolver, +) { + protoFiles := proto.HybridResolver + protoTypes := protoregistry.GlobalTypes + + // At startup, check that all proto annotations are correct. + if err := validateProtoAnnotations(protoFiles); err != nil { + // Once we switch to using protoreflect-based ante handlers, we might + // want to panic here instead of logging a warning. + _, _ = fmt.Fprintln(os.Stderr, err.Error()) + } + + msgRouterBuilder := stf.NewMsgRouterBuilder() + app := &App[T]{ + storeKeys: nil, + interfaceRegistrar: interfaceRegistrar, + amino: amino, + msgRouterBuilder: msgRouterBuilder, + queryRouterBuilder: stf.NewMsgRouterBuilder(), // TODO dedicated query router + GRPCMethodsToMessageMap: map[string]func() proto.Message{}, + storeLoader: DefaultStoreLoader, + } + appBuilder := &AppBuilder[T]{app: app} + + return appBuilder, msgRouterBuilder, appModule[T]{app}, protoFiles, protoTypes +} + +type AppInputs struct { + depinject.In + + Config *runtimev2.Module + AppBuilder *AppBuilder[transaction.Tx] + ModuleManager *MM[transaction.Tx] + InterfaceRegistrar registry.InterfaceRegistrar + LegacyAmino registry.AminoRegistrar + Logger log.Logger + DynamicConfig server.DynamicConfig `optional:"true"` // can be nil in client wiring +} + +func SetupAppBuilder(inputs AppInputs) { + app := inputs.AppBuilder.app + app.config = inputs.Config + app.logger = inputs.Logger + app.moduleManager = inputs.ModuleManager + app.moduleManager.RegisterInterfaces(inputs.InterfaceRegistrar) + app.moduleManager.RegisterLegacyAminoCodec(inputs.LegacyAmino) + + if inputs.DynamicConfig != nil { + inputs.AppBuilder.config = inputs.DynamicConfig + } +} + +func ProvideModuleManager[T transaction.Tx]( + logger log.Logger, + config *runtimev2.Module, + modules map[string]appmodulev2.AppModule, +) *MM[T] { + return NewModuleManager[T](logger, config, modules) +} + +// ProvideEnvironment provides the environment for keeper modules, while maintaining backward compatibility and provide services directly as well. +func ProvideEnvironment[T transaction.Tx]( + logger log.Logger, + config *runtimev2.Module, + key depinject.ModuleKey, + appBuilder *AppBuilder[T], +) ( + appmodulev2.Environment, + store.KVStoreService, + store.MemoryStoreService, +) { + var ( + kvService store.KVStoreService = failingStoreService{} + memKvService store.MemoryStoreService = failingStoreService{} + ) + + // skips modules that have no store + if !slices.Contains(config.SkipStoreKeys, key.Name()) { + var kvStoreKey string + storeKeyOverride := storeKeyOverride(config, key.Name()) + if storeKeyOverride != nil { + kvStoreKey = storeKeyOverride.KvStoreKey + } else { + kvStoreKey = key.Name() + } + + registerStoreKey(appBuilder, kvStoreKey) + kvService = stf.NewKVStoreService([]byte(kvStoreKey)) + + memStoreKey := fmt.Sprintf("memory:%s", key.Name()) + registerStoreKey(appBuilder, memStoreKey) + memKvService = stf.NewMemoryStoreService([]byte(memStoreKey)) + } + + env := appmodulev2.Environment{ + Logger: logger, + BranchService: stf.BranchService{}, + EventService: stf.NewEventService(), + GasService: stf.NewGasMeterService(), + HeaderService: stf.HeaderService{}, + QueryRouterService: stf.NewQueryRouterService(), + MsgRouterService: stf.NewMsgRouterService([]byte(key.Name())), + TransactionService: services.NewContextAwareTransactionService(), + KVStoreService: kvService, + MemStoreService: memKvService, + } + + return env, kvService, memKvService +} + +func registerStoreKey[T transaction.Tx](wrapper *AppBuilder[T], key string) { + wrapper.app.storeKeys = append(wrapper.app.storeKeys, key) +} + +func storeKeyOverride(config *runtimev2.Module, moduleName string) *runtimev2.StoreKeyConfig { + for _, cfg := range config.OverrideStoreKeys { + if cfg.ModuleName == moduleName { + return cfg + } + } + + return nil +} + +func ProvideCometService() comet.Service { + return &services.ContextAwareCometInfoService{} +} diff --git a/runtime/v2/store.go b/runtime/v2/store.go new file mode 100644 index 000000000000..5268033ad323 --- /dev/null +++ b/runtime/v2/store.go @@ -0,0 +1,90 @@ +package runtime + +import ( + "fmt" + + "cosmossdk.io/core/store" + "cosmossdk.io/server/v2/stf" + storev2 "cosmossdk.io/store/v2" + "cosmossdk.io/store/v2/proof" +) + +// NewKVStoreService creates a new KVStoreService. +// This wrapper is kept for backwards compatibility. +// When migrating from runtime to runtime/v2, use runtimev2.NewKVStoreService(storeKey.Name()) instead of runtime.NewKVStoreService(storeKey). +func NewKVStoreService(storeKey string) store.KVStoreService { + return stf.NewKVStoreService([]byte(storeKey)) +} + +type Store interface { + // GetLatestVersion returns the latest version that consensus has been made on + GetLatestVersion() (uint64, error) + // StateLatest returns a readonly view over the latest + // committed state of the store. Alongside the version + // associated with it. + StateLatest() (uint64, store.ReaderMap, error) + + // StateAt returns a readonly view over the provided + // version. Must error when the version does not exist. + StateAt(version uint64) (store.ReaderMap, error) + + // SetInitialVersion sets the initial version of the store. + SetInitialVersion(uint64) error + + // WorkingHash writes the provided changeset to the state and returns + // the working hash of the state. + WorkingHash(changeset *store.Changeset) (store.Hash, error) + + // Commit commits the provided changeset and returns the new state root of the state. + Commit(changeset *store.Changeset) (store.Hash, error) + + // Query is a key/value query directly to the underlying database. This skips the appmanager + Query(storeKey []byte, version uint64, key []byte, prove bool) (storev2.QueryResult, error) + + // GetStateStorage returns the SS backend. + GetStateStorage() storev2.VersionedDatabase + + // GetStateCommitment returns the SC backend. + GetStateCommitment() storev2.Committer + + // LoadVersion loads the RootStore to the given version. + LoadVersion(version uint64) error + + // LoadLatestVersion behaves identically to LoadVersion except it loads the + // latest version implicitly. + LoadLatestVersion() error + + // LastCommitID returns the latest commit ID + LastCommitID() (proof.CommitID, error) +} + +// 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 + +// DefaultStoreLoader just calls LoadLatestVersion on the store +func DefaultStoreLoader(store Store) error { + return store.LoadLatestVersion() +} + +// UpgradeStoreLoader upgrades the store if the upgrade height matches the current version, it is used as a replacement +// for the DefaultStoreLoader when there are store upgrades +func UpgradeStoreLoader(upgradeHeight int64, storeUpgrades *store.StoreUpgrades) StoreLoader { + return func(store Store) error { + latestVersion, err := store.GetLatestVersion() + if err != nil { + return err + } + + if uint64(upgradeHeight) == latestVersion+1 { + if len(storeUpgrades.Deleted) > 0 || len(storeUpgrades.Added) > 0 { + if upgrader, ok := store.(storev2.UpgradeableStore); ok { + return upgrader.LoadVersionAndUpgrade(latestVersion, storeUpgrades) + } + + return fmt.Errorf("store does not support upgrades") + } + } + + return DefaultStoreLoader(store) + } +} diff --git a/simapp/v2/upgrades.go b/simapp/v2/upgrades.go index aa8ff003bb93..35355b28c367 100644 --- a/simapp/v2/upgrades.go +++ b/simapp/v2/upgrades.go @@ -5,6 +5,7 @@ import ( "cosmossdk.io/core/appmodule" "cosmossdk.io/core/store" + "cosmossdk.io/runtime/v2" "cosmossdk.io/x/accounts" protocolpooltypes "cosmossdk.io/x/protocolpool/types" upgradetypes "cosmossdk.io/x/upgrade/types" @@ -40,8 +41,6 @@ func (app *SimApp[T]) RegisterUpgradeHandlers() { Deleted: []string{"crisis"}, // The SDK discontinued the crisis module in v0.52.0 } - // configure store loader that checks if version == upgradeHeight and applies store upgrades - _ = storeUpgrades - // app.SetStoreLoader(upgradetypes.UpgradeStoreLoader(upgradeInfo.Height, &storeUpgrades)) + app.SetStoreLoader(runtime.UpgradeStoreLoader(upgradeInfo.Height, &storeUpgrades)) } } diff --git a/x/upgrade/README.md b/x/upgrade/README.md index be6e3476a156..81534d422694 100644 --- a/x/upgrade/README.md +++ b/x/upgrade/README.md @@ -94,6 +94,9 @@ expected upgrade. It eliminiates the chances for the new binary to execute `Stor times every time on restart. Also if there are multiple upgrades planned on same height, the `Name` will ensure these `StoreUpgrades` takes place only in planned upgrade handler. +**Note:** The `StoreLoader` helper function for StoreUpgrades in v2 is not part of the `x/upgrade` module; +instead, you can find it in the runtime v2 module. + ### Proposal Typically, a `Plan` is proposed and submitted through governance via a proposal diff --git a/x/upgrade/types/storeloader.go b/x/upgrade/types/storeloader.go index 881c8df29eab..7099c9395dee 100644 --- a/x/upgrade/types/storeloader.go +++ b/x/upgrade/types/storeloader.go @@ -1,28 +1,6 @@ package types -import ( - corestore "cosmossdk.io/core/store" - storetypes "cosmossdk.io/store/types" +import "github.com/cosmos/cosmos-sdk/runtime" - "github.com/cosmos/cosmos-sdk/baseapp" -) - -// UpgradeStoreLoader is used to prepare baseapp with a fixed StoreLoader -// pattern. This is useful for custom upgrade loading logic. -func UpgradeStoreLoader(upgradeHeight int64, storeUpgrades *corestore.StoreUpgrades) baseapp.StoreLoader { - return func(ms storetypes.CommitMultiStore) error { - if upgradeHeight == ms.LastCommitID().Version+1 { - // Check if the current commit version and upgrade height matches - if len(storeUpgrades.Deleted) > 0 || len(storeUpgrades.Added) > 0 { - stup := &storetypes.StoreUpgrades{ - Added: storeUpgrades.Added, - Deleted: storeUpgrades.Deleted, - } - return ms.LoadLatestVersionAndUpgrade(stup) - } - } - - // Otherwise load default store loader - return baseapp.DefaultStoreLoader(ms) - } -} +// UpgradeStoreLoader moved to runtime package, keeping this for backwards compatibility +var UpgradeStoreLoader = runtime.UpgradeStoreLoader From 0fbba7675254eb4e6085059227bd1c592e2567d1 Mon Sep 17 00:00:00 2001 From: Julien Robert Date: Fri, 13 Sep 2024 16:30:24 +0200 Subject: [PATCH 2/3] remove runtime and use pseudo version --- runtime/v2/app.go | 125 ---------------------- runtime/v2/module.go | 239 ------------------------------------------- runtime/v2/store.go | 90 ---------------- simapp/v2/go.mod | 4 +- simapp/v2/go.sum | 8 +- 5 files changed, 6 insertions(+), 460 deletions(-) delete mode 100644 runtime/v2/app.go delete mode 100644 runtime/v2/module.go delete mode 100644 runtime/v2/store.go diff --git a/runtime/v2/app.go b/runtime/v2/app.go deleted file mode 100644 index b7104f9e4774..000000000000 --- a/runtime/v2/app.go +++ /dev/null @@ -1,125 +0,0 @@ -package runtime - -import ( - "encoding/json" - "errors" - "slices" - - gogoproto "github.com/cosmos/gogoproto/proto" - - runtimev2 "cosmossdk.io/api/cosmos/app/runtime/v2" - "cosmossdk.io/core/registry" - "cosmossdk.io/core/transaction" - "cosmossdk.io/log" - "cosmossdk.io/server/v2/appmanager" - "cosmossdk.io/server/v2/stf" -) - -// App is a wrapper around AppManager and ModuleManager that can be used in hybrid -// app.go/app config scenarios or directly as a servertypes.Application instance. -// To get an instance of *App, *AppBuilder must be requested as a dependency -// in a container which declares the runtime module and the AppBuilder.Build() -// method must be called. -// -// App can be used to create a hybrid app.go setup where some configuration is -// done declaratively with an app config and the rest of it is done the old way. -// See simapp/app_v2.go for an example of this setup. -type App[T transaction.Tx] struct { - *appmanager.AppManager[T] - - // app manager dependencies - stf *stf.STF[T] - msgRouterBuilder *stf.MsgRouterBuilder - queryRouterBuilder *stf.MsgRouterBuilder - db Store - - // app configuration - logger log.Logger - config *runtimev2.Module - - // modules configuration - storeKeys []string - interfaceRegistrar registry.InterfaceRegistrar - amino registry.AminoRegistrar - moduleManager *MM[T] - - // GRPCMethodsToMessageMap maps gRPC method name to a function that decodes the request - // bytes into a gogoproto.Message, which then can be passed to appmanager. - GRPCMethodsToMessageMap map[string]func() gogoproto.Message - - storeLoader StoreLoader -} - -// Name returns the app name. -func (a *App[T]) Name() string { - return a.config.AppName -} - -// Logger returns the app logger. -func (a *App[T]) Logger() log.Logger { - return a.logger -} - -// ModuleManager returns the module manager. -func (a *App[T]) ModuleManager() *MM[T] { - return a.moduleManager -} - -// DefaultGenesis returns a default genesis from the registered modules. -func (a *App[T]) DefaultGenesis() map[string]json.RawMessage { - return a.moduleManager.DefaultGenesis() -} - -// SetStoreLoader sets the store loader. -func (a *App[T]) SetStoreLoader(loader StoreLoader) { - a.storeLoader = loader -} - -// LoadLatest loads the latest version. -func (a *App[T]) LoadLatest() error { - return a.storeLoader(a.db) -} - -// LoadHeight loads a particular height -func (a *App[T]) LoadHeight(height uint64) error { - return a.db.LoadVersion(height) -} - -// LoadLatestHeight loads the latest height. -func (a *App[T]) LoadLatestHeight() (uint64, error) { - return a.db.GetLatestVersion() -} - -// Close is called in start cmd to gracefully cleanup resources. -func (a *App[T]) Close() error { - return nil -} - -// GetStoreKeys returns all the app store keys. -func (a *App[T]) GetStoreKeys() []string { - return a.storeKeys -} - -// UnsafeFindStoreKey fetches a registered StoreKey from the App in linear time. -// NOTE: This should only be used in testing. -func (a *App[T]) UnsafeFindStoreKey(storeKey string) (string, error) { - i := slices.IndexFunc(a.storeKeys, func(s string) bool { return s == storeKey }) - if i == -1 { - return "", errors.New("store key not found") - } - - return a.storeKeys[i], nil -} - -// GetStore returns the app store. -func (a *App[T]) GetStore() Store { - return a.db -} - -func (a *App[T]) GetAppManager() *appmanager.AppManager[T] { - return a.AppManager -} - -func (a *App[T]) GetGPRCMethodsToMessageMap() map[string]func() gogoproto.Message { - return a.GRPCMethodsToMessageMap -} diff --git a/runtime/v2/module.go b/runtime/v2/module.go deleted file mode 100644 index 8db0f557a57b..000000000000 --- a/runtime/v2/module.go +++ /dev/null @@ -1,239 +0,0 @@ -package runtime - -import ( - "fmt" - "os" - "slices" - - "github.com/cosmos/gogoproto/proto" - "google.golang.org/grpc" - "google.golang.org/protobuf/reflect/protodesc" - "google.golang.org/protobuf/reflect/protoregistry" - - runtimev2 "cosmossdk.io/api/cosmos/app/runtime/v2" - appv1alpha1 "cosmossdk.io/api/cosmos/app/v1alpha1" - autocliv1 "cosmossdk.io/api/cosmos/autocli/v1" - reflectionv1 "cosmossdk.io/api/cosmos/reflection/v1" - appmodulev2 "cosmossdk.io/core/appmodule/v2" - "cosmossdk.io/core/comet" - "cosmossdk.io/core/registry" - "cosmossdk.io/core/server" - "cosmossdk.io/core/store" - "cosmossdk.io/core/transaction" - "cosmossdk.io/depinject" - "cosmossdk.io/depinject/appconfig" - "cosmossdk.io/log" - "cosmossdk.io/runtime/v2/services" - "cosmossdk.io/server/v2/stf" -) - -var ( - _ appmodulev2.AppModule = appModule[transaction.Tx]{} - _ hasServicesV1 = appModule[transaction.Tx]{} -) - -type appModule[T transaction.Tx] struct { - app *App[T] -} - -func (m appModule[T]) IsOnePerModuleType() {} -func (m appModule[T]) IsAppModule() {} - -func (m appModule[T]) RegisterServices(registar grpc.ServiceRegistrar) error { - autoCliQueryService, err := services.NewAutoCLIQueryService(m.app.moduleManager.modules) - if err != nil { - return err - } - - autocliv1.RegisterQueryServer(registar, autoCliQueryService) - - reflectionSvc, err := services.NewReflectionService() - if err != nil { - return err - } - reflectionv1.RegisterReflectionServiceServer(registar, reflectionSvc) - - return nil -} - -func (m appModule[T]) AutoCLIOptions() *autocliv1.ModuleOptions { - return &autocliv1.ModuleOptions{ - Query: &autocliv1.ServiceCommandDescriptor{ - Service: appv1alpha1.Query_ServiceDesc.ServiceName, - RpcCommandOptions: []*autocliv1.RpcCommandOptions{ - { - RpcMethod: "Config", - Short: "Query the current app config", - }, - }, - SubCommands: map[string]*autocliv1.ServiceCommandDescriptor{ - "autocli": { - Service: autocliv1.Query_ServiceDesc.ServiceName, - RpcCommandOptions: []*autocliv1.RpcCommandOptions{ - { - RpcMethod: "AppOptions", - Short: "Query the custom autocli options", - }, - }, - }, - "reflection": { - Service: reflectionv1.ReflectionService_ServiceDesc.ServiceName, - RpcCommandOptions: []*autocliv1.RpcCommandOptions{ - { - RpcMethod: "FileDescriptors", - Short: "Query the app's protobuf file descriptors", - }, - }, - }, - }, - }, - } -} - -func init() { - appconfig.Register(&runtimev2.Module{}, - appconfig.Provide( - ProvideAppBuilder[transaction.Tx], - ProvideEnvironment[transaction.Tx], - ProvideModuleManager[transaction.Tx], - ProvideCometService, - ), - appconfig.Invoke(SetupAppBuilder), - ) -} - -func ProvideAppBuilder[T transaction.Tx]( - interfaceRegistrar registry.InterfaceRegistrar, - amino registry.AminoRegistrar, -) ( - *AppBuilder[T], - *stf.MsgRouterBuilder, - appmodulev2.AppModule, - protodesc.Resolver, - protoregistry.MessageTypeResolver, -) { - protoFiles := proto.HybridResolver - protoTypes := protoregistry.GlobalTypes - - // At startup, check that all proto annotations are correct. - if err := validateProtoAnnotations(protoFiles); err != nil { - // Once we switch to using protoreflect-based ante handlers, we might - // want to panic here instead of logging a warning. - _, _ = fmt.Fprintln(os.Stderr, err.Error()) - } - - msgRouterBuilder := stf.NewMsgRouterBuilder() - app := &App[T]{ - storeKeys: nil, - interfaceRegistrar: interfaceRegistrar, - amino: amino, - msgRouterBuilder: msgRouterBuilder, - queryRouterBuilder: stf.NewMsgRouterBuilder(), // TODO dedicated query router - GRPCMethodsToMessageMap: map[string]func() proto.Message{}, - storeLoader: DefaultStoreLoader, - } - appBuilder := &AppBuilder[T]{app: app} - - return appBuilder, msgRouterBuilder, appModule[T]{app}, protoFiles, protoTypes -} - -type AppInputs struct { - depinject.In - - Config *runtimev2.Module - AppBuilder *AppBuilder[transaction.Tx] - ModuleManager *MM[transaction.Tx] - InterfaceRegistrar registry.InterfaceRegistrar - LegacyAmino registry.AminoRegistrar - Logger log.Logger - DynamicConfig server.DynamicConfig `optional:"true"` // can be nil in client wiring -} - -func SetupAppBuilder(inputs AppInputs) { - app := inputs.AppBuilder.app - app.config = inputs.Config - app.logger = inputs.Logger - app.moduleManager = inputs.ModuleManager - app.moduleManager.RegisterInterfaces(inputs.InterfaceRegistrar) - app.moduleManager.RegisterLegacyAminoCodec(inputs.LegacyAmino) - - if inputs.DynamicConfig != nil { - inputs.AppBuilder.config = inputs.DynamicConfig - } -} - -func ProvideModuleManager[T transaction.Tx]( - logger log.Logger, - config *runtimev2.Module, - modules map[string]appmodulev2.AppModule, -) *MM[T] { - return NewModuleManager[T](logger, config, modules) -} - -// ProvideEnvironment provides the environment for keeper modules, while maintaining backward compatibility and provide services directly as well. -func ProvideEnvironment[T transaction.Tx]( - logger log.Logger, - config *runtimev2.Module, - key depinject.ModuleKey, - appBuilder *AppBuilder[T], -) ( - appmodulev2.Environment, - store.KVStoreService, - store.MemoryStoreService, -) { - var ( - kvService store.KVStoreService = failingStoreService{} - memKvService store.MemoryStoreService = failingStoreService{} - ) - - // skips modules that have no store - if !slices.Contains(config.SkipStoreKeys, key.Name()) { - var kvStoreKey string - storeKeyOverride := storeKeyOverride(config, key.Name()) - if storeKeyOverride != nil { - kvStoreKey = storeKeyOverride.KvStoreKey - } else { - kvStoreKey = key.Name() - } - - registerStoreKey(appBuilder, kvStoreKey) - kvService = stf.NewKVStoreService([]byte(kvStoreKey)) - - memStoreKey := fmt.Sprintf("memory:%s", key.Name()) - registerStoreKey(appBuilder, memStoreKey) - memKvService = stf.NewMemoryStoreService([]byte(memStoreKey)) - } - - env := appmodulev2.Environment{ - Logger: logger, - BranchService: stf.BranchService{}, - EventService: stf.NewEventService(), - GasService: stf.NewGasMeterService(), - HeaderService: stf.HeaderService{}, - QueryRouterService: stf.NewQueryRouterService(), - MsgRouterService: stf.NewMsgRouterService([]byte(key.Name())), - TransactionService: services.NewContextAwareTransactionService(), - KVStoreService: kvService, - MemStoreService: memKvService, - } - - return env, kvService, memKvService -} - -func registerStoreKey[T transaction.Tx](wrapper *AppBuilder[T], key string) { - wrapper.app.storeKeys = append(wrapper.app.storeKeys, key) -} - -func storeKeyOverride(config *runtimev2.Module, moduleName string) *runtimev2.StoreKeyConfig { - for _, cfg := range config.OverrideStoreKeys { - if cfg.ModuleName == moduleName { - return cfg - } - } - - return nil -} - -func ProvideCometService() comet.Service { - return &services.ContextAwareCometInfoService{} -} diff --git a/runtime/v2/store.go b/runtime/v2/store.go deleted file mode 100644 index 5268033ad323..000000000000 --- a/runtime/v2/store.go +++ /dev/null @@ -1,90 +0,0 @@ -package runtime - -import ( - "fmt" - - "cosmossdk.io/core/store" - "cosmossdk.io/server/v2/stf" - storev2 "cosmossdk.io/store/v2" - "cosmossdk.io/store/v2/proof" -) - -// NewKVStoreService creates a new KVStoreService. -// This wrapper is kept for backwards compatibility. -// When migrating from runtime to runtime/v2, use runtimev2.NewKVStoreService(storeKey.Name()) instead of runtime.NewKVStoreService(storeKey). -func NewKVStoreService(storeKey string) store.KVStoreService { - return stf.NewKVStoreService([]byte(storeKey)) -} - -type Store interface { - // GetLatestVersion returns the latest version that consensus has been made on - GetLatestVersion() (uint64, error) - // StateLatest returns a readonly view over the latest - // committed state of the store. Alongside the version - // associated with it. - StateLatest() (uint64, store.ReaderMap, error) - - // StateAt returns a readonly view over the provided - // version. Must error when the version does not exist. - StateAt(version uint64) (store.ReaderMap, error) - - // SetInitialVersion sets the initial version of the store. - SetInitialVersion(uint64) error - - // WorkingHash writes the provided changeset to the state and returns - // the working hash of the state. - WorkingHash(changeset *store.Changeset) (store.Hash, error) - - // Commit commits the provided changeset and returns the new state root of the state. - Commit(changeset *store.Changeset) (store.Hash, error) - - // Query is a key/value query directly to the underlying database. This skips the appmanager - Query(storeKey []byte, version uint64, key []byte, prove bool) (storev2.QueryResult, error) - - // GetStateStorage returns the SS backend. - GetStateStorage() storev2.VersionedDatabase - - // GetStateCommitment returns the SC backend. - GetStateCommitment() storev2.Committer - - // LoadVersion loads the RootStore to the given version. - LoadVersion(version uint64) error - - // LoadLatestVersion behaves identically to LoadVersion except it loads the - // latest version implicitly. - LoadLatestVersion() error - - // LastCommitID returns the latest commit ID - LastCommitID() (proof.CommitID, error) -} - -// 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 - -// DefaultStoreLoader just calls LoadLatestVersion on the store -func DefaultStoreLoader(store Store) error { - return store.LoadLatestVersion() -} - -// UpgradeStoreLoader upgrades the store if the upgrade height matches the current version, it is used as a replacement -// for the DefaultStoreLoader when there are store upgrades -func UpgradeStoreLoader(upgradeHeight int64, storeUpgrades *store.StoreUpgrades) StoreLoader { - return func(store Store) error { - latestVersion, err := store.GetLatestVersion() - if err != nil { - return err - } - - if uint64(upgradeHeight) == latestVersion+1 { - if len(storeUpgrades.Deleted) > 0 || len(storeUpgrades.Added) > 0 { - if upgrader, ok := store.(storev2.UpgradeableStore); ok { - return upgrader.LoadVersionAndUpgrade(latestVersion, storeUpgrades) - } - - return fmt.Errorf("store does not support upgrades") - } - } - - return DefaultStoreLoader(store) - } -} diff --git a/simapp/v2/go.mod b/simapp/v2/go.mod index 707d3b46f1ce..54104b1a64a2 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-20240913102804-9fc6675885f9 // main + cosmossdk.io/runtime/v2 v2.0.0-20240913142214-7d6ff0df5642 // 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 @@ -227,7 +227,7 @@ require ( google.golang.org/genproto v0.0.0-20240617180043-68d350f18fd4 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240725223205-93522f1f2a9f // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect - google.golang.org/grpc v1.66.1 // indirect + google.golang.org/grpc v1.66.2 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect gotest.tools/v3 v3.5.1 // indirect diff --git a/simapp/v2/go.sum b/simapp/v2/go.sum index dc3a52245d48..dee0b887644e 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-20240913102804-9fc6675885f9 h1:xAPMtcHd8bW8F+Nt/lRmoe3gAD/zghEkzHdXb/5hkwY= -cosmossdk.io/runtime/v2 v2.0.0-20240913102804-9fc6675885f9/go.mod h1:bIUuDyiXe4zAa/QbOhciGdtTsmyytU8kt1FjLVtcZ1Q= +cosmossdk.io/runtime/v2 v2.0.0-20240913142214-7d6ff0df5642 h1:04qILoFdYGZX8knOWFNSvs/Pineca+uzErliyqx0t3s= +cosmossdk.io/runtime/v2 v2.0.0-20240913142214-7d6ff0df5642/go.mod h1:d0B92058w/s/Yegf0ySClPPJn+Vp7Cz+H3ORggSSGm4= 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= @@ -1399,8 +1399,8 @@ google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACu google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= google.golang.org/grpc v1.50.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= -google.golang.org/grpc v1.66.1 h1:hO5qAXR19+/Z44hmvIM4dQFMSYX9XcWsByfoxutBpAM= -google.golang.org/grpc v1.66.1/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y= +google.golang.org/grpc v1.66.2 h1:3QdXkuq3Bkh7w+ywLdLvM56cmGvQHUMZpiCzt6Rqaoo= +google.golang.org/grpc v1.66.2/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= From 6712e3a4c3b6bb646849440ed158b702b649bda1 Mon Sep 17 00:00:00 2001 From: Julien Robert Date: Fri, 13 Sep 2024 16:31:29 +0200 Subject: [PATCH 3/3] cl --- CHANGELOG.md | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 99529f57e299..ed47aeb34cf8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,17 +40,10 @@ Ref: https://keepachangelog.com/en/1.0.0/ Every module contains its own CHANGELOG.md. Please refer to the module you are interested in. -<<<<<<< HEAD -======= -### Features - -* (baseapp) [#20291](https://github.com/cosmos/cosmos-sdk/pull/20291) Simulate nested messages. -* (runtime) [#21704](https://github.com/cosmos/cosmos-sdk/pull/21704) Add StoreLoader in simappv2. - ->>>>>>> 7d6ff0df5 (feat(runtime/v2): store loader on simappv2 (#21704)) ### Improvements * (genutil) [#21701](https://github.com/cosmos/cosmos-sdk/pull/21701) Improved error messages for genesis validation. +* (runtime) [#21704](https://github.com/cosmos/cosmos-sdk/pull/21704) Move `upgradetypes.StoreLoader` to runtime and alias it in upgrade for backward compatibility. ## [v0.52.0](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.52.0) - 2024-XX-XX