github.com/Finschia/finschia-sdk@v0.48.1/x/auth/tx/service_test.go (about) 1 // build +norace 2 3 package tx_test 4 5 import ( 6 "context" 7 "fmt" 8 "strings" 9 "testing" 10 "time" 11 12 "github.com/stretchr/testify/suite" 13 14 "github.com/Finschia/finschia-sdk/client" 15 "github.com/Finschia/finschia-sdk/client/flags" 16 clienttx "github.com/Finschia/finschia-sdk/client/tx" 17 "github.com/Finschia/finschia-sdk/crypto/hd" 18 "github.com/Finschia/finschia-sdk/crypto/keyring" 19 kmultisig "github.com/Finschia/finschia-sdk/crypto/keys/multisig" 20 cryptotypes "github.com/Finschia/finschia-sdk/crypto/types" 21 "github.com/Finschia/finschia-sdk/testutil" 22 "github.com/Finschia/finschia-sdk/testutil/network" 23 "github.com/Finschia/finschia-sdk/testutil/rest" 24 "github.com/Finschia/finschia-sdk/testutil/testdata" 25 sdk "github.com/Finschia/finschia-sdk/types" 26 sdkerrors "github.com/Finschia/finschia-sdk/types/errors" 27 "github.com/Finschia/finschia-sdk/types/query" 28 "github.com/Finschia/finschia-sdk/types/tx" 29 "github.com/Finschia/finschia-sdk/types/tx/signing" 30 authclient "github.com/Finschia/finschia-sdk/x/auth/client" 31 authtest "github.com/Finschia/finschia-sdk/x/auth/client/testutil" 32 tx2 "github.com/Finschia/finschia-sdk/x/auth/tx" 33 bankcli "github.com/Finschia/finschia-sdk/x/bank/client/testutil" 34 banktypes "github.com/Finschia/finschia-sdk/x/bank/types" 35 ) 36 37 var bankMsgSendEventAction = fmt.Sprintf("message.action='%s'", sdk.MsgTypeURL(&banktypes.MsgSend{})) 38 39 type IntegrationTestSuite struct { 40 suite.Suite 41 42 cfg network.Config 43 network *network.Network 44 45 txHeight int64 46 queryClient tx.ServiceClient 47 txRes sdk.TxResponse 48 } 49 50 func (s *IntegrationTestSuite) SetupSuite() { 51 s.T().Log("setting up integration test suite") 52 53 cfg := network.DefaultConfig() 54 cfg.NumValidators = 1 55 56 s.cfg = cfg 57 s.network = network.New(s.T(), cfg) 58 s.Require().NotNil(s.network) 59 60 val := s.network.Validators[0] 61 62 _, err := s.network.WaitForHeight(1) 63 s.Require().NoError(err) 64 65 s.queryClient = tx.NewServiceClient(val.ClientCtx) 66 67 // Create a new MsgSend tx from val to itself. 68 out, err := bankcli.MsgSendExec( 69 val.ClientCtx, 70 val.Address, 71 val.Address, 72 sdk.NewCoins( 73 sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10)), 74 ), 75 fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), 76 fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), 77 fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), 78 fmt.Sprintf("--gas=%d", flags.DefaultGasLimit), 79 fmt.Sprintf("--%s=foobar", flags.FlagNote), 80 ) 81 s.Require().NoError(err) 82 s.Require().NoError(val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &s.txRes)) 83 s.Require().Equal(uint32(0), s.txRes.Code) 84 85 out, err = bankcli.MsgSendExec( 86 val.ClientCtx, 87 val.Address, 88 val.Address, 89 sdk.NewCoins( 90 sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(1)), 91 ), 92 fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), 93 fmt.Sprintf("--%s=2", flags.FlagSequence), 94 fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), 95 fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), 96 fmt.Sprintf("--gas=%d", flags.DefaultGasLimit), 97 fmt.Sprintf("--%s=foobar", flags.FlagNote), 98 ) 99 s.Require().NoError(err) 100 var tr sdk.TxResponse 101 s.Require().NoError(val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &tr)) 102 s.Require().Equal(uint32(0), tr.Code) 103 104 s.Require().NoError(s.network.WaitForNextBlock()) 105 height, err := s.network.LatestHeight() 106 s.Require().NoError(err) 107 s.txHeight = height 108 } 109 110 func (s *IntegrationTestSuite) TearDownSuite() { 111 s.T().Log("tearing down integration test suite") 112 s.network.Cleanup() 113 } 114 115 func (s IntegrationTestSuite) TestSimulateTx_GRPC() { 116 val := s.network.Validators[0] 117 txBuilder := s.mkTxBuilder() 118 // Convert the txBuilder to a tx.Tx. 119 protoTx, err := txBuilderToProtoTx(txBuilder) 120 s.Require().NoError(err) 121 // Encode the txBuilder to txBytes. 122 txBytes, err := val.ClientCtx.TxConfig.TxEncoder()(txBuilder.GetTx()) 123 s.Require().NoError(err) 124 125 testCases := []struct { 126 name string 127 req *tx.SimulateRequest 128 expErr bool 129 expErrMsg string 130 }{ 131 {"nil request", nil, true, "request cannot be nil"}, 132 {"empty request", &tx.SimulateRequest{}, true, "empty txBytes is not allowed"}, 133 {"valid request with proto tx (deprecated)", &tx.SimulateRequest{Tx: protoTx}, false, ""}, 134 {"valid request with tx_bytes", &tx.SimulateRequest{TxBytes: txBytes}, false, ""}, 135 } 136 137 for _, tc := range testCases { 138 tc := tc 139 s.Run(tc.name, func() { 140 // Broadcast the tx via gRPC via the validator's clientCtx (which goes 141 // through Tendermint). 142 res, err := s.queryClient.Simulate(context.Background(), tc.req) 143 if tc.expErr { 144 s.Require().Error(err) 145 s.Require().Contains(err.Error(), tc.expErrMsg) 146 } else { 147 s.Require().NoError(err) 148 // Check the result and gas used are correct. 149 // 150 // NOTE(0Tech): This comment should be updated after applying #11985. 151 // Events from the antehandlers would not be included in the simulation. The 4 events are: 152 // - Sending Amount to recipient: coin_spent, coin_received and transfer 153 // - Msg events: message.module=bank, message.action=/cosmos.bank.v1beta1.MsgSend and message.sender=<val1> (in one message) 154 s.Require().Equal(4, len(res.GetResult().GetEvents())) 155 s.Require().True(res.GetGasInfo().GetGasUsed() > 0) // Gas used sometimes change, just check it's not empty. 156 } 157 }) 158 } 159 } 160 161 func (s IntegrationTestSuite) TestSimulateTx_GRPCGateway() { 162 val := s.network.Validators[0] 163 txBuilder := s.mkTxBuilder() 164 // Convert the txBuilder to a tx.Tx. 165 protoTx, err := txBuilderToProtoTx(txBuilder) 166 s.Require().NoError(err) 167 // Encode the txBuilder to txBytes. 168 txBytes, err := val.ClientCtx.TxConfig.TxEncoder()(txBuilder.GetTx()) 169 s.Require().NoError(err) 170 171 testCases := []struct { 172 name string 173 req *tx.SimulateRequest 174 expErr bool 175 expErrMsg string 176 }{ 177 {"empty request", &tx.SimulateRequest{}, true, "empty txBytes is not allowed"}, 178 {"valid request with proto tx (deprecated)", &tx.SimulateRequest{Tx: protoTx}, false, ""}, 179 {"valid request with tx_bytes", &tx.SimulateRequest{TxBytes: txBytes}, false, ""}, 180 } 181 182 for _, tc := range testCases { 183 s.Run(tc.name, func() { 184 req, err := val.ClientCtx.Codec.MarshalJSON(tc.req) 185 s.Require().NoError(err) 186 res, err := rest.PostRequest(fmt.Sprintf("%s/cosmos/tx/v1beta1/simulate", val.APIAddress), "application/json", req) 187 s.Require().NoError(err) 188 if tc.expErr { 189 s.Require().Contains(string(res), tc.expErrMsg) 190 } else { 191 var result tx.SimulateResponse 192 err = val.ClientCtx.Codec.UnmarshalJSON(res, &result) 193 s.Require().NoError(err) 194 // Check the result and gas used are correct. 195 s.Require().Equal(4, len(result.GetResult().GetEvents())) // See TestSimulateTx_GRPC for the 4 events. 196 s.Require().True(result.GetGasInfo().GetGasUsed() > 0) // Gas used sometimes change, just check it's not empty. 197 } 198 }) 199 } 200 } 201 202 func (s IntegrationTestSuite) TestGetTxEvents_GRPC() { 203 testCases := []struct { 204 name string 205 req *tx.GetTxsEventRequest 206 expErr bool 207 expErrMsg string 208 }{ 209 { 210 "nil request", 211 nil, 212 true, "request cannot be nil", 213 }, 214 { 215 "empty request", 216 &tx.GetTxsEventRequest{}, 217 true, "must declare at least one event to search", 218 }, 219 { 220 "request with dummy event", 221 &tx.GetTxsEventRequest{Events: []string{"foobar"}}, 222 true, "event foobar should be of the format: {eventType}.{eventAttribute}={value}", 223 }, 224 { 225 "request with order-by", 226 &tx.GetTxsEventRequest{ 227 Events: []string{bankMsgSendEventAction}, 228 OrderBy: tx.OrderBy_ORDER_BY_ASC, 229 }, 230 false, "", 231 }, 232 { 233 "without pagination", 234 &tx.GetTxsEventRequest{ 235 Events: []string{bankMsgSendEventAction}, 236 }, 237 false, "", 238 }, 239 { 240 "with pagination", 241 &tx.GetTxsEventRequest{ 242 Events: []string{bankMsgSendEventAction}, 243 Pagination: &query.PageRequest{ 244 CountTotal: false, 245 Offset: 0, 246 Limit: 1, 247 }, 248 }, 249 false, "", 250 }, 251 { 252 "with multi events", 253 &tx.GetTxsEventRequest{ 254 Events: []string{bankMsgSendEventAction, "message.module='bank'"}, 255 }, 256 false, "", 257 }, 258 } 259 for _, tc := range testCases { 260 s.Run(tc.name, func() { 261 // Query the tx via gRPC. 262 grpcRes, err := s.queryClient.GetTxsEvent(context.Background(), tc.req) 263 if tc.expErr { 264 s.Require().Error(err) 265 s.Require().Contains(err.Error(), tc.expErrMsg) 266 } else { 267 s.Require().NoError(err) 268 s.Require().GreaterOrEqual(len(grpcRes.Txs), 1) 269 s.Require().Equal("foobar", grpcRes.Txs[0].Body.Memo) 270 271 // Make sure fields are populated. 272 // ref: https://github.com/cosmos/cosmos-sdk/issues/8680 273 // ref: https://github.com/cosmos/cosmos-sdk/issues/8681 274 s.Require().NotEmpty(grpcRes.TxResponses[0].Timestamp) 275 s.Require().NotEmpty(grpcRes.TxResponses[0].RawLog) 276 } 277 }) 278 } 279 } 280 281 func (s IntegrationTestSuite) TestGetTxEvents_GRPCGateway() { 282 val := s.network.Validators[0] 283 testCases := []struct { 284 name string 285 url string 286 expErr bool 287 expErrMsg string 288 }{ 289 { 290 "empty params", 291 fmt.Sprintf("%s/cosmos/tx/v1beta1/txs", val.APIAddress), 292 true, 293 "must declare at least one event to search", 294 }, 295 { 296 "without pagination", 297 fmt.Sprintf("%s/cosmos/tx/v1beta1/txs?events=%s", val.APIAddress, bankMsgSendEventAction), 298 false, 299 "", 300 }, 301 { 302 "with pagination", 303 fmt.Sprintf("%s/cosmos/tx/v1beta1/txs?events=%s&pagination.offset=%d&pagination.limit=%d", val.APIAddress, bankMsgSendEventAction, 0, 10), 304 false, 305 "", 306 }, 307 { 308 "valid request: order by asc", 309 fmt.Sprintf("%s/cosmos/tx/v1beta1/txs?events=%s&events=%s&order_by=ORDER_BY_ASC", val.APIAddress, bankMsgSendEventAction, "message.module='bank'"), 310 false, 311 "", 312 }, 313 { 314 "valid request: order by desc", 315 fmt.Sprintf("%s/cosmos/tx/v1beta1/txs?events=%s&events=%s&order_by=ORDER_BY_DESC", val.APIAddress, bankMsgSendEventAction, "message.module='bank'"), 316 false, 317 "", 318 }, 319 { 320 "invalid request: invalid order by", 321 fmt.Sprintf("%s/cosmos/tx/v1beta1/txs?events=%s&events=%s&order_by=invalid_order", val.APIAddress, bankMsgSendEventAction, "message.module='bank'"), 322 true, 323 "is not a valid tx.OrderBy", 324 }, 325 { 326 "expect pass with multiple-events", 327 fmt.Sprintf("%s/cosmos/tx/v1beta1/txs?events=%s&events=%s", val.APIAddress, bankMsgSendEventAction, "message.module='bank'"), 328 false, 329 "", 330 }, 331 { 332 "expect pass with escape event", 333 fmt.Sprintf("%s/cosmos/tx/v1beta1/txs?events=%s", val.APIAddress, "message.action%3D'/cosmos.bank.v1beta1.MsgSend'"), 334 false, 335 "", 336 }, 337 } 338 for _, tc := range testCases { 339 s.Run(tc.name, func() { 340 res, err := rest.GetRequest(tc.url) 341 s.Require().NoError(err) 342 if tc.expErr { 343 s.Require().Contains(string(res), tc.expErrMsg) 344 } else { 345 var result tx.GetTxsEventResponse 346 err = val.ClientCtx.Codec.UnmarshalJSON(res, &result) 347 s.Require().NoError(err) 348 s.Require().GreaterOrEqual(len(result.Txs), 1) 349 s.Require().Equal("foobar", result.Txs[0].Body.Memo) 350 s.Require().NotZero(result.TxResponses[0].Height) 351 } 352 }) 353 } 354 } 355 356 func (s IntegrationTestSuite) TestGetTx_GRPC() { 357 testCases := []struct { 358 name string 359 req *tx.GetTxRequest 360 expErr bool 361 expErrMsg string 362 }{ 363 {"nil request", nil, true, "request cannot be nil"}, 364 {"empty request", &tx.GetTxRequest{}, true, "tx hash cannot be empty"}, 365 {"request with dummy hash of wrong length", &tx.GetTxRequest{Hash: "deadbeef"}, true, "The length of tx hash must be 64"}, 366 {"request with dummy hash of correct length but invalid", &tx.GetTxRequest{Hash: "2AAC6096A87A9B9ABF604316313950D518DFDD86F2E597DD84A5808582DD0C02"}, true, "tx not found: 2AAC6096A87A9B9ABF604316313950D518DFDD86F2E597DD84A5808582DD0C02"}, 367 {"good request", &tx.GetTxRequest{Hash: s.txRes.TxHash}, false, ""}, 368 } 369 for _, tc := range testCases { 370 s.Run(tc.name, func() { 371 // Query the tx via gRPC. 372 grpcRes, err := s.queryClient.GetTx(context.Background(), tc.req) 373 if tc.expErr { 374 s.Require().Error(err) 375 s.Require().Contains(err.Error(), tc.expErrMsg) 376 } else { 377 s.Require().NoError(err) 378 s.Require().Equal("foobar", grpcRes.Tx.Body.Memo) 379 } 380 }) 381 } 382 } 383 384 func (s IntegrationTestSuite) TestGetTx_GRPCGateway() { 385 val := s.network.Validators[0] 386 testCases := []struct { 387 name string 388 url string 389 expErr bool 390 expErrMsg string 391 }{ 392 { 393 "empty params", 394 fmt.Sprintf("%s/cosmos/tx/v1beta1/txs/", val.APIAddress), 395 true, "tx hash cannot be empty", 396 }, 397 { 398 "dummy hash of wrong length", 399 fmt.Sprintf("%s/cosmos/tx/v1beta1/txs/%s", val.APIAddress, "deadbeef"), 400 true, "The length of tx hash must be 64", 401 }, 402 { 403 "dummy hash of correct length but invalid", 404 fmt.Sprintf("%s/cosmos/tx/v1beta1/txs/%s", val.APIAddress, "2AAC6096A87A9B9ABF604316313950D518DFDD86F2E597DD84A5808582DD0C02"), 405 true, "tx not found: 2AAC6096A87A9B9ABF604316313950D518DFDD86F2E597DD84A5808582DD0C02", 406 }, 407 { 408 "good hash", 409 fmt.Sprintf("%s/cosmos/tx/v1beta1/txs/%s", val.APIAddress, s.txRes.TxHash), 410 false, "", 411 }, 412 } 413 for _, tc := range testCases { 414 s.Run(tc.name, func() { 415 res, err := rest.GetRequest(tc.url) 416 s.Require().NoError(err) 417 if tc.expErr { 418 s.Require().Contains(string(res), tc.expErrMsg) 419 } else { 420 var result tx.GetTxResponse 421 err = val.ClientCtx.Codec.UnmarshalJSON(res, &result) 422 s.Require().NoError(err) 423 s.Require().Equal("foobar", result.Tx.Body.Memo) 424 s.Require().NotZero(result.TxResponse.Height) 425 426 // Make sure fields are populated. 427 // ref: https://github.com/cosmos/cosmos-sdk/issues/8680 428 // ref: https://github.com/cosmos/cosmos-sdk/issues/8681 429 s.Require().NotEmpty(result.TxResponse.Timestamp) 430 s.Require().NotEmpty(result.TxResponse.RawLog) 431 } 432 }) 433 } 434 } 435 436 func (s IntegrationTestSuite) TestBroadcastTx_GRPC() { 437 val := s.network.Validators[0] 438 txBuilder := s.mkTxBuilder() 439 txBytes, err := val.ClientCtx.TxConfig.TxEncoder()(txBuilder.GetTx()) 440 s.Require().NoError(err) 441 442 testCases := []struct { 443 name string 444 req *tx.BroadcastTxRequest 445 expErr bool 446 expErrMsg string 447 }{ 448 {"nil request", nil, true, "request cannot be nil"}, 449 {"empty request", &tx.BroadcastTxRequest{}, true, "invalid empty tx"}, 450 {"no mode", &tx.BroadcastTxRequest{ 451 TxBytes: txBytes, 452 }, true, "supported types: sync, async, block"}, 453 {"valid request", &tx.BroadcastTxRequest{ 454 Mode: tx.BroadcastMode_BROADCAST_MODE_SYNC, 455 TxBytes: txBytes, 456 }, false, ""}, 457 } 458 459 for _, tc := range testCases { 460 tc := tc 461 s.Run(tc.name, func() { 462 // Broadcast the tx via gRPC via the validator's clientCtx (which goes 463 // through Tendermint). 464 grpcRes, err := s.queryClient.BroadcastTx(context.Background(), tc.req) 465 if tc.expErr { 466 s.Require().Error(err) 467 s.Require().Contains(err.Error(), tc.expErrMsg) 468 } else { 469 s.Require().NoError(err) 470 s.Require().Equal(uint32(0), grpcRes.TxResponse.Code) 471 } 472 }) 473 } 474 time.Sleep(1 * time.Second) // wait for block confirm time before executing next test 475 } 476 477 func (s IntegrationTestSuite) TestBroadcastTx_GRPCGateway() { 478 val := s.network.Validators[0] 479 txBuilder := s.mkTxBuilder() 480 txBytes, err := val.ClientCtx.TxConfig.TxEncoder()(txBuilder.GetTx()) 481 s.Require().NoError(err) 482 483 testCases := []struct { 484 name string 485 req *tx.BroadcastTxRequest 486 expErr bool 487 expErrMsg string 488 }{ 489 {"empty request", &tx.BroadcastTxRequest{}, true, "invalid empty tx"}, 490 {"no mode", &tx.BroadcastTxRequest{TxBytes: txBytes}, true, "supported types: sync, async, block"}, 491 {"valid request", &tx.BroadcastTxRequest{ 492 Mode: tx.BroadcastMode_BROADCAST_MODE_SYNC, 493 TxBytes: txBytes, 494 }, false, ""}, 495 } 496 497 for _, tc := range testCases { 498 s.Run(tc.name, func() { 499 req, err := val.ClientCtx.Codec.MarshalJSON(tc.req) 500 s.Require().NoError(err) 501 res, err := rest.PostRequest(fmt.Sprintf("%s/cosmos/tx/v1beta1/txs", val.APIAddress), "application/json", req) 502 s.Require().NoError(err) 503 if tc.expErr { 504 s.Require().Contains(string(res), tc.expErrMsg) 505 } else { 506 var result tx.BroadcastTxResponse 507 err = val.ClientCtx.Codec.UnmarshalJSON(res, &result) 508 s.Require().NoError(err) 509 s.Require().Equal(uint32(0), result.TxResponse.Code, "rawlog", result.TxResponse.RawLog) 510 } 511 }) 512 } 513 time.Sleep(1 * time.Second) // wait for block confirm time before executing next test 514 } 515 516 func (s *IntegrationTestSuite) TestSimMultiSigTx() { 517 val1 := *s.network.Validators[0] 518 519 kr := val1.ClientCtx.Keyring 520 521 account1, _, err := kr.NewMnemonic("newAccount1", keyring.English, sdk.FullFundraiserPath, keyring.DefaultBIP39Passphrase, hd.Secp256k1) 522 s.Require().NoError(err) 523 524 account2, _, err := kr.NewMnemonic("newAccount2", keyring.English, sdk.FullFundraiserPath, keyring.DefaultBIP39Passphrase, hd.Secp256k1) 525 s.Require().NoError(err) 526 527 multi := kmultisig.NewLegacyAminoPubKey(2, []cryptotypes.PubKey{account1.GetPubKey(), account2.GetPubKey()}) 528 _, err = kr.SaveMultisig("multi", multi) 529 s.Require().NoError(err) 530 531 _, err = s.network.WaitForHeight(1) 532 s.Require().NoError(err) 533 534 multisigInfo, err := val1.ClientCtx.Keyring.Key("multi") 535 s.Require().NoError(err) 536 537 height, err := s.network.LatestHeight() 538 _, err = s.network.WaitForHeight(height + 1) 539 s.Require().NoError(err) 540 541 // Send coins from validator to multisig. 542 coins := sdk.NewInt64Coin(s.cfg.BondDenom, 15) 543 _, err = bankcli.MsgSendExec( 544 val1.ClientCtx, 545 val1.Address, 546 multisigInfo.GetAddress(), 547 sdk.NewCoins(coins), 548 fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), 549 fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), 550 fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), 551 fmt.Sprintf("--gas=%d", flags.DefaultGasLimit), 552 ) 553 554 height, err = s.network.LatestHeight() 555 _, err = s.network.WaitForHeight(height + 1) 556 s.Require().NoError(err) 557 558 // Generate multisig transaction. 559 multiGeneratedTx, err := bankcli.MsgSendExec( 560 val1.ClientCtx, 561 multisigInfo.GetAddress(), 562 val1.Address, 563 sdk.NewCoins( 564 sdk.NewInt64Coin(s.cfg.BondDenom, 5), 565 ), 566 fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), 567 fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), 568 fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), 569 fmt.Sprintf("--%s=true", flags.FlagGenerateOnly), 570 fmt.Sprintf("--%s=foobar", flags.FlagNote), 571 ) 572 s.Require().NoError(err) 573 574 // Save tx to file 575 multiGeneratedTxFile := testutil.WriteToNewTempFile(s.T(), multiGeneratedTx.String()) 576 577 // Sign with account1 578 val1.ClientCtx.HomeDir = strings.Replace(val1.ClientCtx.HomeDir, "simd", "simcli", 1) 579 account1Signature, err := authtest.TxSignExec(val1.ClientCtx, account1.GetAddress(), multiGeneratedTxFile.Name(), "--multisig", multisigInfo.GetAddress().String()) 580 s.Require().NoError(err) 581 sign1File := testutil.WriteToNewTempFile(s.T(), account1Signature.String()) 582 583 // Sign with account2 584 account2Signature, err := authtest.TxSignExec(val1.ClientCtx, account2.GetAddress(), multiGeneratedTxFile.Name(), "--multisig", multisigInfo.GetAddress().String()) 585 s.Require().NoError(err) 586 sign2File := testutil.WriteToNewTempFile(s.T(), account2Signature.String()) 587 588 // multisign tx 589 val1.ClientCtx.Offline = false 590 multiSigWith2Signatures, err := authtest.TxMultiSignExec(val1.ClientCtx, multisigInfo.GetName(), multiGeneratedTxFile.Name(), sign1File.Name(), sign2File.Name()) 591 s.Require().NoError(err) 592 593 // convert from protoJSON to protoBinary for sim 594 sdkTx, err := val1.ClientCtx.TxConfig.TxJSONDecoder()(multiSigWith2Signatures.Bytes()) 595 txBytes, err := val1.ClientCtx.TxConfig.TxEncoder()(sdkTx) 596 597 // simulate tx 598 sim := &tx.SimulateRequest{TxBytes: txBytes} 599 res, err := s.queryClient.Simulate(context.Background(), sim) 600 s.Require().NoError(err) 601 602 // make sure gas was used 603 s.Require().Greater(res.GasInfo.GasUsed, uint64(0)) 604 } 605 606 func (s IntegrationTestSuite) TestGetBlockWithTxs_GRPC() { 607 testCases := []struct { 608 name string 609 req *tx.GetBlockWithTxsRequest 610 expErr bool 611 expErrMsg string 612 expTxsLen int 613 }{ 614 {"nil request", nil, true, "request cannot be nil", 0}, 615 {"empty request", &tx.GetBlockWithTxsRequest{}, true, "height must not be less than 1 or greater than the current height", 0}, 616 {"bad height", &tx.GetBlockWithTxsRequest{Height: 99999999}, true, "height must not be less than 1 or greater than the current height", 0}, 617 {"bad pagination", &tx.GetBlockWithTxsRequest{Height: s.txHeight, Pagination: &query.PageRequest{Offset: 1000, Limit: 100}}, true, "out of range", 0}, 618 {"good request", &tx.GetBlockWithTxsRequest{Height: s.txHeight}, false, "", 1}, 619 {"with pagination request", &tx.GetBlockWithTxsRequest{Height: s.txHeight, Pagination: &query.PageRequest{Offset: 0, Limit: 1}}, false, "", 1}, 620 {"page all request", &tx.GetBlockWithTxsRequest{Height: s.txHeight, Pagination: &query.PageRequest{Offset: 0, Limit: 100}}, false, "", 1}, 621 {"block with 0 tx", &tx.GetBlockWithTxsRequest{Height: s.txHeight - 1, Pagination: &query.PageRequest{Offset: 0, Limit: 100}}, false, "", 0}, 622 } 623 for _, tc := range testCases { 624 s.Run(tc.name, func() { 625 // Query the tx via gRPC. 626 grpcRes, err := s.queryClient.GetBlockWithTxs(context.Background(), tc.req) 627 if tc.expErr { 628 s.Require().Error(err) 629 s.Require().Contains(err.Error(), tc.expErrMsg) 630 } else { 631 s.Require().NoError(err) 632 if tc.expTxsLen > 0 { 633 s.Require().Equal("foobar", grpcRes.Txs[0].Body.Memo) 634 } 635 s.Require().Equal(grpcRes.Block.Header.Height, tc.req.Height) 636 if tc.req.Pagination != nil { 637 s.Require().LessOrEqual(len(grpcRes.Txs), int(tc.req.Pagination.Limit)) 638 } 639 } 640 }) 641 } 642 } 643 644 func (s IntegrationTestSuite) TestGetBlockWithTxs() { 645 srv := tx2.NewTxServer(client.Context{}, nil, nil) 646 647 _, err := srv.GetBlockWithTxs(context.Background(), nil) 648 s.Require().Contains(err.Error(), "request cannot be nil") 649 } 650 651 func (s IntegrationTestSuite) TestGetBlockWithTxs_GRPCGateway() { 652 val := s.network.Validators[0] 653 testCases := []struct { 654 name string 655 url string 656 expErr bool 657 expErrMsg string 658 }{ 659 { 660 "empty params", 661 fmt.Sprintf("%s/cosmos/tx/v1beta1/txs/block/0", val.APIAddress), 662 true, "height must not be less than 1 or greater than the current height", 663 }, 664 { 665 "bad height", 666 fmt.Sprintf("%s/cosmos/tx/v1beta1/txs/block/%d", val.APIAddress, 9999999), 667 true, "height must not be less than 1 or greater than the current height", 668 }, 669 { 670 "good request", 671 fmt.Sprintf("%s/cosmos/tx/v1beta1/txs/block/%d", val.APIAddress, s.txHeight), 672 false, "", 673 }, 674 } 675 for _, tc := range testCases { 676 s.Run(tc.name, func() { 677 res, err := rest.GetRequest(tc.url) 678 s.Require().NoError(err) 679 if tc.expErr { 680 s.Require().Contains(string(res), tc.expErrMsg) 681 } else { 682 var result tx.GetBlockWithTxsResponse 683 err = val.ClientCtx.Codec.UnmarshalJSON(res, &result) 684 s.Require().NoError(err) 685 s.Require().Equal("foobar", result.Txs[0].Body.Memo) 686 s.Require().Equal(result.Block.Header.Height, s.txHeight) 687 } 688 }) 689 } 690 } 691 692 func TestIntegrationTestSuite(t *testing.T) { 693 suite.Run(t, new(IntegrationTestSuite)) 694 } 695 696 func (s IntegrationTestSuite) mkTxBuilder() client.TxBuilder { 697 val := s.network.Validators[0] 698 s.Require().NoError(s.network.WaitForNextBlock()) 699 700 // prepare txBuilder with msg 701 txBuilder := val.ClientCtx.TxConfig.NewTxBuilder() 702 feeAmount := sdk.Coins{sdk.NewInt64Coin(s.cfg.BondDenom, 10)} 703 gasLimit := testdata.NewTestGasLimit() 704 s.Require().NoError( 705 txBuilder.SetMsgs(&banktypes.MsgSend{ 706 FromAddress: val.Address.String(), 707 ToAddress: val.Address.String(), 708 Amount: sdk.Coins{sdk.NewInt64Coin(s.cfg.BondDenom, 10)}, 709 }), 710 ) 711 txBuilder.SetFeeAmount(feeAmount) 712 txBuilder.SetGasLimit(gasLimit) 713 txBuilder.SetMemo("foobar") 714 715 // setup txFactory 716 txFactory := clienttx.Factory{}. 717 WithChainID(val.ClientCtx.ChainID). 718 WithKeybase(val.ClientCtx.Keyring). 719 WithTxConfig(val.ClientCtx.TxConfig). 720 WithSignMode(signing.SignMode_SIGN_MODE_DIRECT) 721 722 // Sign Tx. 723 err := authclient.SignTx(txFactory, val.ClientCtx, val.Moniker, txBuilder, false, true) 724 s.Require().NoError(err) 725 726 return txBuilder 727 } 728 729 // protoTxProvider is a type which can provide a proto transaction. It is a 730 // workaround to get access to the wrapper TxBuilder's method GetProtoTx(). 731 // Deprecated: It's only used for testing the deprecated Simulate gRPC endpoint 732 // using a proto Tx field. 733 type protoTxProvider interface { 734 GetProtoTx() *tx.Tx 735 } 736 737 // txBuilderToProtoTx converts a txBuilder into a proto tx.Tx. 738 // Deprecated: It's only used for testing the deprecated Simulate gRPC endpoint 739 // using a proto Tx field. 740 func txBuilderToProtoTx(txBuilder client.TxBuilder) (*tx.Tx, error) { // nolint 741 protoProvider, ok := txBuilder.(protoTxProvider) 742 if !ok { 743 return nil, sdkerrors.Wrapf(sdkerrors.ErrInvalidType, "expected proto tx builder, got %T", txBuilder) 744 } 745 746 return protoProvider.GetProtoTx(), nil 747 }