Skip to content

Commit 379690f

Browse files
tac0turtlemichaelfig
authored andcommitted
Revert "Make gRPC requests go through tendermint Query (cosmos#8549)"
This reverts commit e306e5b.
1 parent 0b1a3a6 commit 379690f

File tree

6 files changed

+93
-140
lines changed

6 files changed

+93
-140
lines changed

baseapp/grpcrouter.go

Lines changed: 2 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package baseapp
22

33
import (
44
"fmt"
5-
"reflect"
65

76
"github.com/cosmos/cosmos-sdk/client/grpc/reflection"
87

@@ -14,19 +13,13 @@ import (
1413

1514
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
1615
sdk "github.com/cosmos/cosmos-sdk/types"
17-
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
1816
)
1917

2018
var protoCodec = encoding.GetCodec(proto.Name)
2119

2220
// GRPCQueryRouter routes ABCI Query requests to GRPC handlers
2321
type GRPCQueryRouter struct {
24-
routes map[string]GRPCQueryHandler
25-
// returnTypes is a map of FQ method name => its return type. It is used
26-
// for cache purposes: the first time a method handler is run, we save its
27-
// return type in this map. Then, on subsequent method handler calls, we
28-
// decode the ABCI response bytes using the cached return type.
29-
returnTypes map[string]reflect.Type
22+
routes map[string]GRPCQueryHandler
3023
interfaceRegistry codectypes.InterfaceRegistry
3124
serviceData []serviceData
3225
}
@@ -42,8 +35,7 @@ var _ gogogrpc.Server = &GRPCQueryRouter{}
4235
// NewGRPCQueryRouter creates a new GRPCQueryRouter
4336
func NewGRPCQueryRouter() *GRPCQueryRouter {
4437
return &GRPCQueryRouter{
45-
returnTypes: map[string]reflect.Type{},
46-
routes: map[string]GRPCQueryHandler{},
38+
routes: map[string]GRPCQueryHandler{},
4739
}
4840
}
4941

@@ -98,17 +90,8 @@ func (qrt *GRPCQueryRouter) RegisterService(sd *grpc.ServiceDesc, handler interf
9890
if qrt.interfaceRegistry != nil {
9991
return codectypes.UnpackInterfaces(i, qrt.interfaceRegistry)
10092
}
101-
10293
return nil
10394
}, nil)
104-
105-
// If it's the first time we call this handler, then we save
106-
// the return type of the handler in the `returnTypes` map.
107-
// The return type will be used for decoding subsequent requests.
108-
if _, found := qrt.returnTypes[fqName]; !found {
109-
qrt.returnTypes[fqName] = reflect.TypeOf(res)
110-
}
111-
11295
if err != nil {
11396
return abci.ResponseQuery{}, err
11497
}
@@ -144,16 +127,3 @@ func (qrt *GRPCQueryRouter) SetInterfaceRegistry(interfaceRegistry codectypes.In
144127
reflection.NewReflectionServiceServer(interfaceRegistry),
145128
)
146129
}
147-
148-
// returnTypeOf returns the return type of a gRPC method handler. With the way the
149-
// `returnTypes` cache map is set up, the return type of a method handler is
150-
// guaranteed to be found if it's retrieved **after** the method handler ran at
151-
// least once. If not, then a logic error is return.
152-
func (qrt *GRPCQueryRouter) returnTypeOf(method string) (reflect.Type, error) {
153-
returnType, found := qrt.returnTypes[method]
154-
if !found {
155-
return nil, sdkerrors.Wrapf(sdkerrors.ErrLogic, "cannot find %s return type", method)
156-
}
157-
158-
return returnType, nil
159-
}

baseapp/grpcserver.go

Lines changed: 36 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -2,78 +2,67 @@ package baseapp
22

33
import (
44
"context"
5-
"reflect"
5+
"strconv"
66

77
gogogrpc "github.com/gogo/protobuf/grpc"
88
grpcmiddleware "github.com/grpc-ecosystem/go-grpc-middleware"
99
grpcrecovery "github.com/grpc-ecosystem/go-grpc-middleware/recovery"
1010
"google.golang.org/grpc"
11+
"google.golang.org/grpc/codes"
1112
"google.golang.org/grpc/metadata"
13+
"google.golang.org/grpc/status"
1214

13-
"github.com/cosmos/cosmos-sdk/client"
15+
sdk "github.com/cosmos/cosmos-sdk/types"
1416
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
15-
"github.com/cosmos/cosmos-sdk/types/tx"
17+
grpctypes "github.com/cosmos/cosmos-sdk/types/grpc"
1618
)
1719

1820
// GRPCQueryRouter returns the GRPCQueryRouter of a BaseApp.
1921
func (app *BaseApp) GRPCQueryRouter() *GRPCQueryRouter { return app.grpcQueryRouter }
2022

