diff --git a/core/appmodule/v2/environment.go b/core/appmodule/v2/environment.go index eccc7eb297af..e3592eb210ad 100644 --- a/core/appmodule/v2/environment.go +++ b/core/appmodule/v2/environment.go @@ -7,6 +7,7 @@ import ( "cosmossdk.io/core/header" "cosmossdk.io/core/router" "cosmossdk.io/core/store" + "cosmossdk.io/core/transaction" "cosmossdk.io/log" ) @@ -14,11 +15,12 @@ import ( type Environment struct { Logger log.Logger - BranchService branch.Service - EventService event.Service - GasService gas.Service - HeaderService header.Service - RouterService router.Service + BranchService branch.Service + EventService event.Service + GasService gas.Service + HeaderService header.Service + RouterService router.Service + TransactionService transaction.Service KVStoreService store.KVStoreService MemStoreService store.MemoryStoreService diff --git a/core/transaction/service.go b/core/transaction/service.go new file mode 100644 index 000000000000..ac97b9a0595d --- /dev/null +++ b/core/transaction/service.go @@ -0,0 +1,24 @@ +package transaction + +import "context" + +// ExecMode defines the execution mode which can be set on a Context. +type ExecMode uint8 + +// All possible execution modes. +const ( + ExecModeCheck ExecMode = iota + _ + ExecModeSimulate + _ + _ + _ + _ + ExecModeFinalize +) + +// Service creates a transaction service. +// This service is used to get information about which context is used to execute a transaction. +type Service interface { + ExecMode(ctx context.Context) ExecMode +} diff --git a/runtime/autocli.go b/runtime/autocli.go index a942a3bee2fa..e7929876a892 100644 --- a/runtime/autocli.go +++ b/runtime/autocli.go @@ -1,33 +1,148 @@ package runtime import ( + "context" + + gogogrpc "github.com/cosmos/gogoproto/grpc" + "github.com/cosmos/gogoproto/proto" + "google.golang.org/grpc" + protobuf "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/reflect/protoreflect" + "google.golang.org/protobuf/reflect/protoregistry" + autocliv1 "cosmossdk.io/api/cosmos/autocli/v1" - reflectionv1 "cosmossdk.io/api/cosmos/reflection/v1" + cosmosmsg "cosmossdk.io/api/cosmos/msg/v1" + "cosmossdk.io/core/appmodule" + + "github.com/cosmos/cosmos-sdk/types/module" ) -func (m appModule) AutoCLIOptions() *autocliv1.ModuleOptions { - return &autocliv1.ModuleOptions{ - Query: &autocliv1.ServiceCommandDescriptor{ - 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", - }, - }, - }, - }, - }, +// AutoCLIQueryService implements the cosmos.autocli.v1.Query service. +type AutoCLIQueryService struct { + autocliv1.UnimplementedQueryServer + + moduleOptions map[string]*autocliv1.ModuleOptions +} + +// NewAutoCLIQueryService returns a AutoCLIQueryService for the provided modules. +func NewAutoCLIQueryService(appModules map[string]appmodule.AppModule) *AutoCLIQueryService { + return &AutoCLIQueryService{ + moduleOptions: ExtractAutoCLIOptions(appModules), + } +} + +// ExtractAutoCLIOptions extracts autocli ModuleOptions from the provided app modules. +// +// Example Usage: +// +// ExtractAutoCLIOptions(ModuleManager.Modules) +func ExtractAutoCLIOptions(appModules map[string]appmodule.AppModule) map[string]*autocliv1.ModuleOptions { + moduleOptions := map[string]*autocliv1.ModuleOptions{} + for modName, mod := range appModules { + if autoCliMod, ok := mod.(interface { + AutoCLIOptions() *autocliv1.ModuleOptions + }); ok { + moduleOptions[modName] = autoCliMod.AutoCLIOptions() + continue + } + + cfg := &autocliConfigurator{} + + // try to auto-discover options based on the last msg and query + // services registered for the module + if mod, ok := mod.(module.HasServices); ok { + mod.RegisterServices(cfg) + } + + if mod, ok := mod.(appmodule.HasServices); ok { + err := mod.RegisterServices(cfg) + if err != nil { + panic(err) + } + } + + // check for errors in the configurator + if cfg.Error() != nil { + panic(cfg.Error()) + } + + haveServices := false + modOptions := &autocliv1.ModuleOptions{} + if cfg.msgServer.serviceName != "" { + haveServices = true + modOptions.Tx = &autocliv1.ServiceCommandDescriptor{ + Service: cfg.msgServer.serviceName, + } + } + + if cfg.queryServer.serviceName != "" { + haveServices = true + modOptions.Query = &autocliv1.ServiceCommandDescriptor{ + Service: cfg.queryServer.serviceName, + } + } + + if haveServices { + moduleOptions[modName] = modOptions + } } + return moduleOptions +} + +func (a AutoCLIQueryService) AppOptions(context.Context, *autocliv1.AppOptionsRequest) (*autocliv1.AppOptionsResponse, error) { + return &autocliv1.AppOptionsResponse{ + ModuleOptions: a.moduleOptions, + }, nil +} + +// autocliConfigurator allows us to call RegisterServices and introspect the services +type autocliConfigurator struct { + msgServer autocliServiceRegistrar + queryServer autocliServiceRegistrar + registryCache *protoregistry.Files + err error } + +var _ module.Configurator = &autocliConfigurator{} // nolint:staticcheck // SA1019: Configurator is deprecated but still used in runtime v1. + +func (a *autocliConfigurator) MsgServer() gogogrpc.Server { return &a.msgServer } + +func (a *autocliConfigurator) QueryServer() gogogrpc.Server { return &a.queryServer } + +func (a *autocliConfigurator) RegisterMigration(string, uint64, module.MigrationHandler) error { + return nil +} + +func (a *autocliConfigurator) Register(string, uint64, appmodule.MigrationHandler) error { + return nil +} + +func (a *autocliConfigurator) RegisterService(sd *grpc.ServiceDesc, ss interface{}) { + if a.registryCache == nil { + a.registryCache, a.err = proto.MergedRegistry() + } + + desc, err := a.registryCache.FindDescriptorByName(protoreflect.FullName(sd.ServiceName)) + if err != nil { + a.err = err + return + } + + if protobuf.HasExtension(desc.Options(), cosmosmsg.E_Service) { + a.msgServer.RegisterService(sd, ss) + } else { + a.queryServer.RegisterService(sd, ss) + } +} +func (a *autocliConfigurator) Error() error { return nil } + +// autocliServiceRegistrar is used to capture the service name for registered services +type autocliServiceRegistrar struct { + serviceName string +} + +func (a *autocliServiceRegistrar) RegisterService(sd *grpc.ServiceDesc, _ interface{}) { + a.serviceName = sd.ServiceName +} + +var _ autocliv1.QueryServer = &AutoCLIQueryService{} diff --git a/runtime/environment.go b/runtime/environment.go index 38500ecb6b35..6fce64b35dec 100644 --- a/runtime/environment.go +++ b/runtime/environment.go @@ -17,12 +17,13 @@ func NewEnvironment( opts ...EnvOption, ) appmodule.Environment { env := appmodule.Environment{ - Logger: logger, - EventService: EventService{}, - HeaderService: HeaderService{}, - BranchService: BranchService{}, - GasService: GasService{}, - KVStoreService: kvService, + Logger: logger, + EventService: EventService{}, + HeaderService: HeaderService{}, + BranchService: BranchService{}, + GasService: GasService{}, + TransactionService: TransactionService{}, + KVStoreService: kvService, } for _, opt := range opts { diff --git a/runtime/module.go b/runtime/module.go index 41b6b55318d0..2a848bc8f91d 100644 --- a/runtime/module.go +++ b/runtime/module.go @@ -11,6 +11,8 @@ import ( runtimev1alpha1 "cosmossdk.io/api/cosmos/app/runtime/v1alpha1" appv1alpha1 "cosmossdk.io/api/cosmos/app/v1alpha1" authmodulev1 "cosmossdk.io/api/cosmos/auth/module/v1" + autocliv1 "cosmossdk.io/api/cosmos/autocli/v1" + reflectionv1 "cosmossdk.io/api/cosmos/reflection/v1" stakingmodulev1 "cosmossdk.io/api/cosmos/staking/module/v1" "cosmossdk.io/core/address" "cosmossdk.io/core/appmodule" @@ -31,19 +33,50 @@ import ( "github.com/cosmos/cosmos-sdk/types/msgservice" ) +// appModule defines runtime as an AppModule type appModule struct { app *App } +func (m appModule) IsOnePerModuleType() {} +func (m appModule) IsAppModule() {} + func (m appModule) RegisterServices(configurator module.Configurator) { // nolint:staticcheck // SA1019: Configurator is deprecated but still used in runtime v1. - err := m.app.registerRuntimeServices(configurator) + autocliv1.RegisterQueryServer(configurator.QueryServer(), NewAutoCLIQueryService(m.app.ModuleManager.Modules)) + + reflectionSvc, err := NewReflectionService() if err != nil { panic(err) } + reflectionv1.RegisterReflectionServiceServer(configurator.QueryServer(), reflectionSvc) } -func (m appModule) IsOnePerModuleType() {} -func (m appModule) IsAppModule() {} +func (m appModule) AutoCLIOptions() *autocliv1.ModuleOptions { + return &autocliv1.ModuleOptions{ + Query: &autocliv1.ServiceCommandDescriptor{ + 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", + }, + }, + }, + }, + }, + } +} var ( _ appmodule.AppModule = appModule{} diff --git a/runtime/services/reflection.go b/runtime/reflection.go similarity index 98% rename from runtime/services/reflection.go rename to runtime/reflection.go index 65225a482606..2a82b27d896f 100644 --- a/runtime/services/reflection.go +++ b/runtime/reflection.go @@ -1,4 +1,4 @@ -package services +package runtime import ( "context" diff --git a/runtime/services.go b/runtime/services.go deleted file mode 100644 index 2f453898ff88..000000000000 --- a/runtime/services.go +++ /dev/null @@ -1,21 +0,0 @@ -package runtime - -import ( - autocliv1 "cosmossdk.io/api/cosmos/autocli/v1" - reflectionv1 "cosmossdk.io/api/cosmos/reflection/v1" - - "github.com/cosmos/cosmos-sdk/runtime/services" - "github.com/cosmos/cosmos-sdk/types/module" -) - -func (a *App) registerRuntimeServices(cfg module.Configurator) error { // nolint:staticcheck // SA1019: Configurator is deprecated but still used in runtime v1. - autocliv1.RegisterQueryServer(cfg.QueryServer(), services.NewAutoCLIQueryService(a.ModuleManager.Modules)) - - reflectionSvc, err := services.NewReflectionService() - if err != nil { - return err - } - reflectionv1.RegisterReflectionServiceServer(cfg.QueryServer(), reflectionSvc) - - return nil -} diff --git a/runtime/services/autocli.go b/runtime/services/autocli.go deleted file mode 100644 index b130af0f1c11..000000000000 --- a/runtime/services/autocli.go +++ /dev/null @@ -1,148 +0,0 @@ -package services - -import ( - "context" - - gogogrpc "github.com/cosmos/gogoproto/grpc" - "github.com/cosmos/gogoproto/proto" - "google.golang.org/grpc" - protobuf "google.golang.org/protobuf/proto" - "google.golang.org/protobuf/reflect/protoreflect" - "google.golang.org/protobuf/reflect/protoregistry" - - autocliv1 "cosmossdk.io/api/cosmos/autocli/v1" - cosmosmsg "cosmossdk.io/api/cosmos/msg/v1" - "cosmossdk.io/core/appmodule" - - "github.com/cosmos/cosmos-sdk/types/module" -) - -// AutoCLIQueryService implements the cosmos.autocli.v1.Query service. -type AutoCLIQueryService struct { - autocliv1.UnimplementedQueryServer - - moduleOptions map[string]*autocliv1.ModuleOptions -} - -// NewAutoCLIQueryService returns a AutoCLIQueryService for the provided modules. -func NewAutoCLIQueryService(appModules map[string]appmodule.AppModule) *AutoCLIQueryService { - return &AutoCLIQueryService{ - moduleOptions: ExtractAutoCLIOptions(appModules), - } -} - -// ExtractAutoCLIOptions extracts autocli ModuleOptions from the provided app modules. -// -// Example Usage: -// -// ExtractAutoCLIOptions(ModuleManager.Modules) -func ExtractAutoCLIOptions(appModules map[string]appmodule.AppModule) map[string]*autocliv1.ModuleOptions { - moduleOptions := map[string]*autocliv1.ModuleOptions{} - for modName, mod := range appModules { - if autoCliMod, ok := mod.(interface { - AutoCLIOptions() *autocliv1.ModuleOptions - }); ok { - moduleOptions[modName] = autoCliMod.AutoCLIOptions() - continue - } - - cfg := &autocliConfigurator{} - - // try to auto-discover options based on the last msg and query - // services registered for the module - if mod, ok := mod.(module.HasServices); ok { - mod.RegisterServices(cfg) - } - - if mod, ok := mod.(appmodule.HasServices); ok { - err := mod.RegisterServices(cfg) - if err != nil { - panic(err) - } - } - - // check for errors in the configurator - if cfg.Error() != nil { - panic(cfg.Error()) - } - - haveServices := false - modOptions := &autocliv1.ModuleOptions{} - if cfg.msgServer.serviceName != "" { - haveServices = true - modOptions.Tx = &autocliv1.ServiceCommandDescriptor{ - Service: cfg.msgServer.serviceName, - } - } - - if cfg.queryServer.serviceName != "" { - haveServices = true - modOptions.Query = &autocliv1.ServiceCommandDescriptor{ - Service: cfg.queryServer.serviceName, - } - } - - if haveServices { - moduleOptions[modName] = modOptions - } - } - return moduleOptions -} - -func (a AutoCLIQueryService) AppOptions(context.Context, *autocliv1.AppOptionsRequest) (*autocliv1.AppOptionsResponse, error) { - return &autocliv1.AppOptionsResponse{ - ModuleOptions: a.moduleOptions, - }, nil -} - -// autocliConfigurator allows us to call RegisterServices and introspect the services -type autocliConfigurator struct { - msgServer autocliServiceRegistrar - queryServer autocliServiceRegistrar - registryCache *protoregistry.Files - err error -} - -var _ module.Configurator = &autocliConfigurator{} // nolint:staticcheck // SA1019: Configurator is deprecated but still used in runtime v1. - -func (a *autocliConfigurator) MsgServer() gogogrpc.Server { return &a.msgServer } - -func (a *autocliConfigurator) QueryServer() gogogrpc.Server { return &a.queryServer } - -func (a *autocliConfigurator) RegisterMigration(string, uint64, module.MigrationHandler) error { - return nil -} - -func (a *autocliConfigurator) Register(string, uint64, appmodule.MigrationHandler) error { - return nil -} - -func (a *autocliConfigurator) RegisterService(sd *grpc.ServiceDesc, ss interface{}) { - if a.registryCache == nil { - a.registryCache, a.err = proto.MergedRegistry() - } - - desc, err := a.registryCache.FindDescriptorByName(protoreflect.FullName(sd.ServiceName)) - if err != nil { - a.err = err - return - } - - if protobuf.HasExtension(desc.Options(), cosmosmsg.E_Service) { - a.msgServer.RegisterService(sd, ss) - } else { - a.queryServer.RegisterService(sd, ss) - } -} -func (a *autocliConfigurator) Error() error { return nil } - -// autocliServiceRegistrar is used to capture the service name for registered services -type autocliServiceRegistrar struct { - serviceName string -} - -func (a *autocliServiceRegistrar) RegisterService(sd *grpc.ServiceDesc, _ interface{}) { - a.serviceName = sd.ServiceName -} - -var _ autocliv1.QueryServer = &AutoCLIQueryService{} diff --git a/runtime/transaction.go b/runtime/transaction.go new file mode 100644 index 000000000000..27b4734661d1 --- /dev/null +++ b/runtime/transaction.go @@ -0,0 +1,19 @@ +package runtime + +import ( + "context" + + "cosmossdk.io/core/transaction" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +var _ transaction.Service = TransactionService{} + +type TransactionService struct{} + +// ExecMode implements transaction.Service. +func (t TransactionService) ExecMode(ctx context.Context) transaction.ExecMode { + sdkCtx := sdk.UnwrapSDKContext(ctx) + return transaction.ExecMode(sdkCtx.ExecMode()) +}