Add amino compatibility layer for proto Any (#6151)

* WIP on Any amino compatibility layer

* Add tests & JSON

* Refactor Marshal/UnmarshalAny

* remove extra test

* Add support for nested Any's

* Add docs

* Update codec/any_test.go

Co-authored-by: Federico Kunze <31522760+fedekunze@users.noreply.github.com>
This commit is contained in:
Aaron Craelius 2020-05-06 16:47:03 -04:00 committed by GitHub
parent f3e3a30e5e
commit 9d022c17b7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 939 additions and 94 deletions

View File

@ -16,52 +16,116 @@ func NewAminoCodec(amino *Codec) Marshaler {
return &AminoCodec{amino}
}
func (ac *AminoCodec) marshalAnys(o ProtoMarshaler) error {
return types.UnpackInterfaces(o, types.AminoPacker{Cdc: ac.amino})
}
func (ac *AminoCodec) unmarshalAnys(o ProtoMarshaler) error {
return types.UnpackInterfaces(o, types.AminoUnpacker{Cdc: ac.amino})
}
func (ac *AminoCodec) jsonMarshalAnys(o interface{}) error {
return types.UnpackInterfaces(o, types.AminoJSONPacker{Cdc: ac.amino})
}
func (ac *AminoCodec) jsonUnmarshalAnys(o interface{}) error {
return types.UnpackInterfaces(o, types.AminoJSONUnpacker{Cdc: ac.amino})
}
func (ac *AminoCodec) MarshalBinaryBare(o ProtoMarshaler) ([]byte, error) {
err := ac.marshalAnys(o)
if err != nil {
return nil, err
}
return ac.amino.MarshalBinaryBare(o)
}
func (ac *AminoCodec) MustMarshalBinaryBare(o ProtoMarshaler) []byte {
err := ac.marshalAnys(o)
if err != nil {
panic(err)
}
return ac.amino.MustMarshalBinaryBare(o)
}
func (ac *AminoCodec) MarshalBinaryLengthPrefixed(o ProtoMarshaler) ([]byte, error) {
err := ac.marshalAnys(o)
if err != nil {
return nil, err
}
return ac.amino.MarshalBinaryLengthPrefixed(o)
}
func (ac *AminoCodec) MustMarshalBinaryLengthPrefixed(o ProtoMarshaler) []byte {
err := ac.marshalAnys(o)
if err != nil {
panic(err)
}
return ac.amino.MustMarshalBinaryLengthPrefixed(o)
}
func (ac *AminoCodec) UnmarshalBinaryBare(bz []byte, ptr ProtoMarshaler) error {
return ac.amino.UnmarshalBinaryBare(bz, ptr)
err := ac.amino.UnmarshalBinaryBare(bz, ptr)
if err != nil {
return err
}
return ac.unmarshalAnys(ptr)
}
func (ac *AminoCodec) MustUnmarshalBinaryBare(bz []byte, ptr ProtoMarshaler) {
ac.amino.MustUnmarshalBinaryBare(bz, ptr)
err := ac.unmarshalAnys(ptr)
if err != nil {
panic(err)
}
}
func (ac *AminoCodec) UnmarshalBinaryLengthPrefixed(bz []byte, ptr ProtoMarshaler) error {
return ac.amino.UnmarshalBinaryLengthPrefixed(bz, ptr)
err := ac.amino.UnmarshalBinaryLengthPrefixed(bz, ptr)
if err != nil {
return err
}
return ac.unmarshalAnys(ptr)
}
func (ac *AminoCodec) MustUnmarshalBinaryLengthPrefixed(bz []byte, ptr ProtoMarshaler) {
ac.amino.MustUnmarshalBinaryLengthPrefixed(bz, ptr)
err := ac.unmarshalAnys(ptr)
if err != nil {
panic(err)
}
}
func (ac *AminoCodec) MarshalJSON(o interface{}) ([]byte, error) {
err := ac.jsonMarshalAnys(o)
if err != nil {
return nil, err
}
return ac.amino.MarshalJSON(o)
}
func (ac *AminoCodec) MustMarshalJSON(o interface{}) []byte {
err := ac.jsonMarshalAnys(o)
if err != nil {
panic(err)
}
return ac.amino.MustMarshalJSON(o)
}
func (ac *AminoCodec) UnmarshalJSON(bz []byte, ptr interface{}) error {
return ac.amino.UnmarshalJSON(bz, ptr)
err := ac.amino.UnmarshalJSON(bz, ptr)
if err != nil {
return err
}
return ac.jsonUnmarshalAnys(ptr)
}
func (ac *AminoCodec) MustUnmarshalJSON(bz []byte, ptr interface{}) {
ac.amino.MustUnmarshalJSON(bz, ptr)
err := ac.jsonUnmarshalAnys(ptr)
if err != nil {
panic(err)
}
}
func (*AminoCodec) UnpackAny(*types.Any, interface{}) error {

View File

@ -3,6 +3,8 @@ package codec_test
import (
"testing"
"github.com/cosmos/cosmos-sdk/codec/types"
"github.com/stretchr/testify/require"
amino "github.com/tendermint/go-amino"
@ -21,6 +23,9 @@ func createTestCodec() *amino.Codec {
}
func TestAminoCodec(t *testing.T) {
any, err := types.NewAnyWithValue(&testdata.Dog{Name: "rufus"})
require.NoError(t, err)
testCases := []struct {
name string
codec codec.Marshaler
@ -45,6 +50,14 @@ func TestAminoCodec(t *testing.T) {
false,
true,
},
{
"any marshaling",
codec.NewAminoCodec(createTestCodec()),
&testdata.HasAnimal{Animal: any},
&testdata.HasAnimal{Animal: any},
false,
false,
},
}
for _, tc := range testCases {

44
codec/any.go Normal file
View File

@ -0,0 +1,44 @@
package codec
import (
"fmt"
"github.com/gogo/protobuf/proto"
"github.com/cosmos/cosmos-sdk/codec/types"
)
// MarshalAny is a convenience function for packing the provided value in an
// Any and then proto marshaling it to bytes
func MarshalAny(m Marshaler, x interface{}) ([]byte, error) {
msg, ok := x.(proto.Message)
if !ok {
return nil, fmt.Errorf("can't proto marshal %T", x)
}
any := &types.Any{}
err := any.Pack(msg)
if err != nil {
return nil, err
}
return m.MarshalBinaryBare(any)
}
// UnmarshalAny is a convenience function for proto unmarshaling an Any from
// bz and then unpacking it to the interface pointer passed in as iface using
// the provided AnyUnpacker or returning an error
//
// Ex:
// var x MyInterface
// err := UnmarshalAny(unpacker, &x, bz)
func UnmarshalAny(m Marshaler, iface interface{}, bz []byte) error {
any := &types.Any{}
err := m.UnmarshalBinaryBare(bz, any)
if err != nil {
return err
}
return m.UnpackAny(any, iface)
}

54
codec/any_test.go Normal file
View File

@ -0,0 +1,54 @@
package codec
import (
"testing"
"github.com/stretchr/testify/require"
"github.com/cosmos/cosmos-sdk/codec/testdata"
"github.com/cosmos/cosmos-sdk/codec/types"
)
func NewTestInterfaceRegistry() types.InterfaceRegistry {
registry := types.NewInterfaceRegistry()
registry.RegisterInterface("Animal", (*testdata.Animal)(nil))
registry.RegisterImplementations(
(*testdata.Animal)(nil),
&testdata.Dog{},
&testdata.Cat{},
)
return registry
}
func TestMarshalAny(t *testing.T) {
registry := types.NewInterfaceRegistry()
cdc := NewProtoCodec(registry)
kitty := &testdata.Cat{Moniker: "Kitty"}
bz, err := MarshalAny(cdc, kitty)
require.NoError(t, err)
var animal testdata.Animal
// empty registry should fail
err = UnmarshalAny(cdc, &animal, bz)
require.Error(t, err)
// wrong type registration should fail
registry.RegisterImplementations((*testdata.Animal)(nil), &testdata.Dog{})
err = UnmarshalAny(cdc, &animal, bz)
require.Error(t, err)
// should pass
registry = NewTestInterfaceRegistry()
cdc = NewProtoCodec(registry)
err = UnmarshalAny(cdc, &animal, bz)
require.NoError(t, err)
require.Equal(t, kitty, animal)
// nil should fail
registry = NewTestInterfaceRegistry()
err = UnmarshalAny(cdc, nil, bz)
require.Error(t, err)
}

View File

@ -27,3 +27,47 @@ func (m HasAnimal) UnpackInterfaces(unpacker types.AnyUnpacker) error {
var animal Animal
return unpacker.UnpackAny(m.Animal, &animal)
}
type HasAnimalI interface {
TheAnimal() Animal
}
var _ HasAnimalI = &HasAnimal{}
func (m HasAnimal) TheAnimal() Animal {
return m.Animal.GetCachedValue().(Animal)
}
type HasHasAnimalI interface {
TheHasAnimal() HasAnimalI
}
var _ HasHasAnimalI = &HasHasAnimal{}
func (m HasHasAnimal) TheHasAnimal() HasAnimalI {
return m.HasAnimal.GetCachedValue().(HasAnimalI)
}
var _ types.UnpackInterfacesMessage = HasHasAnimal{}
func (m HasHasAnimal) UnpackInterfaces(unpacker types.AnyUnpacker) error {
var animal HasAnimalI
return unpacker.UnpackAny(m.HasAnimal, &animal)
}
type HasHasHasAnimalI interface {
TheHasHasAnimal() HasHasAnimalI
}
var _ HasHasAnimalI = &HasHasAnimal{}
func (m HasHasHasAnimal) TheHasHasAnimal() HasHasAnimalI {
return m.HasHasAnimal.GetCachedValue().(HasHasAnimalI)
}
var _ types.UnpackInterfacesMessage = HasHasHasAnimal{}
func (m HasHasHasAnimal) UnpackInterfaces(unpacker types.AnyUnpacker) error {
var animal HasHasAnimalI
return unpacker.UnpackAny(m.HasHasAnimal, &animal)
}

View File

@ -179,33 +179,125 @@ func (m *HasAnimal) GetX() int64 {
return 0
}
type HasHasAnimal struct {
HasAnimal *types.Any `protobuf:"bytes,1,opt,name=has_animal,json=hasAnimal,proto3" json:"has_animal,omitempty"`
}
func (m *HasHasAnimal) Reset() { *m = HasHasAnimal{} }
func (m *HasHasAnimal) String() string { return proto.CompactTextString(m) }
func (*HasHasAnimal) ProtoMessage() {}
func (*HasHasAnimal) Descriptor() ([]byte, []int) {
return fileDescriptor_ae1353846770e6e2, []int{3}
}
func (m *HasHasAnimal) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *HasHasAnimal) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_HasHasAnimal.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 *HasHasAnimal) XXX_Merge(src proto.Message) {
xxx_messageInfo_HasHasAnimal.Merge(m, src)
}
func (m *HasHasAnimal) XXX_Size() int {
return m.Size()
}
func (m *HasHasAnimal) XXX_DiscardUnknown() {
xxx_messageInfo_HasHasAnimal.DiscardUnknown(m)
}
var xxx_messageInfo_HasHasAnimal proto.InternalMessageInfo
func (m *HasHasAnimal) GetHasAnimal() *types.Any {
if m != nil {
return m.HasAnimal
}
return nil
}
type HasHasHasAnimal struct {
HasHasAnimal *types.Any `protobuf:"bytes,1,opt,name=has_has_animal,json=hasHasAnimal,proto3" json:"has_has_animal,omitempty"`
}
func (m *HasHasHasAnimal) Reset() { *m = HasHasHasAnimal{} }
func (m *HasHasHasAnimal) String() string { return proto.CompactTextString(m) }
func (*HasHasHasAnimal) ProtoMessage() {}
func (*HasHasHasAnimal) Descriptor() ([]byte, []int) {
return fileDescriptor_ae1353846770e6e2, []int{4}
}
func (m *HasHasHasAnimal) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *HasHasHasAnimal) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_HasHasHasAnimal.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 *HasHasHasAnimal) XXX_Merge(src proto.Message) {
xxx_messageInfo_HasHasHasAnimal.Merge(m, src)
}
func (m *HasHasHasAnimal) XXX_Size() int {
return m.Size()
}
func (m *HasHasHasAnimal) XXX_DiscardUnknown() {
xxx_messageInfo_HasHasHasAnimal.DiscardUnknown(m)
}
var xxx_messageInfo_HasHasHasAnimal proto.InternalMessageInfo
func (m *HasHasHasAnimal) GetHasHasAnimal() *types.Any {
if m != nil {
return m.HasHasAnimal
}
return nil
}
func init() {
proto.RegisterType((*Dog)(nil), "cosmos_sdk.codec.v1.Dog")
proto.RegisterType((*Cat)(nil), "cosmos_sdk.codec.v1.Cat")
proto.RegisterType((*HasAnimal)(nil), "cosmos_sdk.codec.v1.HasAnimal")
proto.RegisterType((*HasHasAnimal)(nil), "cosmos_sdk.codec.v1.HasHasAnimal")
proto.RegisterType((*HasHasHasAnimal)(nil), "cosmos_sdk.codec.v1.HasHasHasAnimal")
}
func init() { proto.RegisterFile("codec/testdata/proto.proto", fileDescriptor_ae1353846770e6e2) }
var fileDescriptor_ae1353846770e6e2 = []byte{
// 264 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x54, 0x90, 0xbd, 0x4e, 0xc3, 0x30,
0x14, 0x85, 0x63, 0x42, 0x8b, 0x6a, 0x98, 0x4c, 0x87, 0xd0, 0xc1, 0x42, 0x99, 0x90, 0xa0, 0xb6,
0x00, 0xf1, 0x00, 0xe5, 0x47, 0x30, 0x67, 0x64, 0x41, 0x4e, 0x62, 0x82, 0x95, 0xd8, 0x17, 0xd5,
0x6e, 0xd5, 0xf2, 0x14, 0x3c, 0x16, 0x63, 0x47, 0x46, 0x94, 0xbc, 0x08, 0xaa, 0x9d, 0x0e, 0x5d,
0xec, 0x73, 0xaf, 0xbe, 0x63, 0x1f, 0x1d, 0x3c, 0x29, 0xa0, 0x94, 0x05, 0x77, 0xd2, 0xba, 0x52,
0x38, 0xc1, 0x3f, 0xe7, 0xe0, 0x80, 0xf9, 0x93, 0x9c, 0x16, 0x60, 0x35, 0xd8, 0x37, 0x5b, 0xd6,
0xcc, 0x63, 0x6c, 0x79, 0x3d, 0x39, 0xab, 0x00, 0xaa, 0x46, 0x06, 0x30, 0x5f, 0xbc, 0x73, 0x61,
0xd6, 0x81, 0x4f, 0xa7, 0x38, 0x7e, 0x84, 0x8a, 0x10, 0x7c, 0x68, 0xd5, 0x97, 0x4c, 0xd0, 0x39,
0xba, 0x18, 0x65, 0x5e, 0x6f, 0x77, 0x46, 0x68, 0x99, 0x1c, 0x84, 0xdd, 0x56, 0xa7, 0x77, 0x38,
0x7e, 0x10, 0x8e, 0x24, 0xf8, 0x48, 0x83, 0x51, 0xb5, 0x9c, 0xf7, 0x8e, 0xdd, 0x48, 0xc6, 0x78,
0xd0, 0xa8, 0xa5, 0xb4, 0xde, 0x35, 0xc8, 0xc2, 0x90, 0x3e, 0xe3, 0xd1, 0x8b, 0xb0, 0x33, 0xa3,
0xb4, 0x68, 0xc8, 0x15, 0x1e, 0x0a, 0xaf, 0xbc, 0xf7, 0xf8, 0x66, 0xcc, 0x42, 0x3c, 0xb6, 0x8b,
0xc7, 0x66, 0x66, 0x9d, 0xf5, 0x0c, 0x39, 0xc1, 0x68, 0xe5, 0x1f, 0x8b, 0x33, 0xb4, 0xba, 0x7f,
0xfa, 0x69, 0x29, 0xda, 0xb4, 0x14, 0xfd, 0xb5, 0x14, 0x7d, 0x77, 0x34, 0xda, 0x74, 0x34, 0xfa,
0xed, 0x68, 0xf4, 0x7a, 0x59, 0x29, 0xf7, 0xb1, 0xc8, 0x59, 0x01, 0x9a, 0x87, 0x0e, 0xfa, 0x6b,
0x6a, 0xcb, 0x9a, 0xef, 0x37, 0x96, 0x0f, 0xfd, 0x57, 0xb7, 0xff, 0x01, 0x00, 0x00, 0xff, 0xff,
0x75, 0xe8, 0x4c, 0xa3, 0x4a, 0x01, 0x00, 0x00,
// 304 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x91, 0xbf, 0x4e, 0xc3, 0x30,
0x10, 0xc6, 0x6b, 0x4a, 0x8b, 0x7a, 0x54, 0x20, 0x99, 0x0e, 0xa1, 0x83, 0x85, 0x32, 0x21, 0x41,
0x1d, 0x41, 0xc5, 0xc2, 0x56, 0x0a, 0xa2, 0x0b, 0x4b, 0x46, 0x96, 0xca, 0x49, 0x4c, 0x12, 0xe5,
0x8f, 0x51, 0xed, 0x56, 0x2d, 0x4f, 0xc1, 0x63, 0x31, 0x76, 0x64, 0x44, 0xc9, 0x8b, 0xa0, 0xd8,
0x89, 0x0a, 0x5b, 0x17, 0xfb, 0xbb, 0xd3, 0xf7, 0xfd, 0xee, 0xa4, 0x83, 0xa1, 0x2f, 0x02, 0xee,
0x3b, 0x8a, 0x4b, 0x15, 0x30, 0xc5, 0x9c, 0xf7, 0x85, 0x50, 0x82, 0xea, 0x17, 0x9f, 0xf9, 0x42,
0x66, 0x42, 0xce, 0x65, 0x90, 0x50, 0x6d, 0xa3, 0xab, 0x9b, 0xe1, 0x79, 0x28, 0x44, 0x98, 0x72,
0x63, 0xf4, 0x96, 0x6f, 0x0e, 0xcb, 0x37, 0xc6, 0x6f, 0x8f, 0xa0, 0xfd, 0x28, 0x42, 0x8c, 0xe1,
0x50, 0xc6, 0x1f, 0xdc, 0x42, 0x17, 0xe8, 0xb2, 0xe7, 0x6a, 0x5d, 0xf5, 0x72, 0x96, 0x71, 0xeb,
0xc0, 0xf4, 0x2a, 0x6d, 0xdf, 0x41, 0x7b, 0xca, 0x14, 0xb6, 0xe0, 0x28, 0x13, 0x79, 0x9c, 0xf0,
0x45, 0x9d, 0x68, 0x4a, 0x3c, 0x80, 0x4e, 0x1a, 0xaf, 0xb8, 0xd4, 0xa9, 0x8e, 0x6b, 0x0a, 0xfb,
0x19, 0x7a, 0x33, 0x26, 0x27, 0x79, 0x9c, 0xb1, 0x14, 0x5f, 0x43, 0x97, 0x69, 0xa5, 0xb3, 0xc7,
0xb7, 0x03, 0x6a, 0xd6, 0xa3, 0xcd, 0x7a, 0x74, 0x92, 0x6f, 0xdc, 0xda, 0x83, 0xfb, 0x80, 0xd6,
0x1a, 0xd6, 0x76, 0xd1, 0xda, 0x9e, 0x42, 0x7f, 0xc6, 0xe4, 0x8e, 0x35, 0x06, 0x88, 0x98, 0x9c,
0xef, 0xc1, 0xeb, 0x45, 0x4d, 0xc8, 0x7e, 0x81, 0x53, 0x03, 0xd9, 0x71, 0xee, 0xe1, 0xa4, 0xe2,
0xec, 0xc9, 0xea, 0x47, 0x7f, 0xb2, 0x0f, 0x4f, 0x5f, 0x05, 0x41, 0xdb, 0x82, 0xa0, 0x9f, 0x82,
0xa0, 0xcf, 0x92, 0xb4, 0xb6, 0x25, 0x69, 0x7d, 0x97, 0xa4, 0xf5, 0x7a, 0x15, 0xc6, 0x2a, 0x5a,
0x7a, 0xd4, 0x17, 0x99, 0x63, 0xee, 0x52, 0x7f, 0x23, 0x19, 0x24, 0xce, 0xff, 0x2b, 0x7a, 0x5d,
0x3d, 0x62, 0xfc, 0x1b, 0x00, 0x00, 0xff, 0xff, 0x65, 0xc6, 0xd8, 0xe9, 0xde, 0x01, 0x00, 0x00,
}
func (m *Dog) Marshal() (dAtA []byte, err error) {
@ -320,6 +412,76 @@ func (m *HasAnimal) MarshalToSizedBuffer(dAtA []byte) (int, error) {
return len(dAtA) - i, nil
}
func (m *HasHasAnimal) 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 *HasHasAnimal) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *HasHasAnimal) 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 (m *HasHasHasAnimal) 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 *HasHasHasAnimal) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *HasHasHasAnimal) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.HasHasAnimal != nil {
{
size, err := m.HasHasAnimal.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
@ -380,6 +542,32 @@ func (m *HasAnimal) Size() (n int) {
return n
}
func (m *HasHasAnimal) 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 (m *HasHasHasAnimal) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.HasHasAnimal != nil {
l = m.HasHasAnimal.Size()
n += 1 + l + sovProto(uint64(l))
}
return n
}
func sovProto(x uint64) (n int) {
return (math_bits.Len64(x|1) + 6) / 7
}
@ -715,6 +903,184 @@ func (m *HasAnimal) Unmarshal(dAtA []byte) error {
}
return nil
}
func (m *HasHasAnimal) 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: HasHasAnimal: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: HasHasAnimal: 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 = &types.Any{}
}
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 (m *HasHasHasAnimal) 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: HasHasHasAnimal: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: HasHasHasAnimal: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field HasHasAnimal", 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.HasHasAnimal == nil {
m.HasHasAnimal = &types.Any{}
}
if err := m.HasHasAnimal.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

