From eeeb5b86b5879c25d72f025351f4c6f9a2ff75e0 Mon Sep 17 00:00:00 2001 From: Julien Robert Date: Mon, 9 Sep 2024 11:01:46 +0200 Subject: [PATCH] refactor(core): re-add handlers (#21575) --- core/appmodule/v2/handlers.go | 134 ++++++++++++++++++++++++++++++---- runtime/v2/manager.go | 13 +++- server/v2/stf/stf_router.go | 8 +- 3 files changed, 133 insertions(+), 22 deletions(-) diff --git a/core/appmodule/v2/handlers.go b/core/appmodule/v2/handlers.go index c446a46cebd2..b42698b7e86f 100644 --- a/core/appmodule/v2/handlers.go +++ b/core/appmodule/v2/handlers.go @@ -2,6 +2,7 @@ package appmodulev2 import ( "context" + "fmt" transaction "cosmossdk.io/core/transaction" ) @@ -16,47 +17,152 @@ type ( PostMsgHandler = func(ctx context.Context, msg, msgResp transaction.Msg) error ) -// msg handler - +// PreMsgRouter is a router that allows you to register PreMsgHandlers for specific message types. type PreMsgRouter interface { // RegisterPreHandler will register a specific message handler hooking into the message with // the provided name. - RegisterPreHandler(msgName string, handler PreMsgHandler) + RegisterPreMsgHandler(msgName string, handler PreMsgHandler) // RegisterGlobalPreHandler will register a global message handler hooking into any message // being executed. - RegisterGlobalPreHandler(handler PreMsgHandler) + RegisterGlobalPreMsgHandler(handler PreMsgHandler) } +// HasPreMsgHandlers is an interface that modules must implement if they want to register PreMsgHandlers. type HasPreMsgHandlers interface { RegisterPreMsgHandlers(router PreMsgRouter) } -type MsgRouter interface { - Register(msgName string, handler Handler) -} +// RegisterMsgPreHandler is a helper function that modules can use to not lose type safety when registering PreMsgHandler to the +// PreMsgRouter. Example usage: +// ```go +// +// func (h Handlers) BeforeSend(ctx context.Context, req *types.MsgSend) error { +// ... before send logic ... +// } +// +// func (m Module) RegisterPreMsgHandlers(router appmodule.PreMsgRouter) { +// handlers := keeper.NewHandlers(m.keeper) +// appmodule.RegisterMsgPreHandler(router, gogoproto.MessageName(types.MsgSend{}), handlers.BeforeSend) +// } +// +// ``` +func RegisterMsgPreHandler[Req transaction.Msg]( + router PreMsgRouter, + msgName string, + handler func(ctx context.Context, msg Req) error, +) { + untypedHandler := func(ctx context.Context, m transaction.Msg) error { + typed, ok := m.(Req) + if !ok { + return fmt.Errorf("unexpected type %T, wanted: %T", m, *new(Req)) + } + return handler(ctx, typed) + } -type HasMsgHandlers interface { - RegisterMsgHandlers(router MsgRouter) + router.RegisterPreMsgHandler(msgName, untypedHandler) } +// PostMsgRouter is a router that allows you to register PostMsgHandlers for specific message types. type PostMsgRouter interface { // RegisterPostHandler will register a specific message handler hooking after the execution of message with // the provided name. - RegisterPostHandler(msgName string, handler PostMsgHandler) + RegisterPostMsgHandler(msgName string, handler PostMsgHandler) // RegisterGlobalPostHandler will register a global message handler hooking after the execution of any message. - RegisterGlobalPostHandler(handler PostMsgHandler) + RegisterGlobalPostMsgHandler(handler PostMsgHandler) } +// HasPostMsgHandlers is an interface that modules must implement if they want to register PostMsgHandlers. type HasPostMsgHandlers interface { RegisterPostMsgHandlers(router PostMsgRouter) } -// query handler +// RegisterPostHandler is a helper function that modules can use to not lose type safety when registering handlers to the +// PostMsgRouter. Example usage: +// ```go +// +// func (h Handlers) AfterSend(ctx context.Context, req *types.MsgSend, resp *types.MsgSendResponse) error { +// ... query logic ... +// } +// +// func (m Module) RegisterPostMsgHandlers(router appmodule.PostMsgRouter) { +// handlers := keeper.NewHandlers(m.keeper) +// appmodule.RegisterPostMsgHandler(router, gogoproto.MessageName(types.MsgSend{}), handlers.AfterSend) +// } +// +// ``` +func RegisterPostMsgHandler[Req, Resp transaction.Msg]( + router PostMsgRouter, + msgName string, + handler func(ctx context.Context, msg Req, msgResp Resp) error, +) { + untypedHandler := func(ctx context.Context, m, mResp transaction.Msg) error { + typed, ok := m.(Req) + if !ok { + return fmt.Errorf("unexpected type %T, wanted: %T", m, *new(Req)) + } + typedResp, ok := mResp.(Resp) + if !ok { + return fmt.Errorf("unexpected type %T, wanted: %T", m, *new(Resp)) + } + return handler(ctx, typed, typedResp) + } + + router.RegisterPostMsgHandler(msgName, untypedHandler) +} + +// MsgRouter is a router that allows you to register Handlers for specific message types. +type MsgRouter = interface { + RegisterHandler(msgName string, handler Handler) error +} -type QueryRouter interface { - Register(queryName string, handler Handler) +// HasMsgHandlers is an interface that modules must implement if they want to register Handlers. +type HasMsgHandlers interface { + RegisterMsgHandlers(router MsgRouter) } +// QueryRouter is a router that allows you to register QueryHandlers for specific query types. +type QueryRouter = MsgRouter + +// HasQueryHandlers is an interface that modules must implement if they want to register QueryHandlers. type HasQueryHandlers interface { RegisterQueryHandlers(router QueryRouter) } + +// RegisterMsgHandler is a helper function that modules can use to not lose type safety when registering handlers to the MsgRouter and Query Router. +// Example usage: +// ```go +// +// func (h Handlers) Mint(ctx context.Context, req *types.MsgMint) (*types.MsgMintResponse, error) { +// ... msg logic ... +// } +// +// func (h Handlers) QueryBalance(ctx context.Context, req *types.QueryBalanceRequest) (*types.QueryBalanceResponse, error) { +// ... query logic ... +// } +// +// func (m Module) RegisterMsgHandlers(router appmodule.MsgRouter) { +// handlers := keeper.NewHandlers(m.keeper) +// err := appmodule.RegisterHandler(router, gogoproto.MessageName(types.MsgMint{}), handlers.MsgMint) +// } +// +// func (m Module) RegisterQueryHandlers(router appmodule.QueryRouter) { +// handlers := keeper.NewHandlers(m.keeper) +// err := appmodule.RegisterHandler(router, gogoproto.MessageName(types.QueryBalanceRequest{}), handlers.QueryBalance) +// } +// +// ``` +func RegisterHandler[Req, Resp transaction.Msg]( + router MsgRouter, + msgName string, + handler func(ctx context.Context, msg Req) (msgResp Resp, err error), +) error { + untypedHandler := func(ctx context.Context, m transaction.Msg) (transaction.Msg, error) { + typed, ok := m.(Req) + if !ok { + return nil, fmt.Errorf("unexpected type %T, wanted: %T", m, *new(Req)) + } + return handler(ctx, typed) + } + + return router.RegisterHandler(msgName, untypedHandler) +} diff --git a/runtime/v2/manager.go b/runtime/v2/manager.go index 3aeb9770e170..eea180843800 100644 --- a/runtime/v2/manager.go +++ b/runtime/v2/manager.go @@ -604,14 +604,19 @@ func registerServices[T transaction.Tx](s hasServicesV1, app *App[T], registry * err: nil, } - err := s.RegisterServices(c) - if err != nil { + if err := s.RegisterServices(c); err != nil { return fmt.Errorf("unable to register services: %w", err) } + + if c.err != nil { + app.logger.Warn("error registering services", "error", c.err) + } + // merge maps for path, decoder := range c.grpcQueryDecoders { app.GRPCMethodsToMessageMap[path] = decoder } + return nil } @@ -654,7 +659,7 @@ func (c *configurator) registerQueryHandlers(sd *grpc.ServiceDesc, ss interface{ // TODO(tip): what if a query is not deterministic? requestFullName, err := registerMethod(c.stfQueryRouter, sd, md, ss) if err != nil { - return fmt.Errorf("unable to register query handler %s: %w", md.MethodName, err) + return fmt.Errorf("unable to register query handler %s.%s: %w", sd.ServiceName, md.MethodName, err) } // register gRPC query method. @@ -675,7 +680,7 @@ func (c *configurator) registerMsgHandlers(sd *grpc.ServiceDesc, ss interface{}) for _, md := range sd.Methods { _, err := registerMethod(c.stfMsgRouter, sd, md, ss) if err != nil { - return fmt.Errorf("unable to register msg handler %s: %w", md.MethodName, err) + return fmt.Errorf("unable to register msg handler %s.%s: %w", sd.ServiceName, md.MethodName, err) } } return nil diff --git a/server/v2/stf/stf_router.go b/server/v2/stf/stf_router.go index 0593dddf71b4..0417788e4b78 100644 --- a/server/v2/stf/stf_router.go +++ b/server/v2/stf/stf_router.go @@ -41,19 +41,19 @@ func (b *MsgRouterBuilder) RegisterHandler(msgType string, handler appmodulev2.H return nil } -func (b *MsgRouterBuilder) RegisterGlobalPreHandler(handler appmodulev2.PreMsgHandler) { +func (b *MsgRouterBuilder) RegisterGlobalPreMsgHandler(handler appmodulev2.PreMsgHandler) { b.globalPreHandlers = append(b.globalPreHandlers, handler) } -func (b *MsgRouterBuilder) RegisterPreHandler(msgType string, handler appmodulev2.PreMsgHandler) { +func (b *MsgRouterBuilder) RegisterPreMsgHandler(msgType string, handler appmodulev2.PreMsgHandler) { b.preHandlers[msgType] = append(b.preHandlers[msgType], handler) } -func (b *MsgRouterBuilder) RegisterPostHandler(msgType string, handler appmodulev2.PostMsgHandler) { +func (b *MsgRouterBuilder) RegisterPostMsgHandler(msgType string, handler appmodulev2.PostMsgHandler) { b.postHandlers[msgType] = append(b.postHandlers[msgType], handler) } -func (b *MsgRouterBuilder) RegisterGlobalPostHandler(handler appmodulev2.PostMsgHandler) { +func (b *MsgRouterBuilder) RegisterGlobalPostMsgHandler(handler appmodulev2.PostMsgHandler) { b.globalPostHandlers = append(b.globalPostHandlers, handler) }