Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(runtime): message router service #19571

Merged
merged 20 commits into from
Mar 4, 2024
Merged
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ Every module contains its own CHANGELOG.md. Please refer to the module you are i

### Features

* (runtime) [#19571](https://github.com/cosmos/cosmos-sdk/pull/19571) Implement `core/router.Service` it in runtime. This service is present in all modules (when using depinject).
julienrbrt marked this conversation as resolved.
Show resolved Hide resolved
* (types) [#19164](https://github.com/cosmos/cosmos-sdk/pull/19164) Add a ValueCodec for the math.Uint type that can be used in collections maps.
* (types) [#19281](https://github.com/cosmos/cosmos-sdk/pull/19281) Added a new method, `IsGT`, for `types.Coin`. This method is used to check if a `types.Coin` is greater than another `types.Coin`.
* (client) [#18557](https://github.com/cosmos/cosmos-sdk/pull/18557) Add `--qrcode` flag to `keys show` command to support displaying keys address QR code.
Expand Down
3 changes: 3 additions & 0 deletions baseapp/msg_service_router.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ import (
type MessageRouter interface {
Handler(msg sdk.Msg) MsgServiceHandler
HandlerByTypeURL(typeURL string) MsgServiceHandler

ResponseNameByRequestName(msgName string) string
julienrbrt marked this conversation as resolved.
Show resolved Hide resolved
HybridHandlerByMsgName(msgName string) func(ctx context.Context, req, resp protoiface.MessageV1) error
}

// MsgServiceRouter routes fully-qualified Msg service methods to their handler.
Expand Down
3 changes: 2 additions & 1 deletion core/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ Ref: https://keepachangelog.com/en/1.0.0/
* [#18457](https://github.com/cosmos/cosmos-sdk/pull/18457) Add branch.ExecuteWithGasLimit.
* [#19041](https://github.com/cosmos/cosmos-sdk/pull/19041) Add `appmodule.Environment` interface to fetch different services
* [#19370](https://github.com/cosmos/cosmos-sdk/pull/19370) Add `appmodule.Migrations` interface to handle migrations

* [#19571](https://github.com/cosmos/cosmos-sdk/pull/19571) Add `router.Service` and add it in `appmodule.Environment`

### API Breaking Changes

* [#18857](https://github.com/cosmos/cosmos-sdk/pull/18857) Moved `FormatCoins` to `x/tx`.
Expand Down
14 changes: 9 additions & 5 deletions core/appmodule/environment.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,21 @@ import (
"cosmossdk.io/core/event"
"cosmossdk.io/core/gas"
"cosmossdk.io/core/header"
"cosmossdk.io/core/router"
"cosmossdk.io/core/store"
"cosmossdk.io/log"
)

// Environment is used to get all services to their respective module
type Environment struct {
BranchService branch.Service
EventService event.Service
GasService gas.Service
HeaderService header.Service
Logger log.Logger

BranchService branch.Service
EventService event.Service
GasService gas.Service
HeaderService header.Service
MessageRouterService router.Service

KVStoreService store.KVStoreService
MemStoreService store.MemoryStoreService
Logger log.Logger
}
File renamed without changes.
3 changes: 3 additions & 0 deletions core/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ go 1.20

require (
cosmossdk.io/log v1.3.1
github.com/cosmos/gogoproto v1.4.11
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

github.com/stretchr/testify v1.8.4
google.golang.org/grpc v1.62.0
google.golang.org/protobuf v1.32.0
Expand All @@ -12,13 +13,15 @@ require (
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rogpeppe/go-internal v1.11.0 // indirect
github.com/rs/zerolog v1.32.0 // indirect
golang.org/x/exp v0.0.0-20230811145659-89c5cff77bcb // indirect
golang.org/x/net v0.21.0 // indirect
golang.org/x/sys v0.17.0 // indirect
golang.org/x/text v0.14.0 // indirect
Expand Down
5 changes: 5 additions & 0 deletions core/go.sum
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
cosmossdk.io/log v1.3.1 h1:UZx8nWIkfbbNEWusZqzAx3ZGvu54TZacWib3EzUYmGI=
cosmossdk.io/log v1.3.1/go.mod h1:2/dIomt8mKdk6vl3OWJcPk2be3pGOS8OQaLUM/3/tCM=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/cosmos/gogoproto v1.4.11 h1:LZcMHrx4FjUgrqQSWeaGC1v/TeuVFqSLa43CC6aWR2g=
github.com/cosmos/gogoproto v1.4.11/go.mod h1:/g39Mh8m17X8Q/GDEs5zYTSNaNnInBSohtaxzQnYq1Y=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
Expand All @@ -10,6 +12,7 @@ github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
Expand All @@ -36,6 +39,8 @@ github.com/rs/zerolog v1.32.0 h1:keLypqrlIjaFsbmJOBdB/qvyF8KEtCWHwobLp5l/mQ0=
github.com/rs/zerolog v1.32.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
golang.org/x/exp v0.0.0-20230811145659-89c5cff77bcb h1:mIKbk8weKhSeLH2GmUTrvx8CjkyJmnU1wFmg59CUjFA=
golang.org/x/exp v0.0.0-20230811145659-89c5cff77bcb/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
Expand Down
13 changes: 13 additions & 0 deletions core/router/service.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package router

import (
"context"

"google.golang.org/protobuf/runtime/protoiface"
)

// Service is the interface that wraps the basic methods for a router service.
type Service interface {
InvokeTyped(ctx context.Context, req, res protoiface.MessageV1) error
julienrbrt marked this conversation as resolved.
Show resolved Hide resolved
InvokeUntyped(ctx context.Context, req protoiface.MessageV1) (res protoiface.MessageV1, err error)
}
35 changes: 31 additions & 4 deletions runtime/environment.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,44 @@ import (
"cosmossdk.io/core/appmodule"
"cosmossdk.io/core/store"
"cosmossdk.io/log"

"github.com/cosmos/cosmos-sdk/baseapp"
)

// NewEnvironment creates a new environment for the application
// if memstoreservice is needed, it can be added to the environment: environment.MemStoreService = memstoreservice
func NewEnvironment(kvService store.KVStoreService, logger log.Logger) appmodule.Environment {
return appmodule.Environment{
// For setting custom services that aren't set by default, use the EnvOption
// Note: Depinject always provide an environment with all services (mandatory and optional)
func NewEnvironment(
kvService store.KVStoreService,
logger log.Logger,
opts ...EnvOption,
) appmodule.Environment {
env := appmodule.Environment{
Logger: logger,
EventService: EventService{},
HeaderService: HeaderService{},
BranchService: BranchService{},
GasService: GasService{},
KVStoreService: kvService,
Logger: logger,
}

for _, opt := range opts {
opt(&env)
}

return env
}

type EnvOption func(*appmodule.Environment)

func EnvWithMessageRouterService(msgServiceRouter *baseapp.MsgServiceRouter) EnvOption {
return func(env *appmodule.Environment) {
env.MessageRouterService = NewMsgRouterService(env.KVStoreService, msgServiceRouter)
}
}

func EnvWithMemStoreService(memStoreService store.MemoryStoreService) EnvOption {
return func(env *appmodule.Environment) {
env.MemStoreService = memStoreService
}
}
17 changes: 10 additions & 7 deletions runtime/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@ func init() {
ProvideMemoryStoreKey,
ProvideGenesisTxHandler,
ProvideEnvironment,
ProvideMemoryStoreService,
ProvideTransientStoreService,
ProvideModuleManager,
ProvideAppVersionModifier,
Expand Down Expand Up @@ -212,15 +211,19 @@ func ProvideGenesisTxHandler(appBuilder *AppBuilder) genesis.TxHandler {
return appBuilder.app
}

func ProvideEnvironment(config *runtimev1alpha1.Module, key depinject.ModuleKey, app *AppBuilder, logger log.Logger) (store.KVStoreService, appmodule.Environment) {
func ProvideEnvironment(logger log.Logger, config *runtimev1alpha1.Module, key depinject.ModuleKey, app *AppBuilder, msgServiceRouter *baseapp.MsgServiceRouter) (store.KVStoreService, store.MemoryStoreService, appmodule.Environment) {
storeKey := ProvideKVStoreKey(config, key, app)
kvService := kvStoreService{key: storeKey}
return kvService, NewEnvironment(kvService, logger)
}

func ProvideMemoryStoreService(key depinject.ModuleKey, app *AppBuilder) store.MemoryStoreService {
storeKey := ProvideMemoryStoreKey(key, app)
return memStoreService{key: storeKey}
memStoreKey := ProvideMemoryStoreKey(key, app)
memStoreService := memStoreService{key: memStoreKey}

return kvService, memStoreService, NewEnvironment(
kvService,
logger,
EnvWithMessageRouterService(msgServiceRouter),
EnvWithMemStoreService(memStoreService),
)
}

func ProvideTransientStoreService(key depinject.ModuleKey, app *AppBuilder) store.TransientStoreService {
Expand Down
77 changes: 77 additions & 0 deletions runtime/router.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package runtime

import (
"context"
"fmt"
"reflect"

Check notice

Code scanning / CodeQL

Sensitive package import Note

Certain system packages contain functions which may be a possible source of non-determinism

"github.com/cosmos/gogoproto/proto"
protov2 "google.golang.org/protobuf/proto"
"google.golang.org/protobuf/reflect/protoregistry"
"google.golang.org/protobuf/runtime/protoiface"

"cosmossdk.io/core/router"
"cosmossdk.io/core/store"

"github.com/cosmos/cosmos-sdk/baseapp"
)

func NewMsgRouterService(storeService store.KVStoreService, router baseapp.MessageRouter) router.Service {
julienrbrt marked this conversation as resolved.
Show resolved Hide resolved
return &msgRouterService{
storeService: storeService,
router: router,
julienrbrt marked this conversation as resolved.
Show resolved Hide resolved
resolver: protoregistry.GlobalTypes,
}
}

type msgRouterService struct {
storeService store.KVStoreService
router baseapp.MessageRouter
resolver protoregistry.MessageTypeResolver
}

// InvokeTyped execute a message and fill-in a response.
// The response must be known and passed as a parameter.
// Use InvokeUntyped if the response type is not known.
func (m *msgRouterService) InvokeTyped(ctx context.Context, msg, resp protoiface.MessageV1) error {
messageName := msgTypeURL(msg)
handler := m.router.HybridHandlerByMsgName(messageName)
if handler == nil {
return fmt.Errorf("unknown message: %s", messageName)
}

return handler(ctx, msg, resp)
}

// InvokeUntyped execute a message and returns a response.
func (m *msgRouterService) InvokeUntyped(ctx context.Context, msg protoiface.MessageV1) (protoiface.MessageV1, error) {
messageName := msgTypeURL(msg)
respName := m.router.ResponseNameByRequestName(messageName)
if respName == "" {
return nil, fmt.Errorf("could not find response type for message %T", msg)
}

// get response type
typ := proto.MessageType(respName)
if typ == nil {
return nil, fmt.Errorf("no message type found for %s", respName)
}
msgResp := reflect.New(typ.Elem()).Interface().(protoiface.MessageV1)
julienrbrt marked this conversation as resolved.
Show resolved Hide resolved

handler := m.router.HybridHandlerByMsgName(messageName)
if handler == nil {
return nil, fmt.Errorf("unknown message: %s", messageName)
}

err := handler(ctx, msg, msgResp)
return msgResp, err
}

// msgTypeURL returns the TypeURL of a proto message.
func msgTypeURL(msg proto.Message) string {
if m, ok := msg.(protov2.Message); ok {
return "/" + string(m.ProtoReflect().Descriptor().FullName())
}

return "/" + proto.MessageName(msg)
}
2 changes: 1 addition & 1 deletion testutil/integration/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ func NewIntegrationApp(

if keys[consensusparamtypes.StoreKey] != nil {
// set baseApp param store
consensusParamsKeeper := consensusparamkeeper.NewKeeper(appCodec, runtime.NewEnvironment(runtime.NewKVStoreService(keys[consensusparamtypes.StoreKey]), log.NewNopLogger()), authtypes.NewModuleAddress("gov").String())
consensusParamsKeeper := consensusparamkeeper.NewKeeper(appCodec, runtime.NewEnvironment(runtime.NewKVStoreService(keys[consensusparamtypes.StoreKey]), log.NewNopLogger(), runtime.EnvWithMessageRouterService(router)), authtypes.NewModuleAddress("gov").String())
bApp.SetParamStore(consensusParamsKeeper.ParamsStore)

if err := bApp.LoadLatestVersion(); err != nil {
Expand Down
Loading