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