Move and update codec.MarshalAny functions to codec.Marshaler interface (#8080)

* Changelog update

* Rename codec.MarshalAny

* move codec.MarshalInterface to codec.Marshaler

* fix tests

* Update amino_codec for compliance with MarshalerInterface

* update tests and comments

* add tests

* change order of args in UnmarshalInterface to a canonical one

* uplift MarshalInterface to take ProtoMessage as an argument

* wip

* add nil check

* make tests working

* tests cleanup

* add support for *JSON methods

* Update changelog

* linter fixes

* fix test types

* update evidence genesis_test

* adding test

* review updates

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
This commit is contained in:
Robert Zaremba 2020-12-08 10:27:08 +01:00 committed by GitHub
parent da6b7f7755
commit b219c54c2d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 375 additions and 362 deletions

View File

@ -51,13 +51,20 @@ Ref: https://keepachangelog.com/en/1.0.0/
* (version) [\#7848](https://github.com/cosmos/cosmos-sdk/pull/7848) [\#7941](https://github.com/cosmos/cosmos-sdk/pull/7941) `version --long` output now shows the list of build dependencies and replaced build dependencies.
### State Machine Breaking Changes
* (x/upgrade) [\#7979](https://github.com/cosmos/cosmos-sdk/pull/7979) keeper pubkey storage serialization migration from bech32 to protobuf.
* (x/upgrade) [\#7979](https://github.com/cosmos/cosmos-sdk/pull/7979) keeper pubkey storage serialization migration from bech32 to protobuf.
### Bug Fixes
* (crypto) [\#7966](https://github.com/cosmos/cosmos-sdk/issues/7966) `Bip44Params` `String()` function now correctly returns the absolute HD path by adding the `m/` prefix.
### API Breaking
* [\#8080](https://github.com/cosmos/cosmos-sdk/pull/8080) Updated the `codec.Marshaler` interface
* Moved `MarshalAny` and `UnmarshalAny` helper functions to `codec.Marshaler` and renamed to `MarshalInterface` and `UnmarshalInterface` respectively. These functions must take interface as a parameter (not a concrete type nor `Any` object). Underneath they use `Any` wrapping for correct protobuf serialization.
## [v0.40.0-rc3](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.40.0-rc3) - 2020-11-06
### Client Breaking
@ -74,7 +81,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
### Client Breaking
* (x/upgrade) [#7697](https://github.com/cosmos/cosmos-sdk/pull/7697) Rename flag name "--time" to "--upgrade-time", "--info" to "--upgrade-info", to keep it consistent with help message.
* (x/auth) [#7788](https://github.com/cosmos/cosmos-sdk/pull/7788) Remove `tx auth` subcommands, all auth subcommands exist as `tx <subcommand>`
* (x/auth) [#7788](https://github.com/cosmos/cosmos-sdk/pull/7788) Remove `tx auth` subcommands, all auth subcommands exist as `tx <subcommand>`
### API Breaking
@ -93,6 +100,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
* All outward facing APIs will now check that capability is not nil and name is not empty before performing any state-machine changes
* `SetIndex` has been renamed to `InitializeIndex`
### Features
* (tx) [\#7688](https://github.com/cosmos/cosmos-sdk/pull/7688) Add a new Tx gRPC service with methods `Simulate` and `GetTx` (by hash).
@ -134,7 +142,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
### Features
* (modules) [\#7540](https://github.com/cosmos/cosmos-sdk/issues/7540) Protobuf service definitions can now be used for
packing `Msg`s in transactions as defined in [ADR 031](./docs/architecture/adr-031-msg-service.md). All modules now
packing `Msg`s in transactions as defined in [ADR 031](./docs/architecture/adr-031-msg-service.md). All modules now
define a `Msg` protobuf service.
* (codec) [\#7519](https://github.com/cosmos/cosmos-sdk/pull/7519) `InterfaceRegistry` now inherits `jsonpb.AnyResolver`, and has a `RegisterCustomTypeURL` method to support ADR 031 packing of `Any`s. `AnyResolver` is now a required parameter to `RejectUnknownFields`.
* (baseapp) [\#7519](https://github.com/cosmos/cosmos-sdk/pull/7519) Add `ServiceMsgRouter` to BaseApp to handle routing of protobuf service `Msg`s. The two new types defined in ADR 031, `sdk.ServiceMsg` and `sdk.MsgRequest` are introduced with this router.
@ -738,7 +746,7 @@ generalized genesis accounts through the `GenesisAccount` interface.
* (sdk) [\#4758](https://github.com/cosmos/cosmos-sdk/issues/4758) update `x/genaccounts` to match module spec
* (simulation) [\#4824](https://github.com/cosmos/cosmos-sdk/issues/4824) `PrintAllInvariants` flag will print all failed invariants
* (simulation) [\#4490](https://github.com/cosmos/cosmos-sdk/issues/4490) add `InitialBlockHeight` flag to resume a simulation from a given block
* Support exporting the simulation stats to a given JSON file
* (simulation) [\#4847](https://github.com/cosmos/cosmos-sdk/issues/4847), [\#4838](https://github.com/cosmos/cosmos-sdk/pull/4838) and [\#4869](https://github.com/cosmos/cosmos-sdk/pull/4869) `SimApp` and simulation refactors:
* Implement `SimulationManager` for executing modules' simulation functionalities in a modularized way
@ -1052,7 +1060,7 @@ that error is that the account doesn't exist.
* (simulation) PrintAllInvariants flag will print all failed invariants
* (simulation) Add `InitialBlockHeight` flag to resume a simulation from a given block
* (simulation) [\#4670](https://github.com/cosmos/cosmos-sdk/issues/4670) Update simulation statistics to JSON format
- Support exporting the simulation stats to a given JSON file
* [\#4775](https://github.com/cosmos/cosmos-sdk/issues/4775) Refactor CI config
* Upgrade IAVL to v0.12.4
@ -1658,9 +1666,9 @@ BREAKING CHANGES
FEATURES
* Gaia REST API
* [\#2358](https://github.com/cosmos/cosmos-sdk/issues/2358) Add distribution module REST interface
* Gaia CLI (`gaiacli`)
* [\#3429](https://github.com/cosmos/cosmos-sdk/issues/3429) Support querying
for all delegator distribution rewards.

View File

@ -1,6 +1,8 @@
package codec
import "github.com/gogo/protobuf/proto"
import (
"github.com/gogo/protobuf/proto"
)
// AminoCodec defines a codec that utilizes Codec for both binary and JSON
// encoding.
@ -78,3 +80,45 @@ func (ac *AminoCodec) UnmarshalJSON(bz []byte, ptr proto.Message) error {
func (ac *AminoCodec) MustUnmarshalJSON(bz []byte, ptr proto.Message) {
ac.LegacyAmino.MustUnmarshalJSON(bz, ptr)
}
// MarshalInterface is a convenience function for amino marshaling interfaces.
// The `i` must be an interface.
// NOTE: to marshal a concrete type, you should use MarshalBinaryBare instead
func (ac *AminoCodec) MarshalInterface(i proto.Message) ([]byte, error) {
if err := assertNotNil(i); err != nil {
return nil, err
}
return ac.LegacyAmino.MarshalBinaryBare(i)
}
// UnmarshalInterface is a convenience function for amino unmarshaling interfaces.
// `ptr` must be a pointer to an interface.
// NOTE: to unmarshal a concrete type, you should use UnmarshalBinaryBare instead
//
// Example:
// var x MyInterface
// err := cdc.UnmarshalInterface(bz, &x)
func (ac *AminoCodec) UnmarshalInterface(bz []byte, ptr interface{}) error {
return ac.LegacyAmino.UnmarshalBinaryBare(bz, ptr)
}
// MarshalInterfaceJSON is a convenience function for amino marshaling interfaces.
// The `i` must be an interface.
// NOTE: to marshal a concrete type, you should use MarshalJSON instead
func (ac *AminoCodec) MarshalInterfaceJSON(i proto.Message) ([]byte, error) {
if err := assertNotNil(i); err != nil {
return nil, err
}
return ac.LegacyAmino.MarshalJSON(i)
}
// UnmarshalInterfaceJSON is a convenience function for amino unmarshaling interfaces.
// `ptr` must be a pointer to an interface.
// NOTE: to unmarshal a concrete type, you should use UnmarshalJSON instead
//
// Example:
// var x MyInterface
// err := cdc.UnmarshalInterfaceJSON(bz, &x)
func (ac *AminoCodec) UnmarshalInterfaceJSON(bz []byte, ptr interface{}) error {
return ac.LegacyAmino.UnmarshalJSON(bz, ptr)
}

View File

@ -15,121 +15,26 @@ import (
func createTestCodec() *codec.LegacyAmino {
cdc := codec.NewLegacyAmino()
cdc.RegisterInterface((*testdata.Animal)(nil), nil)
cdc.RegisterConcrete(testdata.Dog{}, "testdata/Dog", nil)
cdc.RegisterConcrete(testdata.Cat{}, "testdata/Cat", nil)
// NOTE: since we unmarshal interface using pointers, we need to register a pointer
// types here.
cdc.RegisterConcrete(&testdata.Dog{}, "testdata/Dog", nil)
cdc.RegisterConcrete(&testdata.Cat{}, "testdata/Cat", nil)
return cdc
}
func TestAminoMarsharlInterface(t *testing.T) {
cdc := codec.NewAminoCodec(createTestCodec())
m := interfaceMarshaler{cdc.MarshalInterface, cdc.UnmarshalInterface}
testInterfaceMarshaling(require.New(t), m, true)
m = interfaceMarshaler{cdc.MarshalInterfaceJSON, cdc.UnmarshalInterfaceJSON}
testInterfaceMarshaling(require.New(t), m, false)
}
func TestAminoCodec(t *testing.T) {
any, err := types.NewAnyWithValue(&testdata.Dog{Name: "rufus"})
require.NoError(t, err)
testCases := []struct {
name string
codec *codec.AminoCodec
input codec.ProtoMarshaler
recv codec.ProtoMarshaler
marshalErr bool
unmarshalErr bool
}{
{
"valid encoding and decoding",
codec.NewAminoCodec(createTestCodec()),
&testdata.Dog{Name: "rufus"},
&testdata.Dog{},
false,
false,
},
{
"invalid decode type",
codec.NewAminoCodec(createTestCodec()),
&testdata.Dog{Name: "rufus"},
&testdata.Cat{},
false,
true,
},
{
"any marshaling",
codec.NewAminoCodec(createTestCodec()),
&testdata.HasAnimal{Animal: any},
&testdata.HasAnimal{Animal: any},
false,
false,
},
}
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
bz, err := tc.codec.MarshalBinaryBare(tc.input)
if tc.marshalErr {
require.Error(t, err)
require.Panics(t, func() { tc.codec.MustMarshalBinaryBare(tc.input) })
} else {
var bz2 []byte
require.NoError(t, err)
require.NotPanics(t, func() { bz2 = tc.codec.MustMarshalBinaryBare(tc.input) })
require.Equal(t, bz, bz2)
err := tc.codec.UnmarshalBinaryBare(bz, tc.recv)
if tc.unmarshalErr {
require.Error(t, err)
require.Panics(t, func() { tc.codec.MustUnmarshalBinaryBare(bz, tc.recv) })
} else {
require.NoError(t, err)
require.NotPanics(t, func() { tc.codec.MustUnmarshalBinaryBare(bz, tc.recv) })
require.Equal(t, tc.input, tc.recv)
}
}
bz, err = tc.codec.MarshalBinaryLengthPrefixed(tc.input)
if tc.marshalErr {
require.Error(t, err)
require.Panics(t, func() { tc.codec.MustMarshalBinaryLengthPrefixed(tc.input) })
} else {
var bz2 []byte
require.NoError(t, err)
require.NotPanics(t, func() { bz2 = tc.codec.MustMarshalBinaryLengthPrefixed(tc.input) })
require.Equal(t, bz, bz2)
err := tc.codec.UnmarshalBinaryLengthPrefixed(bz, tc.recv)
if tc.unmarshalErr {
require.Error(t, err)
require.Panics(t, func() { tc.codec.MustUnmarshalBinaryLengthPrefixed(bz, tc.recv) })
} else {
require.NoError(t, err)
require.NotPanics(t, func() { tc.codec.MustUnmarshalBinaryLengthPrefixed(bz, tc.recv) })
require.Equal(t, tc.input, tc.recv)
}
}
bz, err = tc.codec.MarshalJSON(tc.input)
if tc.marshalErr {
require.Error(t, err)
require.Panics(t, func() { tc.codec.MustMarshalJSON(tc.input) })
} else {
var bz2 []byte
require.NoError(t, err)
require.NotPanics(t, func() { bz2 = tc.codec.MustMarshalJSON(tc.input) })
require.Equal(t, bz, bz2)
err := tc.codec.UnmarshalJSON(bz, tc.recv)
if tc.unmarshalErr {
require.Error(t, err)
require.Panics(t, func() { tc.codec.MustUnmarshalJSON(bz, tc.recv) })
} else {
require.NoError(t, err)
require.NotPanics(t, func() { tc.codec.MustUnmarshalJSON(bz, tc.recv) })
require.Equal(t, tc.input, tc.recv)
}
}
})
}
testMarshaling(t, codec.NewAminoCodec(createTestCodec()))
}
func TestAminoCodecMarshalJSONIndent(t *testing.T) {

View File

@ -1,44 +0,0 @@
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 BinaryMarshaler, 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 BinaryMarshaler, iface interface{}, bz []byte) error {
any := &types.Any{}
err := m.UnmarshalBinaryBare(bz, any)
if err != nil {
return err
}
return m.UnpackAny(any, iface)
}

View File

@ -1,7 +1,6 @@
package codec_test
import (
"errors"
"testing"
"github.com/stretchr/testify/require"
@ -28,38 +27,29 @@ func TestMarshalAny(t *testing.T) {
cdc := codec.NewProtoCodec(registry)
kitty := &testdata.Cat{Moniker: "Kitty"}
bz, err := codec.MarshalAny(cdc, kitty)
bz, err := cdc.MarshalInterface(kitty)
require.NoError(t, err)
var animal testdata.Animal
// empty registry should fail
err = codec.UnmarshalAny(cdc, &animal, bz)
err = cdc.UnmarshalInterface(bz, &animal)
require.Error(t, err)
// wrong type registration should fail
registry.RegisterImplementations((*testdata.Animal)(nil), &testdata.Dog{})
err = codec.UnmarshalAny(cdc, &animal, bz)
err = cdc.UnmarshalInterface(bz, &animal)
require.Error(t, err)
// should pass
registry = NewTestInterfaceRegistry()
cdc = codec.NewProtoCodec(registry)
err = codec.UnmarshalAny(cdc, &animal, bz)
err = cdc.UnmarshalInterface(bz, &animal)
require.NoError(t, err)
require.Equal(t, kitty, animal)
// nil should fail
registry = NewTestInterfaceRegistry()
err = codec.UnmarshalAny(cdc, nil, bz)
err = cdc.UnmarshalInterface(bz, nil)
require.Error(t, err)
}
func TestMarshalAnyNonProtoErrors(t *testing.T) {
registry := types.NewInterfaceRegistry()
cdc := codec.NewProtoCodec(registry)
_, err := codec.MarshalAny(cdc, 29)
require.Error(t, err)
require.Equal(t, err, errors.New("can't proto marshal int"))
}

View File

@ -32,12 +32,17 @@ type (
UnmarshalBinaryLengthPrefixed(bz []byte, ptr ProtoMarshaler) error
MustUnmarshalBinaryLengthPrefixed(bz []byte, ptr ProtoMarshaler)
MarshalInterface(i proto.Message) ([]byte, error)
UnmarshalInterface(bz []byte, ptr interface{}) error
types.AnyUnpacker
}
JSONMarshaler interface {
MarshalJSON(o proto.Message) ([]byte, error)
MustMarshalJSON(o proto.Message) []byte
MarshalInterfaceJSON(i proto.Message) ([]byte, error)
UnmarshalInterfaceJSON(bz []byte, ptr interface{}) error
UnmarshalJSON(bz []byte, ptr proto.Message) error
MustUnmarshalJSON(bz []byte, ptr proto.Message)

135
codec/codec_common_test.go Normal file
View File

@ -0,0 +1,135 @@
package codec_test
import (
"testing"
"github.com/gogo/protobuf/proto"
"github.com/stretchr/testify/require"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/codec/types"
"github.com/cosmos/cosmos-sdk/testutil/testdata"
)
type interfaceMarshaler struct {
marshal func(i proto.Message) ([]byte, error)
unmarshal func(bz []byte, ptr interface{}) error
}
func testInterfaceMarshaling(require *require.Assertions, cdc interfaceMarshaler, isAminoBin bool) {
_, err := cdc.marshal(nil)
require.Error(err, "can't marshal a nil value")
dog := &testdata.Dog{Name: "rufus"}
var dogI testdata.Animal = dog
bz, err := cdc.marshal(dogI)
require.NoError(err)
var animal testdata.Animal
if isAminoBin {
require.PanicsWithValue("Unmarshal expects a pointer", func() {
cdc.unmarshal(bz, animal)
})
} else {
err = cdc.unmarshal(bz, animal)
require.Error(err)
require.Contains(err.Error(), "expects a pointer")
}
require.NoError(cdc.unmarshal(bz, &animal))
require.Equal(dog, animal)
// Amino doesn't wrap into Any, so it doesn't need to register self type
if isAminoBin {
var dog2 testdata.Dog
require.NoError(cdc.unmarshal(bz, &dog2))
require.Equal(*dog, dog2)
}
var cat testdata.Cat
require.Error(cdc.unmarshal(bz, &cat))
}
type mustMarshaler struct {
marshal func(i codec.ProtoMarshaler) ([]byte, error)
mustMarshal func(i codec.ProtoMarshaler) []byte
unmarshal func(bz []byte, ptr codec.ProtoMarshaler) error
mustUnmarshal func(bz []byte, ptr codec.ProtoMarshaler)
}
type testCase struct {
name string
input codec.ProtoMarshaler
recv codec.ProtoMarshaler
marshalErr bool
unmarshalErr bool
}
func testMarshalingTestCase(require *require.Assertions, tc testCase, m mustMarshaler) {
bz, err := m.marshal(tc.input)
if tc.marshalErr {
require.Error(err)
require.Panics(func() { m.mustMarshal(tc.input) })
} else {
var bz2 []byte
require.NoError(err)
require.NotPanics(func() { bz2 = m.mustMarshal(tc.input) })
require.Equal(bz, bz2)
err := m.unmarshal(bz, tc.recv)
if tc.unmarshalErr {
require.Error(err)
require.Panics(func() { m.mustUnmarshal(bz, tc.recv) })
} else {
require.NoError(err)
require.NotPanics(func() { m.mustUnmarshal(bz, tc.recv) })
require.Equal(tc.input, tc.recv)
}
}
}
func testMarshaling(t *testing.T, cdc codec.Marshaler) {
any, err := types.NewAnyWithValue(&testdata.Dog{Name: "rufus"})
require.NoError(t, err)
testCases := []testCase{
{
"valid encoding and decoding",
&testdata.Dog{Name: "rufus"},
&testdata.Dog{},
false,
false,
}, {
"invalid decode type",
&testdata.Dog{Name: "rufus"},
&testdata.Cat{},
false,
true,
}}
if _, ok := cdc.(*codec.AminoCodec); ok {
testCases = append(testCases, testCase{
"any marshaling",
&testdata.HasAnimal{Animal: any},
&testdata.HasAnimal{Animal: any},
false,
false,
})
}
for _, tc := range testCases {
tc := tc
m1 := mustMarshaler{cdc.MarshalBinaryBare, cdc.MustMarshalBinaryBare, cdc.UnmarshalBinaryBare, cdc.MustUnmarshalBinaryBare}
m2 := mustMarshaler{cdc.MarshalBinaryLengthPrefixed, cdc.MustMarshalBinaryLengthPrefixed, cdc.UnmarshalBinaryLengthPrefixed, cdc.MustUnmarshalBinaryLengthPrefixed}
m3 := mustMarshaler{
func(i codec.ProtoMarshaler) ([]byte, error) { return cdc.MarshalJSON(i) },
func(i codec.ProtoMarshaler) []byte { return cdc.MustMarshalJSON(i) },
func(bz []byte, ptr codec.ProtoMarshaler) error { return cdc.UnmarshalJSON(bz, ptr) },
func(bz []byte, ptr codec.ProtoMarshaler) { cdc.MustUnmarshalJSON(bz, ptr) }}
t.Run(tc.name+"_BinaryBare",
func(t *testing.T) { testMarshalingTestCase(require.New(t), tc, m1) })
t.Run(tc.name+"_BinaryLengthPrefixed",
func(t *testing.T) { testMarshalingTestCase(require.New(t), tc, m2) })
t.Run(tc.name+"_JSON",
func(t *testing.T) { testMarshalingTestCase(require.New(t), tc, m3) })
}
}

View File

@ -2,13 +2,14 @@ package codec
import (
"encoding/binary"
"errors"
"fmt"
"strings"
"github.com/cosmos/cosmos-sdk/codec/types"
"github.com/gogo/protobuf/jsonpb"
"github.com/gogo/protobuf/proto"
"github.com/cosmos/cosmos-sdk/codec/types"
)
// ProtoCodecMarshaler defines an interface for codecs that utilize Protobuf for both
@ -160,6 +161,67 @@ func (pc *ProtoCodec) MustUnmarshalJSON(bz []byte, ptr proto.Message) {
}
}
// MarshalInterface is a convenience function for proto marshalling interfaces. It packs
// the provided value, which must be an interface, in an Any and then marshals it to bytes.
// NOTE: to marshal a concrete type, you should use MarshalBinaryBare instead
func (pc *ProtoCodec) MarshalInterface(i proto.Message) ([]byte, error) {
if err := assertNotNil(i); err != nil {
return nil, err
}
any, err := types.NewAnyWithValue(i)
if err != nil {
return nil, err
}
return pc.MarshalBinaryBare(any)
}
// UnmarshalInterface is a convenience function for proto unmarshaling interfaces. It
// unmarshals an Any from bz bytes and then unpacks it to the `ptr`, which must
// be a pointer to a non empty interface with registered implementations.
// NOTE: to unmarshal a concrete type, you should use UnmarshalBinaryBare instead
//
// Example:
// var x MyInterface
// err := cdc.UnmarshalInterface(bz, &x)
func (pc *ProtoCodec) UnmarshalInterface(bz []byte, ptr interface{}) error {
any := &types.Any{}
err := pc.UnmarshalBinaryBare(bz, any)
if err != nil {
return err
}
return pc.UnpackAny(any, ptr)
}
// MarshalInterfaceJSON is a convenience function for proto marshalling interfaces. It
// packs the provided value in an Any and then marshals it to bytes.
// NOTE: to marshal a concrete type, you should use MarshalJSON instead
func (pc *ProtoCodec) MarshalInterfaceJSON(x proto.Message) ([]byte, error) {
any, err := types.NewAnyWithValue(x)
if err != nil {
return nil, err
}
return pc.MarshalJSON(any)
}
// UnmarshalInterfaceJSON is a convenience function for proto unmarshaling interfaces.
// It unmarshals an Any from bz bytes and then unpacks it to the `iface`, which must
// be a pointer to a non empty interface, implementing proto.Message with registered implementations.
// NOTE: to unmarshal a concrete type, you should use UnmarshalJSON instead
//
// Example:
// var x MyInterface // must implement proto.Message
// err := cdc.UnmarshalInterfaceJSON(&x, bz)
func (pc *ProtoCodec) UnmarshalInterfaceJSON(bz []byte, iface interface{}) error {
any := &types.Any{}
err := pc.UnmarshalJSON(bz, any)
if err != nil {
return err
}
return pc.UnpackAny(any, iface)
}
// UnpackAny implements AnyUnpacker.UnpackAny method,
// it unpacks the value in any to the interface pointer passed in as
// iface.
@ -170,3 +232,10 @@ func (pc *ProtoCodec) UnpackAny(any *types.Any, iface interface{}) error {
func (pc *ProtoCodec) InterfaceRegistry() types.InterfaceRegistry {
return pc.interfaceRegistry
}
func assertNotNil(i interface{}) error {
if i == nil {
return errors.New("can't marshal <nil> value")
}
return nil
}

View File

@ -24,102 +24,17 @@ func createTestInterfaceRegistry() types.InterfaceRegistry {
return interfaceRegistry
}
func TestProtoMarsharlInterface(t *testing.T) {
cdc := codec.NewProtoCodec(createTestInterfaceRegistry())
m := interfaceMarshaler{cdc.MarshalInterface, cdc.UnmarshalInterface}
testInterfaceMarshaling(require.New(t), m, false)
m = interfaceMarshaler{cdc.MarshalInterfaceJSON, cdc.UnmarshalInterfaceJSON}
testInterfaceMarshaling(require.New(t), m, false)
}
func TestProtoCodec(t *testing.T) {
testCases := []struct {
name string
codec codec.Marshaler
input codec.ProtoMarshaler
recv codec.ProtoMarshaler
marshalErr bool
unmarshalErr bool
}{
{
"valid encoding and decoding",
codec.NewProtoCodec(createTestInterfaceRegistry()),
&testdata.Dog{Name: "rufus"},
&testdata.Dog{},
false,
false,
},
{
"invalid decode type",
codec.NewProtoCodec(createTestInterfaceRegistry()),
&testdata.Dog{Name: "rufus"},
&testdata.Cat{},
false,
true,
},
}
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
bz, err := tc.codec.MarshalBinaryBare(tc.input)
if tc.marshalErr {
require.Error(t, err)
require.Panics(t, func() { tc.codec.MustMarshalBinaryBare(tc.input) })
} else {
var bz2 []byte
require.NoError(t, err)
require.NotPanics(t, func() { bz2 = tc.codec.MustMarshalBinaryBare(tc.input) })
require.Equal(t, bz, bz2)
err := tc.codec.UnmarshalBinaryBare(bz, tc.recv)
if tc.unmarshalErr {
require.Error(t, err)
require.Panics(t, func() { tc.codec.MustUnmarshalBinaryBare(bz, tc.recv) })
} else {
require.NoError(t, err)
require.NotPanics(t, func() { tc.codec.MustUnmarshalBinaryBare(bz, tc.recv) })
require.Equal(t, tc.input, tc.recv)
}
}
bz, err = tc.codec.MarshalBinaryLengthPrefixed(tc.input)
if tc.marshalErr {
require.Error(t, err)
require.Panics(t, func() { tc.codec.MustMarshalBinaryLengthPrefixed(tc.input) })
} else {
var bz2 []byte
require.NoError(t, err)
require.NotPanics(t, func() { bz2 = tc.codec.MustMarshalBinaryLengthPrefixed(tc.input) })
require.Equal(t, bz, bz2)
err := tc.codec.UnmarshalBinaryLengthPrefixed(bz, tc.recv)
if tc.unmarshalErr {
require.Error(t, err)
require.Panics(t, func() { tc.codec.MustUnmarshalBinaryLengthPrefixed(bz, tc.recv) })
} else {
require.NoError(t, err)
require.NotPanics(t, func() { tc.codec.MustUnmarshalBinaryLengthPrefixed(bz, tc.recv) })
require.Equal(t, tc.input, tc.recv)
}
}
bz, err = tc.codec.MarshalJSON(tc.input)
if tc.marshalErr {
require.Error(t, err)
require.Panics(t, func() { tc.codec.MustMarshalJSON(tc.input) })
} else {
var bz2 []byte
require.NoError(t, err)
require.NotPanics(t, func() { bz2 = tc.codec.MustMarshalJSON(tc.input) })
require.Equal(t, bz, bz2)
err := tc.codec.UnmarshalJSON(bz, tc.recv)
if tc.unmarshalErr {
require.Error(t, err)
require.Panics(t, func() { tc.codec.MustUnmarshalJSON(bz, tc.recv) })
} else {
require.NoError(t, err)
require.NotPanics(t, func() { tc.codec.MustUnmarshalJSON(bz, tc.recv) })
require.Equal(t, tc.input, tc.recv)
}
}
})
}
cdc := codec.NewProtoCodec(createTestInterfaceRegistry())
testMarshaling(t, cdc)
}
type lyingProtoMarshaler struct {

View File

@ -6,6 +6,7 @@
- 2020 Feb 24: Updates to handle messages with interface fields
- 2020 Apr 27: Convert usages of `oneof` for interfaces to `Any`
- 2020 May 15: Describe `cosmos_proto` extensions and amino compatibility
- 2020 Dec 4: Move and rename `MarshalAny` and `UnmarshalAny` into the `codec.Marshaler` interface.
## Status
@ -221,23 +222,20 @@ every module that implements it in order to populate the `InterfaceRegistry`.
### Using `Any` to encode state
The SDK will provide support methods `MarshalAny` and `UnmarshalAny` to allow
easy encoding of state to `Any` in `Codec` implementations. Ex:
The SDK will provide support methods `MarshalInterface` and `UnmarshalInterface` to hide a complexity of wrapping interface types into `Any` and allow easy serialization.
```go
import "github.com/cosmos/cosmos-sdk/codec"
func (c *Codec) MarshalEvidence(evidenceI eviexported.Evidence) ([]byte, error) {
return codec.MarshalAny(evidenceI)
// note: eviexported.Evidence is an interface type
func MarshalEvidence(cdc codec.BinaryMarshaler, e eviexported.Evidence) ([]byte, error) {
return cdc.MarshalInterface(e)
}
func (c *Codec) UnmarshalEvidence(bz []byte) (eviexported.Evidence, error) {
func UnmarshalEvidence(cdc codec.BinaryMarshaler, bz []byte) (eviexported.Evidence, error) {
var evi eviexported.Evidence
err := codec.UnmarshalAny(c.interfaceContext, &evi, bz)
if err != nil {
return nil, err
}
return evi, nil
err := cdc.UnmarshalInterface(&evi, bz)
return err, nil
}
```
@ -375,4 +373,3 @@ seamless.
1. https://github.com/cosmos/cosmos-sdk/issues/4977
2. https://github.com/cosmos/cosmos-sdk/issues/5444

View File

@ -82,7 +82,7 @@ Protobuf types can be defined to encode:
- [`Msg`s](../building-modules/messages-and-queries.md#messages)
- [Query services](../building-modules/query-services.md)
- [genesis](../building-modules/genesis.md)
**Naming and conventions**
We encourage developers to follow industry guidelines: [Protocol Buffers style guide](https://developers.google.com/protocol-buffers/docs/style)
@ -95,11 +95,9 @@ may simply migrate any existing types that
are encoded and persisted via their concrete Amino codec to Protobuf (see 1. for further guidelines) and accept a `Marshaler` as the codec which is implemented via the `ProtoCodec`
without any further customization.
However, if modules are to handle type interfaces, module-level .proto files should define messages which encode interfaces
using [`google.protobuf.Any`](https://github.com/protocolbuffers/protobuf/blob/master/src/google/protobuf/any.proto).
However, if a module type composes an interface, it must wrap it in the `skd.Any` (from `/types` package) type. To do that, a module-level .proto file must use [`google.protobuf.Any`](https://github.com/protocolbuffers/protobuf/blob/master/src/google/protobuf/any.proto) for respective message type interface types.
For example, we can define `MsgSubmitEvidence` as follows where `Evidence` is
an interface:
For example, in the `x/evidence` module defines an `Evidence` interface, which is used by the `MsgSubmitEvidence`. The structure definition must use `sdk.Any` to wrap the evidence file. In the proto file we define it as follows:
```protobuf
// proto/cosmos/evidence/v1beta1/tx.proto
@ -110,8 +108,7 @@ message MsgSubmitEvidence {
}
```
The SDK provides support methods `MarshalAny` and `UnmarshalAny` to allow
easy encoding of state to `Any`.
The SDK `codec.Marshaler` interface provides support methods `MarshalInterface` and `UnmarshalInterface` to easy encoding of state to `Any`.
Module should register interfaces using `InterfaceRegistry` which provides a mechanism for registering interfaces: `RegisterInterface(protoName string, iface interface{})` and implementations: `RegisterImplementations(iface interface{}, impls ...proto.Message)` that can be safely unpacked from Any, similarly to type registration with Amino:

View File

@ -6,10 +6,14 @@ package testdata
import (
"fmt"
"github.com/gogo/protobuf/proto"
"github.com/cosmos/cosmos-sdk/codec/types"
)
type Animal interface {
proto.Message
Greet() string
}

View File

@ -215,23 +215,17 @@ func (ak AccountKeeper) decodeAccount(bz []byte) types.AccountI {
return acc
}
// MarshalAccount marshals an Account interface. If the given type implements
// 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 (ak AccountKeeper) MarshalAccount(accountI types.AccountI) ([]byte, error) {
return codec.MarshalAny(ak.cdc, accountI)
// MarshalAccount protobuf serializes an Account interface
func (ak AccountKeeper) MarshalAccount(accountI types.AccountI) ([]byte, error) { // nolint:interfacer
return ak.cdc.MarshalInterface(accountI)
}
// UnmarshalAccount returns an Account interface from raw encoded account
// bytes of a Proto-based Account type. An error is returned upon decoding
// failure.
// bytes of a Proto-based Account type
func (ak AccountKeeper) UnmarshalAccount(bz []byte) (types.AccountI, error) {
var acc types.AccountI
if err := codec.UnmarshalAny(ak.cdc, &acc, bz); err != nil {
return nil, err
}
return acc, nil
return acc, ak.cdc.UnmarshalInterface(bz, &acc)
}
// GetCodec return codec.Marshaler object used by the keeper
func (ak AccountKeeper) GetCodec() codec.BinaryMarshaler { return ak.cdc }

View File

@ -1,6 +1,8 @@
package exported
import (
"github.com/gogo/protobuf/proto"
sdk "github.com/cosmos/cosmos-sdk/types"
)
@ -14,6 +16,8 @@ type GenesisBalance interface {
// SupplyI defines an inflationary supply interface for modules that handle
// token supply.
type SupplyI interface {
proto.Message
GetTotal() sdk.Coins
SetTotal(total sdk.Coins)

View File

@ -403,21 +403,14 @@ func (k BaseKeeper) trackUndelegation(ctx sdk.Context, addr sdk.AccAddress, amt
return nil
}
// MarshalSupply marshals a Supply interface. If the given type implements
// the Marshaler interface, it is treated as a Proto-defined message and
// serialized that way. Otherwise, it falls back on the internal Amino codec.
// MarshalSupply protobuf serializes a Supply interface
func (k BaseKeeper) MarshalSupply(supplyI exported.SupplyI) ([]byte, error) {
return codec.MarshalAny(k.cdc, supplyI)
return k.cdc.MarshalInterface(supplyI)
}
// UnmarshalSupply returns a Supply interface from raw encoded supply
// bytes of a Proto-based Supply type. An error is returned upon decoding
// failure.
// bytes of a Proto-based Supply type
func (k BaseKeeper) UnmarshalSupply(bz []byte) (exported.SupplyI, error) {
var evi exported.SupplyI
if err := codec.UnmarshalAny(k.cdc, &evi, bz); err != nil {
return nil, err
}
return evi, nil
return evi, k.cdc.UnmarshalInterface(bz, &evi)
}

View File

@ -1,6 +1,7 @@
package exported
import (
"github.com/gogo/protobuf/proto"
tmbytes "github.com/tendermint/tendermint/libs/bytes"
sdk "github.com/cosmos/cosmos-sdk/types"
@ -9,6 +10,8 @@ import (
// Evidence defines the contract which concrete evidence types of misbehavior
// must implement.
type Evidence interface {
proto.Message
Route() string
Type() string
String() string

View File

@ -167,21 +167,14 @@ func (k Keeper) MustMarshalEvidence(evidence exported.Evidence) []byte {
return bz
}
// MarshalEvidence marshals an Evidence interface. If the given type implements
// the Marshaler interface, it is treated as a Proto-defined message and
// serialized that way. Otherwise, it falls back on the internal Amino codec.
// MarshalEvidence protobuf serializes an Evidence interface
func (k Keeper) MarshalEvidence(evidenceI exported.Evidence) ([]byte, error) {
return codec.MarshalAny(k.cdc, evidenceI)
return k.cdc.MarshalInterface(evidenceI)
}
// UnmarshalEvidence returns an Evidence interface from raw encoded evidence
// bytes of a Proto-based Evidence type. An error is returned upon decoding
// failure.
// bytes of a Proto-based Evidence type
func (k Keeper) UnmarshalEvidence(bz []byte) (exported.Evidence, error) {
var evi exported.Evidence
if err := codec.UnmarshalAny(k.cdc, &evi, bz); err != nil {
return nil, err
}
return evi, nil
return evi, k.cdc.UnmarshalInterface(bz, &evi)
}

View File

@ -32,11 +32,11 @@ func TestNewGenesisState(t *testing.T) {
expPass bool
}{
{
"cannot proto marshal",
"can proto marshal",
func() {
evidence = []exported.Evidence{&TestEvidence{}}
},
false,
true,
},
}
@ -175,6 +175,9 @@ func (*TestEvidence) String() string {
return "test-string"
}
func (*TestEvidence) ProtoMessage() {}
func (*TestEvidence) Reset() {}
func (*TestEvidence) Hash() tmbytes.HexBytes {
return tmbytes.HexBytes([]byte("test-hash"))
}

View File

@ -29,11 +29,9 @@ func MustMarshalClientState(cdc codec.BinaryMarshaler, clientState exported.Clie
return bz
}
// MarshalClientState marshals an ClientState interface. If the given type implements
// the Marshaler interface, it is treated as a Proto-defined message and
// serialized that way.
// MarshalClientState protobuf serializes an ClientState interface
func MarshalClientState(cdc codec.BinaryMarshaler, clientStateI exported.ClientState) ([]byte, error) {
return codec.MarshalAny(cdc, clientStateI)
return cdc.MarshalInterface(clientStateI)
}
// UnmarshalClientState returns an ClientState interface from raw encoded clientState
@ -41,7 +39,7 @@ func MarshalClientState(cdc codec.BinaryMarshaler, clientStateI exported.ClientS
// failure.
func UnmarshalClientState(cdc codec.BinaryMarshaler, bz []byte) (exported.ClientState, error) {
var clientState exported.ClientState
if err := codec.UnmarshalAny(cdc, &clientState, bz); err != nil {
if err := cdc.UnmarshalInterface(bz, &clientState); err != nil {
return nil, err
}
@ -70,11 +68,9 @@ func MustMarshalConsensusState(cdc codec.BinaryMarshaler, consensusState exporte
return bz
}
// MarshalConsensusState marshals an ConsensusState interface. If the given type implements
// the Marshaler interface, it is treated as a Proto-defined message and
// serialized that way.
func MarshalConsensusState(cdc codec.BinaryMarshaler, consensusStateI exported.ConsensusState) ([]byte, error) {
return codec.MarshalAny(cdc, consensusStateI)
// MarshalConsensusState protobuf serializes an ConsensusState interface
func MarshalConsensusState(cdc codec.BinaryMarshaler, cs exported.ConsensusState) ([]byte, error) {
return cdc.MarshalInterface(cs)
}
// UnmarshalConsensusState returns an ConsensusState interface from raw encoded clientState
@ -82,7 +78,7 @@ func MarshalConsensusState(cdc codec.BinaryMarshaler, consensusStateI exported.C
// failure.
func UnmarshalConsensusState(cdc codec.BinaryMarshaler, bz []byte) (exported.ConsensusState, error) {
var consensusState exported.ConsensusState
if err := codec.UnmarshalAny(cdc, &consensusState, bz); err != nil {
if err := cdc.UnmarshalInterface(bz, &consensusState); err != nil {
return nil, err
}

View File

@ -2,10 +2,10 @@ package exported
import (
ics23 "github.com/confio/ics23/go"
sdk "github.com/cosmos/cosmos-sdk/types"
proto "github.com/gogo/protobuf/proto"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
)
const (
@ -25,6 +25,8 @@ const (
// ClientState defines the required common functions for light clients.
type ClientState interface {
proto.Message
ClientType() string
GetLatestHeight() Height
IsFrozen() bool
@ -160,6 +162,8 @@ type ClientState interface {
// ConsensusState is the state of the consensus process
type ConsensusState interface {
proto.Message
ClientType() string // Consensus kind
// GetRoot returns the commitment root of the consensus state,

View File

@ -9,7 +9,7 @@ import (
"github.com/cosmos/cosmos-sdk/x/ibc/core/exported"
)
var _ exported.ConsensusState = ConsensusState{}
var _ exported.ConsensusState = &ConsensusState{}
// ClientType returns Solo Machine type.
func (ConsensusState) ClientType() string {

View File

@ -47,7 +47,7 @@ func (cs ClientState) CheckMisbehaviourAndUpdateState(
}
cs.FrozenSequence = soloMisbehaviour.Sequence
return cs, nil
return &cs, nil
}
// verifySignatureAndData verifies that the currently registered public key has signed

View File

@ -6,7 +6,6 @@ import (
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
"github.com/cosmos/cosmos-sdk/codec"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec"
"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
@ -53,7 +52,7 @@ func (suite *SoloMachineTestSuite) GetSequenceFromStore() uint64 {
suite.Require().NotNil(bz)
var clientState exported.ClientState
err := codec.UnmarshalAny(suite.chainA.Codec, &clientState, bz)
err := suite.chainA.Codec.UnmarshalInterface(bz, &clientState)
suite.Require().NoError(err)
return clientState.GetLatestHeight().GetRevisionHeight()
}

View File

@ -183,7 +183,7 @@ func (cs ClientState) VerifyClientState(
return sdkerrors.Wrapf(clienttypes.ErrInvalidClient, "invalid client type %T, expected %T", clientState, &ClientState{})
}
bz, err := codec.MarshalAny(cdc, clientState)
bz, err := cdc.MarshalInterface(clientState)
if err != nil {
return err
}
@ -223,7 +223,7 @@ func (cs ClientState) VerifyClientConsensusState(
return sdkerrors.Wrapf(clienttypes.ErrInvalidConsensus, "invalid consensus type %T, expected %T", consensusState, &ConsensusState{})
}
bz, err := codec.MarshalAny(cdc, consensusState)
bz, err := cdc.MarshalInterface(consensusState)
if err != nil {
return err
}

View File

@ -139,7 +139,7 @@ func (suite *TendermintTestSuite) TestVerifyClientConsensusState() {
testCases := []struct {
name string
clientState *types.ClientState
consensusState types.ConsensusState
consensusState *types.ConsensusState
prefix commitmenttypes.MerklePrefix
proof []byte
expPass bool
@ -157,7 +157,7 @@ func (suite *TendermintTestSuite) TestVerifyClientConsensusState() {
{
name: "ApplyPrefix failed",
clientState: types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false),
consensusState: types.ConsensusState{
consensusState: &types.ConsensusState{
Root: commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()),
},
prefix: commitmenttypes.MerklePrefix{},
@ -166,7 +166,7 @@ func (suite *TendermintTestSuite) TestVerifyClientConsensusState() {
{
name: "latest client height < height",
clientState: types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false),
consensusState: types.ConsensusState{
consensusState: &types.ConsensusState{
Root: commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()),
},
prefix: commitmenttypes.NewMerklePrefix([]byte("ibc")),
@ -175,7 +175,7 @@ func (suite *TendermintTestSuite) TestVerifyClientConsensusState() {
{
name: "client is frozen",
clientState: &types.ClientState{LatestHeight: height, FrozenHeight: clienttypes.NewHeight(height.RevisionNumber, height.RevisionHeight-1)},
consensusState: types.ConsensusState{
consensusState: &types.ConsensusState{
Root: commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()),
},
prefix: commitmenttypes.NewMerklePrefix([]byte("ibc")),
@ -184,7 +184,7 @@ func (suite *TendermintTestSuite) TestVerifyClientConsensusState() {
{
name: "proof verification failed",
clientState: types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false),
consensusState: types.ConsensusState{
consensusState: &types.ConsensusState{
Root: commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()),
NextValidatorsHash: suite.valsHash,
},

View File

@ -76,7 +76,7 @@ func (cs ClientState) VerifyUpgradeAndUpdateState(
}
// Verify client proof
bz, err := codec.MarshalAny(cdc, upgradedClient)
bz, err := cdc.MarshalInterface(upgradedClient)
if err != nil {
return nil, nil, sdkerrors.Wrapf(clienttypes.ErrInvalidClient, "could not marshal client state: %v", err)
}
@ -87,7 +87,7 @@ func (cs ClientState) VerifyUpgradeAndUpdateState(
}
// Verify consensus state proof
bz, err = codec.MarshalAny(cdc, upgradedConsState)
bz, err = cdc.MarshalInterface(upgradedConsState)
if err != nil {
return nil, nil, sdkerrors.Wrapf(clienttypes.ErrInvalidConsensus, "could not marshal consensus state: %v", err)
}

View File

@ -41,7 +41,7 @@ func (k Keeper) Logger(ctx sdk.Context) log.Logger {
// AddPubkey sets a address-pubkey relation
func (k Keeper) AddPubkey(ctx sdk.Context, pubkey cryptotypes.PubKey) error {
bz, err := codec.MarshalAny(k.cdc, pubkey)
bz, err := k.cdc.MarshalInterface(pubkey)
if err != nil {
return err
}
@ -59,8 +59,7 @@ func (k Keeper) GetPubkey(ctx sdk.Context, a cryptotypes.Address) (cryptotypes.P
return nil, fmt.Errorf("address %s not found", sdk.ConsAddress(a))
}
var pk cryptotypes.PubKey
err := codec.UnmarshalAny(k.cdc, &pk, bz)
return pk, err
return pk, k.cdc.UnmarshalInterface(bz, &pk)
}
// Slash attempts to slash a validator. The slash is delegated to the staking

View File

@ -27,10 +27,10 @@ func TestMsgDecode(t *testing.T) {
// firstly we start testing the pubkey serialization
pk1bz, err := codec.MarshalAny(cdc, pk1)
pk1bz, err := cdc.MarshalInterface(pk1)
require.NoError(t, err)
var pkUnmarshaled cryptotypes.PubKey
err = codec.UnmarshalAny(cdc, &pkUnmarshaled, pk1bz)
err = cdc.UnmarshalInterface(pk1bz, &pkUnmarshaled)
require.NoError(t, err)
require.True(t, pk1.Equals(pkUnmarshaled.(*ed25519.PubKey)))
@ -39,11 +39,11 @@ func TestMsgDecode(t *testing.T) {
commission1 := types.NewCommissionRates(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec())
msg, err := types.NewMsgCreateValidator(valAddr1, pk1, coinPos, types.Description{}, commission1, sdk.OneInt())
require.NoError(t, err)
msgSerialized, err := codec.MarshalAny(cdc, msg)
msgSerialized, err := cdc.MarshalInterface(msg)
require.NoError(t, err)
var msgUnmarshaled sdk.Msg
err = codec.UnmarshalAny(cdc, &msgUnmarshaled, msgSerialized)
err = cdc.UnmarshalInterface(msgSerialized, &msgUnmarshaled)
require.NoError(t, err)
msg2, ok := msgUnmarshaled.(*types.MsgCreateValidator)
require.True(t, ok)

View File

@ -23,7 +23,7 @@ func (p Plan) String() string {
if err != nil {
upgradedClientStr = "no upgraded client provided"
} else {
upgradedClientStr = fmt.Sprintf("%s", upgradedClient)
upgradedClientStr = upgradedClient.String()
}
return fmt.Sprintf(`Upgrade Plan
Name: %s