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