From 70725c000cad95046e8fc849e946a584c31a48bf Mon Sep 17 00:00:00 2001 From: Aaron Craelius Date: Tue, 5 May 2020 17:34:01 -0400 Subject: [PATCH 1/7] WIP on Any amino compatibility layer --- codec/amino_codec.go | 44 +++++++++++++++++++++-- codec/types/amino_compat.go | 62 ++++++++++++++++++++++++++++++++ codec/types/amino_compat_test.go | 1 + codec/types/any.go | 2 ++ 4 files changed, 107 insertions(+), 2 deletions(-) create mode 100644 codec/types/amino_compat.go create mode 100644 codec/types/amino_compat_test.go diff --git a/codec/amino_codec.go b/codec/amino_codec.go index fc6fa7fdc45c..290a2c1cce0d 100644 --- a/codec/amino_codec.go +++ b/codec/amino_codec.go @@ -16,36 +16,76 @@ 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) 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) { diff --git a/codec/types/amino_compat.go b/codec/types/amino_compat.go new file mode 100644 index 000000000000..c4aa09a95f11 --- /dev/null +++ b/codec/types/amino_compat.go @@ -0,0 +1,62 @@ +package types + +import ( + "fmt" + + amino "github.com/tendermint/go-amino" +) + +type aminoCompat struct { + aminoBz []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.aminoBz, ac.err +} + +func (any *Any) UnmarshalAmino(bz []byte) error { + any.aminoCompat = &aminoCompat{ + aminoBz: bz, + err: nil, + } + return nil +} + +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.aminoBz, iface) + if err != nil { + return err + } + any.cachedValue = iface + return nil +} + +type AminoPacker struct { + Cdc *amino.Codec +} + +func (a AminoPacker) UnpackAny(any *Any, _ interface{}) error { + bz, err := a.Cdc.MarshalBinaryBare(any.cachedValue) + any.aminoCompat = &aminoCompat{ + aminoBz: bz, + err: err, + } + return err +} + +var _ AnyUnpacker = AminoPacker{} diff --git a/codec/types/amino_compat_test.go b/codec/types/amino_compat_test.go new file mode 100644 index 000000000000..ab1254f4c2be --- /dev/null +++ b/codec/types/amino_compat_test.go @@ -0,0 +1 @@ +package types diff --git a/codec/types/any.go b/codec/types/any.go index 9ce08c4e28a0..e0e8cd985bbc 100644 --- a/codec/types/any.go +++ b/codec/types/any.go @@ -50,6 +50,8 @@ type Any struct { XXX_sizecache int32 `json:"-"` cachedValue interface{} + + aminoCompat *aminoCompat } // NewAnyWithValue constructs a new Any packed with the value provided or From 98dab3f50c979a6b3b0948a8cb34e46cc1d7f13d Mon Sep 17 00:00:00 2001 From: Aaron Craelius Date: Tue, 5 May 2020 20:04:38 -0400 Subject: [PATCH 2/7] Add tests & JSON --- codec/amino_codec.go | 26 +++++++++- codec/amino_codec_test.go | 22 ++++++++ codec/types/amino_compat.go | 72 ++++++++++++++++++++++---- codec/types/amino_compat_test.go | 88 +++++++++++++++++++++++++++++++- 4 files changed, 196 insertions(+), 12 deletions(-) diff --git a/codec/amino_codec.go b/codec/amino_codec.go index 290a2c1cce0d..57dfdf4691e0 100644 --- a/codec/amino_codec.go +++ b/codec/amino_codec.go @@ -24,6 +24,14 @@ 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 { @@ -89,19 +97,35 @@ func (ac *AminoCodec) MustUnmarshalBinaryLengthPrefixed(bz []byte, ptr ProtoMars } 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 { diff --git a/codec/amino_codec_test.go b/codec/amino_codec_test.go index 3836b22d38a6..a7b941bc4596 100644 --- a/codec/amino_codec_test.go +++ b/codec/amino_codec_test.go @@ -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,23 @@ func TestAminoCodec(t *testing.T) { false, true, }, + { + + "valid encoding and decoding", + codec.NewAminoCodec(createTestCodec()), + &testdata.Dog{Name: "rufus"}, + &testdata.Dog{}, + false, + false, + }, + { + "any marshaling", + codec.NewAminoCodec(createTestCodec()), + &testdata.HasAnimal{Animal: any}, + &testdata.HasAnimal{Animal: any}, + false, + false, + }, } for _, tc := range testCases { diff --git a/codec/types/amino_compat.go b/codec/types/amino_compat.go index c4aa09a95f11..4d9bd8b6ffc3 100644 --- a/codec/types/amino_compat.go +++ b/codec/types/amino_compat.go @@ -2,13 +2,15 @@ package types import ( "fmt" + "reflect" amino "github.com/tendermint/go-amino" ) type aminoCompat struct { - aminoBz []byte - err error + bz []byte + jsonBz []byte + err error } func (any Any) MarshalAmino() ([]byte, error) { @@ -16,13 +18,29 @@ func (any Any) MarshalAmino() ([]byte, error) { if ac == nil { return nil, fmt.Errorf("can't amino unmarshal") } - return ac.aminoBz, ac.err + return ac.bz, ac.err } func (any *Any) UnmarshalAmino(bz []byte) error { any.aminoCompat = &aminoCompat{ - aminoBz: bz, - err: nil, + 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 } @@ -38,11 +56,11 @@ func (a AminoUnpacker) UnpackAny(any *Any, iface interface{}) error { if ac == nil { return fmt.Errorf("can't amino unmarshal %T", iface) } - err := a.Cdc.UnmarshalBinaryBare(ac.aminoBz, iface) + err := a.Cdc.UnmarshalBinaryBare(ac.bz, iface) if err != nil { return err } - any.cachedValue = iface + any.cachedValue = reflect.ValueOf(iface).Elem().Interface() return nil } @@ -53,10 +71,44 @@ type AminoPacker struct { func (a AminoPacker) UnpackAny(any *Any, _ interface{}) error { bz, err := a.Cdc.MarshalBinaryBare(any.cachedValue) any.aminoCompat = &aminoCompat{ - aminoBz: bz, - err: err, + bz: bz, + err: err, + } + return err +} + +var _ AnyUnpacker = AminoJSONPacker{} + +type AminoJSONPacker struct { + Cdc *amino.Codec +} + +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 + } + any.cachedValue = reflect.ValueOf(iface).Elem().Interface() + return nil +} + +func (a AminoJSONPacker) UnpackAny(any *Any, _ interface{}) error { + bz, err := a.Cdc.MarshalJSON(any.cachedValue) + any.aminoCompat = &aminoCompat{ + jsonBz: bz, + err: err, } return err } -var _ AnyUnpacker = AminoPacker{} +var _ AnyUnpacker = AminoJSONPacker{} diff --git a/codec/types/amino_compat_test.go b/codec/types/amino_compat_test.go index ab1254f4c2be..dc07018adb1f 100644 --- a/codec/types/amino_compat_test.go +++ b/codec/types/amino_compat_test.go @@ -1 +1,87 @@ -package types +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 TestSuite(t *testing.T) { + suite.Run(t, &Suite{}) +} From cc20ce71d1e9aa87b641092925824aa3e4d5fac5 Mon Sep 17 00:00:00 2001 From: Aaron Craelius Date: Tue, 5 May 2020 20:24:06 -0400 Subject: [PATCH 3/7] Refactor Marshal/UnmarshalAny --- codec/any.go | 44 +++++++++++++++++++++++++++++++++++++ codec/types/any.go | 37 ------------------------------- x/evidence/keeper/keeper.go | 4 ++-- 3 files changed, 46 insertions(+), 39 deletions(-) create mode 100644 codec/any.go diff --git a/codec/any.go b/codec/any.go new file mode 100644 index 000000000000..91543b14ff52 --- /dev/null +++ b/codec/any.go @@ -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) +} diff --git a/codec/types/any.go b/codec/types/any.go index e0e8cd985bbc..714c0f78f21c 100644 --- a/codec/types/any.go +++ b/codec/types/any.go @@ -1,8 +1,6 @@ package types import ( - "fmt" - "github.com/gogo/protobuf/proto" ) @@ -94,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) -} diff --git a/x/evidence/keeper/keeper.go b/x/evidence/keeper/keeper.go index a6de0a2e3e6c..ce629ff0bc4d 100644 --- a/x/evidence/keeper/keeper.go +++ b/x/evidence/keeper/keeper.go @@ -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 } From a0a5f3e6bfbf8af8e8f295d88a44f7cbabf3dcf1 Mon Sep 17 00:00:00 2001 From: Aaron Craelius Date: Tue, 5 May 2020 20:26:46 -0400 Subject: [PATCH 4/7] remove extra test --- codec/amino_codec_test.go | 9 --------- 1 file changed, 9 deletions(-) diff --git a/codec/amino_codec_test.go b/codec/amino_codec_test.go index a7b941bc4596..ff0a85efa552 100644 --- a/codec/amino_codec_test.go +++ b/codec/amino_codec_test.go @@ -50,15 +50,6 @@ func TestAminoCodec(t *testing.T) { false, true, }, - { - - "valid encoding and decoding", - codec.NewAminoCodec(createTestCodec()), - &testdata.Dog{Name: "rufus"}, - &testdata.Dog{}, - false, - false, - }, { "any marshaling", codec.NewAminoCodec(createTestCodec()), From 04523edcaf0afdbe650727c562507a176e3253d0 Mon Sep 17 00:00:00 2001 From: Aaron Craelius Date: Wed, 6 May 2020 14:36:10 -0400 Subject: [PATCH 5/7] Add support for nested Any's --- codec/any_test.go | 53 ++++ codec/testdata/animal.go | 44 ++++ codec/testdata/proto.pb.go | 402 ++++++++++++++++++++++++++++-- codec/testdata/proto.proto | 8 + codec/types/amino_compat.go | 22 +- codec/types/amino_compat_test.go | 45 ++++ codec/types/interface_registry.go | 10 +- codec/types/types_test.go | 71 +++--- 8 files changed, 603 insertions(+), 52 deletions(-) create mode 100644 codec/any_test.go diff --git a/codec/any_test.go b/codec/any_test.go new file mode 100644 index 000000000000..8cb4a458d993 --- /dev/null +++ b/codec/any_test.go @@ -0,0 +1,53 @@ +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) +} diff --git a/codec/testdata/animal.go b/codec/testdata/animal.go index 04617cb04f74..aa9344a38fe8 100644 --- a/codec/testdata/animal.go +++ b/codec/testdata/animal.go @@ -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) +} diff --git a/codec/testdata/proto.pb.go b/codec/testdata/proto.pb.go index 1544aa661a8a..8250f11e4001 100644 --- a/codec/testdata/proto.pb.go +++ b/codec/testdata/proto.pb.go @@ -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 diff --git a/codec/testdata/proto.proto b/codec/testdata/proto.proto index 125db8a327c2..9316a5edee1c 100644 --- a/codec/testdata/proto.proto +++ b/codec/testdata/proto.proto @@ -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; +} diff --git a/codec/types/amino_compat.go b/codec/types/amino_compat.go index 4d9bd8b6ffc3..9bad9f78ee05 100644 --- a/codec/types/amino_compat.go +++ b/codec/types/amino_compat.go @@ -60,7 +60,12 @@ func (a AminoUnpacker) UnpackAny(any *Any, iface interface{}) error { if err != nil { return err } - any.cachedValue = reflect.ValueOf(iface).Elem().Interface() + val := reflect.ValueOf(iface).Elem().Interface() + err = UnpackInterfaces(val, a) + if err != nil { + return err + } + any.cachedValue = val return nil } @@ -69,6 +74,10 @@ type AminoPacker struct { } 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, @@ -98,11 +107,20 @@ func (a AminoJSONUnpacker) UnpackAny(any *Any, iface interface{}) error { if err != nil { return err } - any.cachedValue = reflect.ValueOf(iface).Elem().Interface() + val := reflect.ValueOf(iface).Elem().Interface() + err = UnpackInterfaces(val, a) + if err != nil { + return err + } + any.cachedValue = val return nil } 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, diff --git a/codec/types/amino_compat_test.go b/codec/types/amino_compat_test.go index dc07018adb1f..bf9875d2824f 100644 --- a/codec/types/amino_compat_test.go +++ b/codec/types/amino_compat_test.go @@ -82,6 +82,51 @@ func (s *Suite) TestAminoJSON() { 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{}) } diff --git a/codec/types/interface_registry.go b/codec/types/interface_registry.go index 2403039eed64..d83800d44535 100644 --- a/codec/types/interface_registry.go +++ b/codec/types/interface_registry.go @@ -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] diff --git a/codec/types/types_test.go b/codec/types/types_test.go index f4f44b3a7340..d55dd38a6a4a 100644 --- a/codec/types/types_test.go +++ b/codec/types/types_test.go @@ -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()) +} From 59544f4b21ac2e7402b39c3a7744d6b80083a359 Mon Sep 17 00:00:00 2001 From: Aaron Craelius Date: Wed, 6 May 2020 15:09:46 -0400 Subject: [PATCH 6/7] Add docs --- codec/types/amino_compat.go | 24 ++++++++++++++++-------- go.sum | 2 -- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/codec/types/amino_compat.go b/codec/types/amino_compat.go index 9bad9f78ee05..5bb7be3f9a5b 100644 --- a/codec/types/amino_compat.go +++ b/codec/types/amino_compat.go @@ -45,6 +45,8 @@ func (any *Any) UnmarshalJSON(bz []byte) error { return nil } +// AminoUnpacker is an AnyUnpacker provided for backwards compatibility with +// amino for the binary un-marshaling phase type AminoUnpacker struct { Cdc *amino.Codec } @@ -69,10 +71,14 @@ func (a AminoUnpacker) UnpackAny(any *Any, iface interface{}) error { 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 { @@ -86,12 +92,8 @@ func (a AminoPacker) UnpackAny(any *Any, _ interface{}) error { return err } -var _ AnyUnpacker = AminoJSONPacker{} - -type AminoJSONPacker struct { - Cdc *amino.Codec -} - +// AminoUnpacker is an AnyUnpacker provided for backwards compatibility with +// amino for the JSON marshaling phase type AminoJSONUnpacker struct { Cdc *amino.Codec } @@ -116,6 +118,14 @@ func (a AminoJSONUnpacker) UnpackAny(any *Any, iface interface{}) error { 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 { @@ -128,5 +138,3 @@ func (a AminoJSONPacker) UnpackAny(any *Any, _ interface{}) error { } return err } - -var _ AnyUnpacker = AminoJSONPacker{} diff --git a/go.sum b/go.sum index 67dbeece7892..1ae3080db590 100644 --- a/go.sum +++ b/go.sum @@ -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= From 9bb4bea0493c3b793f78c78eabae4ea98e3fe012 Mon Sep 17 00:00:00 2001 From: Federico Kunze <31522760+fedekunze@users.noreply.github.com> Date: Wed, 6 May 2020 16:01:49 -0400 Subject: [PATCH 7/7] Update codec/any_test.go --- codec/any_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/codec/any_test.go b/codec/any_test.go index 8cb4a458d993..dbca831008f8 100644 --- a/codec/any_test.go +++ b/codec/any_test.go @@ -50,4 +50,5 @@ func TestMarshalAny(t *testing.T) { // nil should fail registry = NewTestInterfaceRegistry() err = UnmarshalAny(cdc, nil, bz) + require.Error(t, err) }