View File

@ -19,3 +19,11 @@ message HasAnimal {
google.protobuf.Any animal = 1;
int64 x = 2;
}
message HasHasAnimal {
google.protobuf.Any has_animal = 1;
}
message HasHasHasAnimal {
google.protobuf.Any has_has_animal = 1;
}

140
codec/types/amino_compat.go Normal file
View File

@ -0,0 +1,140 @@
package types
import (
"fmt"
"reflect"
amino "github.com/tendermint/go-amino"
)
type aminoCompat struct {
bz []byte
jsonBz []byte
err error
}
func (any Any) MarshalAmino() ([]byte, error) {
ac := any.aminoCompat
if ac == nil {
return nil, fmt.Errorf("can't amino unmarshal")
}
return ac.bz, ac.err
}
func (any *Any) UnmarshalAmino(bz []byte) error {
any.aminoCompat = &aminoCompat{
bz: bz,
err: nil,
}
return nil
}
func (any Any) MarshalJSON() ([]byte, error) {
ac := any.aminoCompat
if ac == nil {
return nil, fmt.Errorf("can't JSON marshal")
}
return ac.jsonBz, ac.err
}
func (any *Any) UnmarshalJSON(bz []byte) error {
any.aminoCompat = &aminoCompat{
jsonBz: bz,
err: nil,
}
return nil
}
// AminoUnpacker is an AnyUnpacker provided for backwards compatibility with
// amino for the binary un-marshaling phase
type AminoUnpacker struct {
Cdc *amino.Codec
}
var _ AnyUnpacker = AminoUnpacker{}
func (a AminoUnpacker) UnpackAny(any *Any, iface interface{}) error {
ac := any.aminoCompat
if ac == nil {
return fmt.Errorf("can't amino unmarshal %T", iface)
}
err := a.Cdc.UnmarshalBinaryBare(ac.bz, iface)
if err != nil {
return err
}
val := reflect.ValueOf(iface).Elem().Interface()
err = UnpackInterfaces(val, a)
if err != nil {
return err
}
any.cachedValue = val
return nil
}
// AminoUnpacker is an AnyUnpacker provided for backwards compatibility with
// amino for the binary marshaling phase
type AminoPacker struct {
Cdc *amino.Codec
}
var _ AnyUnpacker = AminoPacker{}
func (a AminoPacker) UnpackAny(any *Any, _ interface{}) error {
err := UnpackInterfaces(any.cachedValue, a)
if err != nil {
return err
}
bz, err := a.Cdc.MarshalBinaryBare(any.cachedValue)
any.aminoCompat = &aminoCompat{
bz: bz,
err: err,
}
return err
}
// AminoUnpacker is an AnyUnpacker provided for backwards compatibility with
// amino for the JSON marshaling phase
type AminoJSONUnpacker struct {
Cdc *amino.Codec
}
var _ AnyUnpacker = AminoJSONUnpacker{}
func (a AminoJSONUnpacker) UnpackAny(any *Any, iface interface{}) error {
ac := any.aminoCompat
if ac == nil {
return fmt.Errorf("can't amino unmarshal %T", iface)
}
err := a.Cdc.UnmarshalJSON(ac.jsonBz, iface)
if err != nil {
return err
}
val := reflect.ValueOf(iface).Elem().Interface()
err = UnpackInterfaces(val, a)
if err != nil {
return err
}
any.cachedValue = val
return nil
}
// AminoUnpacker is an AnyUnpacker provided for backwards compatibility with
// amino for the JSON un-marshaling phase
type AminoJSONPacker struct {
Cdc *amino.Codec
}
var _ AnyUnpacker = AminoJSONPacker{}
func (a AminoJSONPacker) UnpackAny(any *Any, _ interface{}) error {
err := UnpackInterfaces(any.cachedValue, a)
if err != nil {
return err
}
bz, err := a.Cdc.MarshalJSON(any.cachedValue)
any.aminoCompat = &aminoCompat{
jsonBz: bz,
err: err,
}
return err
}

