github.com/cosmos/cosmos-sdk@v0.50.10/server/grpc/server_test.go (about) 1 package grpc_test 2 3 import ( 4 "context" 5 "fmt" 6 "testing" 7 "time" 8 9 "github.com/jhump/protoreflect/grpcreflect" 10 "github.com/stretchr/testify/require" 11 "github.com/stretchr/testify/suite" 12 "google.golang.org/grpc" 13 "google.golang.org/grpc/metadata" 14 15 "github.com/cosmos/cosmos-sdk/client" 16 reflectionv1 "github.com/cosmos/cosmos-sdk/client/grpc/reflection" 17 clienttx "github.com/cosmos/cosmos-sdk/client/tx" 18 "github.com/cosmos/cosmos-sdk/codec" 19 reflectionv2 "github.com/cosmos/cosmos-sdk/server/grpc/reflection/v2alpha1" 20 "github.com/cosmos/cosmos-sdk/testutil/network" 21 "github.com/cosmos/cosmos-sdk/testutil/testdata" 22 sdk "github.com/cosmos/cosmos-sdk/types" 23 grpctypes "github.com/cosmos/cosmos-sdk/types/grpc" 24 txtypes "github.com/cosmos/cosmos-sdk/types/tx" 25 "github.com/cosmos/cosmos-sdk/types/tx/signing" 26 authclient "github.com/cosmos/cosmos-sdk/x/auth/client" 27 banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" 28 stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" 29 ) 30 31 type IntegrationTestSuite struct { 32 suite.Suite 33 34 cfg network.Config 35 network *network.Network 36 conn *grpc.ClientConn 37 } 38 39 func (s *IntegrationTestSuite) SetupSuite() { 40 var err error 41 s.T().Log("setting up integration test suite") 42 43 s.cfg, err = network.DefaultConfigWithAppConfig(network.MinimumAppConfig()) 44 s.NoError(err) 45 s.cfg.NumValidators = 1 46 47 s.network, err = network.New(s.T(), s.T().TempDir(), s.cfg) 48 s.Require().NoError(err) 49 50 _, err = s.network.WaitForHeight(2) 51 s.Require().NoError(err) 52 53 val0 := s.network.Validators[0] 54 s.conn, err = grpc.Dial( //nolint:staticcheck // ignore this line for this linter 55 val0.AppConfig.GRPC.Address, 56 grpc.WithInsecure(), //nolint:staticcheck // ignore SA1019, we don't need to use a secure connection for tests 57 grpc.WithDefaultCallOptions(grpc.ForceCodec(codec.NewProtoCodec(s.cfg.InterfaceRegistry).GRPCCodec())), 58 ) 59 s.Require().NoError(err) 60 } 61 62 func (s *IntegrationTestSuite) TearDownSuite() { 63 s.T().Log("tearing down integration test suite") 64 s.conn.Close() 65 s.network.Cleanup() 66 } 67 68 func (s *IntegrationTestSuite) TestGRPCServer_TestService() { 69 // gRPC query to test service should work 70 testClient := testdata.NewQueryClient(s.conn) 71 testRes, err := testClient.Echo(context.Background(), &testdata.EchoRequest{Message: "hello"}) 72 s.Require().NoError(err) 73 s.Require().Equal("hello", testRes.Message) 74 } 75 76 func (s *IntegrationTestSuite) TestGRPCServer_BankBalance() { 77 val0 := s.network.Validators[0] 78 79 // gRPC query to bank service should work 80 denom := fmt.Sprintf("%stoken", val0.Moniker) 81 bankClient := banktypes.NewQueryClient(s.conn) 82 var header metadata.MD 83 bankRes, err := bankClient.Balance( 84 context.Background(), 85 &banktypes.QueryBalanceRequest{Address: val0.Address.String(), Denom: denom}, 86 grpc.Header(&header), // Also fetch grpc header 87 ) 88 s.Require().NoError(err) 89 s.Require().Equal( 90 sdk.NewCoin(denom, s.network.Config.AccountTokens), 91 *bankRes.GetBalance(), 92 ) 93 blockHeight := header.Get(grpctypes.GRPCBlockHeightHeader) 94 s.Require().NotEmpty(blockHeight[0]) // Should contain the block height 95 96 // Request metadata should work 97 _, err = bankClient.Balance( 98 metadata.AppendToOutgoingContext(context.Background(), grpctypes.GRPCBlockHeightHeader, "1"), // Add metadata to request 99 &banktypes.QueryBalanceRequest{Address: val0.Address.String(), Denom: denom}, 100 grpc.Header(&header), 101 ) 102 s.Require().NoError(err) 103 blockHeight = header.Get(grpctypes.GRPCBlockHeightHeader) 104 s.Require().Equal([]string{"1"}, blockHeight) 105 } 106 107 func (s *IntegrationTestSuite) TestGRPCServer_Reflection() { 108 // Test server reflection 109 ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) 110 defer cancel() 111 // NOTE(fdymylja): we use grpcreflect because it solves imports too 112 // so that we can always assert that given a reflection server it is 113 // possible to fully query all the methods, without having any context 114 // on the proto registry 115 rc := grpcreflect.NewClientAuto(ctx, s.conn) 116 117 services, err := rc.ListServices() 118 s.Require().NoError(err) 119 s.Require().Greater(len(services), 0) 120 121 for _, svc := range services { 122 file, err := rc.FileContainingSymbol(svc) 123 s.Require().NoError(err) 124 sd := file.FindSymbol(svc) 125 s.Require().NotNil(sd) 126 } 127 } 128 129 func (s *IntegrationTestSuite) TestGRPCServer_InterfaceReflection() { 130 // this tests the application reflection capabilities and compatibility between v1 and v2 131 ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) 132 defer cancel() 133 134 clientV2 := reflectionv2.NewReflectionServiceClient(s.conn) 135 clientV1 := reflectionv1.NewReflectionServiceClient(s.conn) 136 codecDesc, err := clientV2.GetCodecDescriptor(ctx, nil) 137 s.Require().NoError(err) 138 139 interfaces, err := clientV1.ListAllInterfaces(ctx, nil) 140 s.Require().NoError(err) 141 s.Require().Equal(len(codecDesc.Codec.Interfaces), len(interfaces.InterfaceNames)) 142 s.Require().Equal(len(s.cfg.InterfaceRegistry.ListAllInterfaces()), len(codecDesc.Codec.Interfaces)) 143 144 for _, iface := range interfaces.InterfaceNames { 145 impls, err := clientV1.ListImplementations(ctx, &reflectionv1.ListImplementationsRequest{InterfaceName: iface}) 146 s.Require().NoError(err) 147 148 s.Require().ElementsMatch(impls.ImplementationMessageNames, s.cfg.InterfaceRegistry.ListImplementations(iface)) 149 } 150 } 151 152 func (s *IntegrationTestSuite) TestGRPCServer_GetTxsEvent() { 153 // Query the tx via gRPC without pagination. This used to panic, see 154 // https://github.com/cosmos/cosmos-sdk/issues/8038. 155 txServiceClient := txtypes.NewServiceClient(s.conn) 156 _, err := txServiceClient.GetTxsEvent( 157 context.Background(), 158 &txtypes.GetTxsEventRequest{ 159 Query: "message.action='send'", 160 }, 161 ) 162 s.Require().NoError(err) 163 } 164 165 func (s *IntegrationTestSuite) TestGRPCServer_BroadcastTx() { 166 val0 := s.network.Validators[0] 167 168 txBuilder := s.mkTxBuilder() 169 170 txBytes, err := val0.ClientCtx.TxConfig.TxEncoder()(txBuilder.GetTx()) 171 s.Require().NoError(err) 172 173 // Broadcast the tx via gRPC. 174 queryClient := txtypes.NewServiceClient(s.conn) 175 176 grpcRes, err := queryClient.BroadcastTx( 177 context.Background(), 178 &txtypes.BroadcastTxRequest{ 179 Mode: txtypes.BroadcastMode_BROADCAST_MODE_SYNC, 180 TxBytes: txBytes, 181 }, 182 ) 183 s.Require().NoError(err) 184 s.Require().Equal(uint32(0), grpcRes.TxResponse.Code) 185 } 186 187 // Test and enforce that we upfront reject any connections to baseapp containing 188 // invalid initial x-cosmos-block-height that aren't positive and in the range [0, max(int64)] 189 // See issue https://github.com/cosmos/cosmos-sdk/issues/7662. 190 func (s *IntegrationTestSuite) TestGRPCServerInvalidHeaderHeights() { 191 t := s.T() 192 193 // We should reject connections with invalid block heights off the bat. 194 invalidHeightStrs := []struct { 195 value string 196 wantErr string 197 }{ 198 {"-1", "height < 0"}, 199 {"9223372036854775808", "value out of range"}, // > max(int64) by 1 200 {"-10", "height < 0"}, 201 {"18446744073709551615", "value out of range"}, // max uint64, which is > max(int64) 202 {"-9223372036854775809", "value out of range"}, // Out of the range of for negative int64 203 } 204 for _, tt := range invalidHeightStrs { 205 t.Run(tt.value, func(t *testing.T) { 206 testClient := testdata.NewQueryClient(s.conn) 207 ctx := metadata.AppendToOutgoingContext(context.Background(), grpctypes.GRPCBlockHeightHeader, tt.value) 208 testRes, err := testClient.Echo(ctx, &testdata.EchoRequest{Message: "hello"}) 209 require.Error(t, err) 210 require.Nil(t, testRes) 211 require.Contains(t, err.Error(), tt.wantErr) 212 }) 213 } 214 } 215 216 // TestGRPCUnpacker - tests the grpc endpoint for Validator and using the interface registry unpack and extract the 217 // ConsAddr. (ref: https://github.com/cosmos/cosmos-sdk/issues/8045) 218 func (s *IntegrationTestSuite) TestGRPCUnpacker() { 219 ir := s.cfg.InterfaceRegistry 220 queryClient := stakingtypes.NewQueryClient(s.conn) 221 validator, err := queryClient.Validator(context.Background(), 222 &stakingtypes.QueryValidatorRequest{ValidatorAddr: s.network.Validators[0].ValAddress.String()}) 223 require.NoError(s.T(), err) 224 225 // no unpacked interfaces yet, so ConsAddr will be nil 226 nilAddr, err := validator.Validator.GetConsAddr() 227 require.Error(s.T(), err) 228 require.Nil(s.T(), nilAddr) 229 230 // unpack the interfaces and now ConsAddr is not nil 231 err = validator.Validator.UnpackInterfaces(ir) 232 require.NoError(s.T(), err) 233 addr, err := validator.Validator.GetConsAddr() 234 require.NotNil(s.T(), addr) 235 require.NoError(s.T(), err) 236 } 237 238 // mkTxBuilder creates a TxBuilder containing a signed tx from validator 0. 239 func (s *IntegrationTestSuite) mkTxBuilder() client.TxBuilder { 240 val := s.network.Validators[0] 241 s.Require().NoError(s.network.WaitForNextBlock()) 242 243 // prepare txBuilder with msg 244 txBuilder := val.ClientCtx.TxConfig.NewTxBuilder() 245 feeAmount := sdk.Coins{sdk.NewInt64Coin(s.cfg.BondDenom, 10)} 246 gasLimit := testdata.NewTestGasLimit() 247 s.Require().NoError( 248 txBuilder.SetMsgs(&banktypes.MsgSend{ 249 FromAddress: val.Address.String(), 250 ToAddress: val.Address.String(), 251 Amount: sdk.Coins{sdk.NewInt64Coin(s.cfg.BondDenom, 10)}, 252 }), 253 ) 254 txBuilder.SetFeeAmount(feeAmount) 255 txBuilder.SetGasLimit(gasLimit) 256 txBuilder.SetMemo("foobar") 257 258 // setup txFactory 259 txFactory := clienttx.Factory{}. 260 WithChainID(val.ClientCtx.ChainID). 261 WithKeybase(val.ClientCtx.Keyring). 262 WithTxConfig(val.ClientCtx.TxConfig). 263 WithSignMode(signing.SignMode_SIGN_MODE_DIRECT) 264 265 // Sign Tx. 266 err := authclient.SignTx(txFactory, val.ClientCtx, val.Moniker, txBuilder, false, true) 267 s.Require().NoError(err) 268 269 return txBuilder 270 } 271 272 func TestIntegrationTestSuite(t *testing.T) { 273 suite.Run(t, new(IntegrationTestSuite)) 274 }