github.com/cosmos/cosmos-sdk@v0.50.10/codec/proto_codec_test.go (about) 1 package codec_test 2 3 import ( 4 "math" 5 "reflect" 6 "testing" 7 8 "github.com/cosmos/gogoproto/proto" 9 "github.com/stretchr/testify/require" 10 "google.golang.org/grpc/codes" 11 "google.golang.org/grpc/encoding" 12 "google.golang.org/grpc/status" 13 protov2 "google.golang.org/protobuf/proto" 14 "google.golang.org/protobuf/reflect/protoregistry" 15 16 bankv1beta1 "cosmossdk.io/api/cosmos/bank/v1beta1" 17 basev1beta1 "cosmossdk.io/api/cosmos/base/v1beta1" 18 sdkmath "cosmossdk.io/math" 19 "cosmossdk.io/x/tx/signing" 20 21 "github.com/cosmos/cosmos-sdk/codec" 22 "github.com/cosmos/cosmos-sdk/codec/types" 23 "github.com/cosmos/cosmos-sdk/testutil/testdata" 24 sdk "github.com/cosmos/cosmos-sdk/types" 25 banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" 26 ) 27 28 func createTestInterfaceRegistry() types.InterfaceRegistry { 29 interfaceRegistry := types.NewInterfaceRegistry() 30 interfaceRegistry.RegisterInterface("testdata.Animal", 31 (*testdata.Animal)(nil), 32 &testdata.Dog{}, 33 &testdata.Cat{}, 34 ) 35 36 return interfaceRegistry 37 } 38 39 func TestProtoMarsharlInterface(t *testing.T) { 40 cdc := codec.NewProtoCodec(createTestInterfaceRegistry()) 41 m := interfaceMarshaler{cdc.MarshalInterface, cdc.UnmarshalInterface} 42 testInterfaceMarshaling(require.New(t), m, false) 43 m = interfaceMarshaler{cdc.MarshalInterfaceJSON, cdc.UnmarshalInterfaceJSON} 44 testInterfaceMarshaling(require.New(t), m, false) 45 } 46 47 func TestProtoCodec(t *testing.T) { 48 cdc := codec.NewProtoCodec(createTestInterfaceRegistry()) 49 testMarshaling(t, cdc) 50 } 51 52 func TestEnsureRegistered(t *testing.T) { 53 interfaceRegistry := types.NewInterfaceRegistry() 54 cat := &testdata.Cat{Moniker: "Garfield"} 55 56 err := interfaceRegistry.EnsureRegistered(*cat) 57 require.ErrorContains(t, err, "testdata.Cat is not a pointer") 58 59 err = interfaceRegistry.EnsureRegistered(cat) 60 require.ErrorContains(t, err, "testdata.Cat does not have a registered interface") 61 62 interfaceRegistry.RegisterInterface("testdata.Animal", 63 (*testdata.Animal)(nil), 64 &testdata.Cat{}, 65 ) 66 67 require.NoError(t, interfaceRegistry.EnsureRegistered(cat)) 68 } 69 70 func TestProtoCodecMarshal(t *testing.T) { 71 interfaceRegistry := types.NewInterfaceRegistry() 72 interfaceRegistry.RegisterInterface("testdata.Animal", 73 (*testdata.Animal)(nil), 74 &testdata.Cat{}, 75 ) 76 cdc := codec.NewProtoCodec(interfaceRegistry) 77 78 cartonRegistry := types.NewInterfaceRegistry() 79 cartonRegistry.RegisterInterface("testdata.Cartoon", 80 (*testdata.Cartoon)(nil), 81 &testdata.Bird{}, 82 ) 83 cartoonCdc := codec.NewProtoCodec(cartonRegistry) 84 85 cat := &testdata.Cat{Moniker: "Garfield", Lives: 6} 86 bird := &testdata.Bird{Species: "Passerina ciris"} 87 require.NoError(t, interfaceRegistry.EnsureRegistered(cat)) 88 89 var ( 90 animal testdata.Animal 91 cartoon testdata.Cartoon 92 ) 93 94 // sanity check 95 require.True(t, reflect.TypeOf(cat).Implements(reflect.TypeOf((*testdata.Animal)(nil)).Elem())) 96 97 bz, err := cdc.MarshalInterface(cat) 98 require.NoError(t, err) 99 100 err = cdc.UnmarshalInterface(bz, &animal) 101 require.NoError(t, err) 102 103 _, err = cdc.MarshalInterface(bird) 104 require.ErrorContains(t, err, "does not have a registered interface") 105 106 bz, err = cartoonCdc.MarshalInterface(bird) 107 require.NoError(t, err) 108 109 err = cdc.UnmarshalInterface(bz, &cartoon) 110 require.ErrorContains(t, err, "no registered implementations") 111 112 err = cartoonCdc.UnmarshalInterface(bz, &cartoon) 113 require.NoError(t, err) 114 115 // test typed nil input shouldn't panic 116 var v *banktypes.QueryBalanceResponse 117 bz, err = grpcServerEncode(cartoonCdc.GRPCCodec(), v) 118 require.NoError(t, err) 119 require.Empty(t, bz) 120 } 121 122 // Emulate grpc server implementation 123 // https://github.com/grpc/grpc-go/blob/b1d7f56b81b7902d871111b82dec6ba45f854ede/rpc_util.go#L590 124 func grpcServerEncode(c encoding.Codec, msg interface{}) ([]byte, error) { 125 if msg == nil { // NOTE: typed nils will not be caught by this check 126 return nil, nil 127 } 128 b, err := c.Marshal(msg) 129 if err != nil { 130 return nil, status.Errorf(codes.Internal, "grpc: error while marshaling: %v", err.Error()) 131 } 132 if uint(len(b)) > math.MaxUint32 { 133 return nil, status.Errorf(codes.ResourceExhausted, "grpc: message too large (%d bytes)", len(b)) 134 } 135 return b, nil 136 } 137 138 func mustAny(msg proto.Message) *types.Any { 139 any, err := types.NewAnyWithValue(msg) 140 if err != nil { 141 panic(err) 142 } 143 return any 144 } 145 146 func BenchmarkProtoCodecMarshalLengthPrefixed(b *testing.B) { 147 pCdc := codec.NewProtoCodec(types.NewInterfaceRegistry()) 148 msg := &testdata.HasAnimal{ 149 X: 1000, 150 Animal: mustAny(&testdata.HasAnimal{ 151 X: 2000, 152 Animal: mustAny(&testdata.HasAnimal{ 153 X: 3000, 154 Animal: mustAny(&testdata.HasAnimal{ 155 X: 4000, 156 Animal: mustAny(&testdata.HasAnimal{ 157 X: 5000, 158 Animal: mustAny(&testdata.Cat{ 159 Moniker: "Garfield", 160 Lives: 6, 161 }), 162 }), 163 }), 164 }), 165 }), 166 } 167 168 b.ResetTimer() 169 b.ReportAllocs() 170 171 for i := 0; i < b.N; i++ { 172 blob, err := pCdc.MarshalLengthPrefixed(msg) 173 if err != nil { 174 b.Fatal(err) 175 } 176 b.SetBytes(int64(len(blob))) 177 } 178 } 179 180 func TestGetSigners(t *testing.T) { 181 interfaceRegistry, err := types.NewInterfaceRegistryWithOptions(types.InterfaceRegistryOptions{ 182 SigningOptions: signing.Options{ 183 AddressCodec: testAddressCodec{}, 184 ValidatorAddressCodec: testAddressCodec{}, 185 }, 186 ProtoFiles: protoregistry.GlobalFiles, 187 }) 188 require.NoError(t, err) 189 cdc := codec.NewProtoCodec(interfaceRegistry) 190 testAddr := sdk.AccAddress("test") 191 testAddrStr := testAddr.String() 192 testAddr2 := sdk.AccAddress("test2") 193 testAddrStr2 := testAddr2.String() 194 195 msgSendV1 := banktypes.NewMsgSend(testAddr, testAddr2, sdk.NewCoins(sdk.NewCoin("foo", sdkmath.NewInt(1)))) 196 msgSendV2 := &bankv1beta1.MsgSend{ 197 FromAddress: testAddrStr, 198 ToAddress: testAddrStr2, 199 Amount: []*basev1beta1.Coin{{Denom: "foo", Amount: "1"}}, 200 } 201 202 signers, msgSendV2Copy, err := cdc.GetMsgV1Signers(msgSendV1) 203 require.NoError(t, err) 204 require.Equal(t, [][]byte{testAddr}, signers) 205 require.True(t, protov2.Equal(msgSendV2, msgSendV2Copy)) 206 207 signers, err = cdc.GetMsgV2Signers(msgSendV2) 208 require.NoError(t, err) 209 require.Equal(t, [][]byte{testAddr}, signers) 210 211 msgSendAny, err := types.NewAnyWithValue(msgSendV1) 212 require.NoError(t, err) 213 signers, msgSendV2Copy, err = cdc.GetMsgAnySigners(msgSendAny) 214 require.NoError(t, err) 215 require.Equal(t, [][]byte{testAddr}, signers) 216 require.True(t, protov2.Equal(msgSendV2, msgSendV2Copy)) 217 } 218 219 type testAddressCodec struct{} 220 221 func (t testAddressCodec) StringToBytes(text string) ([]byte, error) { 222 return sdk.AccAddressFromBech32(text) 223 } 224 225 func (t testAddressCodec) BytesToString(bz []byte) (string, error) { 226 return sdk.AccAddress(bz).String(), nil 227 }