From d1b339b6289466ba0216afb3a5c5fefce3a71a8a Mon Sep 17 00:00:00 2001 From: Julien Robert Date: Fri, 6 Sep 2024 14:25:56 +0200 Subject: [PATCH 1/8] refactor(core): re-add handlers --- core/appmodule/v2/handlers.go | 104 +++++++++++++++++++++++++++++++--- 1 file changed, 97 insertions(+), 7 deletions(-) diff --git a/core/appmodule/v2/handlers.go b/core/appmodule/v2/handlers.go index c446a46cebd2..6cc54c8208bc 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,136 @@ 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) + RegisterPreHandler(handler PreMsgHandler) // RegisterGlobalPreHandler will register a global message handler hooking into any message // being executed. RegisterGlobalPreHandler(handler PreMsgHandler) } +// HasPreMsgHandlers is an interface that modules must implement if they want to register PreMsgHandlers. type HasPreMsgHandlers interface { RegisterPreMsgHandlers(router PreMsgRouter) } +// RegisterPreHandler is a helper function that modules can use to not lose type safety when registering PreMsgHandler to the +// PreMsgRouter. Example usage: +// ```go +// +// func (k Keeper) BeforeSend(ctx context.Context, req *types.MsgSend) (*types.QueryBalanceResponse, error) { +// ... before send logic ... +// } +// +// func (m Module) RegisterPreMsgHandlers(router appmodule.PreMsgRouter) { +// appmodule.RegisterPreHandler(router, keeper.BeforeSend) +// } +// +// ``` +func RegisterPreHandler[Req transaction.Msg]( + router PreMsgRouter, + 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) + } + router.RegisterPreHandler(untypedHandler) +} + +// MsgRouter is a router that allows you to register Handlers for specific message types. type MsgRouter interface { - Register(msgName string, handler Handler) + RegisterHandler(handler Handler) } +// HasMsgHandlers is an interface that modules must implement if they want to register Handlers. type HasMsgHandlers interface { RegisterMsgHandlers(router MsgRouter) } +// RegisterHandler is a helper function that modules can use to not lose type safety when registering handlers to the +// QueryRouter or MsgRouter. Example usage: +// ```go +// +// func (k Keeper) QueryBalance(ctx context.Context, req *types.QueryBalanceRequest) (*types.QueryBalanceResponse, error) { +// ... query logic ... +// } +// +// func (m Module) RegisterQueryHandlers(router appmodule.QueryRouter) { +// appmodule.RegisterHandler(router, keeper.QueryBalance) +// } +// +// ``` +func RegisterHandler[R MsgRouter, Req, Resp transaction.Msg]( + router R, + handler func(ctx context.Context, msg Req) (msgResp Resp, err 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) + } + router.RegisterHandler(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) + RegisterPostHandler(handler PostMsgHandler) // RegisterGlobalPostHandler will register a global message handler hooking after the execution of any message. RegisterGlobalPostHandler(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 (k Keeper) AfterSend(ctx context.Context, req *types.MsgSend, resp *types.MsgSendResponse) error { +// ... query logic ... +// } +// +// func (m Module) RegisterPostMsgHandlers(router appmodule.PostMsgRouter) { +// appmodule.RegisterPostHandler(router, keeper.AfterSend) +// } +// +// ``` +func RegisterPostHandler[Req, Resp transaction.Msg]( + router PostMsgRouter, + 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.RegisterPostHandler(untypedHandler) +} +// QueryRouter is a router that allows you to register QueryHandlers for specific query types. type QueryRouter interface { - Register(queryName string, handler Handler) + Register(handler Handler) } +// HasQueryHandlers is an interface that modules must implement if they want to register QueryHandlers. type HasQueryHandlers interface { RegisterQueryHandlers(router QueryRouter) } From ee88f1d21928670feba402f342fc4546285ddefc Mon Sep 17 00:00:00 2001 From: Julien Robert Date: Fri, 6 Sep 2024 14:34:55 +0200 Subject: [PATCH 2/8] updates --- core/appmodule/v2/handlers.go | 69 +++++++++++++++++++++++++---------- 1 file changed, 50 insertions(+), 19 deletions(-) diff --git a/core/appmodule/v2/handlers.go b/core/appmodule/v2/handlers.go index 6cc54c8208bc..7b76a7232556 100644 --- a/core/appmodule/v2/handlers.go +++ b/core/appmodule/v2/handlers.go @@ -32,20 +32,21 @@ type HasPreMsgHandlers interface { RegisterPreMsgHandlers(router PreMsgRouter) } -// RegisterPreHandler is a helper function that modules can use to not lose type safety when registering PreMsgHandler to the +// RegisterMsgPreHandler is a helper function that modules can use to not lose type safety when registering PreMsgHandler to the // PreMsgRouter. Example usage: // ```go // -// func (k Keeper) BeforeSend(ctx context.Context, req *types.MsgSend) (*types.QueryBalanceResponse, error) { +// func (h Handlers) BeforeSend(ctx context.Context, req *types.MsgSend) (*types.QueryBalanceResponse, error) { // ... before send logic ... // } // // func (m Module) RegisterPreMsgHandlers(router appmodule.PreMsgRouter) { -// appmodule.RegisterPreHandler(router, keeper.BeforeSend) +// handlers := keeper.NewHandlers(m.keeper) +// appmodule.RegisterMsgPreHandler(router, handlers.BeforeSend) // } // // ``` -func RegisterPreHandler[Req transaction.Msg]( +func RegisterMsgPreHandler[Req transaction.Msg]( router PreMsgRouter, handler func(ctx context.Context, msg Req) error, ) { @@ -61,7 +62,7 @@ func RegisterPreHandler[Req transaction.Msg]( // MsgRouter is a router that allows you to register Handlers for specific message types. type MsgRouter interface { - RegisterHandler(handler Handler) + RegisterMsgHandler(handler Handler) } // HasMsgHandlers is an interface that modules must implement if they want to register Handlers. @@ -69,20 +70,21 @@ type HasMsgHandlers interface { RegisterMsgHandlers(router MsgRouter) } -// RegisterHandler is a helper function that modules can use to not lose type safety when registering handlers to the -// QueryRouter or MsgRouter. Example usage: +// RegisterMsgHandler is a helper function that modules can use to not lose type safety when registering handlers to the MsgRouter. +// Example usage: // ```go // -// func (k Keeper) QueryBalance(ctx context.Context, req *types.QueryBalanceRequest) (*types.QueryBalanceResponse, error) { +// func (h Handlers) Mint(ctx context.Context, req *types.MsgMintRequest) (*types.MsgMintResponse, error) { // ... query logic ... // } // -// func (m Module) RegisterQueryHandlers(router appmodule.QueryRouter) { -// appmodule.RegisterHandler(router, keeper.QueryBalance) +// func (m Module) RegisterMsgHandlers(router appmodule.MsgRouter) { +// handlers := keeper.NewHandlers(m.keeper) +// appmodule.RegisterMsgHandler(router, handlers.MsgMint) // } // // ``` -func RegisterHandler[R MsgRouter, Req, Resp transaction.Msg]( +func RegisterMsgHandler[R MsgRouter, Req, Resp transaction.Msg]( router R, handler func(ctx context.Context, msg Req) (msgResp Resp, err error), ) { @@ -93,16 +95,16 @@ func RegisterHandler[R MsgRouter, Req, Resp transaction.Msg]( } return handler(ctx, typed) } - router.RegisterHandler(untypedHandler) + router.RegisterMsgHandler(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(handler PostMsgHandler) + RegisterPostMsgHandler(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. @@ -114,16 +116,17 @@ type HasPostMsgHandlers interface { // PostMsgRouter. Example usage: // ```go // -// func (k Keeper) AfterSend(ctx context.Context, req *types.MsgSend, resp *types.MsgSendResponse) error { +// func (h Handlers) AfterSend(ctx context.Context, req *types.MsgSend, resp *types.MsgSendResponse) error { // ... query logic ... // } // // func (m Module) RegisterPostMsgHandlers(router appmodule.PostMsgRouter) { -// appmodule.RegisterPostHandler(router, keeper.AfterSend) +// handlers := keeper.NewHandlers(m.keeper) +// appmodule.RegisterPostMsgHandler(router, handlers.AfterSend) // } // // ``` -func RegisterPostHandler[Req, Resp transaction.Msg]( +func RegisterPostMsgHandler[Req, Resp transaction.Msg]( router PostMsgRouter, handler func(ctx context.Context, msg Req, msgResp Resp) error, ) { @@ -138,15 +141,43 @@ func RegisterPostHandler[Req, Resp transaction.Msg]( } return handler(ctx, typed, typedResp) } - router.RegisterPostHandler(untypedHandler) + router.RegisterPostMsgHandler(untypedHandler) } // QueryRouter is a router that allows you to register QueryHandlers for specific query types. type QueryRouter interface { - Register(handler Handler) + RegisterQueryHandler(handler Handler) } // HasQueryHandlers is an interface that modules must implement if they want to register QueryHandlers. type HasQueryHandlers interface { RegisterQueryHandlers(router QueryRouter) } + +// RegisterQueryHandler is a helper function that modules can use to not lose type safety when registering handlers to the Query.Router +// Example usage: +// ```go +// +// func (h Handkers) QueryBalance(ctx context.Context, req *types.QueryBalanceRequest) (*types.QueryBalanceResponse, error) { +// ... query logic ... +// } +// +// func (m Module) RegisterQueryHandlers(router appmodule.QueryRouter) { +// handlers := keeper.NewHandlers(m.keeper) +// appmodule.RegisterHandler(router, handlers.QueryBalance) +// } +// +// ``` +func RegisterQueryHandler[R QueryRouter, Req, Resp transaction.Msg]( + router R, + handler func(ctx context.Context, msg Req) (msgResp Resp, err 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) + } + router.RegisterQueryHandler(untypedHandler) +} From 1d29aa605606bcdd5d22228ade531afaf95906b1 Mon Sep 17 00:00:00 2001 From: Julien Robert Date: Fri, 6 Sep 2024 16:18:52 +0200 Subject: [PATCH 3/8] updates --- core/appmodule/v2/handlers.go | 79 +++++++++++++---------------------- runtime/v2/manager.go | 9 ++-- server/v2/stf/stf_router.go | 54 +++++++++++++++++++++--- server/v2/stf/stf_test.go | 1 - 4 files changed, 83 insertions(+), 60 deletions(-) diff --git a/core/appmodule/v2/handlers.go b/core/appmodule/v2/handlers.go index 7b76a7232556..099cfb8cf130 100644 --- a/core/appmodule/v2/handlers.go +++ b/core/appmodule/v2/handlers.go @@ -21,10 +21,10 @@ type ( type PreMsgRouter interface { // RegisterPreHandler will register a specific message handler hooking into the message with // the provided name. - RegisterPreHandler(handler PreMsgHandler) + RegisterPreMsgHandler(handler PreMsgHandler) error // RegisterGlobalPreHandler will register a global message handler hooking into any message // being executed. - RegisterGlobalPreHandler(handler PreMsgHandler) + RegisterGlobalPreMsgHandler(handler PreMsgHandler) error } // HasPreMsgHandlers is an interface that modules must implement if they want to register PreMsgHandlers. @@ -57,54 +57,16 @@ func RegisterMsgPreHandler[Req transaction.Msg]( } return handler(ctx, typed) } - router.RegisterPreHandler(untypedHandler) -} - -// MsgRouter is a router that allows you to register Handlers for specific message types. -type MsgRouter interface { - RegisterMsgHandler(handler Handler) -} - -// HasMsgHandlers is an interface that modules must implement if they want to register Handlers. -type HasMsgHandlers interface { - RegisterMsgHandlers(router MsgRouter) -} - -// RegisterMsgHandler is a helper function that modules can use to not lose type safety when registering handlers to the MsgRouter. -// Example usage: -// ```go -// -// func (h Handlers) Mint(ctx context.Context, req *types.MsgMintRequest) (*types.MsgMintResponse, error) { -// ... query logic ... -// } -// -// func (m Module) RegisterMsgHandlers(router appmodule.MsgRouter) { -// handlers := keeper.NewHandlers(m.keeper) -// appmodule.RegisterMsgHandler(router, handlers.MsgMint) -// } -// -// ``` -func RegisterMsgHandler[R MsgRouter, Req, Resp transaction.Msg]( - router R, - handler func(ctx context.Context, msg Req) (msgResp Resp, err 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) - } - router.RegisterMsgHandler(untypedHandler) + router.RegisterPreMsgHandler(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. - RegisterPostMsgHandler(handler PostMsgHandler) + RegisterPostMsgHandler(handler PostMsgHandler) error // RegisterGlobalPostHandler will register a global message handler hooking after the execution of any message. - RegisterGlobalPostMsgHandler(handler PostMsgHandler) + RegisterGlobalPostMsgHandler(handler PostMsgHandler) error } // HasPostMsgHandlers is an interface that modules must implement if they want to register PostMsgHandlers. @@ -144,31 +106,48 @@ func RegisterPostMsgHandler[Req, Resp transaction.Msg]( router.RegisterPostMsgHandler(untypedHandler) } -// QueryRouter is a router that allows you to register QueryHandlers for specific query types. -type QueryRouter interface { - RegisterQueryHandler(handler Handler) +// MsgRouter is a router that allows you to register Handlers for specific message types. +type MsgRouter interface { + RegisterHandler(handler Handler) error +} + +// 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) } -// RegisterQueryHandler is a helper function that modules can use to not lose type safety when registering handlers to the Query.Router +// 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 Handkers) QueryBalance(ctx context.Context, req *types.QueryBalanceRequest) (*types.QueryBalanceResponse, error) { +// func (h Handlers) Mint(ctx context.Context, req *types.MsgMint) (*types.MsgMintResponse, error) { +// ... query 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) +// appmodule.RegisterHandler(router, handlers.MsgMint) +// } +// // func (m Module) RegisterQueryHandlers(router appmodule.QueryRouter) { // handlers := keeper.NewHandlers(m.keeper) // appmodule.RegisterHandler(router, handlers.QueryBalance) // } // // ``` -func RegisterQueryHandler[R QueryRouter, Req, Resp transaction.Msg]( +func RegisterHandler[R MsgRouter, Req, Resp transaction.Msg]( router R, handler func(ctx context.Context, msg Req) (msgResp Resp, err error), ) { @@ -179,5 +158,5 @@ func RegisterQueryHandler[R QueryRouter, Req, Resp transaction.Msg]( } return handler(ctx, typed) } - router.RegisterQueryHandler(untypedHandler) + router.RegisterHandler(untypedHandler) } diff --git a/runtime/v2/manager.go b/runtime/v2/manager.go index 3aeb9770e170..b9cf446d32f9 100644 --- a/runtime/v2/manager.go +++ b/runtime/v2/manager.go @@ -604,15 +604,16 @@ 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) } + // merge maps for path, decoder := range c.grpcQueryDecoders { app.GRPCMethodsToMessageMap[path] = decoder } - return nil + + return c.err } var _ grpc.ServiceRegistrar = (*configurator)(nil) @@ -706,7 +707,7 @@ func registerMethod( return "", err } - return string(requestName), stfRouter.RegisterHandler(string(requestName), func( + return string(requestName), stfRouter.RegisterHandler(func( ctx context.Context, msg transaction.Msg, ) (resp transaction.Msg, err error) { diff --git a/server/v2/stf/stf_router.go b/server/v2/stf/stf_router.go index 0593dddf71b4..7be34b944ce0 100644 --- a/server/v2/stf/stf_router.go +++ b/server/v2/stf/stf_router.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "reflect" "strings" gogoproto "github.com/cosmos/gogoproto/proto" @@ -32,7 +33,12 @@ type MsgRouterBuilder struct { globalPostHandlers []appmodulev2.PostMsgHandler } -func (b *MsgRouterBuilder) RegisterHandler(msgType string, handler appmodulev2.Handler) error { +func (b *MsgRouterBuilder) RegisterHandler(handler appmodulev2.Handler) error { + msgType, err := msgTypeURLFromHandler(handler) + if err != nil { + return err + } + // panic on override if _, ok := b.handlers[msgType]; ok { return fmt.Errorf("handler already registered: %s", msgType) @@ -41,20 +47,34 @@ 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) error { b.globalPreHandlers = append(b.globalPreHandlers, handler) + return nil } -func (b *MsgRouterBuilder) RegisterPreHandler(msgType string, handler appmodulev2.PreMsgHandler) { +func (b *MsgRouterBuilder) RegisterPreMsgHandler(handler appmodulev2.PreMsgHandler) error { + msgType, err := msgTypeURLFromHandler(handler) + if err != nil { + return err + } + b.preHandlers[msgType] = append(b.preHandlers[msgType], handler) + return nil } -func (b *MsgRouterBuilder) RegisterPostHandler(msgType string, handler appmodulev2.PostMsgHandler) { +func (b *MsgRouterBuilder) RegisterPostMsgHandler(handler appmodulev2.PostMsgHandler) error { + msgType, err := msgTypeURLFromHandler(handler) + if err != nil { + return err + } + b.postHandlers[msgType] = append(b.postHandlers[msgType], handler) + return nil } -func (b *MsgRouterBuilder) RegisterGlobalPostHandler(handler appmodulev2.PostMsgHandler) { +func (b *MsgRouterBuilder) RegisterGlobalPostMsgHandler(handler appmodulev2.PostMsgHandler) error { b.globalPostHandlers = append(b.globalPostHandlers, handler) + return nil } func (b *MsgRouterBuilder) HandlerExists(msgType string) bool { @@ -135,6 +155,30 @@ func buildHandler( } } +func msgTypeURLFromHandler(handler any) (string, error) { + handlerType := reflect.TypeOf(handler) + if handlerType.Kind() != reflect.Func { + return "", fmt.Errorf("handler must be a function") + } + if handlerType.NumIn() != 2 && handlerType.NumIn() != 3 { + return "", fmt.Errorf("handler must have 2-3 input parameters") + } + + // Get the type of the second parameter (transaction.Msg) + msgType := handlerType.In(1) + + if !msgType.Implements(reflect.TypeOf((*transaction.Msg)(nil)).Elem()) { + return "", fmt.Errorf("second parameter must implement transaction.Msg") + } + + msgName := msgTypeURL(*reflect.New(msgType).Interface().(*gogoproto.Message)) + if msgName == "" { + return "", fmt.Errorf("could not get message name") + } + + return msgName, nil +} + // msgTypeURL returns the TypeURL of a proto message. func msgTypeURL(msg gogoproto.Message) string { return gogoproto.MessageName(msg) diff --git a/server/v2/stf/stf_test.go b/server/v2/stf/stf_test.go index 5fa4fce15b40..f7197d8a2adc 100644 --- a/server/v2/stf/stf_test.go +++ b/server/v2/stf/stf_test.go @@ -35,7 +35,6 @@ func addMsgHandlerToSTF[T any, PT interface { t.Helper() msgRouterBuilder := NewMsgRouterBuilder() err := msgRouterBuilder.RegisterHandler( - msgTypeURL(PT(new(T))), func(ctx context.Context, msg transaction.Msg) (msgResp transaction.Msg, err error) { typedReq := msg.(PT) typedResp, err := handler(ctx, typedReq) From d86d287cccb24a3be37778ead23849cbed660163 Mon Sep 17 00:00:00 2001 From: Julien Robert Date: Fri, 6 Sep 2024 16:33:10 +0200 Subject: [PATCH 4/8] error improvement --- runtime/v2/manager.go | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/runtime/v2/manager.go b/runtime/v2/manager.go index b9cf446d32f9..925851e8bda1 100644 --- a/runtime/v2/manager.go +++ b/runtime/v2/manager.go @@ -608,12 +608,16 @@ func registerServices[T transaction.Tx](s hasServicesV1, app *App[T], registry * 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 c.err + return nil } var _ grpc.ServiceRegistrar = (*configurator)(nil) @@ -655,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. @@ -676,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 From ea34ede07b768753ff3e157df89b0669e1a5bd12 Mon Sep 17 00:00:00 2001 From: Julien Robert Date: Fri, 6 Sep 2024 16:40:31 +0200 Subject: [PATCH 5/8] updates --- core/appmodule/v2/handlers.go | 23 ++++++++++-------- runtime/v2/manager.go | 2 +- server/v2/stf/stf_router.go | 46 +++-------------------------------- server/v2/stf/stf_test.go | 1 + 4 files changed, 18 insertions(+), 54 deletions(-) diff --git a/core/appmodule/v2/handlers.go b/core/appmodule/v2/handlers.go index 099cfb8cf130..e1aae28554e7 100644 --- a/core/appmodule/v2/handlers.go +++ b/core/appmodule/v2/handlers.go @@ -21,7 +21,7 @@ type ( type PreMsgRouter interface { // RegisterPreHandler will register a specific message handler hooking into the message with // the provided name. - RegisterPreMsgHandler(handler PreMsgHandler) error + RegisterPreMsgHandler(msgName string, handler PreMsgHandler) error // RegisterGlobalPreHandler will register a global message handler hooking into any message // being executed. RegisterGlobalPreMsgHandler(handler PreMsgHandler) error @@ -42,12 +42,13 @@ type HasPreMsgHandlers interface { // // func (m Module) RegisterPreMsgHandlers(router appmodule.PreMsgRouter) { // handlers := keeper.NewHandlers(m.keeper) -// appmodule.RegisterMsgPreHandler(router, handlers.BeforeSend) +// 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 { @@ -57,14 +58,14 @@ func RegisterMsgPreHandler[Req transaction.Msg]( } return handler(ctx, typed) } - router.RegisterPreMsgHandler(untypedHandler) + 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. - RegisterPostMsgHandler(handler PostMsgHandler) error + RegisterPostMsgHandler(msgName string, handler PostMsgHandler) error // RegisterGlobalPostHandler will register a global message handler hooking after the execution of any message. RegisterGlobalPostMsgHandler(handler PostMsgHandler) error } @@ -84,12 +85,13 @@ type HasPostMsgHandlers interface { // // func (m Module) RegisterPostMsgHandlers(router appmodule.PostMsgRouter) { // handlers := keeper.NewHandlers(m.keeper) -// appmodule.RegisterPostMsgHandler(router, handlers.AfterSend) +// 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 { @@ -103,12 +105,12 @@ func RegisterPostMsgHandler[Req, Resp transaction.Msg]( } return handler(ctx, typed, typedResp) } - router.RegisterPostMsgHandler(untypedHandler) + router.RegisterPostMsgHandler(msgName, untypedHandler) } // MsgRouter is a router that allows you to register Handlers for specific message types. type MsgRouter interface { - RegisterHandler(handler Handler) error + RegisterHandler(msgName string, handler Handler) error } // HasMsgHandlers is an interface that modules must implement if they want to register Handlers. @@ -138,17 +140,18 @@ type HasQueryHandlers interface { // // func (m Module) RegisterMsgHandlers(router appmodule.MsgRouter) { // handlers := keeper.NewHandlers(m.keeper) -// appmodule.RegisterHandler(router, handlers.MsgMint) +// appmodule.RegisterHandler(router, gogoproto.MessageName(types.MsgMint{}), handlers.MsgMint) // } // // func (m Module) RegisterQueryHandlers(router appmodule.QueryRouter) { // handlers := keeper.NewHandlers(m.keeper) -// appmodule.RegisterHandler(router, handlers.QueryBalance) +// appmodule.RegisterHandler(router, gogoproto.MessageName(types.QueryBalanceRequest{}), handlers.QueryBalance) // } // // ``` func RegisterHandler[R MsgRouter, Req, Resp transaction.Msg]( router R, + msgName string, handler func(ctx context.Context, msg Req) (msgResp Resp, err error), ) { untypedHandler := func(ctx context.Context, m transaction.Msg) (transaction.Msg, error) { @@ -158,5 +161,5 @@ func RegisterHandler[R MsgRouter, Req, Resp transaction.Msg]( } return handler(ctx, typed) } - router.RegisterHandler(untypedHandler) + router.RegisterHandler(msgName, untypedHandler) } diff --git a/runtime/v2/manager.go b/runtime/v2/manager.go index 925851e8bda1..eea180843800 100644 --- a/runtime/v2/manager.go +++ b/runtime/v2/manager.go @@ -711,7 +711,7 @@ func registerMethod( return "", err } - return string(requestName), stfRouter.RegisterHandler(func( + return string(requestName), stfRouter.RegisterHandler(string(requestName), func( ctx context.Context, msg transaction.Msg, ) (resp transaction.Msg, err error) { diff --git a/server/v2/stf/stf_router.go b/server/v2/stf/stf_router.go index 7be34b944ce0..1cab21cf2dd8 100644 --- a/server/v2/stf/stf_router.go +++ b/server/v2/stf/stf_router.go @@ -4,7 +4,6 @@ import ( "context" "errors" "fmt" - "reflect" "strings" gogoproto "github.com/cosmos/gogoproto/proto" @@ -33,12 +32,7 @@ type MsgRouterBuilder struct { globalPostHandlers []appmodulev2.PostMsgHandler } -func (b *MsgRouterBuilder) RegisterHandler(handler appmodulev2.Handler) error { - msgType, err := msgTypeURLFromHandler(handler) - if err != nil { - return err - } - +func (b *MsgRouterBuilder) RegisterHandler(msgType string, handler appmodulev2.Handler) error { // panic on override if _, ok := b.handlers[msgType]; ok { return fmt.Errorf("handler already registered: %s", msgType) @@ -52,22 +46,12 @@ func (b *MsgRouterBuilder) RegisterGlobalPreMsgHandler(handler appmodulev2.PreMs return nil } -func (b *MsgRouterBuilder) RegisterPreMsgHandler(handler appmodulev2.PreMsgHandler) error { - msgType, err := msgTypeURLFromHandler(handler) - if err != nil { - return err - } - +func (b *MsgRouterBuilder) RegisterPreMsgHandler(msgType string, handler appmodulev2.PreMsgHandler) error { b.preHandlers[msgType] = append(b.preHandlers[msgType], handler) return nil } -func (b *MsgRouterBuilder) RegisterPostMsgHandler(handler appmodulev2.PostMsgHandler) error { - msgType, err := msgTypeURLFromHandler(handler) - if err != nil { - return err - } - +func (b *MsgRouterBuilder) RegisterPostMsgHandler(msgType string, handler appmodulev2.PostMsgHandler) error { b.postHandlers[msgType] = append(b.postHandlers[msgType], handler) return nil } @@ -155,30 +139,6 @@ func buildHandler( } } -func msgTypeURLFromHandler(handler any) (string, error) { - handlerType := reflect.TypeOf(handler) - if handlerType.Kind() != reflect.Func { - return "", fmt.Errorf("handler must be a function") - } - if handlerType.NumIn() != 2 && handlerType.NumIn() != 3 { - return "", fmt.Errorf("handler must have 2-3 input parameters") - } - - // Get the type of the second parameter (transaction.Msg) - msgType := handlerType.In(1) - - if !msgType.Implements(reflect.TypeOf((*transaction.Msg)(nil)).Elem()) { - return "", fmt.Errorf("second parameter must implement transaction.Msg") - } - - msgName := msgTypeURL(*reflect.New(msgType).Interface().(*gogoproto.Message)) - if msgName == "" { - return "", fmt.Errorf("could not get message name") - } - - return msgName, nil -} - // msgTypeURL returns the TypeURL of a proto message. func msgTypeURL(msg gogoproto.Message) string { return gogoproto.MessageName(msg) diff --git a/server/v2/stf/stf_test.go b/server/v2/stf/stf_test.go index f7197d8a2adc..5fa4fce15b40 100644 --- a/server/v2/stf/stf_test.go +++ b/server/v2/stf/stf_test.go @@ -35,6 +35,7 @@ func addMsgHandlerToSTF[T any, PT interface { t.Helper() msgRouterBuilder := NewMsgRouterBuilder() err := msgRouterBuilder.RegisterHandler( + msgTypeURL(PT(new(T))), func(ctx context.Context, msg transaction.Msg) (msgResp transaction.Msg, err error) { typedReq := msg.(PT) typedResp, err := handler(ctx, typedReq) From 3f0416749ba4c305eeed11c99d2ced79cb865c7a Mon Sep 17 00:00:00 2001 From: Julien Robert Date: Fri, 6 Sep 2024 16:48:06 +0200 Subject: [PATCH 6/8] remove errors --- core/appmodule/v2/handlers.go | 25 ++++++++++++++----------- server/v2/stf/stf_router.go | 12 ++++-------- 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/core/appmodule/v2/handlers.go b/core/appmodule/v2/handlers.go index e1aae28554e7..9c960cbb3572 100644 --- a/core/appmodule/v2/handlers.go +++ b/core/appmodule/v2/handlers.go @@ -21,10 +21,10 @@ type ( type PreMsgRouter interface { // RegisterPreHandler will register a specific message handler hooking into the message with // the provided name. - RegisterPreMsgHandler(msgName string, handler PreMsgHandler) error + RegisterPreMsgHandler(msgName string, handler PreMsgHandler) // RegisterGlobalPreHandler will register a global message handler hooking into any message // being executed. - RegisterGlobalPreMsgHandler(handler PreMsgHandler) error + RegisterGlobalPreMsgHandler(handler PreMsgHandler) } // HasPreMsgHandlers is an interface that modules must implement if they want to register PreMsgHandlers. @@ -58,6 +58,7 @@ func RegisterMsgPreHandler[Req transaction.Msg]( } return handler(ctx, typed) } + router.RegisterPreMsgHandler(msgName, untypedHandler) } @@ -65,9 +66,9 @@ func RegisterMsgPreHandler[Req transaction.Msg]( type PostMsgRouter interface { // RegisterPostHandler will register a specific message handler hooking after the execution of message with // the provided name. - RegisterPostMsgHandler(msgName string, handler PostMsgHandler) error + RegisterPostMsgHandler(msgName string, handler PostMsgHandler) // RegisterGlobalPostHandler will register a global message handler hooking after the execution of any message. - RegisterGlobalPostMsgHandler(handler PostMsgHandler) error + RegisterGlobalPostMsgHandler(handler PostMsgHandler) } // HasPostMsgHandlers is an interface that modules must implement if they want to register PostMsgHandlers. @@ -105,11 +106,12 @@ func RegisterPostMsgHandler[Req, Resp transaction.Msg]( } 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 { +type MsgRouter = interface { RegisterHandler(msgName string, handler Handler) error } @@ -140,20 +142,20 @@ type HasQueryHandlers interface { // // func (m Module) RegisterMsgHandlers(router appmodule.MsgRouter) { // handlers := keeper.NewHandlers(m.keeper) -// appmodule.RegisterHandler(router, gogoproto.MessageName(types.MsgMint{}), handlers.MsgMint) +// err := appmodule.RegisterHandler(router, gogoproto.MessageName(types.MsgMint{}), handlers.MsgMint) // } // // func (m Module) RegisterQueryHandlers(router appmodule.QueryRouter) { // handlers := keeper.NewHandlers(m.keeper) -// appmodule.RegisterHandler(router, gogoproto.MessageName(types.QueryBalanceRequest{}), handlers.QueryBalance) +// err := appmodule.RegisterHandler(router, gogoproto.MessageName(types.QueryBalanceRequest{}), handlers.QueryBalance) // } // // ``` -func RegisterHandler[R MsgRouter, Req, Resp transaction.Msg]( - router R, +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 { @@ -161,5 +163,6 @@ func RegisterHandler[R MsgRouter, Req, Resp transaction.Msg]( } return handler(ctx, typed) } - router.RegisterHandler(msgName, untypedHandler) + + return router.RegisterHandler(msgName, untypedHandler) } diff --git a/server/v2/stf/stf_router.go b/server/v2/stf/stf_router.go index 1cab21cf2dd8..0417788e4b78 100644 --- a/server/v2/stf/stf_router.go +++ b/server/v2/stf/stf_router.go @@ -41,24 +41,20 @@ func (b *MsgRouterBuilder) RegisterHandler(msgType string, handler appmodulev2.H return nil } -func (b *MsgRouterBuilder) RegisterGlobalPreMsgHandler(handler appmodulev2.PreMsgHandler) error { +func (b *MsgRouterBuilder) RegisterGlobalPreMsgHandler(handler appmodulev2.PreMsgHandler) { b.globalPreHandlers = append(b.globalPreHandlers, handler) - return nil } -func (b *MsgRouterBuilder) RegisterPreMsgHandler(msgType string, handler appmodulev2.PreMsgHandler) error { +func (b *MsgRouterBuilder) RegisterPreMsgHandler(msgType string, handler appmodulev2.PreMsgHandler) { b.preHandlers[msgType] = append(b.preHandlers[msgType], handler) - return nil } -func (b *MsgRouterBuilder) RegisterPostMsgHandler(msgType string, handler appmodulev2.PostMsgHandler) error { +func (b *MsgRouterBuilder) RegisterPostMsgHandler(msgType string, handler appmodulev2.PostMsgHandler) { b.postHandlers[msgType] = append(b.postHandlers[msgType], handler) - return nil } -func (b *MsgRouterBuilder) RegisterGlobalPostMsgHandler(handler appmodulev2.PostMsgHandler) error { +func (b *MsgRouterBuilder) RegisterGlobalPostMsgHandler(handler appmodulev2.PostMsgHandler) { b.globalPostHandlers = append(b.globalPostHandlers, handler) - return nil } func (b *MsgRouterBuilder) HandlerExists(msgType string) bool { From 1d8940072c7cd7d8aab5af9e38410e6531587335 Mon Sep 17 00:00:00 2001 From: Julien Robert Date: Mon, 9 Sep 2024 09:08:29 +0200 Subject: [PATCH 7/8] feedback --- core/appmodule/v2/handlers.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/appmodule/v2/handlers.go b/core/appmodule/v2/handlers.go index 9c960cbb3572..df991f37fefc 100644 --- a/core/appmodule/v2/handlers.go +++ b/core/appmodule/v2/handlers.go @@ -133,7 +133,7 @@ type HasQueryHandlers interface { // ```go // // func (h Handlers) Mint(ctx context.Context, req *types.MsgMint) (*types.MsgMintResponse, error) { -// ... query logic ... +// ... msg logic ... // } // // func (h Handlers) QueryBalance(ctx context.Context, req *types.QueryBalanceRequest) (*types.QueryBalanceResponse, error) { From 14323ecc341aa91f1edd3368320d74d3ffbafa56 Mon Sep 17 00:00:00 2001 From: Julien Robert Date: Mon, 9 Sep 2024 10:23:16 +0200 Subject: [PATCH 8/8] docs --- core/appmodule/v2/handlers.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/appmodule/v2/handlers.go b/core/appmodule/v2/handlers.go index df991f37fefc..b42698b7e86f 100644 --- a/core/appmodule/v2/handlers.go +++ b/core/appmodule/v2/handlers.go @@ -36,7 +36,7 @@ type HasPreMsgHandlers interface { // PreMsgRouter. Example usage: // ```go // -// func (h Handlers) BeforeSend(ctx context.Context, req *types.MsgSend) (*types.QueryBalanceResponse, error) { +// func (h Handlers) BeforeSend(ctx context.Context, req *types.MsgSend) error { // ... before send logic ... // } //