github.com/Finschia/finschia-sdk@v0.48.1/x/auth/client/testutil/suite.go (about) 1 package testutil 2 3 import ( 4 "context" 5 "encoding/base64" 6 "encoding/json" 7 "fmt" 8 "os" 9 "path/filepath" 10 "strings" 11 "testing" 12 13 "github.com/stretchr/testify/require" 14 "github.com/stretchr/testify/suite" 15 16 ostcli "github.com/Finschia/ostracon/libs/cli" 17 18 "github.com/Finschia/finschia-sdk/client" 19 "github.com/Finschia/finschia-sdk/client/flags" 20 "github.com/Finschia/finschia-sdk/crypto/hd" 21 "github.com/Finschia/finschia-sdk/crypto/keyring" 22 kmultisig "github.com/Finschia/finschia-sdk/crypto/keys/multisig" 23 cryptotypes "github.com/Finschia/finschia-sdk/crypto/types" 24 "github.com/Finschia/finschia-sdk/simapp" 25 "github.com/Finschia/finschia-sdk/testutil" 26 clitestutil "github.com/Finschia/finschia-sdk/testutil/cli" 27 "github.com/Finschia/finschia-sdk/testutil/network" 28 "github.com/Finschia/finschia-sdk/testutil/testdata" 29 sdk "github.com/Finschia/finschia-sdk/types" 30 "github.com/Finschia/finschia-sdk/types/tx" 31 "github.com/Finschia/finschia-sdk/types/tx/signing" 32 authcli "github.com/Finschia/finschia-sdk/x/auth/client/cli" 33 authtypes "github.com/Finschia/finschia-sdk/x/auth/types" 34 bankcli "github.com/Finschia/finschia-sdk/x/bank/client/testutil" 35 banktypes "github.com/Finschia/finschia-sdk/x/bank/types" 36 "github.com/Finschia/finschia-sdk/x/genutil/client/cli" 37 ) 38 39 type IntegrationTestSuite struct { 40 suite.Suite 41 42 cfg network.Config 43 network *network.Network 44 } 45 46 func NewIntegrationTestSuite(cfg network.Config) *IntegrationTestSuite { 47 return &IntegrationTestSuite{cfg: cfg} 48 } 49 50 func (s *IntegrationTestSuite) SetupSuite() { 51 s.T().Log("setting up integration test suite") 52 53 s.network = network.New(s.T(), s.cfg) 54 55 kb := s.network.Validators[0].ClientCtx.Keyring 56 _, _, err := kb.NewMnemonic("newAccount", keyring.English, sdk.FullFundraiserPath, keyring.DefaultBIP39Passphrase, hd.Secp256k1) 57 s.Require().NoError(err) 58 59 account1, _, err := kb.NewMnemonic("newAccount1", keyring.English, sdk.FullFundraiserPath, keyring.DefaultBIP39Passphrase, hd.Secp256k1) 60 s.Require().NoError(err) 61 62 account2, _, err := kb.NewMnemonic("newAccount2", keyring.English, sdk.FullFundraiserPath, keyring.DefaultBIP39Passphrase, hd.Secp256k1) 63 s.Require().NoError(err) 64 65 multi := kmultisig.NewLegacyAminoPubKey(2, []cryptotypes.PubKey{account1.GetPubKey(), account2.GetPubKey()}) 66 _, err = kb.SaveMultisig("multi", multi) 67 s.Require().NoError(err) 68 69 _, err = s.network.WaitForHeight(1) 70 s.Require().NoError(err) 71 } 72 73 func (s *IntegrationTestSuite) TearDownSuite() { 74 s.T().Log("tearing down integration test suite") 75 s.network.Cleanup() 76 } 77 78 func (s *IntegrationTestSuite) TestCLIValidateSignatures() { 79 val := s.network.Validators[0] 80 sendTokens := sdk.NewCoins( 81 sdk.NewCoin(fmt.Sprintf("%stoken", val.Moniker), sdk.NewInt(10)), 82 sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))) 83 84 res, err := s.createBankMsg(val, val.Address, sendTokens, 85 fmt.Sprintf("--%s=true", flags.FlagGenerateOnly)) 86 s.Require().NoError(err) 87 88 // write unsigned tx to file 89 unsignedTx := testutil.WriteToNewTempFile(s.T(), res.String()) 90 res, err = TxSignExec(val.ClientCtx, val.Address, unsignedTx.Name()) 91 s.Require().NoError(err) 92 signedTx, err := val.ClientCtx.TxConfig.TxJSONDecoder()(res.Bytes()) 93 s.Require().NoError(err) 94 95 signedTxFile := testutil.WriteToNewTempFile(s.T(), res.String()) 96 txBuilder, err := val.ClientCtx.TxConfig.WrapTxBuilder(signedTx) 97 s.Require().NoError(err) 98 _, err = TxValidateSignaturesExec(val.ClientCtx, signedTxFile.Name()) 99 s.Require().NoError(err) 100 101 txBuilder.SetMemo("MODIFIED TX") 102 bz, err := val.ClientCtx.TxConfig.TxJSONEncoder()(txBuilder.GetTx()) 103 s.Require().NoError(err) 104 105 modifiedTxFile := testutil.WriteToNewTempFile(s.T(), string(bz)) 106 107 _, err = TxValidateSignaturesExec(val.ClientCtx, modifiedTxFile.Name()) 108 s.Require().EqualError(err, "signatures validation failed") 109 } 110 111 func (s *IntegrationTestSuite) TestCLISignBatch() { 112 val := s.network.Validators[0] 113 sendTokens := sdk.NewCoins( 114 sdk.NewCoin(fmt.Sprintf("%stoken", val.Moniker), sdk.NewInt(10)), 115 sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10)), 116 ) 117 118 generatedStd, err := s.createBankMsg(val, val.Address, 119 sendTokens, fmt.Sprintf("--%s=true", flags.FlagGenerateOnly)) 120 s.Require().NoError(err) 121 122 outputFile := testutil.WriteToNewTempFile(s.T(), strings.Repeat(generatedStd.String(), 3)) 123 val.ClientCtx.HomeDir = strings.Replace(val.ClientCtx.HomeDir, "simd", "simcli", 1) 124 125 // sign-batch file - offline is set but account-number and sequence are not 126 _, err = TxSignBatchExec(val.ClientCtx, val.Address, outputFile.Name(), fmt.Sprintf("--%s=%s", flags.FlagChainID, val.ClientCtx.ChainID), "--offline") 127 s.Require().EqualError(err, "required flag(s) \"account-number\", \"sequence\" not set") 128 129 // sign-batch file 130 res, err := TxSignBatchExec(val.ClientCtx, val.Address, outputFile.Name(), fmt.Sprintf("--%s=%s", flags.FlagChainID, val.ClientCtx.ChainID)) 131 s.Require().NoError(err) 132 s.Require().Equal(3, len(strings.Split(strings.Trim(res.String(), "\n"), "\n"))) 133 134 // sign-batch file signature only 135 res, err = TxSignBatchExec(val.ClientCtx, val.Address, outputFile.Name(), fmt.Sprintf("--%s=%s", flags.FlagChainID, val.ClientCtx.ChainID), "--signature-only") 136 s.Require().NoError(err) 137 s.Require().Equal(3, len(strings.Split(strings.Trim(res.String(), "\n"), "\n"))) 138 139 // Sign batch malformed tx file. 140 malformedFile := testutil.WriteToNewTempFile(s.T(), fmt.Sprintf("%smalformed", generatedStd)) 141 _, err = TxSignBatchExec(val.ClientCtx, val.Address, malformedFile.Name(), fmt.Sprintf("--%s=%s", flags.FlagChainID, val.ClientCtx.ChainID)) 142 s.Require().Error(err) 143 144 // Sign batch malformed tx file signature only. 145 _, err = TxSignBatchExec(val.ClientCtx, val.Address, malformedFile.Name(), fmt.Sprintf("--%s=%s", flags.FlagChainID, val.ClientCtx.ChainID), "--signature-only") 146 s.Require().Error(err) 147 148 // make a txn to increase the sequence of sender 149 _, seq, err := val.ClientCtx.AccountRetriever.GetAccountNumberSequence(val.ClientCtx, val.Address) 150 s.Require().NoError(err) 151 152 account1, err := val.ClientCtx.Keyring.Key("newAccount1") 153 s.Require().NoError(err) 154 155 addr := account1.GetAddress() 156 s.Require().NoError(err) 157 158 // Send coins from validator to multisig. 159 _, err = s.createBankMsg( 160 val, 161 addr, 162 sdk.NewCoins(sdk.NewInt64Coin(s.cfg.BondDenom, 1000)), 163 ) 164 s.Require().NoError(err) 165 s.Require().NoError(s.network.WaitForNextBlock()) 166 167 // fetch the sequence after a tx, should be incremented. 168 _, seq1, err := val.ClientCtx.AccountRetriever.GetAccountNumberSequence(val.ClientCtx, val.Address) 169 s.Require().NoError(err) 170 s.Require().Equal(seq+1, seq1) 171 172 // signing sign-batch should start from the last sequence. 173 signed, err := TxSignBatchExec(val.ClientCtx, val.Address, outputFile.Name(), fmt.Sprintf("--%s=%s", flags.FlagChainID, val.ClientCtx.ChainID), "--signature-only") 174 s.Require().NoError(err) 175 signedTxs := strings.Split(strings.Trim(signed.String(), "\n"), "\n") 176 s.Require().GreaterOrEqual(len(signedTxs), 1) 177 178 sigs, err := s.cfg.TxConfig.UnmarshalSignatureJSON([]byte(signedTxs[0])) 179 s.Require().NoError(err) 180 s.Require().Equal(sigs[0].Sequence, seq1) 181 } 182 183 func (s *IntegrationTestSuite) TestCLISignAminoJSON() { 184 require := s.Require() 185 val1 := s.network.Validators[0] 186 txCfg := val1.ClientCtx.TxConfig 187 sendTokens := sdk.NewCoins( 188 sdk.NewCoin(fmt.Sprintf("%stoken", val1.Moniker), sdk.NewInt(10)), 189 sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10)), 190 ) 191 txBz, err := s.createBankMsg(val1, val1.Address, 192 sendTokens, fmt.Sprintf("--%s=true", flags.FlagGenerateOnly)) 193 require.NoError(err) 194 fileUnsigned := testutil.WriteToNewTempFile(s.T(), txBz.String()) 195 chainFlag := fmt.Sprintf("--%s=%s", flags.FlagChainID, val1.ClientCtx.ChainID) 196 sigOnlyFlag := "--signature-only" 197 signModeAminoFlag := "--sign-mode=amino-json" 198 199 // SIC! validators have same key names and same addresses as those registered in the keyring, 200 // BUT the keys are different! 201 valInfo, err := val1.ClientCtx.Keyring.Key(val1.Moniker) 202 require.NoError(err) 203 204 // query account info 205 queryResJSON, err := QueryAccountExec(val1.ClientCtx, val1.Address) 206 require.NoError(err) 207 var account authtypes.AccountI 208 require.NoError(val1.ClientCtx.Codec.UnmarshalInterfaceJSON(queryResJSON.Bytes(), &account)) 209 210 /**** test signature-only ****/ 211 res, err := TxSignExec(val1.ClientCtx, val1.Address, fileUnsigned.Name(), chainFlag, 212 sigOnlyFlag, signModeAminoFlag) 213 require.NoError(err) 214 checkSignatures(require, txCfg, res.Bytes(), valInfo.GetPubKey()) 215 sigs, err := txCfg.UnmarshalSignatureJSON(res.Bytes()) 216 require.NoError(err) 217 require.Equal(1, len(sigs)) 218 require.Equal(account.GetSequence(), sigs[0].Sequence) 219 220 /**** test full output ****/ 221 res, err = TxSignExec(val1.ClientCtx, val1.Address, fileUnsigned.Name(), chainFlag, signModeAminoFlag) 222 require.NoError(err) 223 224 // txCfg.UnmarshalSignatureJSON can't unmarshal a fragment of the signature, so we create this structure. 225 type txFragment struct { 226 Signatures []json.RawMessage 227 } 228 var txOut txFragment 229 err = json.Unmarshal(res.Bytes(), &txOut) 230 require.NoError(err) 231 require.Len(txOut.Signatures, 1) 232 233 /**** test file output ****/ 234 filenameSigned := filepath.Join(s.T().TempDir(), "test_sign_out.json") 235 fileFlag := fmt.Sprintf("--%s=%s", flags.FlagOutputDocument, filenameSigned) 236 _, err = TxSignExec(val1.ClientCtx, val1.Address, fileUnsigned.Name(), chainFlag, fileFlag, signModeAminoFlag) 237 require.NoError(err) 238 fContent, err := os.ReadFile(filenameSigned) 239 require.NoError(err) 240 require.Equal(res.String(), string(fContent)) 241 242 /**** try to append to the previously signed transaction ****/ 243 res, err = TxSignExec(val1.ClientCtx, val1.Address, filenameSigned, chainFlag, 244 sigOnlyFlag, signModeAminoFlag) 245 require.NoError(err) 246 checkSignatures(require, txCfg, res.Bytes(), valInfo.GetPubKey(), valInfo.GetPubKey()) 247 248 /**** try to overwrite the previously signed transaction ****/ 249 250 // We can't sign with other address, because the bank send message supports only one signer for a simple 251 // account. Changing the file is too much hacking, because TxDecoder returns sdk.Tx, which doesn't 252 // provide functionality to check / manage `auth_info`. 253 // Cases with different keys are are covered in unit tests of `tx.Sign`. 254 res, err = TxSignExec(val1.ClientCtx, val1.Address, filenameSigned, chainFlag, 255 sigOnlyFlag, "--overwrite", signModeAminoFlag) 256 require.NoError(err) 257 checkSignatures(require, txCfg, res.Bytes(), valInfo.GetPubKey()) 258 259 /**** test flagAmino ****/ 260 res, err = TxSignExec(val1.ClientCtx, val1.Address, filenameSigned, chainFlag, 261 "--amino=true", signModeAminoFlag) 262 require.NoError(err) 263 264 var txAmino authcli.BroadcastReq 265 err = val1.ClientCtx.LegacyAmino.UnmarshalJSON(res.Bytes(), &txAmino) 266 require.NoError(err) 267 require.Len(txAmino.Tx.Signatures, 2) 268 require.Equal(txAmino.Tx.Signatures[0].PubKey, valInfo.GetPubKey()) 269 require.Equal(txAmino.Tx.Signatures[1].PubKey, valInfo.GetPubKey()) 270 } 271 272 func checkSignatures(require *require.Assertions, txCfg client.TxConfig, output []byte, pks ...cryptotypes.PubKey) { 273 sigs, err := txCfg.UnmarshalSignatureJSON(output) 274 require.NoError(err, string(output)) 275 require.Len(sigs, len(pks)) 276 for i := range pks { 277 require.True(sigs[i].PubKey.Equals(pks[i]), "Pub key doesn't match. Got: %s, expected: %s, idx: %d", sigs[i].PubKey, pks[i], i) 278 require.NotEmpty(sigs[i].Data) 279 } 280 } 281 282 func (s *IntegrationTestSuite) TestCLIQueryTxCmdByHash() { 283 val := s.network.Validators[0] 284 285 account2, err := val.ClientCtx.Keyring.Key("newAccount2") 286 s.Require().NoError(err) 287 288 sendTokens := sdk.NewInt64Coin(s.cfg.BondDenom, 10) 289 290 // Send coins. 291 out, err := s.createBankMsg( 292 val, account2.GetAddress(), 293 sdk.NewCoins(sendTokens), 294 ) 295 s.Require().NoError(err) 296 297 var txRes sdk.TxResponse 298 s.Require().NoError(val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &txRes)) 299 s.Require().NoError(s.network.WaitForNextBlock()) 300 301 testCases := []struct { 302 name string 303 args []string 304 expectErr bool 305 rawLogContains string 306 }{ 307 { 308 "not enough args", 309 []string{}, 310 true, "", 311 }, 312 { 313 "with invalid hash", 314 []string{"somethinginvalid", fmt.Sprintf("--%s=json", ostcli.OutputFlag)}, 315 true, "", 316 }, 317 { 318 "with valid and not existing hash", 319 []string{"C7E7D3A86A17AB3A321172239F3B61357937AF0F25D9FA4D2F4DCCAD9B0D7747", fmt.Sprintf("--%s=json", ostcli.OutputFlag)}, 320 true, "", 321 }, 322 { 323 "happy case", 324 []string{txRes.TxHash, fmt.Sprintf("--%s=json", ostcli.OutputFlag)}, 325 false, 326 sdk.MsgTypeURL(&banktypes.MsgSend{}), 327 }, 328 } 329 330 for _, tc := range testCases { 331 tc := tc 332 s.Run(tc.name, func() { 333 cmd := authcli.QueryTxCmd() 334 clientCtx := val.ClientCtx 335 336 out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) 337 338 if tc.expectErr { 339 s.Require().Error(err) 340 s.Require().NotEqual("internal", err.Error()) 341 } else { 342 var result sdk.TxResponse 343 s.Require().NoError(val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &result)) 344 s.Require().NotNil(result.Height) 345 s.Require().Contains(result.RawLog, tc.rawLogContains) 346 } 347 }) 348 } 349 } 350 351 func (s *IntegrationTestSuite) TestCLIQueryTxCmdByEvents() { 352 val := s.network.Validators[0] 353 354 account2, err := val.ClientCtx.Keyring.Key("newAccount2") 355 s.Require().NoError(err) 356 357 sendTokens := sdk.NewInt64Coin(s.cfg.BondDenom, 10) 358 359 // Send coins. 360 out, err := s.createBankMsg( 361 val, account2.GetAddress(), 362 sdk.NewCoins(sendTokens), 363 ) 364 s.Require().NoError(err) 365 var txRes sdk.TxResponse 366 s.Require().NoError(val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &txRes)) 367 s.Require().NoError(s.network.WaitForNextBlock()) 368 369 // Query the tx by hash to get the inner tx. 370 out, err = clitestutil.ExecTestCLICmd(val.ClientCtx, authcli.QueryTxCmd(), []string{txRes.TxHash, fmt.Sprintf("--%s=json", ostcli.OutputFlag)}) 371 s.Require().NoError(err) 372 s.Require().NoError(val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &txRes)) 373 protoTx := txRes.GetTx().(*tx.Tx) 374 375 testCases := []struct { 376 name string 377 args []string 378 expectErr bool 379 expectErrStr string 380 }{ 381 { 382 "invalid --type", 383 []string{ 384 fmt.Sprintf("--type=%s", "foo"), 385 "bar", 386 fmt.Sprintf("--%s=json", ostcli.OutputFlag), 387 }, 388 true, "unknown --type value foo", 389 }, 390 { 391 "--type=acc_seq with no addr+seq", 392 []string{ 393 "--type=acc_seq", 394 "", 395 fmt.Sprintf("--%s=json", ostcli.OutputFlag), 396 }, 397 true, "`acc_seq` type takes an argument '<addr>/<seq>'", 398 }, 399 { 400 "non-existing addr+seq combo", 401 []string{ 402 "--type=acc_seq", 403 "foobar", 404 fmt.Sprintf("--%s=json", ostcli.OutputFlag), 405 }, 406 true, "found no txs matching given address and sequence combination", 407 }, 408 { 409 "addr+seq happy case", 410 []string{ 411 "--type=acc_seq", 412 fmt.Sprintf("%s/%d", val.Address, protoTx.AuthInfo.SignerInfos[0].Sequence), 413 fmt.Sprintf("--%s=json", ostcli.OutputFlag), 414 }, 415 false, "", 416 }, 417 { 418 "--type=signature with no signature", 419 []string{ 420 "--type=signature", 421 "", 422 fmt.Sprintf("--%s=json", ostcli.OutputFlag), 423 }, 424 true, "argument should be comma-separated signatures", 425 }, 426 { 427 "non-existing signatures", 428 []string{ 429 "--type=signature", 430 "foo", 431 fmt.Sprintf("--%s=json", ostcli.OutputFlag), 432 }, 433 true, "found no txs matching given signatures", 434 }, 435 { 436 "with --signatures happy case", 437 []string{ 438 "--type=signature", 439 base64.StdEncoding.EncodeToString(protoTx.Signatures[0]), 440 fmt.Sprintf("--%s=json", ostcli.OutputFlag), 441 }, 442 false, "", 443 }, 444 } 445 446 for _, tc := range testCases { 447 tc := tc 448 s.Run(tc.name, func() { 449 cmd := authcli.QueryTxCmd() 450 clientCtx := val.ClientCtx 451 452 out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) 453 if tc.expectErr { 454 s.Require().Error(err) 455 s.Require().Contains(err.Error(), tc.expectErrStr) 456 } else { 457 var result sdk.TxResponse 458 s.Require().NoError(val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &result)) 459 s.Require().NotNil(result.Height) 460 } 461 }) 462 } 463 } 464 465 func (s *IntegrationTestSuite) TestCLIQueryTxsCmdByEvents() { 466 val := s.network.Validators[0] 467 468 account2, err := val.ClientCtx.Keyring.Key("newAccount2") 469 s.Require().NoError(err) 470 471 sendTokens := sdk.NewInt64Coin(s.cfg.BondDenom, 10) 472 473 // Send coins. 474 out, err := s.createBankMsg( 475 val, account2.GetAddress(), 476 sdk.NewCoins(sendTokens), 477 ) 478 s.Require().NoError(err) 479 var txRes sdk.TxResponse 480 s.Require().NoError(val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &txRes)) 481 s.Require().NoError(s.network.WaitForNextBlock()) 482 483 // Query the tx by hash to get the inner tx. 484 out, err = clitestutil.ExecTestCLICmd(val.ClientCtx, authcli.QueryTxCmd(), []string{txRes.TxHash, fmt.Sprintf("--%s=json", ostcli.OutputFlag)}) 485 s.Require().NoError(err) 486 s.Require().NoError(val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &txRes)) 487 488 testCases := []struct { 489 name string 490 args []string 491 expectErr bool 492 expectEmpty bool 493 }{ 494 { 495 "fee event happy case", 496 []string{ 497 fmt.Sprintf("--events=tx.fee=%s", 498 sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), 499 fmt.Sprintf("--%s=json", ostcli.OutputFlag), 500 }, 501 false, 502 false, 503 }, 504 { 505 "no matching fee event", 506 []string{ 507 fmt.Sprintf("--events=tx.fee=%s", 508 sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(0))).String()), 509 fmt.Sprintf("--%s=json", ostcli.OutputFlag), 510 }, 511 false, 512 true, 513 }, 514 { 515 "wrong number of arguments", 516 []string{ 517 "extra", 518 fmt.Sprintf("--%s=json", ostcli.OutputFlag), 519 }, 520 true, 521 true, 522 }, 523 } 524 525 for _, tc := range testCases { 526 tc := tc 527 s.Run(tc.name, func() { 528 cmd := authcli.QueryTxsByEventsCmd() 529 clientCtx := val.ClientCtx 530 531 out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) 532 if tc.expectErr { 533 s.Require().Error(err) 534 return 535 } 536 s.Require().NoError(err) 537 538 var result sdk.SearchTxsResult 539 s.Require().NoError(val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &result)) 540 541 if tc.expectEmpty { 542 s.Require().Equal(0, len(result.Txs)) 543 } else { 544 s.Require().NotEqual(0, len(result.Txs)) 545 s.Require().NotNil(result.Txs[0]) 546 } 547 }) 548 } 549 } 550 551 func (s *IntegrationTestSuite) TestCLISendGenerateSignAndBroadcast() { 552 val1 := s.network.Validators[0] 553 554 account, err := val1.ClientCtx.Keyring.Key("newAccount") 555 s.Require().NoError(err) 556 557 sendTokens := sdk.NewCoin(s.cfg.BondDenom, sdk.TokensFromConsensusPower(10, sdk.DefaultPowerReduction)) 558 559 normalGeneratedTx, err := s.createBankMsg(val1, account.GetAddress(), 560 sdk.NewCoins(sendTokens), fmt.Sprintf("--%s=true", flags.FlagGenerateOnly)) 561 s.Require().NoError(err) 562 563 txCfg := val1.ClientCtx.TxConfig 564 565 normalGeneratedStdTx, err := txCfg.TxJSONDecoder()(normalGeneratedTx.Bytes()) 566 s.Require().NoError(err) 567 txBuilder, err := txCfg.WrapTxBuilder(normalGeneratedStdTx) 568 s.Require().NoError(err) 569 s.Require().Equal(txBuilder.GetTx().GetGas(), uint64(flags.DefaultGasLimit)) 570 s.Require().Equal(len(txBuilder.GetTx().GetMsgs()), 1) 571 sigs, err := txBuilder.GetTx().GetSignaturesV2() 572 s.Require().NoError(err) 573 s.Require().Equal(0, len(sigs)) 574 575 // Test generate sendTx with --gas=$amount 576 limitedGasGeneratedTx, err := s.createBankMsg(val1, account.GetAddress(), 577 sdk.NewCoins(sendTokens), fmt.Sprintf("--gas=%d", 100), 578 fmt.Sprintf("--%s=true", flags.FlagGenerateOnly), 579 ) 580 s.Require().NoError(err) 581 582 limitedGasStdTx, err := txCfg.TxJSONDecoder()(limitedGasGeneratedTx.Bytes()) 583 s.Require().NoError(err) 584 txBuilder, err = txCfg.WrapTxBuilder(limitedGasStdTx) 585 s.Require().NoError(err) 586 s.Require().Equal(txBuilder.GetTx().GetGas(), uint64(100)) 587 s.Require().Equal(len(txBuilder.GetTx().GetMsgs()), 1) 588 sigs, err = txBuilder.GetTx().GetSignaturesV2() 589 s.Require().NoError(err) 590 s.Require().Equal(0, len(sigs)) 591 592 resp, err := bankcli.QueryBalancesExec(val1.ClientCtx, val1.Address) 593 s.Require().NoError(err) 594 595 var balRes banktypes.QueryAllBalancesResponse 596 err = val1.ClientCtx.Codec.UnmarshalJSON(resp.Bytes(), &balRes) 597 s.Require().NoError(err) 598 startTokens := balRes.Balances.AmountOf(s.cfg.BondDenom) 599 600 // Test generate sendTx, estimate gas 601 finalGeneratedTx, err := s.createBankMsg(val1, account.GetAddress(), 602 sdk.NewCoins(sendTokens), fmt.Sprintf("--gas=%d", flags.DefaultGasLimit), 603 fmt.Sprintf("--%s=true", flags.FlagGenerateOnly)) 604 s.Require().NoError(err) 605 606 finalStdTx, err := txCfg.TxJSONDecoder()(finalGeneratedTx.Bytes()) 607 s.Require().NoError(err) 608 txBuilder, err = txCfg.WrapTxBuilder(finalStdTx) 609 s.Require().NoError(err) 610 s.Require().Equal(uint64(flags.DefaultGasLimit), txBuilder.GetTx().GetGas()) 611 s.Require().Equal(len(finalStdTx.GetMsgs()), 1) 612 613 // Write the output to disk 614 unsignedTxFile := testutil.WriteToNewTempFile(s.T(), finalGeneratedTx.String()) 615 616 // Test validate-signatures 617 res, err := TxValidateSignaturesExec(val1.ClientCtx, unsignedTxFile.Name()) 618 s.Require().EqualError(err, "signatures validation failed") 619 s.Require().True(strings.Contains(res.String(), fmt.Sprintf("Signers:\n 0: %v\n\nSignatures:\n\n", val1.Address.String()))) 620 621 // Test sign 622 623 // Does not work in offline mode 624 _, err = TxSignExec(val1.ClientCtx, val1.Address, unsignedTxFile.Name(), "--offline") 625 s.Require().EqualError(err, "required flag(s) \"account-number\", \"sequence\" not set") 626 627 // But works offline if we set account number and sequence 628 val1.ClientCtx.HomeDir = strings.Replace(val1.ClientCtx.HomeDir, "simd", "simcli", 1) 629 _, err = TxSignExec(val1.ClientCtx, val1.Address, unsignedTxFile.Name(), "--offline", "--account-number", "1", "--sequence", "1") 630 s.Require().NoError(err) 631 632 // Sign transaction 633 signedTx, err := TxSignExec(val1.ClientCtx, val1.Address, unsignedTxFile.Name()) 634 s.Require().NoError(err) 635 signedFinalTx, err := txCfg.TxJSONDecoder()(signedTx.Bytes()) 636 s.Require().NoError(err) 637 txBuilder, err = val1.ClientCtx.TxConfig.WrapTxBuilder(signedFinalTx) 638 s.Require().NoError(err) 639 s.Require().Equal(len(txBuilder.GetTx().GetMsgs()), 1) 640 sigs, err = txBuilder.GetTx().GetSignaturesV2() 641 s.Require().NoError(err) 642 s.Require().Equal(1, len(sigs)) 643 s.Require().Equal(val1.Address.String(), txBuilder.GetTx().GetSigners()[0].String()) 644 645 // Write the output to disk 646 signedTxFile := testutil.WriteToNewTempFile(s.T(), signedTx.String()) 647 648 // validate Signature 649 res, err = TxValidateSignaturesExec(val1.ClientCtx, signedTxFile.Name()) 650 s.Require().NoError(err) 651 s.Require().True(strings.Contains(res.String(), "[OK]")) 652 653 // Ensure foo has right amount of funds 654 resp, err = bankcli.QueryBalancesExec(val1.ClientCtx, val1.Address) 655 s.Require().NoError(err) 656 657 err = val1.ClientCtx.Codec.UnmarshalJSON(resp.Bytes(), &balRes) 658 s.Require().NoError(err) 659 s.Require().Equal(startTokens, balRes.Balances.AmountOf(s.cfg.BondDenom)) 660 661 // Test broadcast 662 663 // Does not work in offline mode 664 _, err = TxBroadcastExec(val1.ClientCtx, signedTxFile.Name(), "--offline") 665 s.Require().EqualError(err, "cannot broadcast tx during offline mode") 666 667 s.Require().NoError(s.network.WaitForNextBlock()) 668 669 // Broadcast correct transaction. 670 val1.ClientCtx.BroadcastMode = flags.BroadcastBlock 671 _, err = TxBroadcastExec(val1.ClientCtx, signedTxFile.Name()) 672 s.Require().NoError(err) 673 674 s.Require().NoError(s.network.WaitForNextBlock()) 675 676 // Ensure destiny account state 677 resp, err = bankcli.QueryBalancesExec(val1.ClientCtx, account.GetAddress()) 678 s.Require().NoError(err) 679 680 err = val1.ClientCtx.Codec.UnmarshalJSON(resp.Bytes(), &balRes) 681 s.Require().NoError(err) 682 s.Require().Equal(sendTokens.Amount, balRes.Balances.AmountOf(s.cfg.BondDenom)) 683 684 // Ensure origin account state 685 resp, err = bankcli.QueryBalancesExec(val1.ClientCtx, val1.Address) 686 s.Require().NoError(err) 687 688 err = val1.ClientCtx.Codec.UnmarshalJSON(resp.Bytes(), &balRes) 689 s.Require().NoError(err) 690 } 691 692 func (s *IntegrationTestSuite) TestCLIMultisignInsufficientCosigners() { 693 val1 := s.network.Validators[0] 694 695 // Fetch account and a multisig info 696 account1, err := val1.ClientCtx.Keyring.Key("newAccount1") 697 s.Require().NoError(err) 698 699 multisigInfo, err := val1.ClientCtx.Keyring.Key("multi") 700 s.Require().NoError(err) 701 702 // Send coins from validator to multisig. 703 _, err = s.createBankMsg( 704 val1, 705 multisigInfo.GetAddress(), 706 sdk.NewCoins( 707 sdk.NewInt64Coin(s.cfg.BondDenom, 10), 708 ), 709 ) 710 s.Require().NoError(err) 711 712 s.Require().NoError(s.network.WaitForNextBlock()) 713 714 // Generate multisig transaction. 715 multiGeneratedTx, err := bankcli.MsgSendExec( 716 val1.ClientCtx, 717 multisigInfo.GetAddress(), 718 val1.Address, 719 sdk.NewCoins( 720 sdk.NewInt64Coin(s.cfg.BondDenom, 5), 721 ), 722 fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), 723 fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), 724 fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), 725 fmt.Sprintf("--%s=true", flags.FlagGenerateOnly), 726 ) 727 s.Require().NoError(err) 728 729 // Save tx to file 730 multiGeneratedTxFile := testutil.WriteToNewTempFile(s.T(), multiGeneratedTx.String()) 731 732 // Multisign, sign with one signature 733 val1.ClientCtx.HomeDir = strings.Replace(val1.ClientCtx.HomeDir, "simd", "simcli", 1) 734 account1Signature, err := TxSignExec(val1.ClientCtx, account1.GetAddress(), multiGeneratedTxFile.Name(), "--multisig", multisigInfo.GetAddress().String()) 735 s.Require().NoError(err) 736 737 sign1File := testutil.WriteToNewTempFile(s.T(), account1Signature.String()) 738 739 multiSigWith1Signature, err := TxMultiSignExec(val1.ClientCtx, multisigInfo.GetName(), multiGeneratedTxFile.Name(), sign1File.Name()) 740 s.Require().NoError(err) 741 742 // Save tx to file 743 multiSigWith1SignatureFile := testutil.WriteToNewTempFile(s.T(), multiSigWith1Signature.String()) 744 745 _, err = TxValidateSignaturesExec(val1.ClientCtx, multiSigWith1SignatureFile.Name()) 746 s.Require().Error(err) 747 } 748 749 func (s *IntegrationTestSuite) TestCLIEncode() { 750 val1 := s.network.Validators[0] 751 752 sendTokens := sdk.NewCoin(s.cfg.BondDenom, sdk.TokensFromConsensusPower(10, sdk.DefaultPowerReduction)) 753 754 normalGeneratedTx, err := s.createBankMsg( 755 val1, val1.Address, 756 sdk.NewCoins(sendTokens), 757 fmt.Sprintf("--%s=true", flags.FlagGenerateOnly), 758 fmt.Sprintf("--%s=deadbeef", flags.FlagNote), 759 ) 760 s.Require().NoError(err) 761 savedTxFile := testutil.WriteToNewTempFile(s.T(), normalGeneratedTx.String()) 762 763 // Encode 764 encodeExec, err := TxEncodeExec(val1.ClientCtx, savedTxFile.Name()) 765 s.Require().NoError(err) 766 trimmedBase64 := strings.Trim(encodeExec.String(), "\"\n") 767 768 // Check that the transaction decodes as expected 769 decodedTx, err := TxDecodeExec(val1.ClientCtx, trimmedBase64) 770 s.Require().NoError(err) 771 772 txCfg := val1.ClientCtx.TxConfig 773 theTx, err := txCfg.TxJSONDecoder()(decodedTx.Bytes()) 774 s.Require().NoError(err) 775 txBuilder, err := val1.ClientCtx.TxConfig.WrapTxBuilder(theTx) 776 s.Require().NoError(err) 777 s.Require().Equal("deadbeef", txBuilder.GetTx().GetMemo()) 778 } 779 780 func (s *IntegrationTestSuite) TestCLIMultisignSortSignatures() { 781 val1 := s.network.Validators[0] 782 783 // Generate 2 accounts and a multisig. 784 account1, err := val1.ClientCtx.Keyring.Key("newAccount1") 785 s.Require().NoError(err) 786 787 account2, err := val1.ClientCtx.Keyring.Key("newAccount2") 788 s.Require().NoError(err) 789 790 multisigInfo, err := val1.ClientCtx.Keyring.Key("multi") 791 s.Require().NoError(err) 792 793 resp, err := bankcli.QueryBalancesExec(val1.ClientCtx, multisigInfo.GetAddress()) 794 s.Require().NoError(err) 795 796 var balRes banktypes.QueryAllBalancesResponse 797 err = val1.ClientCtx.Codec.UnmarshalJSON(resp.Bytes(), &balRes) 798 s.Require().NoError(err) 799 intialCoins := balRes.Balances 800 801 // Send coins from validator to multisig. 802 sendTokens := sdk.NewInt64Coin(s.cfg.BondDenom, 10) 803 _, err = s.createBankMsg( 804 val1, 805 multisigInfo.GetAddress(), 806 sdk.NewCoins(sendTokens), 807 ) 808 s.Require().NoError(err) 809 810 s.Require().NoError(s.network.WaitForNextBlock()) 811 812 resp, err = bankcli.QueryBalancesExec(val1.ClientCtx, multisigInfo.GetAddress()) 813 s.Require().NoError(err) 814 815 err = val1.ClientCtx.Codec.UnmarshalJSON(resp.Bytes(), &balRes) 816 s.Require().NoError(err) 817 diff, _ := balRes.Balances.SafeSub(intialCoins) 818 s.Require().Equal(sendTokens.Amount, diff.AmountOf(s.cfg.BondDenom)) 819 820 // Generate multisig transaction. 821 multiGeneratedTx, err := bankcli.MsgSendExec( 822 val1.ClientCtx, 823 multisigInfo.GetAddress(), 824 val1.Address, 825 sdk.NewCoins( 826 sdk.NewInt64Coin(s.cfg.BondDenom, 5), 827 ), 828 fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), 829 fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), 830 fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), 831 fmt.Sprintf("--%s=true", flags.FlagGenerateOnly), 832 ) 833 s.Require().NoError(err) 834 835 // Save tx to file 836 multiGeneratedTxFile := testutil.WriteToNewTempFile(s.T(), multiGeneratedTx.String()) 837 838 // Sign with account1 839 val1.ClientCtx.HomeDir = strings.Replace(val1.ClientCtx.HomeDir, "simd", "simcli", 1) 840 account1Signature, err := TxSignExec(val1.ClientCtx, account1.GetAddress(), multiGeneratedTxFile.Name(), "--multisig", multisigInfo.GetAddress().String()) 841 s.Require().NoError(err) 842 843 sign1File := testutil.WriteToNewTempFile(s.T(), account1Signature.String()) 844 845 // Sign with account1 846 account2Signature, err := TxSignExec(val1.ClientCtx, account2.GetAddress(), multiGeneratedTxFile.Name(), "--multisig", multisigInfo.GetAddress().String()) 847 s.Require().NoError(err) 848 849 sign2File := testutil.WriteToNewTempFile(s.T(), account2Signature.String()) 850 851 multiSigWith2Signatures, err := TxMultiSignExec(val1.ClientCtx, multisigInfo.GetName(), multiGeneratedTxFile.Name(), sign1File.Name(), sign2File.Name()) 852 s.Require().NoError(err) 853 854 // Write the output to disk 855 signedTxFile := testutil.WriteToNewTempFile(s.T(), multiSigWith2Signatures.String()) 856 857 _, err = TxValidateSignaturesExec(val1.ClientCtx, signedTxFile.Name()) 858 s.Require().NoError(err) 859 860 val1.ClientCtx.BroadcastMode = flags.BroadcastBlock 861 _, err = TxBroadcastExec(val1.ClientCtx, signedTxFile.Name()) 862 s.Require().NoError(err) 863 864 s.Require().NoError(s.network.WaitForNextBlock()) 865 } 866 867 func (s *IntegrationTestSuite) TestSignWithMultisig() { 868 val1 := s.network.Validators[0] 869 870 // Generate a account for signing. 871 account1, err := val1.ClientCtx.Keyring.Key("newAccount1") 872 s.Require().NoError(err) 873 874 addr1 := account1.GetAddress() 875 s.Require().NoError(err) 876 877 // Create an address that is not in the keyring, will be used to simulate `--multisig` 878 multisig := "link1hd6fsrvnz6qkp87s3u86ludegq97agxsccwqll" 879 multisigAddr, err := sdk.AccAddressFromBech32(multisig) 880 s.Require().NoError(err) 881 882 // Generate a transaction for testing --multisig with an address not in the keyring. 883 multisigTx, err := bankcli.MsgSendExec( 884 val1.ClientCtx, 885 val1.Address, 886 val1.Address, 887 sdk.NewCoins( 888 sdk.NewInt64Coin(s.cfg.BondDenom, 5), 889 ), 890 fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), 891 fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), 892 fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), 893 fmt.Sprintf("--%s=true", flags.FlagGenerateOnly), 894 ) 895 s.Require().NoError(err) 896 897 // Save multi tx to file 898 multiGeneratedTx2File := testutil.WriteToNewTempFile(s.T(), multisigTx.String()) 899 900 // Sign using multisig. We're signing a tx on behalf of the multisig address, 901 // even though the tx signer is NOT the multisig address. This is fine though, 902 // as the main point of this test is to test the `--multisig` flag with an address 903 // that is not in the keyring. 904 _, err = TxSignExec(val1.ClientCtx, addr1, multiGeneratedTx2File.Name(), "--multisig", multisigAddr.String()) 905 s.Require().Contains(err.Error(), "tx intended signer does not match the given signer") 906 } 907 908 func (s *IntegrationTestSuite) TestCLIMultisign() { 909 val1 := s.network.Validators[0] 910 911 // Generate 2 accounts and a multisig. 912 account1, err := val1.ClientCtx.Keyring.Key("newAccount1") 913 s.Require().NoError(err) 914 915 account2, err := val1.ClientCtx.Keyring.Key("newAccount2") 916 s.Require().NoError(err) 917 918 multisigInfo, err := val1.ClientCtx.Keyring.Key("multi") 919 s.Require().NoError(err) 920 921 // Send coins from validator to multisig. 922 sendTokens := sdk.NewInt64Coin(s.cfg.BondDenom, 10) 923 _, err = s.createBankMsg( 924 val1, multisigInfo.GetAddress(), 925 sdk.NewCoins(sendTokens), 926 ) 927 s.Require().NoError(err) 928 s.Require().NoError(s.network.WaitForNextBlock()) 929 930 resp, err := bankcli.QueryBalancesExec(val1.ClientCtx, multisigInfo.GetAddress()) 931 s.Require().NoError(err) 932 933 var balRes banktypes.QueryAllBalancesResponse 934 err = val1.ClientCtx.Codec.UnmarshalJSON(resp.Bytes(), &balRes) 935 s.Require().NoError(err) 936 s.Require().Equal(sendTokens.Amount, balRes.Balances.AmountOf(s.cfg.BondDenom)) 937 938 // Generate multisig transaction. 939 multiGeneratedTx, err := bankcli.MsgSendExec( 940 val1.ClientCtx, 941 multisigInfo.GetAddress(), 942 val1.Address, 943 sdk.NewCoins( 944 sdk.NewInt64Coin(s.cfg.BondDenom, 5), 945 ), 946 fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), 947 fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), 948 fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), 949 fmt.Sprintf("--%s=true", flags.FlagGenerateOnly), 950 ) 951 s.Require().NoError(err) 952 953 // Save tx to file 954 multiGeneratedTxFile := testutil.WriteToNewTempFile(s.T(), multiGeneratedTx.String()) 955 956 // Sign with account1 957 val1.ClientCtx.HomeDir = strings.Replace(val1.ClientCtx.HomeDir, "simd", "simcli", 1) 958 account1Signature, err := TxSignExec(val1.ClientCtx, account1.GetAddress(), multiGeneratedTxFile.Name(), "--multisig", multisigInfo.GetAddress().String()) 959 s.Require().NoError(err) 960 961 sign1File := testutil.WriteToNewTempFile(s.T(), account1Signature.String()) 962 963 // Sign with account2 964 account2Signature, err := TxSignExec(val1.ClientCtx, account2.GetAddress(), multiGeneratedTxFile.Name(), "--multisig", multisigInfo.GetAddress().String()) 965 s.Require().NoError(err) 966 967 sign2File := testutil.WriteToNewTempFile(s.T(), account2Signature.String()) 968 969 // Does not work in offline mode. 970 _, err = TxMultiSignExec(val1.ClientCtx, multisigInfo.GetName(), multiGeneratedTxFile.Name(), "--offline", sign1File.Name(), sign2File.Name()) 971 s.Require().EqualError(err, fmt.Sprintf("couldn't verify signature for address %s", account1.GetAddress())) 972 973 val1.ClientCtx.Offline = false 974 multiSigWith2Signatures, err := TxMultiSignExec(val1.ClientCtx, multisigInfo.GetName(), multiGeneratedTxFile.Name(), sign1File.Name(), sign2File.Name()) 975 s.Require().NoError(err) 976 977 // Write the output to disk 978 signedTxFile := testutil.WriteToNewTempFile(s.T(), multiSigWith2Signatures.String()) 979 980 _, err = TxValidateSignaturesExec(val1.ClientCtx, signedTxFile.Name()) 981 s.Require().NoError(err) 982 983 val1.ClientCtx.BroadcastMode = flags.BroadcastBlock 984 _, err = TxBroadcastExec(val1.ClientCtx, signedTxFile.Name()) 985 s.Require().NoError(err) 986 987 s.Require().NoError(s.network.WaitForNextBlock()) 988 } 989 990 func (s *IntegrationTestSuite) TestSignBatchMultisig() { 991 val := s.network.Validators[0] 992 993 // Fetch 2 accounts and a multisig. 994 account1, err := val.ClientCtx.Keyring.Key("newAccount1") 995 s.Require().NoError(err) 996 account2, err := val.ClientCtx.Keyring.Key("newAccount2") 997 s.Require().NoError(err) 998 multisigInfo, err := val.ClientCtx.Keyring.Key("multi") 999 s.Require().NoError(err) 1000 1001 // Send coins from validator to multisig. 1002 sendTokens := sdk.NewInt64Coin(s.cfg.BondDenom, 10) 1003 _, err = s.createBankMsg( 1004 val, 1005 multisigInfo.GetAddress(), 1006 sdk.NewCoins(sendTokens), 1007 ) 1008 s.Require().NoError(err) 1009 s.Require().NoError(s.network.WaitForNextBlock()) 1010 1011 generatedStd, err := bankcli.MsgSendExec( 1012 val.ClientCtx, 1013 multisigInfo.GetAddress(), 1014 val.Address, 1015 sdk.NewCoins( 1016 sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(1)), 1017 ), 1018 fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), 1019 fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), 1020 fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), 1021 fmt.Sprintf("--%s=true", flags.FlagGenerateOnly), 1022 ) 1023 s.Require().NoError(err) 1024 1025 // Write the output to disk 1026 filename := testutil.WriteToNewTempFile(s.T(), strings.Repeat(generatedStd.String(), 1)) 1027 val.ClientCtx.HomeDir = strings.Replace(val.ClientCtx.HomeDir, "simd", "simcli", 1) 1028 1029 // sign-batch file 1030 res, err := TxSignBatchExec(val.ClientCtx, account1.GetAddress(), filename.Name(), fmt.Sprintf("--%s=%s", flags.FlagChainID, val.ClientCtx.ChainID), "--multisig", multisigInfo.GetAddress().String()) 1031 s.Require().NoError(err) 1032 s.Require().Equal(1, len(strings.Split(strings.Trim(res.String(), "\n"), "\n"))) 1033 // write sigs to file 1034 file1 := testutil.WriteToNewTempFile(s.T(), res.String()) 1035 1036 // sign-batch file with account2 1037 res, err = TxSignBatchExec(val.ClientCtx, account2.GetAddress(), filename.Name(), fmt.Sprintf("--%s=%s", flags.FlagChainID, val.ClientCtx.ChainID), "--multisig", multisigInfo.GetAddress().String()) 1038 s.Require().NoError(err) 1039 s.Require().Equal(1, len(strings.Split(strings.Trim(res.String(), "\n"), "\n"))) 1040 // write sigs to file2 1041 file2 := testutil.WriteToNewTempFile(s.T(), res.String()) 1042 1043 // sign-batch file with multisig key name 1044 res, err = TxSignBatchExec(val.ClientCtx, account1.GetAddress(), filename.Name(), fmt.Sprintf("--%s=%s", flags.FlagChainID, val.ClientCtx.ChainID), "--multisig", multisigInfo.GetName()) 1045 s.Require().NoError(err) 1046 s.Require().Equal(1, len(strings.Split(strings.Trim(res.String(), "\n"), "\n"))) 1047 // write sigs to file3 1048 file3 := testutil.WriteToNewTempFile(s.T(), res.String()) 1049 1050 _, err = TxMultiSignExec(val.ClientCtx, multisigInfo.GetName(), filename.Name(), file1.Name(), file2.Name(), file3.Name()) 1051 s.Require().NoError(err) 1052 } 1053 1054 func (s *IntegrationTestSuite) TestMultisignBatch() { 1055 val := s.network.Validators[0] 1056 1057 // Fetch 2 accounts and a multisig. 1058 account1, err := val.ClientCtx.Keyring.Key("newAccount1") 1059 s.Require().NoError(err) 1060 account2, err := val.ClientCtx.Keyring.Key("newAccount2") 1061 s.Require().NoError(err) 1062 multisigInfo, err := val.ClientCtx.Keyring.Key("multi") 1063 s.Require().NoError(err) 1064 1065 // Send coins from validator to multisig. 1066 sendTokens := sdk.NewInt64Coin(s.cfg.BondDenom, 1000) 1067 _, err = s.createBankMsg( 1068 val, 1069 multisigInfo.GetAddress(), 1070 sdk.NewCoins(sendTokens), 1071 ) 1072 s.Require().NoError(err) 1073 s.Require().NoError(s.network.WaitForNextBlock()) 1074 1075 generatedStd, err := bankcli.MsgSendExec( 1076 val.ClientCtx, 1077 multisigInfo.GetAddress(), 1078 val.Address, 1079 sdk.NewCoins( 1080 sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(1)), 1081 ), 1082 fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), 1083 fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), 1084 fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), 1085 fmt.Sprintf("--%s=true", flags.FlagGenerateOnly), 1086 ) 1087 s.Require().NoError(err) 1088 1089 // Write the output to disk 1090 filename := testutil.WriteToNewTempFile(s.T(), strings.Repeat(generatedStd.String(), 3)) 1091 val.ClientCtx.HomeDir = strings.Replace(val.ClientCtx.HomeDir, "simd", "simcli", 1) 1092 1093 queryResJSON, err := QueryAccountExec(val.ClientCtx, multisigInfo.GetAddress()) 1094 s.Require().NoError(err) 1095 var account authtypes.AccountI 1096 s.Require().NoError(val.ClientCtx.Codec.UnmarshalInterfaceJSON(queryResJSON.Bytes(), &account)) 1097 1098 // sign-batch file 1099 res, err := TxSignBatchExec(val.ClientCtx, account1.GetAddress(), filename.Name(), fmt.Sprintf("--%s=%s", flags.FlagChainID, val.ClientCtx.ChainID), "--multisig", multisigInfo.GetAddress().String(), fmt.Sprintf("--%s", flags.FlagOffline), fmt.Sprintf("--%s=%s", flags.FlagAccountNumber, fmt.Sprint(account.GetAccountNumber())), fmt.Sprintf("--%s=%s", flags.FlagSequence, fmt.Sprint(account.GetSequence()))) 1100 s.Require().NoError(err) 1101 s.Require().Equal(3, len(strings.Split(strings.Trim(res.String(), "\n"), "\n"))) 1102 // write sigs to file 1103 file1 := testutil.WriteToNewTempFile(s.T(), res.String()) 1104 1105 // sign-batch file with account2 1106 res, err = TxSignBatchExec(val.ClientCtx, account2.GetAddress(), filename.Name(), fmt.Sprintf("--%s=%s", flags.FlagChainID, val.ClientCtx.ChainID), "--multisig", multisigInfo.GetAddress().String(), fmt.Sprintf("--%s", flags.FlagOffline), fmt.Sprintf("--%s=%s", flags.FlagAccountNumber, fmt.Sprint(account.GetAccountNumber())), fmt.Sprintf("--%s=%s", flags.FlagSequence, fmt.Sprint(account.GetSequence()))) 1107 s.Require().NoError(err) 1108 s.Require().Equal(3, len(strings.Split(strings.Trim(res.String(), "\n"), "\n"))) 1109 1110 // multisign the file 1111 file2 := testutil.WriteToNewTempFile(s.T(), res.String()) 1112 1113 // sign-batch file with multisig key name 1114 res, err = TxSignBatchExec(val.ClientCtx, account1.GetAddress(), filename.Name(), fmt.Sprintf("--%s=%s", flags.FlagChainID, val.ClientCtx.ChainID), "--multisig", multisigInfo.GetName(), fmt.Sprintf("--%s", flags.FlagOffline), fmt.Sprintf("--%s=%s", flags.FlagAccountNumber, fmt.Sprint(account.GetAccountNumber())), fmt.Sprintf("--%s=%s", flags.FlagSequence, fmt.Sprint(account.GetSequence()))) 1115 s.Require().NoError(err) 1116 s.Require().Equal(3, len(strings.Split(strings.Trim(res.String(), "\n"), "\n"))) 1117 // write sigs to file 1118 file3 := testutil.WriteToNewTempFile(s.T(), res.String()) 1119 1120 res, err = TxMultiSignBatchExec(val.ClientCtx, filename.Name(), multisigInfo.GetName(), file1.Name(), file2.Name(), file3.Name()) 1121 s.Require().NoError(err) 1122 signedTxs := strings.Split(strings.Trim(res.String(), "\n"), "\n") 1123 1124 // Broadcast transactions. 1125 for _, signedTx := range signedTxs { 1126 signedTxFile := testutil.WriteToNewTempFile(s.T(), signedTx) 1127 val.ClientCtx.BroadcastMode = flags.BroadcastBlock 1128 _, err = TxBroadcastExec(val.ClientCtx, signedTxFile.Name()) 1129 s.Require().NoError(err) 1130 s.Require().NoError(s.network.WaitForNextBlock()) 1131 } 1132 } 1133 1134 func (s *IntegrationTestSuite) TestGetAccountCmd() { 1135 val := s.network.Validators[0] 1136 _, _, addr1 := testdata.KeyTestPubAddr() 1137 1138 testCases := []struct { 1139 name string 1140 address sdk.AccAddress 1141 expectErr bool 1142 }{ 1143 { 1144 "invalid address", 1145 addr1, 1146 true, 1147 }, 1148 { 1149 "valid address", 1150 val.Address, 1151 false, 1152 }, 1153 } 1154 1155 for _, tc := range testCases { 1156 tc := tc 1157 s.Run(tc.name, func() { 1158 clientCtx := val.ClientCtx 1159 1160 out, err := QueryAccountExec(clientCtx, tc.address) 1161 if tc.expectErr { 1162 s.Require().Error(err) 1163 s.Require().NotEqual("internal", err.Error()) 1164 } else { 1165 var acc authtypes.AccountI 1166 s.Require().NoError(val.ClientCtx.Codec.UnmarshalInterfaceJSON(out.Bytes(), &acc)) 1167 s.Require().Equal(val.Address, acc.GetAddress()) 1168 } 1169 }) 1170 } 1171 } 1172 1173 func (s *IntegrationTestSuite) TestGetAccountsCmd() { 1174 val := s.network.Validators[0] 1175 1176 commonArgs := []string{ 1177 fmt.Sprintf("--%s=json", ostcli.OutputFlag), 1178 } 1179 1180 testCases := map[string]struct { 1181 args []string 1182 valid bool 1183 }{ 1184 "valid request": { 1185 valid: true, 1186 }, 1187 "wrong number of args": { 1188 args: []string{ 1189 "extra", 1190 }, 1191 }, 1192 } 1193 1194 for name, tc := range testCases { 1195 tc := tc 1196 s.Run(name, func() { 1197 cmd := authcli.GetAccountsCmd() 1198 clientCtx := val.ClientCtx 1199 1200 out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, append(tc.args, commonArgs...)) 1201 if !tc.valid { 1202 s.Require().Error(err) 1203 return 1204 } 1205 s.Require().NoError(err) 1206 1207 var res authtypes.QueryAccountsResponse 1208 s.Require().NoError(val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &res)) 1209 s.Require().NotEmpty(res.Accounts) 1210 }) 1211 } 1212 } 1213 1214 func (s *IntegrationTestSuite) TestQueryModuleAccountByNameCmd() { 1215 val := s.network.Validators[0] 1216 1217 testCases := []struct { 1218 name string 1219 moduleName string 1220 expectErr bool 1221 }{ 1222 { 1223 "invalid module name", 1224 "gover", 1225 true, 1226 }, 1227 { 1228 "valid module name", 1229 "mint", 1230 false, 1231 }, 1232 } 1233 1234 for _, tc := range testCases { 1235 tc := tc 1236 s.Run(tc.name, func() { 1237 clientCtx := val.ClientCtx 1238 1239 out, err := clitestutil.ExecTestCLICmd(clientCtx, authcli.QueryModuleAccountByNameCmd(), []string{ 1240 tc.moduleName, 1241 fmt.Sprintf("--%s=json", ostcli.OutputFlag), 1242 }) 1243 if tc.expectErr { 1244 s.Require().Error(err) 1245 s.Require().NotEqual("internal", err.Error()) 1246 } else { 1247 var res authtypes.QueryModuleAccountByNameResponse 1248 s.Require().NoError(val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &res)) 1249 1250 var account authtypes.AccountI 1251 err := val.ClientCtx.InterfaceRegistry.UnpackAny(res.Account, &account) 1252 s.Require().NoError(err) 1253 1254 moduleAccount, ok := account.(authtypes.ModuleAccountI) 1255 s.Require().True(ok) 1256 s.Require().Equal(tc.moduleName, moduleAccount.GetName()) 1257 } 1258 }) 1259 } 1260 } 1261 1262 func TestGetBroadcastCommandOfflineFlag(t *testing.T) { 1263 clientCtx := client.Context{}.WithOffline(true) 1264 clientCtx = clientCtx.WithTxConfig(simapp.MakeTestEncodingConfig().TxConfig) //nolint:staticcheck 1265 1266 cmd := authcli.GetBroadcastCommand() 1267 _ = testutil.ApplyMockIODiscardOutErr(cmd) 1268 cmd.SetArgs([]string{fmt.Sprintf("--%s=true", flags.FlagOffline), ""}) 1269 1270 require.EqualError(t, cmd.Execute(), "cannot broadcast tx during offline mode") 1271 } 1272 1273 func TestGetBroadcastCommandWithoutOfflineFlag(t *testing.T) { 1274 clientCtx := client.Context{} 1275 txCfg := simapp.MakeTestEncodingConfig().TxConfig 1276 clientCtx = clientCtx.WithTxConfig(txCfg) 1277 1278 ctx := context.Background() 1279 ctx = context.WithValue(ctx, client.ClientContextKey, &clientCtx) 1280 1281 cmd := authcli.GetBroadcastCommand() 1282 _, out := testutil.ApplyMockIO(cmd) 1283 1284 // Create new file with tx 1285 builder := txCfg.NewTxBuilder() 1286 builder.SetGasLimit(200000) 1287 from, err := sdk.AccAddressFromBech32("link1xpesesq0zddk2ersedyezgtywr0q92a34ddfku") 1288 require.NoError(t, err) 1289 to, err := sdk.AccAddressFromBech32("link1xpesesq0zddk2ersedyezgtywr0q92a34ddfku") 1290 require.NoError(t, err) 1291 err = builder.SetMsgs(banktypes.NewMsgSend(from, to, sdk.Coins{sdk.NewInt64Coin("stake", 10000)})) 1292 require.NoError(t, err) 1293 txContents, err := txCfg.TxJSONEncoder()(builder.GetTx()) 1294 require.NoError(t, err) 1295 txFile := testutil.WriteToNewTempFile(t, string(txContents)) 1296 1297 cmd.SetArgs([]string{txFile.Name()}) 1298 err = cmd.ExecuteContext(ctx) 1299 require.Error(t, err) 1300 require.Contains(t, err.Error(), "connect: connection refused") 1301 require.Contains(t, out.String(), "connect: connection refused") 1302 } 1303 1304 func (s *IntegrationTestSuite) TestQueryParamsCmd() { 1305 val := s.network.Validators[0] 1306 1307 testCases := []struct { 1308 name string 1309 args []string 1310 expectErr bool 1311 }{ 1312 { 1313 "happy case", 1314 []string{fmt.Sprintf("--%s=json", ostcli.OutputFlag)}, 1315 false, 1316 }, 1317 { 1318 "with specific height", 1319 []string{fmt.Sprintf("--%s=1", flags.FlagHeight), fmt.Sprintf("--%s=json", ostcli.OutputFlag)}, 1320 false, 1321 }, 1322 } 1323 1324 for _, tc := range testCases { 1325 tc := tc 1326 s.Run(tc.name, func() { 1327 cmd := authcli.QueryParamsCmd() 1328 clientCtx := val.ClientCtx 1329 1330 out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) 1331 if tc.expectErr { 1332 s.Require().Error(err) 1333 s.Require().NotEqual("internal", err.Error()) 1334 } else { 1335 var authParams authtypes.Params 1336 s.Require().NoError(val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &authParams)) 1337 s.Require().NotNil(authParams.MaxMemoCharacters) 1338 } 1339 }) 1340 } 1341 } 1342 1343 // TestTxWithoutPublicKey makes sure sending a proto tx message without the 1344 // public key doesn't cause any error in the RPC layer (broadcast). 1345 // See https://github.com/cosmos/cosmos-sdk/issues/7585 for more details. 1346 func (s *IntegrationTestSuite) TestTxWithoutPublicKey() { 1347 val1 := s.network.Validators[0] 1348 txCfg := val1.ClientCtx.TxConfig 1349 1350 // Create a txBuilder with an unsigned tx. 1351 txBuilder := txCfg.NewTxBuilder() 1352 msg := banktypes.NewMsgSend(val1.Address, val1.Address, sdk.NewCoins( 1353 sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10)), 1354 )) 1355 err := txBuilder.SetMsgs(msg) 1356 s.Require().NoError(err) 1357 txBuilder.SetFeeAmount(sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(150)))) 1358 txBuilder.SetGasLimit(testdata.NewTestGasLimit()) 1359 // Set empty signature to set signer infos. 1360 sigV2 := signing.SignatureV2{ 1361 PubKey: val1.PubKey, 1362 Data: &signing.SingleSignatureData{ 1363 SignMode: txCfg.SignModeHandler().DefaultMode(), 1364 Signature: nil, 1365 }, 1366 } 1367 err = txBuilder.SetSignatures(sigV2) 1368 s.Require().NoError(err) 1369 1370 // Create a file with the unsigned tx. 1371 txJSON, err := txCfg.TxJSONEncoder()(txBuilder.GetTx()) 1372 s.Require().NoError(err) 1373 unsignedTxFile := testutil.WriteToNewTempFile(s.T(), string(txJSON)) 1374 1375 // Sign the file with the unsignedTx. 1376 signedTx, err := TxSignExec(val1.ClientCtx, val1.Address, unsignedTxFile.Name(), fmt.Sprintf("--%s=true", cli.FlagOverwrite)) 1377 s.Require().NoError(err) 1378 1379 // Remove the signerInfo's `public_key` field manually from the signedTx. 1380 // Note: this method is only used for test purposes! In general, one should 1381 // use txBuilder and TxEncoder/TxDecoder to manipulate txs. 1382 var tx tx.Tx 1383 err = val1.ClientCtx.Codec.UnmarshalJSON(signedTx.Bytes(), &tx) 1384 s.Require().NoError(err) 1385 tx.AuthInfo.SignerInfos[0].PublicKey = nil 1386 // Re-encode the tx again, to another file. 1387 txJSON, err = val1.ClientCtx.Codec.MarshalJSON(&tx) 1388 s.Require().NoError(err) 1389 signedTxFile := testutil.WriteToNewTempFile(s.T(), string(txJSON)) 1390 s.Require().True(strings.Contains(string(txJSON), "\"public_key\":null")) 1391 1392 // Broadcast tx, test that it shouldn't panic. 1393 val1.ClientCtx.BroadcastMode = flags.BroadcastSync 1394 out, err := TxBroadcastExec(val1.ClientCtx, signedTxFile.Name()) 1395 s.Require().NoError(err) 1396 var res sdk.TxResponse 1397 s.Require().NoError(val1.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &res)) 1398 s.Require().NotEqual(0, res.Code) 1399 } 1400 1401 func (s *IntegrationTestSuite) TestSignWithMultiSignersAminoJSON() { 1402 // test case: 1403 // Create a transaction with 2 messages which has to be signed with 2 different keys 1404 // Sign and append the signatures using the CLI with Amino signing mode. 1405 // Finally send the transaction to the blockchain. It should work. 1406 1407 require := s.Require() 1408 val0, val1 := s.network.Validators[0], s.network.Validators[1] 1409 val0Coin := sdk.NewCoin(fmt.Sprintf("%stoken", val0.Moniker), sdk.NewInt(10)) 1410 val1Coin := sdk.NewCoin(fmt.Sprintf("%stoken", val1.Moniker), sdk.NewInt(10)) 1411 _, _, addr1 := testdata.KeyTestPubAddr() 1412 1413 // Creating a tx with 2 msgs from 2 signers: val0 and val1. 1414 // The validators need to sign with SIGN_MODE_LEGACY_AMINO_JSON, 1415 // because DIRECT doesn't support multi signers via the CLI. 1416 // Since we use amino, we don't need to pre-populate signer_infos. 1417 txBuilder := val0.ClientCtx.TxConfig.NewTxBuilder() 1418 txBuilder.SetMsgs( 1419 banktypes.NewMsgSend(val0.Address, addr1, sdk.NewCoins(val0Coin)), 1420 banktypes.NewMsgSend(val1.Address, addr1, sdk.NewCoins(val1Coin)), 1421 ) 1422 txBuilder.SetFeeAmount(sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10)))) 1423 txBuilder.SetGasLimit(testdata.NewTestGasLimit()) // min required is 101892 1424 require.Equal([]sdk.AccAddress{val0.Address, val1.Address}, txBuilder.GetTx().GetSigners()) 1425 1426 // Write the unsigned tx into a file. 1427 txJSON, err := val0.ClientCtx.TxConfig.TxJSONEncoder()(txBuilder.GetTx()) 1428 require.NoError(err) 1429 unsignedTxFile := testutil.WriteToNewTempFile(s.T(), string(txJSON)) 1430 1431 // Let val0 sign first the file with the unsignedTx. 1432 signedByVal0, err := TxSignExec(val0.ClientCtx, val0.Address, unsignedTxFile.Name(), "--overwrite", "--sign-mode=amino-json") 1433 require.NoError(err) 1434 signedByVal0File := testutil.WriteToNewTempFile(s.T(), signedByVal0.String()) 1435 1436 // Then let val1 sign the file with signedByVal0. 1437 val1AccNum, val1Seq, err := val0.ClientCtx.AccountRetriever.GetAccountNumberSequence(val0.ClientCtx, val1.Address) 1438 require.NoError(err) 1439 signedTx, err := TxSignExec( 1440 val1.ClientCtx, val1.Address, signedByVal0File.Name(), 1441 "--offline", fmt.Sprintf("--account-number=%d", val1AccNum), fmt.Sprintf("--sequence=%d", val1Seq), "--sign-mode=amino-json", 1442 ) 1443 require.NoError(err) 1444 signedTxFile := testutil.WriteToNewTempFile(s.T(), signedTx.String()) 1445 1446 // Now let's try to send this tx. 1447 res, err := TxBroadcastExec( 1448 val0.ClientCtx, 1449 signedTxFile.Name(), 1450 fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), 1451 ) 1452 1453 require.NoError(err) 1454 var txRes sdk.TxResponse 1455 require.NoError(val0.ClientCtx.Codec.UnmarshalJSON(res.Bytes(), &txRes)) 1456 require.Equal(uint32(0), txRes.Code) 1457 1458 // Make sure the addr1's balance got funded. 1459 queryResJSON, err := bankcli.QueryBalancesExec(val0.ClientCtx, addr1) 1460 require.NoError(err) 1461 var queryRes banktypes.QueryAllBalancesResponse 1462 err = val0.ClientCtx.Codec.UnmarshalJSON(queryResJSON.Bytes(), &queryRes) 1463 require.NoError(err) 1464 require.Equal(sdk.NewCoins(val0Coin, val1Coin), queryRes.Balances) 1465 } 1466 1467 func (s *IntegrationTestSuite) createBankMsg(val *network.Validator, toAddr sdk.AccAddress, amount sdk.Coins, extraFlags ...string) (testutil.BufferWriter, error) { 1468 flags := []string{ 1469 fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), 1470 fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), 1471 fmt.Sprintf("--%s=%s", flags.FlagFees, 1472 sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), 1473 } 1474 1475 flags = append(flags, extraFlags...) 1476 return bankcli.MsgSendExec(val.ClientCtx, val.Address, toAddr, amount, flags...) 1477 }