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  }