View File

@ -0,0 +1,132 @@
package types_test
import (
"testing"
"github.com/stretchr/testify/suite"
amino "github.com/tendermint/go-amino"
"github.com/cosmos/cosmos-sdk/codec/testdata"
"github.com/cosmos/cosmos-sdk/codec/types"
)
type TypeWithInterface struct {
Animal testdata.Animal `json:"animal"`
X int64 `json:"x,omitempty"`
}
type Suite struct {
suite.Suite
cdc *amino.Codec
a TypeWithInterface
b testdata.HasAnimal
spot *testdata.Dog
}
func (s *Suite) SetupTest() {
s.cdc = amino.NewCodec()
s.cdc.RegisterInterface((*testdata.Animal)(nil), nil)
s.cdc.RegisterConcrete(&testdata.Dog{}, "testdata/Dob", nil)
s.spot = &testdata.Dog{Size_: "small", Name: "Spot"}
s.a = TypeWithInterface{Animal: s.spot}
any, err := types.NewAnyWithValue(s.spot)
s.Require().NoError(err)
s.b = testdata.HasAnimal{Animal: any}
}
func (s *Suite) TestAminoBinary() {
bz, err := s.cdc.MarshalBinaryBare(s.a)
s.Require().NoError(err)
// expect plain amino marshal to fail
_, err = s.cdc.MarshalBinaryBare(s.b)
s.Require().Error(err)
// expect unpack interfaces before amino marshal to succeed
err = types.UnpackInterfaces(s.b, types.AminoPacker{Cdc: s.cdc})
s.Require().NoError(err)
bz2, err := s.cdc.MarshalBinaryBare(s.b)
s.Require().NoError(err)
s.Require().Equal(bz, bz2)
var c testdata.HasAnimal
err = s.cdc.UnmarshalBinaryBare(bz, &c)
s.Require().NoError(err)
err = types.UnpackInterfaces(c, types.AminoUnpacker{Cdc: s.cdc})
s.Require().NoError(err)
s.Require().Equal(s.spot, c.Animal.GetCachedValue())
}
func (s *Suite) TestAminoJSON() {
bz, err := s.cdc.MarshalJSON(s.a)
s.Require().NoError(err)
// expect plain amino marshal to fail
_, err = s.cdc.MarshalJSON(s.b)
s.Require().Error(err)
// expect unpack interfaces before amino marshal to succeed
err = types.UnpackInterfaces(s.b, types.AminoJSONPacker{Cdc: s.cdc})
s.Require().NoError(err)
bz2, err := s.cdc.MarshalJSON(s.b)
s.Require().NoError(err)
s.Require().Equal(string(bz), string(bz2))
var c testdata.HasAnimal
err = s.cdc.UnmarshalJSON(bz, &c)
s.Require().NoError(err)
err = types.UnpackInterfaces(c, types.AminoJSONUnpacker{Cdc: s.cdc})
s.Require().NoError(err)
s.Require().Equal(s.spot, c.Animal.GetCachedValue())
}
func (s *Suite) TestNested() {
s.cdc.RegisterInterface((*testdata.HasAnimalI)(nil), nil)
s.cdc.RegisterInterface((*testdata.HasHasAnimalI)(nil), nil)
s.cdc.RegisterConcrete(&testdata.HasAnimal{}, "testdata/HasAnimal", nil)
s.cdc.RegisterConcrete(&testdata.HasHasAnimal{}, "testdata/HasHasAnimal", nil)
s.cdc.RegisterConcrete(&testdata.HasHasHasAnimal{}, "testdata/HasHasHasAnimal", nil)
any, err := types.NewAnyWithValue(&s.b)
s.Require().NoError(err)
hha := testdata.HasHasAnimal{HasAnimal: any}
any2, err := types.NewAnyWithValue(&hha)
s.Require().NoError(err)
hhha := testdata.HasHasHasAnimal{HasHasAnimal: any2}
// marshal
err = types.UnpackInterfaces(hhha, types.AminoPacker{Cdc: s.cdc})
s.Require().NoError(err)
bz, err := s.cdc.MarshalBinaryBare(hhha)
s.Require().NoError(err)
// unmarshal
var hhha2 testdata.HasHasHasAnimal
err = s.cdc.UnmarshalBinaryBare(bz, &hhha2)
s.Require().NoError(err)
err = types.UnpackInterfaces(hhha2, types.AminoUnpacker{Cdc: s.cdc})
s.Require().NoError(err)
s.Require().Equal(s.spot, hhha2.TheHasHasAnimal().TheHasAnimal().TheAnimal())
// json marshal
err = types.UnpackInterfaces(hhha, types.AminoJSONPacker{Cdc: s.cdc})
s.Require().NoError(err)
jsonBz, err := s.cdc.MarshalJSON(hhha)
s.Require().NoError(err)
// json unmarshal
var hhha3 testdata.HasHasHasAnimal
err = s.cdc.UnmarshalJSON(jsonBz, &hhha3)
s.Require().NoError(err)
err = types.UnpackInterfaces(hhha3, types.AminoJSONUnpacker{Cdc: s.cdc})
s.Require().NoError(err)
s.Require().Equal(s.spot, hhha3.TheHasHasAnimal().TheHasAnimal().TheAnimal())
}
func TestSuite(t *testing.T) {
suite.Run(t, &Suite{})
}

