258 lines
7.5 KiB
Go
258 lines
7.5 KiB
Go
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/event"
|
|
"cosmossdk.io/core/header"
|
|
"cosmossdk.io/core/registry"
|
|
"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"
|
|
"cosmossdk.io/store/v2/root"
|
|
)
|
|
|
|
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(registrar grpc.ServiceRegistrar) error {
|
|
autoCliQueryService, err := services.NewAutoCLIQueryService(m.app.moduleManager.modules)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
autocliv1.RegisterQueryServer(registrar, autoCliQueryService)
|
|
|
|
reflectionSvc, err := services.NewReflectionService()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
reflectionv1.RegisterReflectionServiceServer(registrar, 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],
|
|
ProvideModuleManager[transaction.Tx],
|
|
ProvideEnvironment,
|
|
ProvideKVService,
|
|
),
|
|
appconfig.Invoke(SetupAppBuilder),
|
|
)
|
|
}
|
|
|
|
func ProvideAppBuilder[T transaction.Tx](
|
|
interfaceRegistrar registry.InterfaceRegistrar,
|
|
amino registry.AminoRegistrar,
|
|
storeBuilder root.Builder,
|
|
) (
|
|
*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]{
|
|
interfaceRegistrar: interfaceRegistrar,
|
|
amino: amino,
|
|
msgRouterBuilder: msgRouterBuilder,
|
|
queryRouterBuilder: stf.NewMsgRouterBuilder(), // TODO dedicated query router
|
|
QueryHandlers: map[string]appmodulev2.Handler{},
|
|
storeLoader: DefaultStoreLoader,
|
|
}
|
|
appBuilder := &AppBuilder[T]{app: app, storeBuilder: storeBuilder}
|
|
|
|
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
|
|
StoreBuilder root.Builder
|
|
}
|
|
|
|
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)
|
|
// STF requires some state to run
|
|
inputs.StoreBuilder.RegisterKey("stf")
|
|
}
|
|
|
|
func ProvideModuleManager[T transaction.Tx](
|
|
logger log.Logger,
|
|
config *runtimev2.Module,
|
|
modules map[string]appmodulev2.AppModule,
|
|
) *MM[T] {
|
|
return NewModuleManager[T](logger, config, modules)
|
|
}
|
|
|
|
func ProvideKVService(
|
|
config *runtimev2.Module,
|
|
key depinject.ModuleKey,
|
|
kvFactory store.KVStoreServiceFactory,
|
|
storeBuilder root.Builder,
|
|
) (store.KVStoreService, store.MemoryStoreService) {
|
|
// skips modules that have no store
|
|
if slices.Contains(config.SkipStoreKeys, key.Name()) {
|
|
return &failingStoreService{}, &failingStoreService{}
|
|
}
|
|
var kvStoreKey string
|
|
override := storeKeyOverride(config, key.Name())
|
|
if override != nil {
|
|
kvStoreKey = override.KvStoreKey
|
|
} else {
|
|
kvStoreKey = key.Name()
|
|
}
|
|
|
|
storeBuilder.RegisterKey(kvStoreKey)
|
|
return kvFactory([]byte(kvStoreKey)), stf.NewMemoryStoreService([]byte(fmt.Sprintf("memory:%s", kvStoreKey)))
|
|
}
|
|
|
|
func storeKeyOverride(config *runtimev2.Module, moduleName string) *runtimev2.StoreKeyConfig {
|
|
for _, cfg := range config.OverrideStoreKeys {
|
|
if cfg.ModuleName == moduleName {
|
|
return cfg
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ProvideEnvironment provides the environment for keeper modules, while maintaining backward compatibility and provide services directly as well.
|
|
func ProvideEnvironment(
|
|
logger log.Logger,
|
|
key depinject.ModuleKey,
|
|
kvService store.KVStoreService,
|
|
memKvService store.MemoryStoreService,
|
|
headerService header.Service,
|
|
eventService event.Service,
|
|
) appmodulev2.Environment {
|
|
return appmodulev2.Environment{
|
|
Logger: logger,
|
|
BranchService: stf.BranchService{},
|
|
EventService: eventService,
|
|
GasService: stf.NewGasMeterService(),
|
|
HeaderService: headerService,
|
|
QueryRouterService: stf.NewQueryRouterService(),
|
|
MsgRouterService: stf.NewMsgRouterService([]byte(key.Name())),
|
|
TransactionService: services.NewContextAwareTransactionService(),
|
|
KVStoreService: kvService,
|
|
MemStoreService: memKvService,
|
|
}
|
|
}
|
|
|
|
// DefaultServiceBindings provides default services for the following service interfaces:
|
|
// - store.KVStoreServiceFactory
|
|
// - header.Service
|
|
// - comet.Service
|
|
// - event.Service
|
|
// - store/v2/root.Builder
|
|
//
|
|
// They are all required. For most use cases these default services bindings should be sufficient.
|
|
// Power users (or tests) may wish to provide their own services bindings, in which case they must
|
|
// supply implementations for each of the above interfaces.
|
|
func DefaultServiceBindings() depinject.Config {
|
|
var (
|
|
kvServiceFactory store.KVStoreServiceFactory = func(actor []byte) store.KVStoreService {
|
|
return services.NewGenesisKVService(
|
|
actor,
|
|
stf.NewKVStoreService(actor),
|
|
)
|
|
}
|
|
cometService comet.Service = &services.ContextAwareCometInfoService{}
|
|
headerService = services.NewGenesisHeaderService(stf.HeaderService{})
|
|
eventService = services.NewGenesisEventService(stf.NewEventService())
|
|
storeBuilder = root.NewBuilder()
|
|
)
|
|
return depinject.Supply(
|
|
kvServiceFactory,
|
|
headerService,
|
|
cometService,
|
|
eventService,
|
|
storeBuilder,
|
|
)
|
|
}
|