Skip to content

Commit

Permalink
feat(core,runtime): transaction service (exec mode)
Browse files Browse the repository at this point in the history
  • Loading branch information
julienrbrt committed Apr 4, 2024
1 parent 1028e27 commit eb8a69a
Show file tree
Hide file tree
Showing 9 changed files with 234 additions and 209 deletions.
12 changes: 7 additions & 5 deletions core/appmodule/v2/environment.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,20 @@ import (
"cosmossdk.io/core/header"
"cosmossdk.io/core/router"
"cosmossdk.io/core/store"
"cosmossdk.io/core/transaction"
"cosmossdk.io/log"
)

// Environment is used to get all services to their respective module
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
Expand Down
24 changes: 24 additions & 0 deletions core/transaction/service.go
Original file line number Diff line number Diff line change
@@ -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
}
165 changes: 140 additions & 25 deletions runtime/autocli.go
Original file line number Diff line number Diff line change
@@ -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{}
13 changes: 7 additions & 6 deletions runtime/environment.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
39 changes: 36 additions & 3 deletions runtime/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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{}
Expand Down
2 changes: 1 addition & 1 deletion runtime/services/reflection.go → runtime/reflection.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package services
package runtime

import (
"context"
Expand Down
21 changes: 0 additions & 21 deletions runtime/services.go

This file was deleted.

Loading

0 comments on commit eb8a69a

Please sign in to comment.