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  }