github.com/Finschia/finschia-sdk@v0.48.1/baseapp/msg_service_router.go (about) 1 package baseapp 2 3 import ( 4 "context" 5 "fmt" 6 7 gogogrpc "github.com/gogo/protobuf/grpc" 8 "github.com/gogo/protobuf/proto" 9 "google.golang.org/grpc" 10 11 codectypes "github.com/Finschia/finschia-sdk/codec/types" 12 sdk "github.com/Finschia/finschia-sdk/types" 13 sdkerrors "github.com/Finschia/finschia-sdk/types/errors" 14 ) 15 16 // MsgServiceRouter routes fully-qualified Msg service methods to their handler. 17 type MsgServiceRouter struct { 18 interfaceRegistry codectypes.InterfaceRegistry 19 routes map[string]MsgServiceHandler 20 } 21 22 var _ gogogrpc.Server = &MsgServiceRouter{} 23 24 // NewMsgServiceRouter creates a new MsgServiceRouter. 25 func NewMsgServiceRouter() *MsgServiceRouter { 26 return &MsgServiceRouter{ 27 routes: map[string]MsgServiceHandler{}, 28 } 29 } 30 31 // MsgServiceHandler defines a function type which handles Msg service message. 32 type MsgServiceHandler = func(ctx sdk.Context, req sdk.Msg) (*sdk.Result, error) 33 34 // Handler returns the MsgServiceHandler for a given msg or nil if not found. 35 func (msr *MsgServiceRouter) Handler(msg sdk.Msg) MsgServiceHandler { 36 return msr.routes[sdk.MsgTypeURL(msg)] 37 } 38 39 // HandlerByTypeURL returns the MsgServiceHandler for a given query route path or nil 40 // if not found. 41 func (msr *MsgServiceRouter) HandlerByTypeURL(typeURL string) MsgServiceHandler { 42 return msr.routes[typeURL] 43 } 44 45 // RegisterService implements the gRPC Server.RegisterService method. sd is a gRPC 46 // service description, handler is an object which implements that gRPC service. 47 // 48 // This function PANICs: 49 // - if it is called before the service `Msg`s have been registered using 50 // RegisterInterfaces, 51 // - or if a service is being registered twice. 52 func (msr *MsgServiceRouter) RegisterService(sd *grpc.ServiceDesc, handler interface{}) { 53 // Adds a top-level query handler based on the gRPC service name. 54 for _, method := range sd.Methods { 55 fqMethod := fmt.Sprintf("/%s/%s", sd.ServiceName, method.MethodName) 56 methodHandler := method.Handler 57 58 var requestTypeName string 59 60 // NOTE: This is how we pull the concrete request type for each handler for registering in the InterfaceRegistry. 61 // This approach is maybe a bit hacky, but less hacky than reflecting on the handler object itself. 62 // We use a no-op interceptor to avoid actually calling into the handler itself. 63 _, _ = methodHandler(nil, context.Background(), func(i interface{}) error { 64 msg, ok := i.(sdk.Msg) 65 if !ok { 66 // We panic here because there is no other alternative and the app cannot be initialized correctly 67 // this should only happen if there is a problem with code generation in which case the app won't 68 // work correctly anyway. 69 panic(fmt.Errorf("can't register request type %T for service method %s", i, fqMethod)) 70 } 71 72 requestTypeName = sdk.MsgTypeURL(msg) 73 return nil 74 }, noopInterceptor) 75 76 // Check that the service Msg fully-qualified method name has already 77 // been registered (via RegisterInterfaces). If the user registers a 78 // service without registering according service Msg type, there might be 79 // some unexpected behavior down the road. Since we can't return an error 80 // (`Server.RegisterService` interface restriction) we panic (at startup). 81 reqType, err := msr.interfaceRegistry.Resolve(requestTypeName) 82 if err != nil || reqType == nil { 83 panic( 84 fmt.Errorf( 85 "type_url %s has not been registered yet. "+ 86 "Before calling RegisterService, you must register all interfaces by calling the `RegisterInterfaces` "+ 87 "method on module.BasicManager. Each module should call `msgservice.RegisterMsgServiceDesc` inside its "+ 88 "`RegisterInterfaces` method with the `_Msg_serviceDesc` generated by proto-gen", 89 requestTypeName, 90 ), 91 ) 92 } 93 94 // Check that each service is only registered once. If a service is 95 // registered more than once, then we should error. Since we can't 96 // return an error (`Server.RegisterService` interface restriction) we 97 // panic (at startup). 98 _, found := msr.routes[requestTypeName] 99 if found { 100 panic( 101 fmt.Errorf( 102 "msg service %s has already been registered. Please make sure to only register each service once. "+ 103 "This usually means that there are conflicting modules registering the same msg service", 104 fqMethod, 105 ), 106 ) 107 } 108 109 msr.routes[requestTypeName] = func(ctx sdk.Context, req sdk.Msg) (*sdk.Result, error) { 110 ctx = ctx.WithEventManager(sdk.NewEventManager()) 111 interceptor := func(goCtx context.Context, _ interface{}, _ *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) { 112 goCtx = context.WithValue(goCtx, sdk.SdkContextKey, ctx) 113 return handler(goCtx, req) 114 } 115 116 // Call the method handler from the service description with the handler object. 117 // We don't do any decoding here because the decoding was already done. 118 res, err := methodHandler(handler, sdk.WrapSDKContext(ctx), noopDecoder, interceptor) 119 if err != nil { 120 return nil, err 121 } 122 123 resMsg, ok := res.(proto.Message) 124 if !ok { 125 return nil, sdkerrors.Wrapf(sdkerrors.ErrInvalidType, "Expecting proto.Message, got %T", resMsg) 126 } 127 128 return sdk.WrapServiceResult(ctx, resMsg, err) 129 } 130 } 131 } 132 133 // SetInterfaceRegistry sets the interface registry for the router. 134 func (msr *MsgServiceRouter) SetInterfaceRegistry(interfaceRegistry codectypes.InterfaceRegistry) { 135 msr.interfaceRegistry = interfaceRegistry 136 } 137 138 func noopDecoder(_ interface{}) error { return nil } 139 func noopInterceptor(_ context.Context, _ interface{}, _ *grpc.UnaryServerInfo, _ grpc.UnaryHandler) (interface{}, error) { 140 return nil, nil 141 }