diff --git a/codec/amino.go b/codec/amino.go index b3302609f..a7348ee4b 100644 --- a/codec/amino.go +++ b/codec/amino.go @@ -3,6 +3,7 @@ package codec import ( "bytes" "encoding/json" + "errors" "fmt" "io" @@ -171,7 +172,7 @@ func (cdc *Codec) MustUnmarshalJSON(bz []byte, ptr interface{}) { } func (*Codec) UnpackAny(*types.Any, interface{}) error { - return fmt.Errorf("AminoCodec can't handle unpack protobuf Any's") + return errors.New("AminoCodec can't handle unpack protobuf Any's") } func (cdc *Codec) RegisterInterface(ptr interface{}, iopts *amino.InterfaceOptions) { diff --git a/codec/amino_codec_test.go b/codec/amino_codec_test.go index 48f5830ad..0df2071db 100644 --- a/codec/amino_codec_test.go +++ b/codec/amino_codec_test.go @@ -1,6 +1,8 @@ package codec_test import ( + "bytes" + "errors" "testing" "github.com/cosmos/cosmos-sdk/codec/types" @@ -129,3 +131,82 @@ func TestAminoCodec(t *testing.T) { }) } } + +func TestAminoCodecMarshalJSONIndent(t *testing.T) { + any, err := types.NewAnyWithValue(&testdata.Dog{Name: "rufus"}) + require.NoError(t, err) + + testCases := []struct { + name string + input interface{} + marshalErr bool + wantJSON string + }{ + { + name: "valid encoding and decoding", + input: &testdata.Dog{Name: "rufus"}, + wantJSON: `{ + "type": "testdata/Dog", + "value": { + "name": "rufus" + } +}`, + }, + { + name: "any marshaling", + input: &testdata.HasAnimal{Animal: any}, + wantJSON: `{ + "animal": { + "type": "testdata/Dog", + "value": { + "name": "rufus" + } + } +}`, + }, + } + + for _, tc := range testCases { + tc := tc + + t.Run(tc.name, func(t *testing.T) { + cdc := codec.NewAminoCodec(createTestCodec()) + bz, err := cdc.MarshalJSONIndent(tc.input, "", " ") + + if tc.marshalErr { + require.Error(t, err) + require.Panics(t, func() { codec.MustMarshalJSONIndent(cdc, tc.input) }) + return + } + + // Otherwise these are expected to pass. + require.NoError(t, err) + require.Equal(t, bz, []byte(tc.wantJSON)) + + var bz2 []byte + require.NotPanics(t, func() { bz2 = codec.MustMarshalJSONIndent(cdc, tc.input) }) + require.Equal(t, bz2, []byte(tc.wantJSON)) + }) + } +} + +func TestAminoCodecPrintTypes(t *testing.T) { + cdc := codec.NewAminoCodec(createTestCodec()) + buf := new(bytes.Buffer) + require.NoError(t, cdc.PrintTypes(buf)) + lines := bytes.Split(buf.Bytes(), []byte("\n")) + require.True(t, len(lines) > 1) + wantHeader := "| Type | Name | Prefix | Length | Notes |" + require.Equal(t, lines[0], []byte(wantHeader)) + + // Expecting the types to be listed in the order that they were registered. + require.True(t, bytes.HasPrefix(lines[2], []byte("| Dog | testdata/Dog |"))) + require.True(t, bytes.HasPrefix(lines[3], []byte("| Cat | testdata/Cat |"))) +} + +func TestAminoCodecUnpackAnyFails(t *testing.T) { + cdc := codec.NewAminoCodec(createTestCodec()) + err := cdc.UnpackAny(new(types.Any), &testdata.Cat{}) + require.Error(t, err) + require.Equal(t, err, errors.New("AminoCodec can't handle unpack protobuf Any's")) +} diff --git a/codec/any_test.go b/codec/any_test.go index a3914f888..19ac26d81 100644 --- a/codec/any_test.go +++ b/codec/any_test.go @@ -1,6 +1,7 @@ package codec_test import ( + "errors" "testing" "github.com/cosmos/cosmos-sdk/codec" @@ -54,3 +55,12 @@ func TestMarshalAny(t *testing.T) { err = codec.UnmarshalAny(cdc, nil, bz) 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")) +} diff --git a/codec/proto_codec_test.go b/codec/proto_codec_test.go index a914b6f3d..6c6c3c203 100644 --- a/codec/proto_codec_test.go +++ b/codec/proto_codec_test.go @@ -1,6 +1,8 @@ package codec_test import ( + "errors" + "fmt" "testing" "github.com/stretchr/testify/require" @@ -118,3 +120,76 @@ func TestProtoCodec(t *testing.T) { }) } } + +func TestProtoCodecMarshalAnyNonProtoErrors(t *testing.T) { + cdc := codec.NewProtoCodec(createTestInterfaceRegistry()) + + input := "this one that one" + _, err := cdc.MarshalJSON(input) + require.Error(t, err) + require.Equal(t, err, errors.New("cannot protobuf JSON encode unsupported type: string")) + + require.Panics(t, func() { cdc.MustMarshalJSON(input) }) +} + +func TestProtoCodecUnmarshalAnyNonProtoErrors(t *testing.T) { + cdc := codec.NewProtoCodec(createTestInterfaceRegistry()) + + recv := new(int) + err := cdc.UnmarshalJSON([]byte("foo"), recv) + require.Error(t, err) + require.Equal(t, err, errors.New("cannot protobuf JSON decode unsupported type: *int")) +} + +type lyingProtoMarshaler struct { + codec.ProtoMarshaler + falseSize int +} + +func (lpm *lyingProtoMarshaler) Size() int { + return lpm.falseSize +} + +func TestProtoCodecUnmarshalBinaryLengthPrefixedChecks(t *testing.T) { + cdc := codec.NewProtoCodec(createTestInterfaceRegistry()) + + truth := &testdata.Cat{Lives: 9, Moniker: "glowing"} + realSize := len(cdc.MustMarshalBinaryBare(truth)) + + falseSizes := []int{ + 100, + 5, + } + + for _, falseSize := range falseSizes { + falseSize := falseSize + + t.Run(fmt.Sprintf("ByMarshaling falseSize=%d", falseSize), func(t *testing.T) { + lpm := &lyingProtoMarshaler{ + ProtoMarshaler: &testdata.Cat{Lives: 9, Moniker: "glowing"}, + falseSize: falseSize, + } + var serialized []byte + require.NotPanics(t, func() { serialized = cdc.MustMarshalBinaryLengthPrefixed(lpm) }) + + recv := new(testdata.Cat) + gotErr := cdc.UnmarshalBinaryLengthPrefixed(serialized, recv) + var wantErr error + if falseSize > realSize { + wantErr = fmt.Errorf("not enough bytes to read; want: %d, got: %d", falseSize, realSize) + } else { + wantErr = fmt.Errorf("too many bytes to read; want: %d, got: %d", falseSize, realSize) + } + require.Equal(t, gotErr, wantErr) + }) + } + + t.Run("Crafted bad uvarint size", func(t *testing.T) { + crafted := []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f} + recv := new(testdata.Cat) + gotErr := cdc.UnmarshalBinaryLengthPrefixed(crafted, recv) + require.Equal(t, gotErr, errors.New("invalid number of bytes read from length-prefixed encoding: -10")) + + require.Panics(t, func() { cdc.MustUnmarshalBinaryLengthPrefixed(crafted, recv) }) + }) +}