Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: wire new handlers to grpc #22333

Merged
merged 17 commits into from
Nov 5, 2024
2,590 changes: 2,590 additions & 0 deletions api/cosmos/base/grpc/v2/service.pulsar.go

Large diffs are not rendered by default.

167 changes: 167 additions & 0 deletions api/cosmos/base/grpc/v2/service_grpc.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

39 changes: 39 additions & 0 deletions proto/cosmos/base/grpc/v2/service.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
syntax = "proto3";
package cosmos.base.grpc.v2;

import "google/protobuf/any.proto";

option go_package = "cosmossdk.io/server/v2/api/grpc";

// Service defines the gRPC service for query server for v2
service Service {
// Query queries the server with a request, the request can be any sdk Msg.
rpc Query(QueryRequest) returns (QueryResponse) {}

// ListQueryHandlers lists all the available query handlers.
rpc ListQueryHandlers(ListQueryHandlersRequest) returns (ListQueryHandlersResponse) {}
}

// QueryRequest is the request for the Query method
message QueryRequest {
google.protobuf.Any request = 1;
}

// QueryResponse is the response for the Query method
message QueryResponse {
google.protobuf.Any response = 1;
}

// ListQueryHandlersRequest is the request for the ListQueryHandlers method
message ListQueryHandlersRequest {}

// ListQueryHandlersResponse is the response for the ListQueryHandlers method
message ListQueryHandlersResponse {
repeated Handler handlers = 1;
}

// Handler defines a query handler
message Handler {
string request_name = 1;
string response_name = 2;
}
3 changes: 3 additions & 0 deletions server/v2/api/grpc/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@ func New[T transaction.Tx](
// Reflection allows external clients to see what services and methods the gRPC server exposes.
gogoreflection.Register(grpcSrv, slices.Collect(maps.Keys(queryHandlers)), logger.With("sub-module", "grpc-reflection"))

// Register V2
RegisterServiceServer(grpcSrv, &v2Service{queryHandlers, queryable})

srv.grpcSrv = grpcSrv
srv.config = serverCfg
srv.logger = logger.With(log.ModuleKey, srv.Name())
Expand Down
73 changes: 73 additions & 0 deletions server/v2/api/grpc/service.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package grpc

import (
"context"

"github.com/cosmos/gogoproto/proto"
gogoproto "github.com/cosmos/gogoproto/types/any"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"

appmodulev2 "cosmossdk.io/core/appmodule/v2"
"cosmossdk.io/core/transaction"
)

// v2Service implements the gRPC service interface for handling queries and listing handlers.
type v2Service struct {
queryHandlers map[string]appmodulev2.Handler
queryable interface {
Query(ctx context.Context, version uint64, msg transaction.Msg) (transaction.Msg, error)
}
}

// Query handles incoming query requests by unmarshaling the request, processing it,
// and returning the response in an Any protobuf message.
func (s v2Service) Query(ctx context.Context, request *QueryRequest) (*QueryResponse, error) {
if request == nil || request.Request == nil {
return nil, status.Error(codes.InvalidArgument, "empty request")
}

msgName := request.Request.TypeUrl

handler, exists := s.queryHandlers[msgName]
if !exists {
return nil, status.Errorf(codes.NotFound, "handler not found for %s", msgName)
}
Comment on lines +34 to +35
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Avoid exposing internal error details to clients

Returning detailed error messages can leak internal information and pose security risks. Consider returning generic error messages to the client and logging the detailed errors internally.

Apply this diff to adjust the error messages:

     if !exists {
-        return nil, status.Errorf(codes.NotFound, "handler not found for %s", msgName)
+        return nil, status.Error(codes.NotFound, "handler not found")
     }

     if err := proto.Unmarshal(request.Request.Value, protoMsg); err != nil {
-        return nil, status.Errorf(codes.InvalidArgument, "failed to unmarshal request: %v", err)
+        return nil, status.Error(codes.InvalidArgument, "invalid request format")
     }

     if err != nil {
-        return nil, status.Errorf(codes.Internal, "query failed: %v", err)
+        return nil, status.Error(codes.Internal, "internal server error")
     }

Also applies to: 52-53, 57-58


protoMsg := handler.MakeMsg()
if err := proto.Unmarshal(request.Request.Value, protoMsg); err != nil {
return nil, status.Errorf(codes.InvalidArgument, "failed to unmarshal request: %v", err)
}

queryResp, err := s.queryable.Query(ctx, 0, protoMsg)
if err != nil {
return nil, status.Errorf(codes.Internal, "query failed: %v", err)
}

respBytes, err := proto.Marshal(queryResp)
if err != nil {
return nil, status.Errorf(codes.Internal, "failed to marshal response: %v", err)
}

anyResp := &gogoproto.Any{
TypeUrl: "/" + proto.MessageName(queryResp),
Value: respBytes,
}

return &QueryResponse{Response: anyResp}, nil
}

func (s v2Service) ListQueryHandlers(_ context.Context, _ *ListQueryHandlersRequest) (*ListQueryHandlersResponse, error) {
var handlerDescriptors []*Handler
for handlerName := range s.queryHandlers {
msg := s.queryHandlers[handlerName].MakeMsg()
resp := s.queryHandlers[handlerName].MakeMsgResp()

handlerDescriptors = append(handlerDescriptors, &Handler{
RequestName: proto.MessageName(msg),
ResponseName: proto.MessageName(resp),
})
}
Comment on lines +62 to +70

Check warning

Code scanning / CodeQL

Iteration over map Warning

Iteration over map may be a possible source of non-determinism

return &ListQueryHandlersResponse{Handlers: handlerDescriptors}, nil
}
Loading
Loading