View File

@ -1,8 +1,6 @@
package types
import (
"fmt"
"github.com/gogo/protobuf/proto"
)
@ -50,6 +48,8 @@ type Any struct {
XXX_sizecache int32 `json:"-"`
cachedValue interface{}
aminoCompat *aminoCompat
}
// NewAnyWithValue constructs a new Any packed with the value provided or
@ -92,38 +92,3 @@ func (any *Any) GetCachedValue() interface{} {
func (any *Any) ClearCachedValue() {
any.cachedValue = nil
}
// MarshalAny is a convenience function for packing the provided value in an
// Any and then proto marshaling it to bytes
func MarshalAny(x interface{}) ([]byte, error) {
msg, ok := x.(proto.Message)
if !ok {
return nil, fmt.Errorf("can't proto marshal %T", x)
}
any := &Any{}
err := any.Pack(msg)
if err != nil {
return nil, err
}
return any.Marshal()
}
// UnmarshalAny is a convenience function for proto unmarshaling an Any from
// bz and then unpacking it to the interface pointer passed in as iface using
// the provided AnyUnpacker or returning an error
//
// Ex:
// var x MyInterface
// err := UnmarshalAny(unpacker, &x, bz)
func UnmarshalAny(unpacker AnyUnpacker, iface interface{}, bz []byte) error {
any := &Any{}
err := any.Unmarshal(bz)
if err != nil {
return err
}
return unpacker.UnpackAny(any, iface)
}

View File

@ -84,7 +84,11 @@ func NewInterfaceRegistry() InterfaceRegistry {
}
func (registry *interfaceRegistry) RegisterInterface(protoName string, iface interface{}, impls ...proto.Message) {
registry.interfaceNames[protoName] = reflect.TypeOf(iface)
typ := reflect.TypeOf(iface)
if typ.Elem().Kind() != reflect.Interface {
panic(fmt.Errorf("%T is not an interface type", iface))
}
registry.interfaceNames[protoName] = typ
registry.RegisterImplementations(iface, impls...)
}
@ -98,7 +102,7 @@ func (registry *interfaceRegistry) RegisterImplementations(iface interface{}, im
for _, impl := range impls {
implType := reflect.TypeOf(impl)
if !implType.AssignableTo(ityp) {
panic(fmt.Errorf("type %T doesn't actually implement interface %T", implType, ityp))
panic(fmt.Errorf("type %T doesn't actually implement interface %+v", impl, ityp))
}
imap["/"+proto.MessageName(impl)] = implType
@ -125,7 +129,7 @@ func (registry *interfaceRegistry) UnpackAny(any *Any, iface interface{}) error
imap, found := registry.interfaceImpls[rt]
if !found {
return fmt.Errorf("no registered implementations of interface type %T", iface)
return fmt.Errorf("no registered implementations of type %+v", rt)
}
typ, found := imap[any.TypeUrl]

View File

@ -18,6 +18,14 @@ func NewTestInterfaceRegistry() types.InterfaceRegistry {
&testdata.Dog{},
&testdata.Cat{},
)
registry.RegisterImplementations(
(*testdata.HasAnimalI)(nil),
&testdata.HasAnimal{},
)
registry.RegisterImplementations(
(*testdata.HasHasAnimalI)(nil),
&testdata.HasHasAnimal{},
)
return registry
}
@ -47,35 +55,6 @@ func TestPackUnpack(t *testing.T) {
require.Equal(t, spot, animal)
}
func TestMarshalAny(t *testing.T) {
registry := types.NewInterfaceRegistry()
kitty := &testdata.Cat{Moniker: "Kitty"}
bz, err := types.MarshalAny(kitty)
require.NoError(t, err)
var animal testdata.Animal
// empty registry should fail
err = types.UnmarshalAny(registry, &animal, bz)
require.Error(t, err)
// wrong type registration should fail
registry.RegisterImplementations((*testdata.Animal)(nil), &testdata.Dog{})
err = types.UnmarshalAny(registry, &animal, bz)
require.Error(t, err)
// should pass
registry = NewTestInterfaceRegistry()
err = types.UnmarshalAny(registry, &animal, bz)
require.NoError(t, err)
require.Equal(t, kitty, animal)
// nil should fail
registry = NewTestInterfaceRegistry()
err = types.UnmarshalAny(registry, nil, bz)
}
type TestI interface {
DoSomething()
}
@ -93,6 +72,9 @@ func TestRegister(t *testing.T) {
require.Panics(t, func() {
registry.RegisterImplementations((*TestI)(nil), nil)
})
require.Panics(t, func() {
registry.RegisterInterface("not_an_interface", (*testdata.Dog)(nil))
})
}
func TestUnpackInterfaces(t *testing.T) {
@ -118,3 +100,34 @@ func TestUnpackInterfaces(t *testing.T) {
require.Equal(t, spot, hasAny2.Animal.GetCachedValue())
}
func TestNested(t *testing.T) {
registry := NewTestInterfaceRegistry()
spot := &testdata.Dog{Name: "Spot"}
any, err := types.NewAnyWithValue(spot)
require.NoError(t, err)
ha := &testdata.HasAnimal{Animal: any}
any2, err := types.NewAnyWithValue(ha)
require.NoError(t, err)
hha := &testdata.HasHasAnimal{HasAnimal: any2}
any3, err := types.NewAnyWithValue(hha)
require.NoError(t, err)
hhha := testdata.HasHasHasAnimal{HasHasAnimal: any3}
// marshal
bz, err := hhha.Marshal()
require.NoError(t, err)
// unmarshal
var hhha2 testdata.HasHasHasAnimal
err = hhha2.Unmarshal(bz)
require.NoError(t, err)
err = types.UnpackInterfaces(hhha2, registry)
require.NoError(t, err)
require.Equal(t, spot, hhha2.TheHasHasAnimal().TheHasAnimal().TheAnimal())
}

2
go.sum
View File

@ -377,8 +377,6 @@ github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a h1:9ZKAASQSHhD
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 h1:MkV+77GLUNo5oJ0jf870itWm3D0Sjh7+Za9gazKc5LQ=
github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/regen-network/cosmos-proto v0.2.2 h1:qAuQxio6lmZ3ghpeSMrhqT+Xq/FkuimzWD8o0YR9Gmo=
github.com/regen-network/cosmos-proto v0.2.2/go.mod h1:4jLYG3Qk6EtkOj3/FK7ziS5+LurpGPzJ41ungpzThcw=
github.com/regen-network/cosmos-proto v0.3.0 h1:24dVpPrPi0GDoPVLesf2Ug98iK5QgVscPl0ga4Eoub0=
github.com/regen-network/cosmos-proto v0.3.0/go.mod h1:zuP2jVPHab6+IIyOx3nXHFN+euFNeS3W8XQkcdd4s7A=
github.com/regen-network/protobuf v1.3.2-alpha.regen.1 h1:YdeZbBS0lG1D13COb7b57+nM/RGgIs8WF9DwitU6EBM=

View File

@ -173,7 +173,7 @@ func (k Keeper) MustMarshalEvidence(evidence exported.Evidence) []byte {
// the Marshaler interface, it is treated as a Proto-defined message and
// serialized that way. Otherwise, it falls back on the internal Amino codec.
func (k Keeper) MarshalEvidence(evidenceI exported.Evidence) ([]byte, error) {
return codectypes.MarshalAny(evidenceI)
return codec.MarshalAny(k.cdc, evidenceI)
}
// UnmarshalEvidence returns an Evidence interface from raw encoded evidence
@ -181,7 +181,7 @@ func (k Keeper) MarshalEvidence(evidenceI exported.Evidence) ([]byte, error) {
// failure.
func (k Keeper) UnmarshalEvidence(bz []byte) (exported.Evidence, error) {
var evi exported.Evidence
if err := codectypes.UnmarshalAny(k.cdc, &evi, bz); err != nil {
if err := codec.UnmarshalAny(k.cdc, &evi, bz); err != nil {
return nil, err
}