github.com/Finschia/finschia-sdk@v0.48.1/baseapp/grpcrouter.go (about)

     1  package baseapp
     2  
     3  import (
     4  	"fmt"
     5  
     6  	gogogrpc "github.com/gogo/protobuf/grpc"
     7  	"google.golang.org/grpc"
     8  	"google.golang.org/grpc/encoding"
     9  	"google.golang.org/grpc/encoding/proto"
    10  
    11  	abci "github.com/tendermint/tendermint/abci/types"
    12  
    13  	"github.com/Finschia/finschia-sdk/client/grpc/reflection"
    14  	codectypes "github.com/Finschia/finschia-sdk/codec/types"
    15  	sdk "github.com/Finschia/finschia-sdk/types"
    16  )
    17  
    18  var protoCodec = encoding.GetCodec(proto.Name)
    19  
    20  // GRPCQueryRouter routes ABCI Query requests to GRPC handlers
    21  type GRPCQueryRouter struct {
    22  	routes            map[string]GRPCQueryHandler
    23  	interfaceRegistry codectypes.InterfaceRegistry
    24  	serviceData       []serviceData
    25  }
    26  
    27  // serviceData represents a gRPC service, along with its handler.
    28  type serviceData struct {
    29  	serviceDesc *grpc.ServiceDesc
    30  	handler     interface{}
    31  }
    32  
    33  var _ gogogrpc.Server = &GRPCQueryRouter{}
    34  
    35  // NewGRPCQueryRouter creates a new GRPCQueryRouter
    36  func NewGRPCQueryRouter() *GRPCQueryRouter {
    37  	return &GRPCQueryRouter{
    38  		routes: map[string]GRPCQueryHandler{},
    39  	}
    40  }
    41  
    42  // GRPCQueryHandler defines a function type which handles ABCI Query requests
    43  // using gRPC
    44  type GRPCQueryHandler = func(ctx sdk.Context, req abci.RequestQuery) (abci.ResponseQuery, error)
    45  
    46  // Route returns the GRPCQueryHandler for a given query route path or nil
    47  // if not found
    48  func (qrt *GRPCQueryRouter) Route(path string) GRPCQueryHandler {
    49  	handler, found := qrt.routes[path]
    50  	if !found {
    51  		return nil
    52  	}
    53  	return handler
    54  }
    55  
    56  // RegisterService implements the gRPC Server.RegisterService method. sd is a gRPC
    57  // service description, handler is an object which implements that gRPC service/
    58  //
    59  // This functions PANICS:
    60  // - if a protobuf service is registered twice.
    61  func (qrt *GRPCQueryRouter) RegisterService(sd *grpc.ServiceDesc, handler interface{}) {
    62  	// adds a top-level query handler based on the gRPC service name
    63  	for _, method := range sd.Methods {
    64  		fqName := fmt.Sprintf("/%s/%s", sd.ServiceName, method.MethodName)
    65  		methodHandler := method.Handler
    66  
    67  		// Check that each service is only registered once. If a service is
    68  		// registered more than once, then we should error. Since we can't
    69  		// return an error (`Server.RegisterService` interface restriction) we
    70  		// panic (at startup).
    71  		_, found := qrt.routes[fqName]
    72  		if found {
    73  			panic(
    74  				fmt.Errorf(
    75  					"gRPC query service %s has already been registered. Please make sure to only register each service once. "+
    76  						"This usually means that there are conflicting modules registering the same gRPC query service",
    77  					fqName,
    78  				),
    79  			)
    80  		}
    81  
    82  		qrt.routes[fqName] = func(ctx sdk.Context, req abci.RequestQuery) (abci.ResponseQuery, error) {
    83  			// call the method handler from the service description with the handler object,
    84  			// a wrapped sdk.Context with proto-unmarshaled data from the ABCI request data
    85  			res, err := methodHandler(handler, sdk.WrapSDKContext(ctx), func(i interface{}) error {
    86  				err := protoCodec.Unmarshal(req.Data, i)
    87  				if err != nil {
    88  					return err
    89  				}
    90  				if qrt.interfaceRegistry != nil {
    91  					return codectypes.UnpackInterfaces(i, qrt.interfaceRegistry)
    92  				}
    93  				return nil
    94  			}, nil)
    95  			if err != nil {
    96  				return abci.ResponseQuery{}, err
    97  			}
    98  
    99  			// proto marshal the result bytes
   100  			resBytes, err := protoCodec.Marshal(res)
   101  			if err != nil {
   102  				return abci.ResponseQuery{}, err
   103  			}
   104  
   105  			// return the result bytes as the response value
   106  			return abci.ResponseQuery{
   107  				Height: req.Height,
   108  				Value:  resBytes,
   109  			}, nil
   110  		}
   111  	}
   112  
   113  	qrt.serviceData = append(qrt.serviceData, serviceData{
   114  		serviceDesc: sd,
   115  		handler:     handler,
   116  	})
   117  }
   118  
   119  // SetInterfaceRegistry sets the interface registry for the router. This will
   120  // also register the interface reflection gRPC service.
   121  func (qrt *GRPCQueryRouter) SetInterfaceRegistry(interfaceRegistry codectypes.InterfaceRegistry) {
   122  	qrt.interfaceRegistry = interfaceRegistry
   123  	// Once we have an interface registry, we can register the interface
   124  	// registry reflection gRPC service.
   125  	reflection.RegisterReflectionServiceServer(
   126  		qrt,
   127  		reflection.NewReflectionServiceServer(interfaceRegistry),
   128  	)
   129  }