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(codec)!: add get signers to codec + merged proto registry to InterfaceRegistry #15600

Merged
merged 11 commits into from
Mar 31, 2023
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,10 @@ Ref: https://keepachangelog.com/en/1.0.0/
* (client) [#15123](https://github.com/cosmos/cosmos-sdk/pull/15123) `NewFactoryCLI` now returns an error, in addition to the `Factory`.
* (x/capability) [#15344](https://github.com/cosmos/cosmos-sdk/pull/15344) Capability module was removed and is now housed in [IBC-GO](https://github.com/cosmos/ibc-go).
* [#15299](https://github.com/cosmos/cosmos-sdk/pull/15299) remove `StdTx` transaction and signing APIs. No SDK version has actually supported `StdTx` since before Stargate.
* [#15600](https://github.com/cosmos/cosmos-sdk/pull/15600) add support for getting signers to `codec.Codec` and protoregistry support to `InterfaceRegistry`:
* `Codec` is now a private interface and has the methods `InterfaceRegistry`, `GetMsgAnySigners`, `GetMsgV1Signers`, and `GetMsgV2Signers` which will fail when using `AminoCodec`
* `InterfaceRegistry` is now a private interface and implements `protodesc.Resolver` plus the `RangeFiles` method
* `AminoCodec` is marked as deprecated.

### Client Breaking Changes

Expand Down
37 changes: 1 addition & 36 deletions client/grpc_query.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
"reflect"
"strconv"

proto "github.com/cosmos/gogoproto/proto"
"google.golang.org/grpc/encoding"

"github.com/cosmos/cosmos-sdk/codec"
Expand All @@ -29,7 +28,7 @@ var _ gogogrpc.ClientConn = Context{}
// 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{})
var fallBackCodec = codec.NewProtoCodec(types.NewInterfaceRegistry())

// 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 @@ -144,40 +143,6 @@ func (ctx Context) gRPCCodec() encoding.Codec {
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")
}

func (f failingInterfaceRegistry) EnsureRegistered(iface interface{}) error {
panic("cannot be called")
}
31 changes: 0 additions & 31 deletions client/internal_client_test.go

This file was deleted.

30 changes: 27 additions & 3 deletions codec/amino_codec.go
Original file line number Diff line number Diff line change
@@ -1,18 +1,24 @@
package codec

import (
"fmt"

"github.com/cosmos/gogoproto/proto"
protov2 "google.golang.org/protobuf/proto"

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

// AminoCodec defines a codec that utilizes Codec for both binary and JSON
// encoding.
// Deprecated: AminoCodec defines a codec that utilizes Codec for both binary and JSON
Copy link
Member

Choose a reason for hiding this comment

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

its unclear how this is deprecated without supporting JSONcodec, is there a way you envision to manage this?

// encoding. Any usage of amino should be done using the LegacyAmino type directly.
// Usage of amino with the Codec type is not well-supported and may be removed in the future.
type AminoCodec struct {
*LegacyAmino
}

var _ Codec = &AminoCodec{}

// NewAminoCodec returns a reference to a new AminoCodec
// Deprecated: NewAminoCodec returns a reference to a new AminoCodec
aaronc marked this conversation as resolved.
Show resolved Hide resolved
func NewAminoCodec(codec *LegacyAmino) *AminoCodec {
return &AminoCodec{LegacyAmino: codec}
}
Expand Down Expand Up @@ -124,3 +130,21 @@ func (ac *AminoCodec) MarshalInterfaceJSON(i proto.Message) ([]byte, error) {
func (ac *AminoCodec) UnmarshalInterfaceJSON(bz []byte, ptr interface{}) error {
return ac.LegacyAmino.UnmarshalJSON(bz, ptr)
}

func (ac *AminoCodec) GetMsgAnySigners(*types.Any) ([]string, protov2.Message, error) {
return nil, nil, fmt.Errorf("amino codec does not support getting msg signers")
}

func (ac *AminoCodec) GetMsgV1Signers(proto.Message) ([]string, protov2.Message, error) {
return nil, nil, fmt.Errorf("amino codec does not support getting msg signers")
}

func (ac *AminoCodec) GetMsgV2Signers(protov2.Message) ([]string, error) {
return nil, fmt.Errorf("amino codec does not support getting msg signers")
}

func (ac *AminoCodec) InterfaceRegistry() types.InterfaceRegistry {
panic("amino codec does not support interface registry")
}

func (ac *AminoCodec) private() {}
19 changes: 19 additions & 0 deletions codec/codec.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package codec
import (
"github.com/cosmos/gogoproto/proto"
"google.golang.org/grpc/encoding"
protov2 "google.golang.org/protobuf/proto"

"github.com/cosmos/cosmos-sdk/codec/types"
)
Expand All @@ -18,6 +19,24 @@ type (
Codec interface {
BinaryCodec
JSONCodec

// InterfaceRegistry returns the interface registry.
InterfaceRegistry() types.InterfaceRegistry

// GetMsgAnySigners returns the signers of the given message encoded in a protobuf Any
// as well as the decoded google.golang.org/protobuf/proto.Message that was used to
// extract the signers so that this can be used in other contexts.
GetMsgAnySigners(msg *types.Any) ([]string, protov2.Message, error)

// GetMsgV2Signers returns the signers of the given message.
GetMsgV2Signers(msg protov2.Message) ([]string, error)

// GetMsgV1Signers returns the signers of the given message plus the
// decoded google.golang.org/protobuf/proto.Message that was used to extract the
// signers so that this can be used in other contexts.
GetMsgV1Signers(msg proto.Message) ([]string, protov2.Message, error)

private()
aaronc marked this conversation as resolved.
Show resolved Hide resolved
}

BinaryCodec interface {
Expand Down
52 changes: 49 additions & 3 deletions codec/proto_codec.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,14 @@ import (
"fmt"
"strings"

"github.com/cosmos/cosmos-proto/anyutil"
"github.com/cosmos/gogoproto/jsonpb"
gogoproto "github.com/cosmos/gogoproto/proto"
"google.golang.org/grpc/encoding"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/known/anypb"

"github.com/cosmos/gogoproto/jsonpb"
gogoproto "github.com/cosmos/gogoproto/proto"
"cosmossdk.io/x/tx/signing"

"github.com/cosmos/cosmos-sdk/codec/types"
)
Expand All @@ -26,6 +29,7 @@ type ProtoCodecMarshaler interface {
// encoding.
type ProtoCodec struct {
interfaceRegistry types.InterfaceRegistry
getSignersCtx *signing.GetSignersContext
}

var (
Expand All @@ -35,7 +39,17 @@ var (

// NewProtoCodec returns a reference to a new ProtoCodec
func NewProtoCodec(interfaceRegistry types.InterfaceRegistry) *ProtoCodec {
return &ProtoCodec{interfaceRegistry: interfaceRegistry}
getSignersCtx, err := signing.NewGetSignersContext(
signing.GetSignersOptions{
ProtoFiles: interfaceRegistry,
})
if err != nil {
panic(err)
}
return &ProtoCodec{
interfaceRegistry: interfaceRegistry,
getSignersCtx: getSignersCtx,
}
}

// Marshal implements BinaryMarshaler.Marshal method.
Expand Down Expand Up @@ -265,11 +279,43 @@ func (pc *ProtoCodec) InterfaceRegistry() types.InterfaceRegistry {
return pc.interfaceRegistry
}

func (pc ProtoCodec) GetMsgAnySigners(msg *types.Any) ([]string, proto.Message, error) {
msgv2, err := anyutil.Unpack(&anypb.Any{
TypeUrl: msg.TypeUrl,
Value: msg.Value,
}, pc.interfaceRegistry, nil)
if err != nil {
return nil, nil, err
}

signers, err := pc.getSignersCtx.GetSigners(msgv2)
return signers, msgv2, err
}

func (pc *ProtoCodec) GetMsgV2Signers(msg proto.Message) ([]string, error) {
return pc.getSignersCtx.GetSigners(msg)
}

func (pc *ProtoCodec) GetMsgV1Signers(msg gogoproto.Message) ([]string, proto.Message, error) {
if msgV2, ok := msg.(proto.Message); ok {
signers, err := pc.getSignersCtx.GetSigners(msgV2)
return signers, msgV2, err
} else {
a, err := types.NewAnyWithValue(msg)
if err != nil {
return nil, nil, err
}
return pc.GetMsgAnySigners(a)
}
}

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

func (pc *ProtoCodec) private() {}

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

// grpcProtoCodec is the implementation of the gRPC proto codec.
Expand Down
36 changes: 36 additions & 0 deletions codec/proto_codec_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,19 @@ import (
"reflect"
"testing"

bankv1beta1 "cosmossdk.io/api/cosmos/bank/v1beta1"
basev1beta1 "cosmossdk.io/api/cosmos/base/v1beta1"
"github.com/cosmos/gogoproto/proto"
"github.com/stretchr/testify/require"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/encoding"
"google.golang.org/grpc/status"
protov2 "google.golang.org/protobuf/proto"

"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/codec/types"
"github.com/cosmos/cosmos-sdk/testutil/testdata"
sdk "github.com/cosmos/cosmos-sdk/types"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
)

Expand Down Expand Up @@ -168,3 +172,35 @@ func BenchmarkProtoCodecMarshalLengthPrefixed(b *testing.B) {
b.SetBytes(int64(len(blob)))
}
}

func TestGetSigners(t *testing.T) {
interfaceRegistry := types.NewInterfaceRegistry()
cdc := codec.NewProtoCodec(interfaceRegistry)
testAddr := sdk.AccAddress([]byte("test"))
testAddrStr := testAddr.String()
testAddr2 := sdk.AccAddress([]byte("test2"))
testAddrStr2 := testAddr2.String()

msgSendV1 := banktypes.NewMsgSend(testAddr, testAddr2, sdk.NewCoins(sdk.NewCoin("foo", sdk.NewInt(1))))
msgSendV2 := &bankv1beta1.MsgSend{
FromAddress: testAddrStr,
ToAddress: testAddrStr2,
Amount: []*basev1beta1.Coin{{Denom: "foo", Amount: "1"}},
}

signers, msgSendV2Copy, err := cdc.GetMsgV1Signers(msgSendV1)
require.NoError(t, err)
require.Equal(t, []string{testAddrStr}, signers)
require.True(t, protov2.Equal(msgSendV2, msgSendV2Copy))

signers, err = cdc.GetMsgV2Signers(msgSendV2)
require.NoError(t, err)
require.Equal(t, []string{testAddrStr}, signers)

msgSendAny, err := types.NewAnyWithValue(msgSendV1)
require.NoError(t, err)
signers, msgSendV2Copy, err = cdc.GetMsgAnySigners(msgSendAny)
require.NoError(t, err)
require.Equal(t, []string{testAddrStr}, signers)
require.True(t, protov2.Equal(msgSendV2, msgSendV2Copy))
}
25 changes: 25 additions & 0 deletions codec/types/interface_registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ import (

"github.com/cosmos/gogoproto/jsonpb"
"github.com/cosmos/gogoproto/proto"
"google.golang.org/protobuf/reflect/protodesc"
"google.golang.org/protobuf/reflect/protoreflect"
"google.golang.org/protobuf/reflect/protoregistry"
)

// AnyUnpacker is an interface which allows safely unpacking types packed
Expand Down Expand Up @@ -54,6 +57,15 @@ type InterfaceRegistry interface {

// EnsureRegistered ensures there is a registered interface for the given concrete type.
EnsureRegistered(iface interface{}) error

protodesc.Resolver

// RangeFiles iterates over all registered files and calls f on each one. This
// implements the part of protoregistry.Files that is needed for reflecting over
// the entire FileDescriptorSet.
RangeFiles(f func(protoreflect.FileDescriptor) bool)

private()
}

// UnpackInterfacesMessage is meant to extend protobuf types (which implement
Expand Down Expand Up @@ -81,6 +93,7 @@ type UnpackInterfacesMessage interface {
}

type interfaceRegistry struct {
*protoregistry.Files
interfaceNames map[string]reflect.Type
interfaceImpls map[reflect.Type]interfaceMap
implInterfaces map[reflect.Type]reflect.Type
Expand All @@ -91,11 +104,21 @@ type interfaceMap = map[string]reflect.Type

// NewInterfaceRegistry returns a new InterfaceRegistry
func NewInterfaceRegistry() InterfaceRegistry {
protoFiles, err := proto.MergedRegistry()
if err != nil {
panic(err)
}
return NewInterfaceRegistryWithProtoFiles(protoFiles)
}

// NewInterfaceRegistry returns a new InterfaceRegistry with the specified *protoregistry.Files instance.
aaronc marked this conversation as resolved.
Show resolved Hide resolved
func NewInterfaceRegistryWithProtoFiles(files *protoregistry.Files) InterfaceRegistry {
return &interfaceRegistry{
interfaceNames: map[string]reflect.Type{},
interfaceImpls: map[reflect.Type]interfaceMap{},
implInterfaces: map[reflect.Type]reflect.Type{},
typeURLMap: map[string]reflect.Type{},
Files: files,
}
}

Expand Down Expand Up @@ -288,6 +311,8 @@ func (registry *interfaceRegistry) Resolve(typeURL string) (proto.Message, error
return msg, nil
}

func (registry *interfaceRegistry) private() {}

// UnpackInterfaces is a convenience function that calls UnpackInterfaces
// on x if x implements UnpackInterfacesMessage
func UnpackInterfaces(x interface{}, unpacker AnyUnpacker) error {
Expand Down
Loading