Skip to content

Commit

Permalink
backport finschia-sdk#1153
Browse files Browse the repository at this point in the history
  • Loading branch information
tkxkd0159 committed Jun 14, 2024
1 parent cc37bdb commit f224614
Show file tree
Hide file tree
Showing 13 changed files with 237 additions and 60 deletions.
2 changes: 2 additions & 0 deletions baseapp/deliver_tx_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"github.com/Finschia/ostracon/libs/log"

"github.com/Finschia/finschia-sdk/codec"
codectypes "github.com/Finschia/finschia-sdk/codec/types"
"github.com/Finschia/finschia-sdk/snapshots"
snapshottypes "github.com/Finschia/finschia-sdk/snapshots/types"
"github.com/Finschia/finschia-sdk/store/rootmulti"
Expand Down Expand Up @@ -336,6 +337,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
23 changes: 9 additions & 14 deletions baseapp/grpcrouter.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,21 @@ import (
"fmt"

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"

abci "github.com/tendermint/tendermint/abci/types"

"github.com/Finschia/finschia-sdk/client/grpc/reflection"
"github.com/Finschia/finschia-sdk/codec"
codectypes "github.com/Finschia/finschia-sdk/codec/types"
sdk "github.com/Finschia/finschia-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 +80,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 +113,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 @@ -12,6 +12,8 @@ import (
"github.com/Finschia/finschia-sdk/client"
"github.com/Finschia/finschia-sdk/client/config"
"github.com/Finschia/finschia-sdk/client/flags"
"github.com/Finschia/finschia-sdk/codec"
codectypes "github.com/Finschia/finschia-sdk/codec/types"
clitestutil "github.com/Finschia/finschia-sdk/testutil/cli"
"github.com/Finschia/finschia-sdk/x/staking/client/cli"
)
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()))

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

import (
gocontext "context"
"errors"
"fmt"
"reflect"
"strconv"

gogogrpc "github.com/gogo/protobuf/grpc"
"github.com/gogo/protobuf/proto"
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/Finschia/finschia-sdk/codec"
"github.com/Finschia/finschia-sdk/codec/types"
sdkerrors "github.com/Finschia/finschia-sdk/types/errors"
grpctypes "github.com/Finschia/finschia-sdk/types/grpc"
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 @@ -51,7 +56,7 @@ func (ctx Context) Invoke(grpcCtx gocontext.Context, method string, req, reply i
}

// 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 +88,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 +119,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/Finschia/finschia-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
}
)
56 changes: 49 additions & 7 deletions codec/proto_codec.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ import (
"strings"

"github.com/gogo/protobuf/jsonpb"
"github.com/gogo/protobuf/proto"
gogoproto "github.com/gogo/protobuf/proto"
"google.golang.org/grpc/encoding"
legacyproto "google.golang.org/protobuf/proto"

"github.com/Finschia/finschia-sdk/codec/types"
)
Expand Down Expand Up @@ -128,7 +130,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 @@ -141,7 +143,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 @@ -154,7 +156,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 @@ -173,7 +175,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 @@ -182,7 +184,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 @@ -216,7 +218,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 @@ -254,6 +256,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
1 change: 0 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -137,5 +137,4 @@ replace (
github.com/99designs/keyring => github.com/cosmos/keyring v1.1.7-0.20210622111912-ef00f8ac3d76
github.com/gogo/protobuf => github.com/regen-network/protobuf v1.3.3-alpha.regen.1
github.com/jhump/protoreflect => github.com/jhump/protoreflect v1.10.3
google.golang.org/grpc => google.golang.org/grpc v1.33.2
)
Loading

0 comments on commit f224614

Please sign in to comment.