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
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,12 @@ Ref: https://keepachangelog.com/en/1.0.0/
* (crypto) [#15070](https://github.com/cosmos/cosmos-sdk/pull/15070) `GenerateFromPassword` and `Cost` from `bcrypt.go` now take a `uint32` instead of a `int` type.
* (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`.
All implementations of `Codec` by other users must now embed an official implementation from the `codec` package.
* `InterfaceRegistry` is now a private interface and implements `protodesc.Resolver` plus the `RangeFiles` method
All implementations of `InterfaceRegistry` by other users must now embed the official implementation.
* `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.

31 changes: 28 additions & 3 deletions codec/amino_codec.go
Original file line number Diff line number Diff line change
@@ -1,18 +1,25 @@
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.
// Use NewLegacyAmino instead.
func NewAminoCodec(codec *LegacyAmino) *AminoCodec {
return &AminoCodec{LegacyAmino: codec}
}
Expand Down Expand Up @@ -124,3 +131,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) mustEmbedCodec() {}
21 changes: 21 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,26 @@ 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)

// mustEmbedCodec requires that all implementations of Codec embed an official implementation from the codec
// package. This allows new methods to be added to the Codec interface without breaking backwards compatibility.
mustEmbedCodec()
}

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) mustEmbedCodec() {}

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))
}
Loading