2123
// RegisterGRPCServer registers gRPC services directly with the gRPC server.
22-
func (app *BaseApp) RegisterGRPCServer(clientCtx client.Context, server gogogrpc.Server) {
23-
// Define an interceptor for all gRPC queries: this interceptor will route
24-
// the query through the `clientCtx`, which itself queries Tendermint.
25-
interceptor := func(grpcCtx context.Context, req interface{}, info *grpc.UnaryServerInfo, _ grpc.UnaryHandler) (interface{}, error) {
26-
// Two things can happen here:
27-
// 1. either we're broadcasting a Tx, in which case we call Tendermint's broadcast endpoint directly,
28-
// 2. or we are querying for state, in which case we call ABCI's Query.
29-
30-
// Case 1. Broadcasting a Tx.
31-
if reqProto, ok := req.(*tx.BroadcastTxRequest); ok {
32-
if !ok {
33-
return nil, sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "expected %T, got %T", (*tx.BroadcastTxRequest)(nil), req)
34-
}
35-
36-
return client.TxServiceBroadcast(grpcCtx, clientCtx, reqProto)
24+
func (app *BaseApp) RegisterGRPCServer(server gogogrpc.Server) {
25+
// Define an interceptor for all gRPC queries: this interceptor will create
26+
// a new sdk.Context, and pass it into the query handler.
27+
interceptor := func(grpcCtx context.Context, req interface{}, _ *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
28+
// If there's some metadata in the context, retrieve it.
29+
md, ok := metadata.FromIncomingContext(grpcCtx)
30+
if !ok {
31+
return nil, status.Error(codes.Internal, "unable to retrieve metadata")
3732
}
3833

39-
// Case 2. Querying state.
40-
inMd, _ := metadata.FromIncomingContext(grpcCtx)
41-
abciRes, outMd, err := client.RunGRPCQuery(clientCtx, grpcCtx, info.FullMethod, req, inMd)
42-
if err != nil {
43-
return nil, err
34+
// Get height header from the request context, if present.
35+
var height int64
36+
if heightHeaders := md.Get(grpctypes.GRPCBlockHeightHeader); len(heightHeaders) > 0 {
37+
height, err = strconv.ParseInt(heightHeaders[0], 10, 64)
38+
if err != nil {
39+
return nil, sdkerrors.Wrapf(
40+
sdkerrors.ErrInvalidRequest,
41+
"Baseapp.RegisterGRPCServer: invalid height header %q: %v", grpctypes.GRPCBlockHeightHeader, err)
42+
}
43+
if err := checkNegativeHeight(height); err != nil {
44+
return nil, err
45+
}
4446
}
4547

46-
// We need to know the return type of the grpc method for
47-
// unmarshalling abciRes.Value.
48-
//
49-
// When we call each method handler for the first time, we save its
50-
// return type in the `returnTypes` map (see the method handler in
51-
// `grpcrouter.go`). By this time, the method handler has already run
52-
// at least once (in the RunGRPCQuery call), so we're sure the
53-
// returnType maps is populated for this method. We're retrieving it
54-
// for decoding.
55-
returnType, err := app.GRPCQueryRouter().returnTypeOf(info.FullMethod)
48+
// Create the sdk.Context. Passing false as 2nd arg, as we can't
49+
// actually support proofs with gRPC right now.
50+
sdkCtx, err := app.createQueryContext(height, false)
5651
if err != nil {
5752
return nil, err
5853
}
5954

60-
// returnType is a pointer to a struct. Here, we're creating res which
61-
// is a new pointer to the underlying struct.
62-
res := reflect.New(returnType.Elem()).Interface()
55+
// Attach the sdk.Context into the gRPC's context.Context.
56+
grpcCtx = context.WithValue(grpcCtx, sdk.SdkContextKey, sdkCtx)
6357

64-
err = protoCodec.Unmarshal(abciRes.Value, res)
65-
if err != nil {
66-
return nil, err
67-
}
68-
69-
// Send the metadata header back. The metadata currently includes:
70-
// - block height.
71-
err = grpc.SendHeader(grpcCtx, outMd)
72-
if err != nil {
73-
return nil, err
58+
// Add relevant gRPC headers
59+
if height == 0 {
60+
height = sdkCtx.BlockHeight() // If height was not set in the request, set it to the latest
7461
}
62+
md = metadata.Pairs(grpctypes.GRPCBlockHeightHeader, strconv.FormatInt(height, 10))
63+
grpc.SetHeader(grpcCtx, md)
7564

76-
return res, nil
65+
return handler(grpcCtx, req)
7766
}
7867

7968
// Loop through all services and methods, add the interceptor, and register

client/grpc_query.go

Lines changed: 48 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -24,109 +24,101 @@ var _ gogogrpc.ClientConn = Context{}
2424
var protoCodec = encoding.GetCodec(proto.Name)
2525

2626
// Invoke implements the grpc ClientConn.Invoke method
27-
func (ctx Context) Invoke(grpcCtx gocontext.Context, method string, req, reply interface{}, opts ...grpc.CallOption) (err error) {
27+
func (ctx Context) Invoke(grpcCtx gocontext.Context, method string, args, reply interface{}, opts ...grpc.CallOption) (err error) {
2828
// Two things can happen here:
2929
// 1. either we're broadcasting a Tx, in which call we call Tendermint's broadcast endpoint directly,
3030
// 2. or we are querying for state, in which case we call ABCI's Query.
3131

32-
// In both cases, we don't allow empty request req (it will panic unexpectedly).
33-
if reflect.ValueOf(req).IsNil() {
32+
// In both cases, we don't allow empty request args (it will panic unexpectedly).
33+
if reflect.ValueOf(args).IsNil() {
3434
return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "request cannot be nil")
3535
}
3636

3737
// Case 1. Broadcasting a Tx.
38-
if reqProto, ok := req.(*tx.BroadcastTxRequest); ok {
38+
if isBroadcast(method) {
39+
req, ok := args.(*tx.BroadcastTxRequest)
3940
if !ok {
40-
return sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "expected %T, got %T", (*tx.BroadcastTxRequest)(nil), req)
41+
return sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "expected %T, got %T", (*tx.BroadcastTxRequest)(nil), args)
4142
}
42-
resProto, ok := reply.(*tx.BroadcastTxResponse)
43+
res, ok := reply.(*tx.BroadcastTxResponse)
4344
if !ok {
44-
return sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "expected %T, got %T", (*tx.BroadcastTxResponse)(nil), req)
45+
return sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "expected %T, got %T", (*tx.BroadcastTxResponse)(nil), args)
4546
}
4647

47-
broadcastRes, err := TxServiceBroadcast(grpcCtx, ctx, reqProto)
48+
broadcastRes, err := TxServiceBroadcast(grpcCtx, ctx, req)
4849
if err != nil {
4950
return err
5051
}
51-
*resProto = *broadcastRes
52+
*res = *broadcastRes
5253

5354
return err
5455
}
5556

5657
// Case 2. Querying state.
57-
inMd, _ := metadata.FromOutgoingContext(grpcCtx)
58-
abciRes, outMd, err := RunGRPCQuery(ctx, grpcCtx, method, req, inMd)
58+
reqBz, err := protoCodec.Marshal(args)
5959
if err != nil {
6060
return err
6161
}
6262

63-
err = protoCodec.Unmarshal(abciRes.Value, reply)
64-
if err != nil {
65-
return err
66-
}
67-
68-
for _, callOpt := range opts {
69-
header, ok := callOpt.(grpc.HeaderCallOption)
70-
if !ok {
71-
continue
72-
}
73-
74-
*header.HeaderAddr = outMd
75-
}
76-
77-
if ctx.InterfaceRegistry != nil {
78-
return types.UnpackInterfaces(reply, ctx.InterfaceRegistry)
79-
}
80-
81-
return nil
82-
}
83-
84-
// NewStream implements the grpc ClientConn.NewStream method
85-
func (Context) NewStream(gocontext.Context, *grpc.StreamDesc, string, ...grpc.CallOption) (grpc.ClientStream, error) {
86-
return nil, fmt.Errorf("streaming rpc not supported")
87-
}
88-
89-
// RunGRPCQuery runs a gRPC query from the clientCtx, given all necessary
90-
// arguments for the gRPC method, and returns the ABCI response. It is used
91-
// to factorize code between client (Invoke) and server (RegisterGRPCServer)
92-
// gRPC handlers.
93-
func RunGRPCQuery(ctx Context, grpcCtx gocontext.Context, method string, req interface{}, md metadata.MD) (abci.ResponseQuery, metadata.MD, error) {
94-
reqBz, err := protoCodec.Marshal(req)
95-
if err != nil {
96-
return abci.ResponseQuery{}, nil, err
97-
}
98-
9963
// parse height header
64+
md, _ := metadata.FromOutgoingContext(grpcCtx)
10065
if heights := md.Get(grpctypes.GRPCBlockHeightHeader); len(heights) > 0 {
10166
height, err := strconv.ParseInt(heights[0], 10, 64)
10267
if err != nil {
103-
return abci.ResponseQuery{}, nil, err
68+
return err
10469
}
10570
if height < 0 {
106-
return abci.ResponseQuery{}, nil, sdkerrors.Wrapf(
71+
return sdkerrors.Wrapf(
10772
sdkerrors.ErrInvalidRequest,
10873
"client.Context.Invoke: height (%d) from %q must be >= 0", height, grpctypes.GRPCBlockHeightHeader)
10974
}
11075

11176
ctx = ctx.WithHeight(height)
11277
}
11378

114-
abciReq := abci.RequestQuery{
115-
Path: method,
116-
Data: reqBz,
79+
req := abci.RequestQuery{
80+
Path: method,
81+
Data: reqBz,
82+
Height: ctx.Height,
11783
}
11884

119-
abciRes, err := ctx.QueryABCI(abciReq)
85+
res, err := ctx.QueryABCI(req)
12086
if err != nil {
121-
return abci.ResponseQuery{}, nil, err
87+
return err
88+
}
89+
90+
err = protoCodec.Unmarshal(res.Value, reply)
91+
if err != nil {
92+
return err
12293
}
12394

12495
// Create header metadata. For now the headers contain:
12596
// - block height
12697
// We then parse all the call options, if the call option is a
12798
// HeaderCallOption, then we manually set the value of that header to the
12899
// metadata.
129-
md = metadata.Pairs(grpctypes.GRPCBlockHeightHeader, strconv.FormatInt(abciRes.Height, 10))
100+
md = metadata.Pairs(grpctypes.GRPCBlockHeightHeader, strconv.FormatInt(res.Height, 10))
101+
for _, callOpt := range opts {
102+
header, ok := callOpt.(grpc.HeaderCallOption)
103+
if !ok {
104+
continue
105+
}
106+
107+
*header.HeaderAddr = md
108+
}
109+
110+
if ctx.InterfaceRegistry != nil {
111+
return types.UnpackInterfaces(reply, ctx.InterfaceRegistry)
112+
}
113+
114+
return nil
115+
}
116+
117+
// NewStream implements the grpc ClientConn.NewStream method
118+
func (Context) NewStream(gocontext.Context, *grpc.StreamDesc, string, ...grpc.CallOption) (grpc.ClientStream, error) {
119+
return nil, fmt.Errorf("streaming rpc not supported")
120+
}
130121

131-
return abciRes, md, nil
122+
func isBroadcast(method string) bool {
123+
return method == "/cosmos.tx.v1beta1.Service/BroadcastTx"
132124
}

server/grpc/server.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import (
1717
// StartGRPCServer starts a gRPC server on the given address.
1818
func StartGRPCServer(clientCtx client.Context, app types.Application, address string) (*grpc.Server, error) {
1919
grpcSrv := grpc.NewServer()
20-
app.RegisterGRPCServer(clientCtx, grpcSrv)
20+
app.RegisterGRPCServer(grpcSrv)
2121
// reflection allows consumers to build dynamic clients that can write
2222
// to any cosmos-sdk application without relying on application packages at compile time
2323
err := reflection.Register(grpcSrv, reflection.Config{

server/grpc/server_test.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ func (s *IntegrationTestSuite) TestGRPCServer_BankBalance() {
104104
)
105105
s.Require().NoError(err)
106106
blockHeight = header.Get(grpctypes.GRPCBlockHeightHeader)
107-
s.Require().NotEmpty(blockHeight[0]) // blockHeight is []string, first element is block height.
107+
s.Require().Equal([]string{"1"}, blockHeight)
108108
}
109109

110110
func (s *IntegrationTestSuite) TestGRPCServer_Reflection() {
@@ -163,6 +163,8 @@ func (s *IntegrationTestSuite) TestGRPCServer_GetTxsEvent() {
163163
Events: []string{"message.action='send'"},
164164
},
165165
)
166+
// TODO Once https://github.com/cosmos/cosmos-sdk/pull/8029 is merged, this
167+
// should not error anymore.
166168
s.Require().NoError(err)
167169
}
168170

@@ -199,9 +201,9 @@ func (s *IntegrationTestSuite) TestGRPCServerInvalidHeaderHeights() {
199201
value string
200202
wantErr string
201203
}{
202-
{"-1", "\"x-cosmos-block-height\" must be >= 0"},
204+
{"-1", "height < 0"},
203205
{"9223372036854775808", "value out of range"}, // > max(int64) by 1
204-
{"-10", "\"x-cosmos-block-height\" must be >= 0"},
206+
{"-10", "height < 0"},
205207
{"18446744073709551615", "value out of range"}, // max uint64, which is > max(int64)
206208
{"-9223372036854775809", "value out of range"}, // Out of the range of for negative int64
207209
}

server/types/app.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ type (
4343

4444
// RegisterGRPCServer registers gRPC services directly with the gRPC
4545
// server.
46-
RegisterGRPCServer(client.Context, grpc.Server)
46+
RegisterGRPCServer(grpc.Server)
4747

4848
// RegisterTxService registers the gRPC Query service for tx (such as tx
4949
// simulation, fetching txs by hash...).

0 commit comments

Comments
 (0)