From 849429ecdab8911edd2ed77c41ab5ffda2cdefc4 Mon Sep 17 00:00:00 2001 From: Aaron Craelius Date: Fri, 3 Jul 2020 12:42:12 -0400 Subject: [PATCH] Add proper support for Any in gRPC queries (#6594) * Add proper gRPC Any support via AnyUnpacker * Wire up grpc query router AnyUnpacker --- baseapp/baseapp.go | 3 +- baseapp/grpcrouter.go | 24 +- baseapp/grpcrouter_helpers.go | 22 +- baseapp/grpcrouter_test.go | 12 + client/context.go | 55 ++-- client/grpc_query.go | 13 +- codec/testdata/proto.pb.go | 458 +++++++++++++++++++++++++++++-- codec/testdata/proto.proto | 9 + codec/testdata/test_service.go | 34 +++ simapp/app.go | 33 ++- simapp/cmd/simcli/main.go | 1 + types/query/pagination_test.go | 2 +- x/bank/keeper/grpc_query_test.go | 8 +- 13 files changed, 600 insertions(+), 74 deletions(-) diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 492dbec6cc..fe58222886 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -5,7 +5,6 @@ import ( "reflect" "strings" - "github.com/gogo/protobuf/grpc" "github.com/gogo/protobuf/proto" abci "github.com/tendermint/tendermint/abci/types" @@ -292,7 +291,7 @@ func (app *BaseApp) Router() sdk.Router { func (app *BaseApp) QueryRouter() sdk.QueryRouter { return app.queryRouter } // GRPCQueryRouter returns the GRPCQueryRouter of a BaseApp. -func (app *BaseApp) GRPCQueryRouter() grpc.Server { return app.grpcQueryRouter } +func (app *BaseApp) GRPCQueryRouter() *GRPCQueryRouter { return app.grpcQueryRouter } // Seal seals a BaseApp. It prohibits any further modifications to a BaseApp. func (app *BaseApp) Seal() { app.sealed = true } diff --git a/baseapp/grpcrouter.go b/baseapp/grpcrouter.go index 4c809655d2..8bb9fcdaa3 100644 --- a/baseapp/grpcrouter.go +++ b/baseapp/grpcrouter.go @@ -3,6 +3,8 @@ package baseapp import ( "fmt" + "github.com/cosmos/cosmos-sdk/codec/types" + gogogrpc "github.com/gogo/protobuf/grpc" abci "github.com/tendermint/tendermint/abci/types" "google.golang.org/grpc" @@ -16,7 +18,8 @@ var protoCodec = encoding.GetCodec(proto.Name) // GRPCQueryRouter routes ABCI Query requests to GRPC handlers type GRPCQueryRouter struct { - routes map[string]GRPCQueryHandler + routes map[string]GRPCQueryHandler + anyUnpacker types.AnyUnpacker } var _ gogogrpc.Server @@ -54,7 +57,14 @@ func (qrt *GRPCQueryRouter) RegisterService(sd *grpc.ServiceDesc, handler interf // call the method handler from the service description with the handler object, // a wrapped sdk.Context with proto-unmarshaled data from the ABCI request data res, err := methodHandler(handler, sdk.WrapSDKContext(ctx), func(i interface{}) error { - return protoCodec.Unmarshal(req.Data, i) + err := protoCodec.Unmarshal(req.Data, i) + if err != nil { + return err + } + if qrt.anyUnpacker != nil { + return types.UnpackInterfaces(i, qrt.anyUnpacker) + } + return nil }, nil) if err != nil { return abci.ResponseQuery{}, err @@ -74,3 +84,13 @@ func (qrt *GRPCQueryRouter) RegisterService(sd *grpc.ServiceDesc, handler interf } } } + +// AnyUnpacker returns the AnyUnpacker for the router +func (qrt *GRPCQueryRouter) AnyUnpacker() types.AnyUnpacker { + return qrt.anyUnpacker +} + +// SetAnyUnpacker sets the AnyUnpacker for the router +func (qrt *GRPCQueryRouter) SetAnyUnpacker(anyUnpacker types.AnyUnpacker) { + qrt.anyUnpacker = anyUnpacker +} diff --git a/baseapp/grpcrouter_helpers.go b/baseapp/grpcrouter_helpers.go index beb8bfbc08..c40f226485 100644 --- a/baseapp/grpcrouter_helpers.go +++ b/baseapp/grpcrouter_helpers.go @@ -4,6 +4,8 @@ import ( gocontext "context" "fmt" + "github.com/cosmos/cosmos-sdk/codec/types" + gogogrpc "github.com/gogo/protobuf/grpc" abci "github.com/tendermint/tendermint/abci/types" "google.golang.org/grpc" @@ -22,8 +24,10 @@ type QueryServiceTestHelper struct { // NewQueryServerTestHelper creates a new QueryServiceTestHelper that wraps // the provided sdk.Context -func NewQueryServerTestHelper(ctx sdk.Context) *QueryServiceTestHelper { - return &QueryServiceTestHelper{GRPCQueryRouter: NewGRPCQueryRouter(), ctx: ctx} +func NewQueryServerTestHelper(ctx sdk.Context, anyUnpacker types.AnyUnpacker) *QueryServiceTestHelper { + qrt := NewGRPCQueryRouter() + qrt.SetAnyUnpacker(anyUnpacker) + return &QueryServiceTestHelper{GRPCQueryRouter: qrt, ctx: ctx} } // Invoke implements the grpc ClientConn.Invoke method @@ -36,12 +40,22 @@ func (q *QueryServiceTestHelper) Invoke(_ gocontext.Context, method string, args if err != nil { return err } - res, err := querier(q.ctx, abci.RequestQuery{Data: reqBz}) + res, err := querier(q.ctx, abci.RequestQuery{Data: reqBz}) if err != nil { return err } - return protoCodec.Unmarshal(res.Value, reply) + + err = protoCodec.Unmarshal(res.Value, reply) + if err != nil { + return err + } + + if q.anyUnpacker != nil { + return types.UnpackInterfaces(reply, q.anyUnpacker) + } + + return nil } // NewStream implements the grpc ClientConn.NewStream method diff --git a/baseapp/grpcrouter_test.go b/baseapp/grpcrouter_test.go index 428b3127d6..5e4c905163 100644 --- a/baseapp/grpcrouter_test.go +++ b/baseapp/grpcrouter_test.go @@ -4,6 +4,8 @@ import ( "context" "testing" + "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/stretchr/testify/require" "github.com/cosmos/cosmos-sdk/codec/testdata" @@ -12,6 +14,8 @@ import ( func TestGRPCRouter(t *testing.T) { qr := NewGRPCQueryRouter() + interfaceRegistry := testdata.NewTestInterfaceRegistry() + qr.SetAnyUnpacker(interfaceRegistry) testdata.RegisterTestServiceServer(qr, testdata.TestServiceImpl{}) helper := &QueryServiceTestHelper{ GRPCQueryRouter: qr, @@ -32,4 +36,12 @@ func TestGRPCRouter(t *testing.T) { require.Nil(t, err) require.NotNil(t, res) require.Equal(t, "Hello Foo!", res2.Greeting) + + spot := &testdata.Dog{Name: "Spot", Size_: "big"} + any, err := types.NewAnyWithValue(spot) + require.NoError(t, err) + res3, err := client.TestAny(context.Background(), &testdata.TestAnyRequest{AnyAnimal: any}) + require.NoError(t, err) + require.NotNil(t, res3) + require.Equal(t, spot, res3.HasAnimal.Animal.GetCachedValue()) } diff --git a/client/context.go b/client/context.go index 60f150200a..525c851ae8 100644 --- a/client/context.go +++ b/client/context.go @@ -7,6 +7,8 @@ import ( "io" "os" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/pkg/errors" "github.com/spf13/viper" yaml "gopkg.in/yaml.v2" @@ -25,29 +27,30 @@ import ( // Context implements a typical context created in SDK modules for transaction // handling and queries. type Context struct { - FromAddress sdk.AccAddress - Client rpcclient.Client - ChainID string - JSONMarshaler codec.JSONMarshaler - Input io.Reader - Keyring keyring.Keyring - Output io.Writer - OutputFormat string - Height int64 - HomeDir string - From string - BroadcastMode string - FromName string - TrustNode bool - UseLedger bool - Simulate bool - GenerateOnly bool - Offline bool - SkipConfirm bool - TxGenerator TxGenerator - AccountRetriever AccountRetriever - NodeURI string - Verifier tmlite.Verifier + FromAddress sdk.AccAddress + Client rpcclient.Client + ChainID string + JSONMarshaler codec.JSONMarshaler + InterfaceRegistry codectypes.InterfaceRegistry + Input io.Reader + Keyring keyring.Keyring + Output io.Writer + OutputFormat string + Height int64 + HomeDir string + From string + BroadcastMode string + FromName string + TrustNode bool + UseLedger bool + Simulate bool + GenerateOnly bool + Offline bool + SkipConfirm bool + TxGenerator TxGenerator + AccountRetriever AccountRetriever + NodeURI string + Verifier tmlite.Verifier // TODO: Deprecated (remove). Codec *codec.Codec @@ -328,6 +331,12 @@ func (ctx Context) WithAccountRetriever(retriever AccountRetriever) Context { return ctx } +// WithInterfaceRegistry returns the context with an updated InterfaceRegistry +func (ctx Context) WithInterfaceRegistry(interfaceRegistry codectypes.InterfaceRegistry) Context { + ctx.InterfaceRegistry = interfaceRegistry + return ctx +} + // PrintOutput outputs toPrint to the ctx.Output based on ctx.OutputFormat which is // either text or json. If text, toPrint will be YAML encoded. Otherwise, toPrint // will be JSON encoded using ctx.JSONMarshaler. An error is returned upon failure. diff --git a/client/grpc_query.go b/client/grpc_query.go index efd6c335f8..73aaf2e99a 100644 --- a/client/grpc_query.go +++ b/client/grpc_query.go @@ -4,6 +4,7 @@ import ( gocontext "context" "fmt" + "github.com/cosmos/cosmos-sdk/codec/types" gogogrpc "github.com/gogo/protobuf/grpc" "google.golang.org/grpc" "google.golang.org/grpc/encoding" @@ -24,7 +25,17 @@ func (ctx Context) Invoke(_ gocontext.Context, method string, args, reply interf if err != nil { return err } - return protoCodec.Unmarshal(resBz, reply) + + err = protoCodec.Unmarshal(resBz, reply) + if err != nil { + return err + } + + if ctx.InterfaceRegistry != nil { + return types.UnpackInterfaces(reply, ctx.InterfaceRegistry) + } + + return nil } // NewStream implements the grpc ClientConn.NewStream method diff --git a/codec/testdata/proto.pb.go b/codec/testdata/proto.pb.go index b10536e732..01e5f19eda 100644 --- a/codec/testdata/proto.pb.go +++ b/codec/testdata/proto.pb.go @@ -448,6 +448,94 @@ func (m *SayHelloResponse) GetGreeting() string { return "" } +type TestAnyRequest struct { + AnyAnimal *types.Any `protobuf:"bytes,1,opt,name=any_animal,json=anyAnimal,proto3" json:"any_animal,omitempty"` +} + +func (m *TestAnyRequest) Reset() { *m = TestAnyRequest{} } +func (m *TestAnyRequest) String() string { return proto.CompactTextString(m) } +func (*TestAnyRequest) ProtoMessage() {} +func (*TestAnyRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_2fcc84b9998d60d8, []int{9} +} +func (m *TestAnyRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *TestAnyRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_TestAnyRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *TestAnyRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_TestAnyRequest.Merge(m, src) +} +func (m *TestAnyRequest) XXX_Size() int { + return m.Size() +} +func (m *TestAnyRequest) XXX_DiscardUnknown() { + xxx_messageInfo_TestAnyRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_TestAnyRequest proto.InternalMessageInfo + +func (m *TestAnyRequest) GetAnyAnimal() *types.Any { + if m != nil { + return m.AnyAnimal + } + return nil +} + +type TestAnyResponse struct { + HasAnimal *HasAnimal `protobuf:"bytes,1,opt,name=has_animal,json=hasAnimal,proto3" json:"has_animal,omitempty"` +} + +func (m *TestAnyResponse) Reset() { *m = TestAnyResponse{} } +func (m *TestAnyResponse) String() string { return proto.CompactTextString(m) } +func (*TestAnyResponse) ProtoMessage() {} +func (*TestAnyResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_2fcc84b9998d60d8, []int{10} +} +func (m *TestAnyResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *TestAnyResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_TestAnyResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *TestAnyResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_TestAnyResponse.Merge(m, src) +} +func (m *TestAnyResponse) XXX_Size() int { + return m.Size() +} +func (m *TestAnyResponse) XXX_DiscardUnknown() { + xxx_messageInfo_TestAnyResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_TestAnyResponse proto.InternalMessageInfo + +func (m *TestAnyResponse) GetHasAnimal() *HasAnimal { + if m != nil { + return m.HasAnimal + } + return nil +} + func init() { proto.RegisterType((*Dog)(nil), "testdata.Dog") proto.RegisterType((*Cat)(nil), "testdata.Cat") @@ -458,38 +546,44 @@ func init() { proto.RegisterType((*EchoResponse)(nil), "testdata.EchoResponse") proto.RegisterType((*SayHelloRequest)(nil), "testdata.SayHelloRequest") proto.RegisterType((*SayHelloResponse)(nil), "testdata.SayHelloResponse") + proto.RegisterType((*TestAnyRequest)(nil), "testdata.TestAnyRequest") + proto.RegisterType((*TestAnyResponse)(nil), "testdata.TestAnyResponse") } func init() { proto.RegisterFile("proto.proto", fileDescriptor_2fcc84b9998d60d8) } var fileDescriptor_2fcc84b9998d60d8 = []byte{ - // 414 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x92, 0xb1, 0x8e, 0xd3, 0x40, - 0x10, 0x86, 0xb3, 0xe4, 0xee, 0x48, 0xc6, 0x16, 0x87, 0x56, 0x07, 0xca, 0xb9, 0xb0, 0x90, 0x25, - 0xc4, 0x49, 0x70, 0x6b, 0xe9, 0x4e, 0xd7, 0xd0, 0x85, 0x10, 0x91, 0x86, 0xc6, 0xa1, 0xa2, 0x41, - 0x1b, 0x67, 0xb0, 0xad, 0xd8, 0xde, 0xe0, 0xdd, 0x44, 0x09, 0x2f, 0x40, 0xcb, 0x63, 0x51, 0xa6, - 0xa4, 0x44, 0xc9, 0x8b, 0x20, 0xef, 0xda, 0x71, 0x14, 0x21, 0x94, 0xc6, 0x9e, 0x19, 0xff, 0xff, - 0xb7, 0xe3, 0x99, 0x05, 0x6b, 0x5e, 0x08, 0x25, 0x98, 0x7e, 0xd2, 0x8e, 0x42, 0xa9, 0xa6, 0x5c, - 0x71, 0xe7, 0x3a, 0x12, 0x22, 0x4a, 0xd1, 0xd7, 0xf5, 0xc9, 0xe2, 0xab, 0xcf, 0xf3, 0xb5, 0x11, - 0x79, 0xb7, 0xd0, 0x7e, 0x2f, 0x22, 0x4a, 0xe1, 0x4c, 0x26, 0xdf, 0xb1, 0x47, 0x5e, 0x90, 0x9b, - 0x6e, 0xa0, 0xe3, 0xb2, 0x96, 0xf3, 0x0c, 0x7b, 0x8f, 0x4c, 0xad, 0x8c, 0xbd, 0x07, 0x68, 0x0f, - 0xb8, 0xa2, 0x3d, 0x78, 0x9c, 0x89, 0x3c, 0x99, 0x61, 0x51, 0x39, 0xea, 0x94, 0x5e, 0xc1, 0x79, - 0x9a, 0x2c, 0x51, 0x6a, 0xd7, 0x79, 0x60, 0x12, 0xef, 0x03, 0x74, 0x47, 0x5c, 0xf6, 0xf3, 0x24, - 0xe3, 0x29, 0x7d, 0x03, 0x17, 0x5c, 0x47, 0xda, 0x6b, 0xdd, 0x5d, 0x31, 0xd3, 0x1e, 0xab, 0xdb, - 0x63, 0xfd, 0x7c, 0x1d, 0x54, 0x1a, 0x6a, 0x03, 0x59, 0x69, 0x58, 0x3b, 0x20, 0x2b, 0x6f, 0x00, - 0xf6, 0x88, 0xcb, 0x86, 0x75, 0x0f, 0x10, 0x73, 0xf9, 0xe5, 0x04, 0x5e, 0x37, 0xae, 0x4d, 0xde, - 0x47, 0xb8, 0x34, 0x90, 0x86, 0xf3, 0x16, 0x9e, 0x94, 0x9c, 0x13, 0x59, 0x76, 0x7c, 0xe0, 0xf5, - 0x5e, 0x81, 0x35, 0x0c, 0x63, 0x11, 0xe0, 0xb7, 0x05, 0x4a, 0x33, 0x1b, 0x94, 0x92, 0x47, 0xb8, - 0x9f, 0x8d, 0x49, 0xbd, 0x1b, 0xb0, 0x8d, 0x50, 0xce, 0x45, 0x2e, 0xf1, 0x3f, 0xca, 0x97, 0x70, - 0x39, 0xe6, 0xeb, 0x11, 0xa6, 0xe9, 0x1e, 0x5b, 0x6f, 0x83, 0x1c, 0x6c, 0x83, 0xc1, 0xd3, 0x46, - 0x56, 0x41, 0x1d, 0xe8, 0x44, 0x05, 0xa2, 0x4a, 0xf2, 0xa8, 0xd2, 0xee, 0xf3, 0xbb, 0x1f, 0x04, - 0xac, 0x4f, 0x28, 0xd5, 0x18, 0x8b, 0x65, 0x12, 0x22, 0x7d, 0x80, 0xb3, 0xb2, 0x21, 0xfa, 0x8c, - 0xd5, 0x57, 0x85, 0x1d, 0xfc, 0x89, 0xf3, 0xfc, 0xb8, 0x5c, 0x1d, 0xd1, 0x87, 0x4e, 0x7d, 0x2c, - 0xbd, 0x6e, 0x34, 0x47, 0x1d, 0x3b, 0xce, 0xbf, 0x3e, 0x19, 0xc4, 0xbb, 0xe1, 0xaf, 0xad, 0x4b, - 0x36, 0x5b, 0x97, 0xfc, 0xd9, 0xba, 0xe4, 0xe7, 0xce, 0x6d, 0x6d, 0x76, 0x6e, 0xeb, 0xf7, 0xce, - 0x6d, 0x7d, 0x7e, 0x1d, 0x25, 0x2a, 0x5e, 0x4c, 0x58, 0x28, 0x32, 0x3f, 0x14, 0x32, 0x13, 0xb2, - 0x7a, 0xdd, 0xca, 0xe9, 0xcc, 0x0f, 0xc5, 0x14, 0x43, 0xbf, 0xe6, 0x4e, 0x2e, 0xf4, 0x5a, 0xee, - 0xff, 0x06, 0x00, 0x00, 0xff, 0xff, 0x38, 0x43, 0xe8, 0xad, 0xf8, 0x02, 0x00, 0x00, + // 467 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x92, 0x4f, 0x6f, 0xd3, 0x30, + 0x18, 0xc6, 0x6b, 0xba, 0x3f, 0xed, 0xdb, 0x6a, 0x45, 0x66, 0xa0, 0x36, 0x87, 0x08, 0x45, 0x42, + 0x4c, 0x82, 0x25, 0x52, 0xab, 0x5d, 0x38, 0x20, 0x95, 0x51, 0xd1, 0x0b, 0x97, 0x8c, 0x13, 0x17, + 0xe4, 0xa6, 0x2f, 0x49, 0xb4, 0xc4, 0x1e, 0x75, 0x3a, 0x2d, 0x7c, 0x0a, 0xbe, 0x15, 0x1c, 0x77, + 0xe4, 0x88, 0xda, 0x2f, 0x82, 0x62, 0x3b, 0x7f, 0x98, 0x2a, 0xd4, 0x4b, 0xeb, 0xf7, 0xf5, 0xf3, + 0xfc, 0x6c, 0x3f, 0x6f, 0xa0, 0x77, 0xb3, 0x12, 0x99, 0x70, 0xd5, 0x2f, 0xed, 0x64, 0x28, 0xb3, + 0x25, 0xcb, 0x98, 0x35, 0x0a, 0x85, 0x08, 0x13, 0xf4, 0x54, 0x7f, 0xb1, 0xfe, 0xea, 0x31, 0x9e, + 0x6b, 0x91, 0x73, 0x0e, 0xed, 0xf7, 0x22, 0xa4, 0x14, 0x0e, 0x64, 0xfc, 0x1d, 0x87, 0xe4, 0x39, + 0x39, 0xeb, 0xfa, 0x6a, 0x5d, 0xf4, 0x38, 0x4b, 0x71, 0xf8, 0x48, 0xf7, 0x8a, 0xb5, 0x73, 0x01, + 0xed, 0x4b, 0x96, 0xd1, 0x21, 0x1c, 0xa7, 0x82, 0xc7, 0xd7, 0xb8, 0x32, 0x8e, 0xb2, 0xa4, 0xa7, + 0x70, 0x98, 0xc4, 0xb7, 0x28, 0x95, 0xeb, 0xd0, 0xd7, 0x85, 0xf3, 0x01, 0xba, 0x73, 0x26, 0xa7, + 0x3c, 0x4e, 0x59, 0x42, 0x5f, 0xc3, 0x11, 0x53, 0x2b, 0xe5, 0xed, 0x8d, 0x4f, 0x5d, 0x7d, 0x3d, + 0xb7, 0xbc, 0x9e, 0x3b, 0xe5, 0xb9, 0x6f, 0x34, 0xb4, 0x0f, 0xe4, 0x4e, 0xc1, 0xda, 0x3e, 0xb9, + 0x73, 0x2e, 0xa1, 0x3f, 0x67, 0xb2, 0x66, 0x4d, 0x00, 0x22, 0x26, 0xbf, 0xec, 0xc1, 0xeb, 0x46, + 0xa5, 0xc9, 0xf9, 0x08, 0x03, 0x0d, 0xa9, 0x39, 0x6f, 0xe0, 0xa4, 0xe0, 0xec, 0xc9, 0xea, 0x47, + 0x0d, 0xaf, 0xf3, 0x12, 0x7a, 0xb3, 0x20, 0x12, 0x3e, 0x7e, 0x5b, 0xa3, 0xd4, 0xd9, 0xa0, 0x94, + 0x2c, 0xc4, 0x2a, 0x1b, 0x5d, 0x3a, 0x67, 0xd0, 0xd7, 0x42, 0x79, 0x23, 0xb8, 0xc4, 0xff, 0x28, + 0x5f, 0xc0, 0xe0, 0x8a, 0xe5, 0x73, 0x4c, 0x92, 0x0a, 0x5b, 0x4e, 0x83, 0x34, 0xa6, 0xe1, 0xc2, + 0xe3, 0x5a, 0x66, 0xa0, 0x16, 0x74, 0xc2, 0x15, 0x62, 0x16, 0xf3, 0xd0, 0x68, 0xab, 0xda, 0x99, + 0xc1, 0xc9, 0x27, 0x94, 0x59, 0xf1, 0x04, 0x43, 0x9d, 0x00, 0x30, 0x9e, 0xef, 0x95, 0x1f, 0xe3, + 0xb9, 0x79, 0xf0, 0x0c, 0x06, 0x15, 0xc6, 0x9c, 0x3a, 0xde, 0x31, 0x87, 0x27, 0x6e, 0xf9, 0x01, + 0xba, 0x55, 0x58, 0x8d, 0x31, 0x8c, 0x7f, 0x12, 0xe8, 0x15, 0x9c, 0x2b, 0x5c, 0xdd, 0xc6, 0x01, + 0xd2, 0x0b, 0x38, 0x28, 0xe2, 0xa1, 0x4f, 0x6b, 0x5f, 0x23, 0x57, 0xeb, 0xd9, 0xc3, 0xb6, 0x39, + 0x7a, 0x0a, 0x9d, 0x32, 0x04, 0x3a, 0xaa, 0x35, 0x0f, 0xf2, 0xb3, 0xac, 0x5d, 0x5b, 0x06, 0xf1, + 0x16, 0x8e, 0xcd, 0x83, 0xe8, 0xb0, 0x96, 0xfd, 0x1b, 0x95, 0x35, 0xda, 0xb1, 0xa3, 0xfd, 0xef, + 0x66, 0xbf, 0x36, 0x36, 0xb9, 0xdf, 0xd8, 0xe4, 0xcf, 0xc6, 0x26, 0x3f, 0xb6, 0x76, 0xeb, 0x7e, + 0x6b, 0xb7, 0x7e, 0x6f, 0xed, 0xd6, 0xe7, 0x57, 0x61, 0x9c, 0x45, 0xeb, 0x85, 0x1b, 0x88, 0xd4, + 0x0b, 0x84, 0x4c, 0x85, 0x34, 0x7f, 0xe7, 0x72, 0x79, 0xed, 0x05, 0x62, 0x89, 0x81, 0x57, 0x62, + 0x17, 0x47, 0x2a, 0xf0, 0xc9, 0xdf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x82, 0x85, 0xb8, 0x56, 0xc6, + 0x03, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -506,6 +600,7 @@ const _ = grpc.SupportPackageIsVersion4 type TestServiceClient interface { Echo(ctx context.Context, in *EchoRequest, opts ...grpc.CallOption) (*EchoResponse, error) SayHello(ctx context.Context, in *SayHelloRequest, opts ...grpc.CallOption) (*SayHelloResponse, error) + TestAny(ctx context.Context, in *TestAnyRequest, opts ...grpc.CallOption) (*TestAnyResponse, error) } type testServiceClient struct { @@ -534,10 +629,20 @@ func (c *testServiceClient) SayHello(ctx context.Context, in *SayHelloRequest, o return out, nil } +func (c *testServiceClient) TestAny(ctx context.Context, in *TestAnyRequest, opts ...grpc.CallOption) (*TestAnyResponse, error) { + out := new(TestAnyResponse) + err := c.cc.Invoke(ctx, "/testdata.TestService/TestAny", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // TestServiceServer is the server API for TestService service. type TestServiceServer interface { Echo(context.Context, *EchoRequest) (*EchoResponse, error) SayHello(context.Context, *SayHelloRequest) (*SayHelloResponse, error) + TestAny(context.Context, *TestAnyRequest) (*TestAnyResponse, error) } // UnimplementedTestServiceServer can be embedded to have forward compatible implementations. @@ -550,6 +655,9 @@ func (*UnimplementedTestServiceServer) Echo(ctx context.Context, req *EchoReques func (*UnimplementedTestServiceServer) SayHello(ctx context.Context, req *SayHelloRequest) (*SayHelloResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method SayHello not implemented") } +func (*UnimplementedTestServiceServer) TestAny(ctx context.Context, req *TestAnyRequest) (*TestAnyResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method TestAny not implemented") +} func RegisterTestServiceServer(s grpc1.Server, srv TestServiceServer) { s.RegisterService(&_TestService_serviceDesc, srv) @@ -591,6 +699,24 @@ func _TestService_SayHello_Handler(srv interface{}, ctx context.Context, dec fun return interceptor(ctx, in, info, handler) } +func _TestService_TestAny_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(TestAnyRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(TestServiceServer).TestAny(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/testdata.TestService/TestAny", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(TestServiceServer).TestAny(ctx, req.(*TestAnyRequest)) + } + return interceptor(ctx, in, info, handler) +} + var _TestService_serviceDesc = grpc.ServiceDesc{ ServiceName: "testdata.TestService", HandlerType: (*TestServiceServer)(nil), @@ -603,6 +729,10 @@ var _TestService_serviceDesc = grpc.ServiceDesc{ MethodName: "SayHello", Handler: _TestService_SayHello_Handler, }, + { + MethodName: "TestAny", + Handler: _TestService_TestAny_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "proto.proto", @@ -910,6 +1040,76 @@ func (m *SayHelloResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *TestAnyRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *TestAnyRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TestAnyRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.AnyAnimal != nil { + { + size, err := m.AnyAnimal.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintProto(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *TestAnyResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *TestAnyResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TestAnyResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.HasAnimal != nil { + { + size, err := m.HasAnimal.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintProto(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + func encodeVarintProto(dAtA []byte, offset int, v uint64) int { offset -= sovProto(v) base := offset @@ -1048,6 +1248,32 @@ func (m *SayHelloResponse) Size() (n int) { return n } +func (m *TestAnyRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.AnyAnimal != nil { + l = m.AnyAnimal.Size() + n += 1 + l + sovProto(uint64(l)) + } + return n +} + +func (m *TestAnyResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.HasAnimal != nil { + l = m.HasAnimal.Size() + n += 1 + l + sovProto(uint64(l)) + } + return n +} + func sovProto(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } @@ -1901,6 +2127,184 @@ func (m *SayHelloResponse) Unmarshal(dAtA []byte) error { } return nil } +func (m *TestAnyRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: TestAnyRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: TestAnyRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AnyAnimal", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthProto + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthProto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.AnyAnimal == nil { + m.AnyAnimal = &types.Any{} + } + if err := m.AnyAnimal.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipProto(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthProto + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthProto + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *TestAnyResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: TestAnyResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: TestAnyResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field HasAnimal", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthProto + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthProto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.HasAnimal == nil { + m.HasAnimal = &HasAnimal{} + } + if err := m.HasAnimal.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipProto(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthProto + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthProto + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func skipProto(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 diff --git a/codec/testdata/proto.proto b/codec/testdata/proto.proto index 617cb1f21b..3b6cf3ddae 100644 --- a/codec/testdata/proto.proto +++ b/codec/testdata/proto.proto @@ -31,6 +31,7 @@ message HasHasHasAnimal { service TestService { rpc Echo(EchoRequest) returns (EchoResponse); rpc SayHello(SayHelloRequest) returns (SayHelloResponse); + rpc TestAny(TestAnyRequest) returns (TestAnyResponse); } message EchoRequest { @@ -48,3 +49,11 @@ message SayHelloRequest { message SayHelloResponse { string greeting = 1; } + +message TestAnyRequest { + google.protobuf.Any any_animal = 1; +} + +message TestAnyResponse { + HasAnimal has_animal = 1; +} diff --git a/codec/testdata/test_service.go b/codec/testdata/test_service.go index 3463be872b..5311c160b8 100644 --- a/codec/testdata/test_service.go +++ b/codec/testdata/test_service.go @@ -3,10 +3,31 @@ package testdata import ( "context" "fmt" + + "github.com/gogo/protobuf/proto" + + "github.com/cosmos/cosmos-sdk/codec/types" ) type TestServiceImpl struct{} +func (e TestServiceImpl) TestAny(_ context.Context, request *TestAnyRequest) (*TestAnyResponse, error) { + animal, ok := request.AnyAnimal.GetCachedValue().(Animal) + if !ok { + return nil, fmt.Errorf("expected Animal") + } + + any, err := types.NewAnyWithValue(animal.(proto.Message)) + if err != nil { + return nil, err + } + + return &TestAnyResponse{HasAnimal: &HasAnimal{ + Animal: any, + X: 10, + }}, nil +} + func (e TestServiceImpl) Echo(_ context.Context, req *EchoRequest) (*EchoResponse, error) { return &EchoResponse{Message: req.Message}, nil } @@ -17,3 +38,16 @@ func (e TestServiceImpl) SayHello(_ context.Context, request *SayHelloRequest) ( } var _ TestServiceServer = TestServiceImpl{} + +var _ types.UnpackInterfacesMessage = &TestAnyRequest{} + +func (m *TestAnyRequest) UnpackInterfaces(unpacker types.AnyUnpacker) error { + var animal Animal + return unpacker.UnpackAny(m.AnyAnimal, &animal) +} + +var _ types.UnpackInterfacesMessage = &TestAnyResponse{} + +func (m *TestAnyResponse) UnpackInterfaces(unpacker types.AnyUnpacker) error { + return m.HasAnimal.UnpackInterfaces(unpacker) +} diff --git a/simapp/app.go b/simapp/app.go index b501a7c9ac..63615fb3db 100644 --- a/simapp/app.go +++ b/simapp/app.go @@ -4,6 +4,8 @@ import ( "io" "os" + "github.com/cosmos/cosmos-sdk/codec/types" + abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/libs/log" tmos "github.com/tendermint/tendermint/libs/os" @@ -129,8 +131,9 @@ var _ App = (*SimApp)(nil) // capabilities aren't needed for testing. type SimApp struct { *baseapp.BaseApp - cdc *codec.Codec - appCodec codec.Marshaler + cdc *codec.Codec + appCodec codec.Marshaler + interfaceRegistry types.InterfaceRegistry invCheckPeriod uint @@ -173,11 +176,15 @@ func NewSimApp( ) *SimApp { // TODO: Remove cdc in favor of appCodec once all modules are migrated. - appCodec, cdc := MakeCodecs() + encodingConfig := MakeEncodingConfig() + appCodec := encodingConfig.Marshaler + cdc := encodingConfig.Amino + interfaceRegistry := encodingConfig.InterfaceRegistry bApp := baseapp.NewBaseApp(appName, logger, db, authtypes.DefaultTxDecoder(cdc), baseAppOptions...) bApp.SetCommitMultiStoreTracer(traceStore) bApp.SetAppVersion(version.Version) + bApp.GRPCQueryRouter().SetAnyUnpacker(interfaceRegistry) keys := sdk.NewKVStoreKeys( authtypes.StoreKey, banktypes.StoreKey, stakingtypes.StoreKey, @@ -189,13 +196,14 @@ func NewSimApp( memKeys := sdk.NewMemoryStoreKeys(capabilitytypes.MemStoreKey) app := &SimApp{ - BaseApp: bApp, - cdc: cdc, - appCodec: appCodec, - invCheckPeriod: invCheckPeriod, - keys: keys, - tkeys: tkeys, - memKeys: memKeys, + BaseApp: bApp, + cdc: cdc, + appCodec: appCodec, + interfaceRegistry: interfaceRegistry, + invCheckPeriod: invCheckPeriod, + keys: keys, + tkeys: tkeys, + memKeys: memKeys, } app.ParamsKeeper = initParamsKeeper(appCodec, keys[paramstypes.StoreKey], tkeys[paramstypes.TStoreKey]) @@ -456,6 +464,11 @@ func (app *SimApp) AppCodec() codec.Marshaler { return app.appCodec } +// InterfaceRegistry returns SimApp's InterfaceRegistry +func (app *SimApp) InterfaceRegistry() types.InterfaceRegistry { + return app.interfaceRegistry +} + // GetKey returns the KVStoreKey for the provided store key. // // NOTE: This is solely to be used for testing purposes. diff --git a/simapp/cmd/simcli/main.go b/simapp/cmd/simcli/main.go index 0a6a76c193..779acf2fb5 100644 --- a/simapp/cmd/simcli/main.go +++ b/simapp/cmd/simcli/main.go @@ -24,6 +24,7 @@ var ( encodingConfig = simapp.MakeEncodingConfig() initClientCtx = client.Context{}. WithJSONMarshaler(encodingConfig.Marshaler). + WithInterfaceRegistry(encodingConfig.InterfaceRegistry). WithTxGenerator(encodingConfig.TxGenerator). WithCodec(encodingConfig.Amino). WithInput(os.Stdin). diff --git a/types/query/pagination_test.go b/types/query/pagination_test.go index d4585103b3..f2e19e4e42 100644 --- a/types/query/pagination_test.go +++ b/types/query/pagination_test.go @@ -38,7 +38,7 @@ const ( func TestPagination(t *testing.T) { app, ctx, _ := setupTest() - queryHelper := baseapp.NewQueryServerTestHelper(ctx) + queryHelper := baseapp.NewQueryServerTestHelper(ctx, app.InterfaceRegistry()) types.RegisterQueryServer(queryHelper, app.BankKeeper) queryClient := types.NewQueryClient(queryHelper) diff --git a/x/bank/keeper/grpc_query_test.go b/x/bank/keeper/grpc_query_test.go index bdd15535bf..5715aa84cf 100644 --- a/x/bank/keeper/grpc_query_test.go +++ b/x/bank/keeper/grpc_query_test.go @@ -15,7 +15,7 @@ func (suite *IntegrationTestSuite) TestQueryBalance() { app, ctx := suite.app, suite.ctx _, _, addr := authtypes.KeyTestPubAddr() - queryHelper := baseapp.NewQueryServerTestHelper(ctx) + queryHelper := baseapp.NewQueryServerTestHelper(ctx, app.InterfaceRegistry()) types.RegisterQueryServer(queryHelper, app.BankKeeper) queryClient := types.NewQueryClient(queryHelper) @@ -47,7 +47,7 @@ func (suite *IntegrationTestSuite) TestQueryAllBalances() { app, ctx := suite.app, suite.ctx _, _, addr := authtypes.KeyTestPubAddr() - queryHelper := baseapp.NewQueryServerTestHelper(ctx) + queryHelper := baseapp.NewQueryServerTestHelper(ctx, app.InterfaceRegistry()) types.RegisterQueryServer(queryHelper, app.BankKeeper) queryClient := types.NewQueryClient(queryHelper) @@ -97,7 +97,7 @@ func (suite *IntegrationTestSuite) TestQueryTotalSupply() { expectedTotalSupply := types.NewSupply(sdk.NewCoins(sdk.NewInt64Coin("test", 400000000))) app.BankKeeper.SetSupply(ctx, expectedTotalSupply) - queryHelper := baseapp.NewQueryServerTestHelper(ctx) + queryHelper := baseapp.NewQueryServerTestHelper(ctx, app.InterfaceRegistry()) types.RegisterQueryServer(queryHelper, app.BankKeeper) queryClient := types.NewQueryClient(queryHelper) @@ -116,7 +116,7 @@ func (suite *IntegrationTestSuite) TestQueryTotalSupplyOf() { expectedTotalSupply := types.NewSupply(sdk.NewCoins(test1Supply, test2Supply)) app.BankKeeper.SetSupply(ctx, expectedTotalSupply) - queryHelper := baseapp.NewQueryServerTestHelper(ctx) + queryHelper := baseapp.NewQueryServerTestHelper(ctx, app.InterfaceRegistry()) types.RegisterQueryServer(queryHelper, app.BankKeeper) queryClient := types.NewQueryClient(queryHelper)