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

fix: remove grpc replace directive #11089

Merged
merged 20 commits into from
Feb 4, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@ Ref: https://keepachangelog.com/en/1.0.0/
* (gov) [\#11036](https://github.com/cosmos/cosmos-sdk/pull/11036) Add in-place migrations for 0.43->0.46. Add a `migrate v0.46` CLI command for v0.43->0.46 JSON genesis migration.

### API Breaking Changes

* [\#10950](https://github.com/cosmos/cosmos-sdk/pull/10950) Add `envPrefix` parameter to `cmd.Execute`.
* (x/mint) [\#10441](https://github.com/cosmos/cosmos-sdk/pull/10441) The `NewAppModule` function now accepts an inflation calculation function as an argument.
* [\#10295](https://github.com/cosmos/cosmos-sdk/pull/10295) Remove store type aliases from /types
Expand Down Expand Up @@ -122,7 +121,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
* [\#10868](https://github.com/cosmos/cosmos-sdk/pull/10868), [\#10989](https://github.com/cosmos/cosmos-sdk/pull/10989), [\#11093](https://github.com/cosmos/cosmos-sdk/pull/11093) The Gov keeper accepts now 2 more mandatory arguments, the ServiceMsgRouter and a gov Config including the max metadata length.

### Client Breaking Changes

* [\#11089](https://github.com/cosmos/cosmos-sdk/pull/11089]) interacting with the node through `grpc.Dial` requires clients to pass a codec refer to [doc](docs/run-node/interact-node.md).
* [\#9594](https://github.com/cosmos/cosmos-sdk/pull/9594) Remove legacy REST API. Please see the [REST Endpoints Migration guide](https://docs.cosmos.network/master/migrations/rest.html) to migrate to the new REST endpoints.
* [\#9995](https://github.com/cosmos/cosmos-sdk/pull/9995) Increased gas cost for creating proposals.
* [\#11029](https://github.com/cosmos/cosmos-sdk/pull/11029) The deprecated Vote Option field is removed in gov v1beta2 and nil in v1beta1. Use Options instead.
Expand All @@ -137,7 +136,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
* [\#10684](https://github.com/cosmos/cosmos-sdk/pull/10684) Rename `edit-validator` command's `--moniker` flag to `--new-moniker`

### Improvements

* [\#11089](https://github.com/cosmos/cosmos-sdk/pull/11089]) Now cosmos-sdk consumers can upgrade gRPC to its newest versions.
* [\#10439](https://github.com/cosmos/cosmos-sdk/pull/10439) Check error for `RegisterQueryHandlerClient` in all modules `RegisterGRPCGatewayRoutes`.
* [\#9780](https://github.com/cosmos/cosmos-sdk/pull/9780) Remove gogoproto `moretags` YAML annotations and add `sigs.k8s.io/yaml` for YAML marshalling.
* (x/bank) [\#10134](https://github.com/cosmos/cosmos-sdk/pull/10134) Add `HasDenomMetadata` function to bank `Keeper` to check if a client coin denom metadata exists in state.
Expand Down
1 change: 1 addition & 0 deletions baseapp/baseapp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1793,6 +1793,7 @@ func TestGRPCQuery(t *testing.T) {
}

app := setupBaseApp(t, grpcQueryOpt)
app.GRPCQueryRouter().SetInterfaceRegistry(codectypes.NewInterfaceRegistry())

app.InitChain(abci.RequestInitChain{})
header := tmproto.Header{Height: app.LastBlockHeight() + 1}
Expand Down
22 changes: 9 additions & 13 deletions baseapp/grpcrouter.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,24 @@ package baseapp

import (
"fmt"
"github.com/cosmos/cosmos-sdk/codec"
"google.golang.org/grpc/encoding"

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

gogogrpc "github.com/gogo/protobuf/grpc"
abci "github.com/tendermint/tendermint/abci/types"
"google.golang.org/grpc"
"google.golang.org/grpc/encoding"
"google.golang.org/grpc/encoding/proto"

codectypes "github.com/cosmos/cosmos-sdk/codec/types"
sdk "github.com/cosmos/cosmos-sdk/types"
)

var protoCodec = encoding.GetCodec(proto.Name)

// GRPCQueryRouter routes ABCI Query requests to GRPC handlers
type GRPCQueryRouter struct {
routes map[string]GRPCQueryHandler
interfaceRegistry codectypes.InterfaceRegistry
serviceData []serviceData
routes map[string]GRPCQueryHandler
cdc encoding.Codec
serviceData []serviceData
}

// serviceData represents a gRPC service, along with its handler.
Expand Down Expand Up @@ -83,21 +81,18 @@ 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 {
err := protoCodec.Unmarshal(req.Data, i)
err := qrt.cdc.Unmarshal(req.Data, i)
if err != nil {
return err
}
if qrt.interfaceRegistry != nil {
return codectypes.UnpackInterfaces(i, qrt.interfaceRegistry)
}
return nil
}, nil)
if err != nil {
return abci.ResponseQuery{}, err
}

// proto marshal the result bytes
resBytes, err := protoCodec.Marshal(res)
resBytes, err := qrt.cdc.Marshal(res)
if err != nil {
return abci.ResponseQuery{}, err
}
Expand All @@ -119,7 +114,8 @@ func (qrt *GRPCQueryRouter) RegisterService(sd *grpc.ServiceDesc, handler interf
// SetInterfaceRegistry sets the interface registry for the router. This will
// also register the interface reflection gRPC service.
func (qrt *GRPCQueryRouter) SetInterfaceRegistry(interfaceRegistry codectypes.InterfaceRegistry) {
qrt.interfaceRegistry = interfaceRegistry
// instantiate the codec
qrt.cdc = codec.NewProtoCodec(interfaceRegistry).GRPCCodec()
// Once we have an interface registry, we can register the interface
// registry reflection gRPC service.
reflection.RegisterReflectionServiceServer(
Expand Down
8 changes: 2 additions & 6 deletions baseapp/grpcrouter_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ func (q *QueryServiceTestHelper) Invoke(_ gocontext.Context, method string, args
if querier == nil {
return fmt.Errorf("handler not found for %s", method)
}
reqBz, err := protoCodec.Marshal(args)
reqBz, err := q.cdc.Marshal(args)
if err != nil {
return err
}
Expand All @@ -50,15 +50,11 @@ func (q *QueryServiceTestHelper) Invoke(_ gocontext.Context, method string, args
return err
}

err = protoCodec.Unmarshal(res.Value, reply)
err = q.cdc.Unmarshal(res.Value, reply)
if err != nil {
return err
}

if q.interfaceRegistry != nil {
return types.UnpackInterfaces(reply, q.interfaceRegistry)
}

return nil
}

Expand Down
5 changes: 4 additions & 1 deletion client/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package config_test
import (
"bytes"
"fmt"
"github.com/cosmos/cosmos-sdk/codec"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
"io"
"os"
"testing"
Expand All @@ -27,7 +29,8 @@ func initClientContext(t *testing.T, envVar string) (client.Context, func()) {
home := t.TempDir()
clientCtx := client.Context{}.
WithHomeDir(home).
WithViper("")
WithViper("").
WithCodec(codec.NewProtoCodec(codectypes.NewInterfaceRegistry()))
Copy link
Contributor

Choose a reason for hiding this comment

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

client.Context always expects a Codec (should we relax this?)

How would you relax it? Probably only WithInterfaceRegistry is required?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes given the interface registry we have our codec too.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

As far as relaxing goes, we could default to the gogoproto marshaler (if no codec is specified) but IDK how it would behave with Interface unpacking (let's assume WithInterfaceRegistry was not called).

If it worked before with the default gRPC codec then I would assume it also would work this way.

Copy link
Member

Choose a reason for hiding this comment

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

with orm landing I don't think we can default to gogoproto. I think the force is fine for a single release and but then most teams will have migrated anyways


clientCtx.Viper.BindEnv(nodeEnv)
if envVar != "" {
Expand Down
65 changes: 59 additions & 6 deletions client/grpc_query.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,17 @@ package client

import (
gocontext "context"
"errors"
"fmt"
"github.com/cosmos/cosmos-sdk/codec"
proto "github.com/gogo/protobuf/proto"
"google.golang.org/grpc/encoding"
"reflect"
"strconv"

gogogrpc "github.com/gogo/protobuf/grpc"
abci "github.com/tendermint/tendermint/abci/types"
"google.golang.org/grpc"
"google.golang.org/grpc/encoding"
"google.golang.org/grpc/encoding/proto"
"google.golang.org/grpc/metadata"

"github.com/cosmos/cosmos-sdk/codec/types"
Expand All @@ -21,7 +23,10 @@ import (

var _ gogogrpc.ClientConn = Context{}

var protoCodec = encoding.GetCodec(proto.Name)
// fallBackCodec is used by Context in case Codec is not set.
// it can process every gRPC type, except the ones which contain
// interfaces in their types.
var fallBackCodec = codec.NewProtoCodec(failingInterfaceRegistry{})

// Invoke implements the grpc ClientConn.Invoke method
func (ctx Context) Invoke(grpcCtx gocontext.Context, method string, req, reply interface{}, opts ...grpc.CallOption) (err error) {
Expand Down Expand Up @@ -50,8 +55,7 @@ func (ctx Context) Invoke(grpcCtx gocontext.Context, method string, req, reply i
return err
}

// Case 2. Querying state.
reqBz, err := protoCodec.Marshal(req)
reqBz, err := ctx.gRPCCodec().Marshal(req)
if err != nil {
return err
}
Expand Down Expand Up @@ -83,7 +87,7 @@ func (ctx Context) Invoke(grpcCtx gocontext.Context, method string, req, reply i
return err
}

err = protoCodec.Unmarshal(res.Value, reply)
err = ctx.gRPCCodec().Unmarshal(res.Value, reply)
if err != nil {
return err
}
Expand Down Expand Up @@ -114,3 +118,52 @@ func (ctx Context) Invoke(grpcCtx gocontext.Context, method string, req, reply i
func (Context) NewStream(gocontext.Context, *grpc.StreamDesc, string, ...grpc.CallOption) (grpc.ClientStream, error) {
return nil, fmt.Errorf("streaming rpc not supported")
}

// gRPCCodec checks if Context's Codec is codec.GRPCCodecProvider
// otherwise it returns fallBackCodec.
func (ctx Context) gRPCCodec() encoding.Codec {
if ctx.Codec == nil {
return fallBackCodec.GRPCCodec()
}

pc, ok := ctx.Codec.(codec.GRPCCodecProvider)
if !ok {
return fallBackCodec.GRPCCodec()
}

return pc.GRPCCodec()
}

var _ types.InterfaceRegistry = failingInterfaceRegistry{}

// failingInterfaceRegistry is used by the fallback codec
// in case Context's Codec is not set.
type failingInterfaceRegistry struct{}

// errCodecNotSet is return by failingInterfaceRegistry in case there are attempt to decode
// or encode a type which contains an interface field.
var errCodecNotSet = errors.New("client: cannot encode or decode type which requires the application specific codec")

func (f failingInterfaceRegistry) UnpackAny(any *types.Any, iface interface{}) error {
return errCodecNotSet
}

func (f failingInterfaceRegistry) Resolve(typeUrl string) (proto.Message, error) {
return nil, errCodecNotSet
}

func (f failingInterfaceRegistry) RegisterInterface(protoName string, iface interface{}, impls ...proto.Message) {
panic("cannot be called")
}

func (f failingInterfaceRegistry) RegisterImplementations(iface interface{}, impls ...proto.Message) {
panic("cannot be called")
}

func (f failingInterfaceRegistry) ListAllInterfaces() []string {
panic("cannot be called")
}

func (f failingInterfaceRegistry) ListImplementations(ifaceTypeURL string) []string {
panic("cannot be called")
}
9 changes: 9 additions & 0 deletions codec/codec.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package codec

import (
"github.com/gogo/protobuf/proto"
"google.golang.org/grpc/encoding"

"github.com/cosmos/cosmos-sdk/codec/types"
)
Expand Down Expand Up @@ -95,4 +96,12 @@ type (
MarshalAminoJSON() ([]byte, error)
UnmarshalAminoJSON([]byte) error
}

// GRPCCodecProvider is implemented by the Codec
// implementations which return a gRPC encoding.Codec.
// And it is used to decode requests and encode responses
// passed through gRPC.
GRPCCodecProvider interface {
GRPCCodec() encoding.Codec
}
)
59 changes: 50 additions & 9 deletions codec/proto_codec.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@ import (
"encoding/binary"
"errors"
"fmt"
legacyproto "github.com/golang/protobuf/proto"
"google.golang.org/grpc/encoding"
"strings"

"github.com/gogo/protobuf/jsonpb"
"github.com/gogo/protobuf/proto"

"github.com/cosmos/cosmos-sdk/codec/types"
"github.com/gogo/protobuf/jsonpb"
gogoproto "github.com/gogo/protobuf/proto"
)

// ProtoCodecMarshaler defines an interface for codecs that utilize Protobuf for both
Expand Down Expand Up @@ -126,7 +127,7 @@ func (pc *ProtoCodec) MustUnmarshalLengthPrefixed(bz []byte, ptr ProtoMarshaler)
// it marshals to JSON using proto codec.
// NOTE: this function must be used with a concrete type which
// implements proto.Message. For interface please use the codec.MarshalInterfaceJSON
func (pc *ProtoCodec) MarshalJSON(o proto.Message) ([]byte, error) {
func (pc *ProtoCodec) MarshalJSON(o gogoproto.Message) ([]byte, error) {
m, ok := o.(ProtoMarshaler)
if !ok {
return nil, fmt.Errorf("cannot protobuf JSON encode unsupported type: %T", o)
Expand All @@ -139,7 +140,7 @@ func (pc *ProtoCodec) MarshalJSON(o proto.Message) ([]byte, error) {
// it executes MarshalJSON except it panics upon failure.
// NOTE: this function must be used with a concrete type which
// implements proto.Message. For interface please use the codec.MarshalInterfaceJSON
func (pc *ProtoCodec) MustMarshalJSON(o proto.Message) []byte {
func (pc *ProtoCodec) MustMarshalJSON(o gogoproto.Message) []byte {
bz, err := pc.MarshalJSON(o)
if err != nil {
panic(err)
Expand All @@ -152,7 +153,7 @@ func (pc *ProtoCodec) MustMarshalJSON(o proto.Message) []byte {
// it unmarshals from JSON using proto codec.
// NOTE: this function must be used with a concrete type which
// implements proto.Message. For interface please use the codec.UnmarshalInterfaceJSON
func (pc *ProtoCodec) UnmarshalJSON(bz []byte, ptr proto.Message) error {
func (pc *ProtoCodec) UnmarshalJSON(bz []byte, ptr gogoproto.Message) error {
m, ok := ptr.(ProtoMarshaler)
if !ok {
return fmt.Errorf("cannot protobuf JSON decode unsupported type: %T", ptr)
Expand All @@ -171,7 +172,7 @@ func (pc *ProtoCodec) UnmarshalJSON(bz []byte, ptr proto.Message) error {
// it executes UnmarshalJSON except it panics upon failure.
// NOTE: this function must be used with a concrete type which
// implements proto.Message. For interface please use the codec.UnmarshalInterfaceJSON
func (pc *ProtoCodec) MustUnmarshalJSON(bz []byte, ptr proto.Message) {
func (pc *ProtoCodec) MustUnmarshalJSON(bz []byte, ptr gogoproto.Message) {
if err := pc.UnmarshalJSON(bz, ptr); err != nil {
panic(err)
}
Expand All @@ -180,7 +181,7 @@ func (pc *ProtoCodec) MustUnmarshalJSON(bz []byte, ptr proto.Message) {
// MarshalInterface is a convenience function for proto marshalling interfaces. It packs
// the provided value, which must be an interface, in an Any and then marshals it to bytes.
// NOTE: to marshal a concrete type, you should use Marshal instead
func (pc *ProtoCodec) MarshalInterface(i proto.Message) ([]byte, error) {
func (pc *ProtoCodec) MarshalInterface(i gogoproto.Message) ([]byte, error) {
if err := assertNotNil(i); err != nil {
return nil, err
}
Expand Down Expand Up @@ -213,7 +214,7 @@ func (pc *ProtoCodec) UnmarshalInterface(bz []byte, ptr interface{}) error {
// MarshalInterfaceJSON is a convenience function for proto marshalling interfaces. It
// packs the provided value in an Any and then marshals it to bytes.
// NOTE: to marshal a concrete type, you should use MarshalJSON instead
func (pc *ProtoCodec) MarshalInterfaceJSON(x proto.Message) ([]byte, error) {
func (pc *ProtoCodec) MarshalInterfaceJSON(x gogoproto.Message) ([]byte, error) {
any, err := types.NewAnyWithValue(x)
if err != nil {
return nil, err
Expand Down Expand Up @@ -250,6 +251,46 @@ func (pc *ProtoCodec) InterfaceRegistry() types.InterfaceRegistry {
return pc.interfaceRegistry
}

// GRPCCodec returns the gRPC Codec for this specific ProtoCodec
func (pc *ProtoCodec) GRPCCodec() encoding.Codec {
return &grpcProtoCodec{cdc: pc}
}

var errUnknownProtoType = errors.New("codec: unknown proto type") // sentinel error

// grpcProtoCodec is the implementation of the gRPC proto codec.
type grpcProtoCodec struct {
cdc *ProtoCodec
}

func (g grpcProtoCodec) Marshal(v interface{}) ([]byte, error) {
// TODO(fdymylja): maybe this is the correct place to support protov2 types too for gRPC
switch m := v.(type) {
case ProtoMarshaler:
return g.cdc.Marshal(m)
case legacyproto.Message:
return legacyproto.Marshal(m)
default:
return nil, fmt.Errorf("%w: cannot marshal type %T", errUnknownProtoType, v)
}
}

func (g grpcProtoCodec) Unmarshal(data []byte, v interface{}) error {
// TODO(fdymylja): maybe this is the correct place to support protov2 types too for gRPC
switch m := v.(type) {
case ProtoMarshaler:
return g.cdc.Unmarshal(data, m)
case legacyproto.Message:
return legacyproto.Unmarshal(data, m)
default:
return fmt.Errorf("%w: cannot unmarshal type %T", errUnknownProtoType, v)
}
}

func (g grpcProtoCodec) Name() string {
return "cosmos-sdk-grpc-codec"
}

func assertNotNil(i interface{}) error {
if i == nil {
return errors.New("can't marshal <nil> value")
Expand Down
Loading