From 4733fc1769a6a82a54ffd8fd3679a978d842547d Mon Sep 17 00:00:00 2001 From: Akhil Kumar P <36399231+akhilkumarpilli@users.noreply.github.com> Date: Fri, 11 Oct 2024 17:43:00 +0530 Subject: [PATCH] test: migrate e2e/auth to system tests and fix sign-batch (#22149) --- client/account_retriever.go | 24 +- tests/e2e/auth/cli_test.go | 20 - tests/e2e/auth/suite.go | 1808 ------------------------ tests/systemtests/auth_test.go | 508 +++++++ tests/systemtests/cli.go | 18 - tests/systemtests/distribution_test.go | 4 +- x/auth/client/cli/tx_sign.go | 8 +- 7 files changed, 538 insertions(+), 1852 deletions(-) delete mode 100644 tests/e2e/auth/cli_test.go delete mode 100644 tests/e2e/auth/suite.go create mode 100644 tests/systemtests/auth_test.go diff --git a/client/account_retriever.go b/client/account_retriever.go index 63ff674dfd1e..7ffcc0e17f42 100644 --- a/client/account_retriever.go +++ b/client/account_retriever.go @@ -13,6 +13,26 @@ type Account interface { GetSequence() uint64 } +type mockAccount struct { + addr []byte +} + +func (m mockAccount) GetAddress() sdk.AccAddress { + return m.addr +} + +func (m mockAccount) GetPubKey() cryptotypes.PubKey { + return nil +} + +func (m mockAccount) GetAccountNumber() uint64 { + return 0 +} + +func (m mockAccount) GetSequence() uint64 { + return 0 +} + // AccountRetriever defines the interfaces required by transactions to // ensure an account exists and to be able to query for account fields necessary // for signing. @@ -32,8 +52,8 @@ type MockAccountRetriever struct { ReturnAccNum, ReturnAccSeq uint64 } -func (mar MockAccountRetriever) GetAccount(_ Context, _ sdk.AccAddress) (Account, error) { - return nil, nil +func (mar MockAccountRetriever) GetAccount(_ Context, address sdk.AccAddress) (Account, error) { + return mockAccount{addr: address}, nil } func (mar MockAccountRetriever) GetAccountWithHeight(_ Context, _ sdk.AccAddress) (Account, int64, error) { diff --git a/tests/e2e/auth/cli_test.go b/tests/e2e/auth/cli_test.go deleted file mode 100644 index a029338e3d57..000000000000 --- a/tests/e2e/auth/cli_test.go +++ /dev/null @@ -1,20 +0,0 @@ -//go:build e2e -// +build e2e - -package auth - -import ( - "testing" - - "github.com/stretchr/testify/suite" - - "cosmossdk.io/simapp" - - "github.com/cosmos/cosmos-sdk/testutil/network" -) - -func TestE2ETestSuite(t *testing.T) { - cfg := network.DefaultConfig(simapp.NewTestNetworkFixture) - cfg.NumValidators = 2 - suite.Run(t, NewE2ETestSuite(cfg)) -} diff --git a/tests/e2e/auth/suite.go b/tests/e2e/auth/suite.go deleted file mode 100644 index 31dca150f87e..000000000000 --- a/tests/e2e/auth/suite.go +++ /dev/null @@ -1,1808 +0,0 @@ -package auth - -import ( - "context" - "encoding/base64" - "fmt" - "strings" - "testing" - - abci "github.com/cometbft/cometbft/api/cometbft/abci/v1" - "github.com/stretchr/testify/require" - "github.com/stretchr/testify/suite" - - "cosmossdk.io/core/address" - "cosmossdk.io/depinject" - "cosmossdk.io/math" - banktypes "cosmossdk.io/x/bank/types" - govtestutil "cosmossdk.io/x/gov/client/testutil" - govtypes "cosmossdk.io/x/gov/types/v1beta1" - - "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/client/flags" - addresscodec "github.com/cosmos/cosmos-sdk/codec/address" - "github.com/cosmos/cosmos-sdk/crypto/hd" - "github.com/cosmos/cosmos-sdk/crypto/keyring" - kmultisig "github.com/cosmos/cosmos-sdk/crypto/keys/multisig" - cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" - authtestkeeper "github.com/cosmos/cosmos-sdk/tests/e2e/auth/keeper" - "github.com/cosmos/cosmos-sdk/testutil" - clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" - "github.com/cosmos/cosmos-sdk/testutil/network" - "github.com/cosmos/cosmos-sdk/testutil/testdata" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/types/tx" - "github.com/cosmos/cosmos-sdk/types/tx/signing" - authcli "github.com/cosmos/cosmos-sdk/x/auth/client/cli" - authclitestutil "github.com/cosmos/cosmos-sdk/x/auth/client/testutil" - "github.com/cosmos/cosmos-sdk/x/genutil/client/cli" -) - -type E2ETestSuite struct { - suite.Suite - - cfg network.Config - ac address.Codec - network network.NetworkI -} - -func NewE2ETestSuite(cfg network.Config) *E2ETestSuite { - return &E2ETestSuite{cfg: cfg} -} - -func (s *E2ETestSuite) SetupSuite() { - s.T().Log("setting up e2e test suite") - var err error - s.network, err = network.New(s.T(), s.T().TempDir(), s.cfg) - s.Require().NoError(err) - - kb := s.network.GetValidators()[0].GetClientCtx().Keyring - _, _, err = kb.NewMnemonic("newAccount", keyring.English, sdk.FullFundraiserPath, keyring.DefaultBIP39Passphrase, hd.Secp256k1) - s.Require().NoError(err) - - account1, _, err := kb.NewMnemonic("newAccount1", keyring.English, sdk.FullFundraiserPath, keyring.DefaultBIP39Passphrase, hd.Secp256k1) - s.Require().NoError(err) - - account2, _, err := kb.NewMnemonic("newAccount2", keyring.English, sdk.FullFundraiserPath, keyring.DefaultBIP39Passphrase, hd.Secp256k1) - s.Require().NoError(err) - pub1, err := account1.GetPubKey() - s.Require().NoError(err) - pub2, err := account2.GetPubKey() - s.Require().NoError(err) - - // Create a dummy account for testing purpose - _, _, err = kb.NewMnemonic("dummyAccount", keyring.English, sdk.FullFundraiserPath, keyring.DefaultBIP39Passphrase, hd.Secp256k1) - s.Require().NoError(err) - - multi := kmultisig.NewLegacyAminoPubKey(2, []cryptotypes.PubKey{pub1, pub2}) - _, err = kb.SaveMultisig("multi", multi) - s.Require().NoError(err) - s.Require().NoError(s.network.WaitForNextBlock()) - - s.ac = addresscodec.NewBech32Codec("cosmos") -} - -func (s *E2ETestSuite) TearDownSuite() { - s.T().Log("tearing down e2e test suite") - s.network.Cleanup() -} - -func (s *E2ETestSuite) TestCLISignGenOnly() { - val := s.network.GetValidators()[0] - val2 := s.network.GetValidators()[1] - - k, err := val.GetClientCtx().Keyring.KeyByAddress(val.GetAddress()) - s.Require().NoError(err) - keyName := k.Name - - addr, err := k.GetAddress() - s.Require().NoError(err) - - account, err := val.GetClientCtx().AccountRetriever.GetAccount(val.GetClientCtx(), addr) - s.Require().NoError(err) - - sendTokens := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, math.NewInt(10))) - msgSend := &banktypes.MsgSend{ - FromAddress: val.GetAddress().String(), - ToAddress: val2.GetAddress().String(), - Amount: sendTokens, - } - - generatedStd, err := clitestutil.SubmitTestTx( - val.GetClientCtx(), - msgSend, - val.GetAddress(), - clitestutil.TestTxConfig{ - GenOnly: true, - }, - ) - s.Require().NoError(err) - opFile := testutil.WriteToNewTempFile(s.T(), generatedStd.String()) - defer opFile.Close() - - commonArgs := []string{ - fmt.Sprintf("--%s=%s", flags.FlagKeyringBackend, keyring.BackendTest), - fmt.Sprintf("--%s=%s", flags.FlagHome, strings.Replace(val.GetClientCtx().HomeDir, "simd", "simcli", 1)), - fmt.Sprintf("--%s=%s", flags.FlagChainID, val.GetClientCtx().ChainID), - } - - cases := []struct { - name string - args []string - expErr bool - errMsg string - }{ - { - "offline mode with account-number, sequence and keyname (valid)", - []string{ - opFile.Name(), - fmt.Sprintf("--%s=true", flags.FlagOffline), - fmt.Sprintf("--%s=%s", flags.FlagFrom, keyName), - fmt.Sprintf("--%s=%d", flags.FlagAccountNumber, account.GetAccountNumber()), - fmt.Sprintf("--%s=%d", flags.FlagSequence, account.GetSequence()), - }, - false, - "", - }, - { - "offline mode with account-number, sequence and address key (valid)", - []string{ - opFile.Name(), - fmt.Sprintf("--%s=true", flags.FlagOffline), - fmt.Sprintf("--%s=%s", flags.FlagFrom, val.GetAddress().String()), - fmt.Sprintf("--%s=%d", flags.FlagAccountNumber, account.GetAccountNumber()), - fmt.Sprintf("--%s=%d", flags.FlagSequence, account.GetSequence()), - }, - false, - "", - }, - { - "offline mode without account-number and keyname (invalid)", - []string{ - opFile.Name(), - fmt.Sprintf("--%s=true", flags.FlagOffline), - fmt.Sprintf("--%s=%s", flags.FlagFrom, keyName), - fmt.Sprintf("--%s=%d", flags.FlagSequence, account.GetSequence()), - }, - true, - `required flag(s) "account-number" not set`, - }, - { - "offline mode without sequence and keyname (invalid)", - []string{ - opFile.Name(), - fmt.Sprintf("--%s=true", flags.FlagOffline), - fmt.Sprintf("--%s=%s", flags.FlagFrom, keyName), - fmt.Sprintf("--%s=%d", flags.FlagAccountNumber, account.GetAccountNumber()), - }, - true, - `required flag(s) "sequence" not set`, - }, - { - "offline mode without account-number, sequence and keyname (invalid)", - []string{ - opFile.Name(), - fmt.Sprintf("--%s=%s", flags.FlagFrom, keyName), - fmt.Sprintf("--%s=true", flags.FlagOffline), - }, - true, - `required flag(s) "account-number", "sequence" not set`, - }, - } - - for _, tc := range cases { - cmd := authcli.GetSignCommand() - cmd.PersistentFlags().String(flags.FlagHome, val.GetClientCtx().HomeDir, "directory for config and data") - out, err := clitestutil.ExecTestCLICmd(val.GetClientCtx(), cmd, append(tc.args, commonArgs...)) - if tc.expErr { - s.Require().Error(err) - s.Require().Contains(err.Error(), tc.errMsg) - } else { - s.Require().NoError(err) - func() { - signedTx := testutil.WriteToNewTempFile(s.T(), out.String()) - defer signedTx.Close() - _, err := authclitestutil.TxBroadcastExec(val.GetClientCtx(), signedTx.Name()) - s.Require().NoError(err) - }() - } - } -} - -func (s *E2ETestSuite) TestCLISignBatch() { - val := s.network.GetValidators()[0] - clientCtx := val.GetClientCtx() - sendTokens := sdk.NewCoins( - sdk.NewCoin(fmt.Sprintf("%stoken", val.GetMoniker()), math.NewInt(10)), - sdk.NewCoin(s.cfg.BondDenom, math.NewInt(10)), - ) - - generatedStd, err := s.createBankMsg( - val, - val.GetAddress(), - sendTokens, clitestutil.TestTxConfig{ - GenOnly: true, - }, - ) - s.Require().NoError(err) - - outputFile := testutil.WriteToNewTempFile(s.T(), strings.Repeat(generatedStd.String()+"\n", 3)) - defer outputFile.Close() - clientCtx.HomeDir = strings.Replace(clientCtx.HomeDir, "simd", "simcli", 1) - - // sign-batch file - offline is set but account-number and sequence are not - _, err = authclitestutil.TxSignBatchExec(clientCtx, val.GetAddress(), outputFile.Name(), fmt.Sprintf("--%s=%s", flags.FlagChainID, clientCtx.ChainID), "--offline") - s.Require().EqualError(err, "required flag(s) \"account-number\", \"sequence\" not set") - - // sign-batch file - offline and sequence is set but account-number is not set - _, err = authclitestutil.TxSignBatchExec(clientCtx, val.GetAddress(), outputFile.Name(), fmt.Sprintf("--%s=%s", flags.FlagChainID, clientCtx.ChainID), fmt.Sprintf("--%s=%s", flags.FlagSequence, "1"), "--offline") - s.Require().EqualError(err, "required flag(s) \"account-number\" not set") - - // sign-batch file - offline and account-number is set but sequence is not set - _, err = authclitestutil.TxSignBatchExec(clientCtx, val.GetAddress(), outputFile.Name(), fmt.Sprintf("--%s=%s", flags.FlagChainID, clientCtx.ChainID), fmt.Sprintf("--%s=%s", flags.FlagAccountNumber, "1"), "--offline") - s.Require().EqualError(err, "required flag(s) \"sequence\" not set") - - // sign-batch file - sequence and account-number are set when offline is false - res, err := authclitestutil.TxSignBatchExec(clientCtx, val.GetAddress(), outputFile.Name(), fmt.Sprintf("--%s=%s", flags.FlagChainID, clientCtx.ChainID), fmt.Sprintf("--%s=%s", flags.FlagSequence, "1"), fmt.Sprintf("--%s=%s", flags.FlagAccountNumber, "1")) - s.Require().NoError(err) - s.Require().Equal(3, len(strings.Split(strings.Trim(res.String(), "\n"), "\n"))) - - // sign-batch file - res, err = authclitestutil.TxSignBatchExec(clientCtx, val.GetAddress(), outputFile.Name(), fmt.Sprintf("--%s=%s", flags.FlagChainID, clientCtx.ChainID)) - s.Require().NoError(err) - s.Require().Equal(3, len(strings.Split(strings.Trim(res.String(), "\n"), "\n"))) - - // sign-batch file signature only - res, err = authclitestutil.TxSignBatchExec(clientCtx, val.GetAddress(), outputFile.Name(), fmt.Sprintf("--%s=%s", flags.FlagChainID, clientCtx.ChainID), "--signature-only") - s.Require().NoError(err) - s.Require().Equal(3, len(strings.Split(strings.Trim(res.String(), "\n"), "\n"))) - - // Sign batch malformed tx file. - malformedFile := testutil.WriteToNewTempFile(s.T(), fmt.Sprintf("malformed%s", generatedStd)) - defer malformedFile.Close() - _, err = authclitestutil.TxSignBatchExec(clientCtx, val.GetAddress(), malformedFile.Name(), fmt.Sprintf("--%s=%s", flags.FlagChainID, clientCtx.ChainID)) - s.Require().Error(err) - - // Sign batch malformed tx file signature only. - _, err = authclitestutil.TxSignBatchExec(clientCtx, val.GetAddress(), malformedFile.Name(), fmt.Sprintf("--%s=%s", flags.FlagChainID, clientCtx.ChainID), "--signature-only") - s.Require().Error(err) - - // make a txn to increase the sequence of sender - _, seq, err := clientCtx.AccountRetriever.GetAccountNumberSequence(clientCtx, val.GetAddress()) - s.Require().NoError(err) - - account1, err := clientCtx.Keyring.Key("newAccount1") - s.Require().NoError(err) - - addr, err := account1.GetAddress() - s.Require().NoError(err) - - // Send coins from validator to multisig. - _, err = s.createBankMsg( - val, - addr, - sdk.NewCoins(sdk.NewInt64Coin(s.cfg.BondDenom, 1000)), - clitestutil.TestTxConfig{}, - ) - s.Require().NoError(err) - s.Require().NoError(s.network.WaitForNextBlock()) - - // fetch the sequence after a tx, should be incremented. - _, seq1, err := clientCtx.AccountRetriever.GetAccountNumberSequence(clientCtx, val.GetAddress()) - s.Require().NoError(err) - s.Require().Equal(seq+1, seq1) - - // signing sign-batch should start from the last sequence. - signed, err := authclitestutil.TxSignBatchExec(clientCtx, val.GetAddress(), outputFile.Name(), fmt.Sprintf("--%s=%s", flags.FlagChainID, clientCtx.ChainID), "--signature-only") - s.Require().NoError(err) - signedTxs := strings.Split(strings.Trim(signed.String(), "\n"), "\n") - s.Require().GreaterOrEqual(len(signedTxs), 1) - - sigs, err := s.cfg.TxConfig.UnmarshalSignatureJSON([]byte(signedTxs[0])) - s.Require().NoError(err) - s.Require().Equal(sigs[0].Sequence, seq1) -} - -func (s *E2ETestSuite) TestCLIQueryTxCmdByHash() { - val := s.network.GetValidators()[0] - - account2, err := val.GetClientCtx().Keyring.Key("newAccount2") - s.Require().NoError(err) - - sendTokens := sdk.NewInt64Coin(s.cfg.BondDenom, 10) - - addr, err := account2.GetAddress() - s.Require().NoError(err) - - // Send coins. - res, err := s.createBankMsg( - val, - addr, - sdk.NewCoins(sendTokens), - clitestutil.TestTxConfig{}, - ) - s.Require().NoError(err) - s.Require().NoError(s.network.WaitForNextBlock()) - - var txRes sdk.TxResponse - s.Require().NoError(val.GetClientCtx().Codec.UnmarshalJSON(res.Bytes(), &txRes)) - - testCases := []struct { - name string - args []string - expectErr bool - rawLogContains string - }{ - { - "not enough args", - []string{}, - true, "", - }, - { - "with invalid hash", - []string{"somethinginvalid", fmt.Sprintf("--%s=json", flags.FlagOutput)}, - true, "", - }, - { - "with valid and not existing hash", - []string{"C7E7D3A86A17AB3A321172239F3B61357937AF0F25D9FA4D2F4DCCAD9B0D7747", fmt.Sprintf("--%s=json", flags.FlagOutput)}, - true, "", - }, - { - "happy case", - []string{txRes.TxHash, fmt.Sprintf("--%s=json", flags.FlagOutput)}, - false, - sdk.MsgTypeURL(&banktypes.MsgSend{}), - }, - } - - for _, tc := range testCases { - s.Run(tc.name, func() { - cmd := authcli.QueryTxCmd() - clientCtx := val.GetClientCtx() - var ( - out testutil.BufferWriter - err error - ) - - err = s.network.RetryForBlocks(func() error { - out, err = clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) - return err - }, 2) - if tc.expectErr { - s.Require().Error(err) - s.Require().NotEqual("internal", err.Error()) - } else { - var result sdk.TxResponse - s.Require().NoError(val.GetClientCtx().Codec.UnmarshalJSON(out.Bytes(), &result)) - s.Require().NotNil(result.Height) - if ok := s.deepContains(result.Events, tc.rawLogContains); !ok { - s.Require().Fail("raw log does not contain the expected value, expected value: %s", tc.rawLogContains) - } - } - }) - } -} - -func (s *E2ETestSuite) TestCLIQueryTxCmdByEvents() { - val := s.network.GetValidators()[0] - - account2, err := val.GetClientCtx().Keyring.Key("newAccount2") - s.Require().NoError(err) - - sendTokens := sdk.NewInt64Coin(s.cfg.BondDenom, 10) - - addr2, err := account2.GetAddress() - s.Require().NoError(err) - - // Send coins. - res, err := s.createBankMsg( - val, - addr2, - sdk.NewCoins(sendTokens), - clitestutil.TestTxConfig{}, - ) - s.Require().NoError(err) - var txRes sdk.TxResponse - s.Require().NoError(val.GetClientCtx().Codec.UnmarshalJSON(res.Bytes(), &txRes)) - s.Require().NoError(s.network.WaitForNextBlock()) - - var out testutil.BufferWriter - // Query the tx by hash to get the inner tx. - err = s.network.RetryForBlocks(func() error { - out, err = clitestutil.ExecTestCLICmd(val.GetClientCtx(), authcli.QueryTxCmd(), []string{txRes.TxHash, fmt.Sprintf("--%s=json", flags.FlagOutput)}) - return err - }, 3) - s.Require().NoError(err) - s.Require().NoError(val.GetClientCtx().Codec.UnmarshalJSON(out.Bytes(), &txRes)) - protoTx := txRes.GetTx().(*tx.Tx) - - testCases := []struct { - name string - args []string - expectErr bool - expectErrStr string - }{ - { - "invalid --type", - []string{ - fmt.Sprintf("--type=%s", "foo"), - "bar", - fmt.Sprintf("--%s=json", flags.FlagOutput), - }, - true, "unknown --type value foo", - }, - { - "--type=acc_seq with no addr+seq", - []string{ - "--type=acc_seq", - "", - fmt.Sprintf("--%s=json", flags.FlagOutput), - }, - true, "`acc_seq` type takes an argument '/'", - }, - { - "non-existing addr+seq combo", - []string{ - "--type=acc_seq", - "foobar", - fmt.Sprintf("--%s=json", flags.FlagOutput), - }, - true, "found no txs matching given address and sequence combination", - }, - { - "addr+seq happy case", - []string{ - "--type=acc_seq", - fmt.Sprintf("%s/%d", val.GetAddress(), protoTx.AuthInfo.SignerInfos[0].Sequence), - fmt.Sprintf("--%s=json", flags.FlagOutput), - }, - false, "", - }, - { - "--type=signature with no signature", - []string{ - "--type=signature", - "", - fmt.Sprintf("--%s=json", flags.FlagOutput), - }, - true, "argument should be comma-separated signatures", - }, - { - "non-existing signatures", - []string{ - "--type=signature", - "foo", - fmt.Sprintf("--%s=json", flags.FlagOutput), - }, - true, "found no txs matching given signatures", - }, - { - "with --signatures happy case", - []string{ - "--type=signature", - base64.StdEncoding.EncodeToString(protoTx.Signatures[0]), - fmt.Sprintf("--%s=json", flags.FlagOutput), - }, - false, "", - }, - } - - for _, tc := range testCases { - s.Run(tc.name, func() { - cmd := authcli.QueryTxCmd() - clientCtx := val.GetClientCtx() - - out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) - if tc.expectErr { - s.Require().Error(err) - s.Require().Contains(err.Error(), tc.expectErrStr) - } else { - var result sdk.TxResponse - s.Require().NoError(err) - s.Require().NoError(val.GetClientCtx().Codec.UnmarshalJSON(out.Bytes(), &result)) - s.Require().NotNil(result.Height) - } - }) - } -} - -func (s *E2ETestSuite) TestCLIQueryTxsCmdByEvents() { - val := s.network.GetValidators()[0] - - account2, err := val.GetClientCtx().Keyring.Key("newAccount2") - s.Require().NoError(err) - - sendTokens := sdk.NewInt64Coin(s.cfg.BondDenom, 10) - - addr2, err := account2.GetAddress() - s.Require().NoError(err) - // Send coins. - res, err := s.createBankMsg( - val, - addr2, - sdk.NewCoins(sendTokens), - clitestutil.TestTxConfig{}, - ) - s.Require().NoError(err) - var txRes sdk.TxResponse - s.Require().NoError(val.GetClientCtx().Codec.UnmarshalJSON(res.Bytes(), &txRes)) - s.Require().NoError(s.network.WaitForNextBlock()) - - var out testutil.BufferWriter - // Query the tx by hash to get the inner tx. - err = s.network.RetryForBlocks(func() error { - out, err = clitestutil.ExecTestCLICmd(val.GetClientCtx(), authcli.QueryTxCmd(), []string{txRes.TxHash, fmt.Sprintf("--%s=json", flags.FlagOutput)}) - return err - }, 3) - s.Require().NoError(err) - s.Require().NoError(val.GetClientCtx().Codec.UnmarshalJSON(out.Bytes(), &txRes)) - - testCases := []struct { - name string - args []string - expectEmpty bool - }{ - { - "fee event happy case", - []string{ - fmt.Sprintf( - "--query=tx.fee='%s'", - sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, math.NewInt(10))).String(), - ), - fmt.Sprintf("--%s=json", flags.FlagOutput), - }, - false, - }, - { - "no matching fee event", - []string{ - fmt.Sprintf( - "--query=tx.fee='%s'", - sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, math.NewInt(0))).String(), - ), - fmt.Sprintf("--%s=json", flags.FlagOutput), - }, - true, - }, - } - - for _, tc := range testCases { - s.Run(tc.name, func() { - cmd := authcli.QueryTxsByEventsCmd() - clientCtx := val.GetClientCtx() - - out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) - s.Require().NoError(err) - - var result sdk.SearchTxsResult - s.Require().NoError(val.GetClientCtx().Codec.UnmarshalJSON(out.Bytes(), &result)) - - if tc.expectEmpty { - s.Require().Equal(0, len(result.Txs)) - } else { - s.Require().NotEqual(0, len(result.Txs)) - s.Require().NotNil(result.Txs[0]) - } - }) - } -} - -func (s *E2ETestSuite) TestCLISendGenerateSignAndBroadcast() { - val1 := s.network.GetValidators()[0] - clientCtx := val1.GetClientCtx() - - account, err := clientCtx.Keyring.Key("newAccount") - s.Require().NoError(err) - - sendTokens := sdk.NewCoin(s.cfg.BondDenom, sdk.TokensFromConsensusPower(10, sdk.DefaultPowerReduction)) - - addr, err := account.GetAddress() - s.Require().NoError(err) - normalGeneratedTx, err := s.createBankMsg( - val1, - addr, - sdk.NewCoins(sendTokens), - clitestutil.TestTxConfig{ - GenOnly: true, - }, - ) - s.Require().NoError(err) - txCfg := clientCtx.TxConfig - - normalGeneratedStdTx, err := txCfg.TxJSONDecoder()(normalGeneratedTx.Bytes()) - s.Require().NoError(err) - txBuilder, err := txCfg.WrapTxBuilder(normalGeneratedStdTx) - s.Require().NoError(err) - s.Require().Equal(txBuilder.GetTx().GetGas(), uint64(flags.DefaultGasLimit)) - s.Require().Equal(len(txBuilder.GetTx().GetMsgs()), 1) - sigs, err := txBuilder.GetTx().GetSignaturesV2() - s.Require().NoError(err) - s.Require().Equal(0, len(sigs)) - - // Test generate sendTx with --gas=$amount - limitedGasGeneratedTx, err := s.createBankMsg(val1, addr, - sdk.NewCoins(sendTokens), clitestutil.TestTxConfig{ - GenOnly: true, - Gas: 100, - }, - ) - s.Require().NoError(err) - - limitedGasStdTx, err := txCfg.TxJSONDecoder()(limitedGasGeneratedTx.Bytes()) - s.Require().NoError(err) - txBuilder, err = txCfg.WrapTxBuilder(limitedGasStdTx) - s.Require().NoError(err) - s.Require().Equal(txBuilder.GetTx().GetGas(), uint64(100)) - s.Require().Equal(len(txBuilder.GetTx().GetMsgs()), 1) - sigs, err = txBuilder.GetTx().GetSignaturesV2() - s.Require().NoError(err) - s.Require().Equal(0, len(sigs)) - - resp, err := testutil.GetRequest(fmt.Sprintf("%s/cosmos/bank/v1beta1/balances/%s", val1.GetAPIAddress(), val1.GetAddress())) - s.Require().NoError(err) - - var balRes banktypes.QueryAllBalancesResponse - err = clientCtx.Codec.UnmarshalJSON(resp, &balRes) - s.Require().NoError(err) - startTokens := balRes.Balances.AmountOf(s.cfg.BondDenom) - - // Test generate sendTx, estimate gas - finalGeneratedTx, err := s.createBankMsg( - val1, - addr, - sdk.NewCoins(sendTokens), clitestutil.TestTxConfig{ - GenOnly: true, - Gas: flags.DefaultGasLimit, - }) - s.Require().NoError(err) - - finalStdTx, err := txCfg.TxJSONDecoder()(finalGeneratedTx.Bytes()) - s.Require().NoError(err) - txBuilder, err = txCfg.WrapTxBuilder(finalStdTx) - s.Require().NoError(err) - s.Require().Equal(uint64(flags.DefaultGasLimit), txBuilder.GetTx().GetGas()) - s.Require().Equal(len(finalStdTx.GetMsgs()), 1) - - // Write the output to disk - unsignedTxFile := testutil.WriteToNewTempFile(s.T(), finalGeneratedTx.String()) - defer unsignedTxFile.Close() - - // Test validate-signatures - res, err := authclitestutil.TxValidateSignaturesExec(clientCtx, unsignedTxFile.Name()) - s.Require().EqualError(err, "signatures validation failed") - s.Require().True(strings.Contains(res.String(), fmt.Sprintf("Signers:\n 0: %v\n\nSignatures:\n\n", val1.GetAddress().String()))) - - // Test sign - - // Does not work in offline mode - _, err = authclitestutil.TxSignExec(clientCtx, val1.GetAddress(), unsignedTxFile.Name(), "--offline") - s.Require().EqualError(err, "required flag(s) \"account-number\", \"sequence\" not set") - - // But works offline if we set account number and sequence - clientCtx.HomeDir = strings.Replace(clientCtx.HomeDir, "simd", "simcli", 1) - _, err = authclitestutil.TxSignExec(clientCtx, val1.GetAddress(), unsignedTxFile.Name(), "--offline", "--account-number", "1", "--sequence", "1") - s.Require().NoError(err) - - // Sign transaction - signedTx, err := authclitestutil.TxSignExec(clientCtx, val1.GetAddress(), unsignedTxFile.Name()) - s.Require().NoError(err) - signedFinalTx, err := txCfg.TxJSONDecoder()(signedTx.Bytes()) - s.Require().NoError(err) - txBuilder, err = clientCtx.TxConfig.WrapTxBuilder(signedFinalTx) - s.Require().NoError(err) - s.Require().Equal(len(txBuilder.GetTx().GetMsgs()), 1) - sigs, err = txBuilder.GetTx().GetSignaturesV2() - s.Require().NoError(err) - s.Require().Equal(1, len(sigs)) - signers, err := txBuilder.GetTx().GetSigners() - s.Require().NoError(err) - s.Require().Equal([]byte(val1.GetAddress()), signers[0]) - - // Write the output to disk - signedTxFile := testutil.WriteToNewTempFile(s.T(), signedTx.String()) - defer signedTxFile.Close() - - // validate Signature - res, err = authclitestutil.TxValidateSignaturesExec(clientCtx, signedTxFile.Name()) - s.Require().NoError(err) - s.Require().True(strings.Contains(res.String(), "[OK]")) - s.Require().NoError(s.network.WaitForNextBlock()) - - // Ensure foo has right amount of funds - resp, err = testutil.GetRequest(fmt.Sprintf("%s/cosmos/bank/v1beta1/balances/%s", val1.GetAPIAddress(), val1.GetAddress())) - s.Require().NoError(err) - err = clientCtx.Codec.UnmarshalJSON(resp, &balRes) - s.Require().NoError(err) - s.Require().Equal(startTokens, balRes.Balances.AmountOf(s.cfg.BondDenom)) - - // Test broadcast - - // Does not work in offline mode - _, err = authclitestutil.TxBroadcastExec(clientCtx, signedTxFile.Name(), "--offline") - s.Require().EqualError(err, "cannot broadcast tx during offline mode") - s.Require().NoError(s.network.WaitForNextBlock()) - - // Broadcast correct transaction. - clientCtx.BroadcastMode = flags.BroadcastSync - _, err = authclitestutil.TxBroadcastExec(clientCtx, signedTxFile.Name()) - s.Require().NoError(err) - s.Require().NoError(s.network.WaitForNextBlock()) - - // Ensure destiny account state - err = s.network.RetryForBlocks(func() error { - resp, err = testutil.GetRequest(fmt.Sprintf("%s/cosmos/bank/v1beta1/balances/%s", val1.GetAPIAddress(), addr)) - s.Require().NoError(err) - return err - }, 3) - s.Require().NoError(err) - - err = clientCtx.Codec.UnmarshalJSON(resp, &balRes) - s.Require().NoError(err) - s.Require().Equal(sendTokens.Amount, balRes.Balances.AmountOf(s.cfg.BondDenom)) - - // Ensure origin account state - resp, err = testutil.GetRequest(fmt.Sprintf("%s/cosmos/bank/v1beta1/balances/%s", val1.GetAPIAddress(), val1.GetAddress())) - s.Require().NoError(err) - err = clientCtx.Codec.UnmarshalJSON(resp, &balRes) - s.Require().NoError(err) -} - -func (s *E2ETestSuite) TestCLIMultisignInsufficientCosigners() { - val1 := s.network.GetValidators()[0] - clientCtx := val1.GetClientCtx() - - // Fetch account and a multisig info - account1, err := clientCtx.Keyring.Key("newAccount1") - s.Require().NoError(err) - - multisigRecord, err := clientCtx.Keyring.Key("multi") - s.Require().NoError(err) - - addr, err := multisigRecord.GetAddress() - s.Require().NoError(err) - // Send coins from validator to multisig. - _, err = s.createBankMsg( - val1, - addr, - sdk.NewCoins( - sdk.NewInt64Coin(s.cfg.BondDenom, 10), - ), - clitestutil.TestTxConfig{}, - ) - s.Require().NoError(err) - s.Require().NoError(s.network.WaitForNextBlock()) - - coins := sdk.NewCoins(sdk.NewInt64Coin(s.cfg.BondDenom, 5)) - msgSend := &banktypes.MsgSend{ - FromAddress: addr.String(), - ToAddress: val1.GetAddress().String(), - Amount: coins, - } - - // Generate multisig transaction. - multiGeneratedTx, err := clitestutil.SubmitTestTx( - clientCtx, - msgSend, - addr, - clitestutil.TestTxConfig{ - GenOnly: true, - }, - ) - s.Require().NoError(err) - - // Save tx to file - multiGeneratedTxFile := testutil.WriteToNewTempFile(s.T(), multiGeneratedTx.String()) - defer multiGeneratedTxFile.Close() - - // Multisign, sign with one signature - clientCtx.HomeDir = strings.Replace(clientCtx.HomeDir, "simd", "simcli", 1) - addr1, err := account1.GetAddress() - s.Require().NoError(err) - account1Signature, err := authclitestutil.TxSignExec(clientCtx, addr1, multiGeneratedTxFile.Name(), "--multisig", addr.String()) - s.Require().NoError(err) - - sign1File := testutil.WriteToNewTempFile(s.T(), account1Signature.String()) - defer sign1File.Close() - - multiSigWith1Signature, err := authclitestutil.TxMultiSignExec(clientCtx, multisigRecord.Name, multiGeneratedTxFile.Name(), sign1File.Name()) - s.Require().NoError(err) - - // Save tx to file - multiSigWith1SignatureFile := testutil.WriteToNewTempFile(s.T(), multiSigWith1Signature.String()) - defer multiSigWith1SignatureFile.Close() - - _, err = authclitestutil.TxValidateSignaturesExec(clientCtx, multiSigWith1SignatureFile.Name()) - s.Require().Error(err) -} - -func (s *E2ETestSuite) TestCLIEncode() { - val1 := s.network.GetValidators()[0] - - sendTokens := sdk.NewCoin(s.cfg.BondDenom, sdk.TokensFromConsensusPower(10, sdk.DefaultPowerReduction)) - - normalGeneratedTx, err := s.createBankMsg( - val1, val1.GetAddress(), - sdk.NewCoins(sendTokens), - clitestutil.TestTxConfig{ - GenOnly: true, - Memo: "deadbeef", - }, - ) - s.Require().NoError(err) - savedTxFile := testutil.WriteToNewTempFile(s.T(), normalGeneratedTx.String()) - defer savedTxFile.Close() - - // Encode - encodeExec, err := authclitestutil.TxEncodeExec(val1.GetClientCtx(), savedTxFile.Name()) - s.Require().NoError(err) - trimmedBase64 := strings.Trim(encodeExec.String(), "\"\n") - - // Check that the transaction decodes as expected - decodedTx, err := authclitestutil.TxDecodeExec(val1.GetClientCtx(), trimmedBase64) - s.Require().NoError(err) - - txCfg := val1.GetClientCtx().TxConfig - theTx, err := txCfg.TxJSONDecoder()(decodedTx.Bytes()) - s.Require().NoError(err) - txBuilder, err := val1.GetClientCtx().TxConfig.WrapTxBuilder(theTx) - s.Require().NoError(err) - s.Require().Equal("deadbeef", txBuilder.GetTx().GetMemo()) -} - -func (s *E2ETestSuite) TestCLIMultisignSortSignatures() { - val1 := s.network.GetValidators()[0] - clientCtx := val1.GetClientCtx() - - // Generate 2 accounts and a multisig. - account1, err := clientCtx.Keyring.Key("newAccount1") - s.Require().NoError(err) - - account2, err := clientCtx.Keyring.Key("newAccount2") - s.Require().NoError(err) - - multisigRecord, err := clientCtx.Keyring.Key("multi") - s.Require().NoError(err) - - // Generate dummy account which is not a part of multisig. - dummyAcc, err := clientCtx.Keyring.Key("dummyAccount") - s.Require().NoError(err) - - addr, err := multisigRecord.GetAddress() - s.Require().NoError(err) - resp, err := testutil.GetRequest(fmt.Sprintf("%s/cosmos/bank/v1beta1/balances/%s", val1.GetAPIAddress(), addr)) - s.Require().NoError(err) - - var balRes banktypes.QueryAllBalancesResponse - err = clientCtx.Codec.UnmarshalJSON(resp, &balRes) - s.Require().NoError(err) - initialCoins := balRes.Balances - - // Send coins from validator to multisig. - sendTokens := sdk.NewInt64Coin(s.cfg.BondDenom, 10) - _, err = s.createBankMsg( - val1, - addr, - sdk.NewCoins(sendTokens), - clitestutil.TestTxConfig{}, - ) - s.Require().NoError(err) - s.Require().NoError(s.network.WaitForNextBlock()) - - resp, err = testutil.GetRequest(fmt.Sprintf("%s/cosmos/bank/v1beta1/balances/%s", val1.GetAPIAddress(), addr)) - s.Require().NoError(err) - err = clientCtx.Codec.UnmarshalJSON(resp, &balRes) - s.Require().NoError(err) - diff, _ := balRes.Balances.SafeSub(initialCoins...) - s.Require().Equal(sendTokens.Amount, diff.AmountOf(s.cfg.BondDenom)) - - tokens := sdk.NewCoins(sdk.NewInt64Coin(s.cfg.BondDenom, 5)) - msgSend := &banktypes.MsgSend{ - FromAddress: addr.String(), - ToAddress: val1.GetAddress().String(), - Amount: tokens, - } - - // Generate multisig transaction. - multiGeneratedTx, err := clitestutil.SubmitTestTx( - clientCtx, - msgSend, - addr, - clitestutil.TestTxConfig{ - GenOnly: true, - }, - ) - s.Require().NoError(err) - - // Save tx to file - multiGeneratedTxFile := testutil.WriteToNewTempFile(s.T(), multiGeneratedTx.String()) - defer multiGeneratedTxFile.Close() - - // Sign with account1 - addr1, err := account1.GetAddress() - s.Require().NoError(err) - clientCtx.HomeDir = strings.Replace(clientCtx.HomeDir, "simd", "simcli", 1) - account1Signature, err := authclitestutil.TxSignExec(clientCtx, addr1, multiGeneratedTxFile.Name(), "--multisig", addr.String()) - s.Require().NoError(err) - - sign1File := testutil.WriteToNewTempFile(s.T(), account1Signature.String()) - defer sign1File.Close() - - // Sign with account2 - addr2, err := account2.GetAddress() - s.Require().NoError(err) - account2Signature, err := authclitestutil.TxSignExec(clientCtx, addr2, multiGeneratedTxFile.Name(), "--multisig", addr.String()) - s.Require().NoError(err) - - sign2File := testutil.WriteToNewTempFile(s.T(), account2Signature.String()) - defer sign2File.Close() - - // Sign with dummy account - dummyAddr, err := dummyAcc.GetAddress() - s.Require().NoError(err) - _, err = authclitestutil.TxSignExec(clientCtx, dummyAddr, multiGeneratedTxFile.Name(), "--multisig", addr.String()) - s.Require().Error(err) - s.Require().Contains(err.Error(), "signing key is not a part of multisig key") - - multiSigWith2Signatures, err := authclitestutil.TxMultiSignExec(clientCtx, multisigRecord.Name, multiGeneratedTxFile.Name(), sign1File.Name(), sign2File.Name()) - s.Require().NoError(err) - - // Write the output to disk - signedTxFile := testutil.WriteToNewTempFile(s.T(), multiSigWith2Signatures.String()) - defer signedTxFile.Close() - - _, err = authclitestutil.TxValidateSignaturesExec(clientCtx, signedTxFile.Name()) - s.Require().NoError(err) - - clientCtx.BroadcastMode = flags.BroadcastSync - _, err = authclitestutil.TxBroadcastExec(clientCtx, signedTxFile.Name()) - s.Require().NoError(err) - - s.Require().NoError(s.network.WaitForNextBlock()) -} - -func (s *E2ETestSuite) TestSignWithMultisig() { - val1 := s.network.GetValidators()[0] - - // Generate a account for signing. - account1, err := val1.GetClientCtx().Keyring.Key("newAccount1") - s.Require().NoError(err) - - addr1, err := account1.GetAddress() - s.Require().NoError(err) - - // Create an address that is not in the keyring, will be used to simulate `--multisig` - multisig := "cosmos1hd6fsrvnz6qkp87s3u86ludegq97agxsdkwzyh" - multisigAddr, err := sdk.AccAddressFromBech32(multisig) - s.Require().NoError(err) - - tokens := sdk.NewCoins( - sdk.NewInt64Coin(s.cfg.BondDenom, 5), - ) - msgSend := &banktypes.MsgSend{ - FromAddress: val1.GetAddress().String(), - ToAddress: val1.GetAddress().String(), - Amount: tokens, - } - - // Generate a transaction for testing --multisig with an address not in the keyring. - multisigTx, err := clitestutil.SubmitTestTx( - val1.GetClientCtx(), - msgSend, - val1.GetAddress(), - clitestutil.TestTxConfig{ - GenOnly: true, - }, - ) - s.Require().NoError(err) - - // Save multi tx to file - multiGeneratedTx2File := testutil.WriteToNewTempFile(s.T(), multisigTx.String()) - defer multiGeneratedTx2File.Close() - - // Sign using multisig. We're signing a tx on behalf of the multisig address, - // even though the tx signer is NOT the multisig address. This is fine though, - // as the main point of this test is to test the `--multisig` flag with an address - // that is not in the keyring. - _, err = authclitestutil.TxSignExec(val1.GetClientCtx(), addr1, multiGeneratedTx2File.Name(), "--multisig", multisigAddr.String()) - s.Require().Contains(err.Error(), "error getting account from keybase") -} - -func (s *E2ETestSuite) TestCLIMultisign() { - val1 := s.network.GetValidators()[0] - clientCtx := val1.GetClientCtx() - - // Generate 2 accounts and a multisig. - account1, err := clientCtx.Keyring.Key("newAccount1") - s.Require().NoError(err) - - account2, err := clientCtx.Keyring.Key("newAccount2") - s.Require().NoError(err) - - multisigRecord, err := clientCtx.Keyring.Key("multi") - s.Require().NoError(err) - - addr, err := multisigRecord.GetAddress() - s.Require().NoError(err) - - // Send coins from validator to multisig. - sendTokens := sdk.NewInt64Coin(s.cfg.BondDenom, 10) - s.Require().NoError(s.network.WaitForNextBlock()) - _, err = s.createBankMsg( - val1, addr, - sdk.NewCoins(sendTokens), - clitestutil.TestTxConfig{}, - ) - s.Require().NoError(err) - s.Require().NoError(s.network.WaitForNextBlock()) - - var balRes banktypes.QueryAllBalancesResponse - err = s.network.RetryForBlocks(func() error { - resp, err := testutil.GetRequest(fmt.Sprintf("%s/cosmos/bank/v1beta1/balances/%s", val1.GetAPIAddress(), addr)) - if err != nil { - return err - } - return clientCtx.Codec.UnmarshalJSON(resp, &balRes) - }, 3) - s.Require().NoError(err) - s.Require().True(sendTokens.Amount.Equal(balRes.Balances.AmountOf(s.cfg.BondDenom))) - - tokens := sdk.NewCoins(sdk.NewInt64Coin(s.cfg.BondDenom, 5)) - msgSend := &banktypes.MsgSend{ - FromAddress: addr.String(), - ToAddress: val1.GetAddress().String(), - Amount: tokens, - } - - // Generate multisig transaction. - multiGeneratedTx, err := clitestutil.SubmitTestTx( - clientCtx, - msgSend, - addr, - clitestutil.TestTxConfig{ - GenOnly: true, - }, - ) - s.Require().NoError(err) - - // Save tx to file - multiGeneratedTxFile := testutil.WriteToNewTempFile(s.T(), multiGeneratedTx.String()) - defer multiGeneratedTxFile.Close() - - addr1, err := account1.GetAddress() - s.Require().NoError(err) - // Sign with account1 - clientCtx.HomeDir = strings.Replace(clientCtx.HomeDir, "simd", "simcli", 1) - account1Signature, err := authclitestutil.TxSignExec(clientCtx, addr1, multiGeneratedTxFile.Name(), "--multisig", addr.String()) - s.Require().NoError(err) - - sign1File := testutil.WriteToNewTempFile(s.T(), account1Signature.String()) - defer sign1File.Close() - - addr2, err := account2.GetAddress() - s.Require().NoError(err) - // Sign with account2 - account2Signature, err := authclitestutil.TxSignExec(clientCtx, addr2, multiGeneratedTxFile.Name(), "--multisig", addr.String()) - s.Require().NoError(err) - - sign2File := testutil.WriteToNewTempFile(s.T(), account2Signature.String()) - defer sign2File.Close() - - // Work in offline mode. - multisigAccNum, multisigSeq, err := clientCtx.AccountRetriever.GetAccountNumberSequence(clientCtx, addr) - s.Require().NoError(err) - _, err = authclitestutil.TxMultiSignExec( - clientCtx, - multisigRecord.Name, - multiGeneratedTxFile.Name(), - fmt.Sprintf("--%s", flags.FlagOffline), - fmt.Sprintf("--%s=%d", flags.FlagAccountNumber, multisigAccNum), - fmt.Sprintf("--%s=%d", flags.FlagSequence, multisigSeq), - sign1File.Name(), - sign2File.Name(), - ) - s.Require().NoError(err) - - clientCtx.Offline = false - multiSigWith2Signatures, err := authclitestutil.TxMultiSignExec(clientCtx, multisigRecord.Name, multiGeneratedTxFile.Name(), sign1File.Name(), sign2File.Name()) - s.Require().NoError(err) - - // Write the output to disk - signedTxFile := testutil.WriteToNewTempFile(s.T(), multiSigWith2Signatures.String()) - defer signedTxFile.Close() - - _, err = authclitestutil.TxValidateSignaturesExec(clientCtx, signedTxFile.Name()) - s.Require().NoError(err) - - clientCtx.BroadcastMode = flags.BroadcastSync - _, err = authclitestutil.TxBroadcastExec(clientCtx, signedTxFile.Name()) - s.Require().NoError(err) - - s.Require().NoError(s.network.WaitForNextBlock()) -} - -func (s *E2ETestSuite) TestSignBatchMultisig() { - val := s.network.GetValidators()[0] - clientCtx := val.GetClientCtx() - - // Fetch 2 accounts and a multisig. - account1, err := clientCtx.Keyring.Key("newAccount1") - s.Require().NoError(err) - account2, err := clientCtx.Keyring.Key("newAccount2") - s.Require().NoError(err) - multisigRecord, err := clientCtx.Keyring.Key("multi") - s.Require().NoError(err) - - addr, err := multisigRecord.GetAddress() - s.Require().NoError(err) - // Send coins from validator to multisig. - sendTokens := sdk.NewInt64Coin(s.cfg.BondDenom, 10) - _, err = s.createBankMsg( - val, - addr, - sdk.NewCoins(sendTokens), - clitestutil.TestTxConfig{}, - ) - s.Require().NoError(err) - s.Require().NoError(s.network.WaitForNextBlock()) - - tokens := sdk.NewCoins( - sdk.NewCoin(s.cfg.BondDenom, math.NewInt(1)), - ) - msgSend := &banktypes.MsgSend{ - FromAddress: addr.String(), - ToAddress: val.GetAddress().String(), - Amount: tokens, - } - - generatedStd, err := clitestutil.SubmitTestTx( - clientCtx, - msgSend, - addr, - clitestutil.TestTxConfig{ - GenOnly: true, - }, - ) - s.Require().NoError(err) - - // Write the output to disk - filename := testutil.WriteToNewTempFile(s.T(), strings.Repeat(generatedStd.String(), 1)) - defer filename.Close() - clientCtx.HomeDir = strings.Replace(clientCtx.HomeDir, "simd", "simcli", 1) - - addr1, err := account1.GetAddress() - s.Require().NoError(err) - // sign-batch file - res, err := authclitestutil.TxSignBatchExec(clientCtx, addr1, filename.Name(), fmt.Sprintf("--%s=%s", flags.FlagChainID, clientCtx.ChainID), "--multisig", addr.String(), "--signature-only") - s.Require().NoError(err) - s.Require().Equal(1, len(strings.Split(strings.Trim(res.String(), "\n"), "\n"))) - // write sigs to file - file1 := testutil.WriteToNewTempFile(s.T(), res.String()) - defer file1.Close() - - addr2, err := account2.GetAddress() - s.Require().NoError(err) - // sign-batch file with account2 - res, err = authclitestutil.TxSignBatchExec(clientCtx, addr2, filename.Name(), fmt.Sprintf("--%s=%s", flags.FlagChainID, clientCtx.ChainID), "--multisig", addr.String(), "--signature-only") - s.Require().NoError(err) - s.Require().Equal(1, len(strings.Split(strings.Trim(res.String(), "\n"), "\n"))) - // write sigs to file2 - file2 := testutil.WriteToNewTempFile(s.T(), res.String()) - defer file2.Close() - _, err = authclitestutil.TxMultiSignExec(clientCtx, multisigRecord.Name, filename.Name(), file1.Name(), file2.Name()) - s.Require().NoError(err) -} - -func (s *E2ETestSuite) TestMultisignBatch() { - val := s.network.GetValidators()[0] - clientCtx := val.GetClientCtx() - - // Fetch 2 accounts and a multisig. - account1, err := clientCtx.Keyring.Key("newAccount1") - s.Require().NoError(err) - account2, err := clientCtx.Keyring.Key("newAccount2") - s.Require().NoError(err) - multisigRecord, err := clientCtx.Keyring.Key("multi") - s.Require().NoError(err) - - addr, err := multisigRecord.GetAddress() - s.Require().NoError(err) - // Send coins from validator to multisig. - sendTokens := sdk.NewInt64Coin(s.cfg.BondDenom, 1000) - _, err = s.createBankMsg( - val, - addr, - sdk.NewCoins(sendTokens), - clitestutil.TestTxConfig{}, - ) - s.Require().NoError(err) - s.Require().NoError(s.network.WaitForNextBlock()) - - tokens := sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, math.NewInt(1))) - msgSend := &banktypes.MsgSend{ - FromAddress: addr.String(), - ToAddress: val.GetAddress().String(), - Amount: tokens, - } - - generatedStd, err := clitestutil.SubmitTestTx( - clientCtx, - msgSend, - addr, - clitestutil.TestTxConfig{ - GenOnly: true, - }, - ) - s.Require().NoError(err) - - // Write the output to disk - filename := testutil.WriteToNewTempFile(s.T(), strings.Repeat(generatedStd.String()+"\n", 3)) - defer filename.Close() - clientCtx.HomeDir = strings.Replace(clientCtx.HomeDir, "simd", "simcli", 1) - - account, err := clientCtx.AccountRetriever.GetAccount(clientCtx, addr) - s.Require().NoError(err) - - // sign-batch file - addr1, err := account1.GetAddress() - s.Require().NoError(err) - res, err := authclitestutil.TxSignBatchExec(clientCtx, addr1, filename.Name(), fmt.Sprintf("--%s=%s", flags.FlagChainID, clientCtx.ChainID), "--multisig", addr.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())), "--signature-only") - s.Require().NoError(err) - s.Require().Equal(3, len(strings.Split(strings.Trim(res.String(), "\n"), "\n"))) - // write sigs to file - file1 := testutil.WriteToNewTempFile(s.T(), res.String()) - defer file1.Close() - - // sign-batch file with account2 - addr2, err := account2.GetAddress() - s.Require().NoError(err) - res, err = authclitestutil.TxSignBatchExec(clientCtx, addr2, filename.Name(), fmt.Sprintf("--%s=%s", flags.FlagChainID, clientCtx.ChainID), "--multisig", addr.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())), "--signature-only") - s.Require().NoError(err) - s.Require().Equal(3, len(strings.Split(strings.Trim(res.String(), "\n"), "\n"))) - - // multisign the file - file2 := testutil.WriteToNewTempFile(s.T(), res.String()) - defer file2.Close() - res, err = authclitestutil.TxMultiSignBatchExec(clientCtx, filename.Name(), multisigRecord.Name, file1.Name(), file2.Name()) - s.Require().NoError(err) - signedTxs := strings.Split(strings.Trim(res.String(), "\n"), "\n") - - // Broadcast transactions. - for _, signedTx := range signedTxs { - func() { - signedTxFile := testutil.WriteToNewTempFile(s.T(), signedTx) - defer signedTxFile.Close() - clientCtx.BroadcastMode = flags.BroadcastSync - _, err = authclitestutil.TxBroadcastExec(clientCtx, signedTxFile.Name()) - s.Require().NoError(err) - s.Require().NoError(s.network.WaitForNextBlock()) - }() - } -} - -func TestGetBroadcastCommandOfflineFlag(t *testing.T) { - cmd := authcli.GetBroadcastCommand() - _ = testutil.ApplyMockIODiscardOutErr(cmd) - cmd.SetArgs([]string{fmt.Sprintf("--%s=true", flags.FlagOffline), ""}) - - require.EqualError(t, cmd.Execute(), "cannot broadcast tx during offline mode") -} - -func TestGetBroadcastCommandWithoutOfflineFlag(t *testing.T) { - var txCfg client.TxConfig - err := depinject.Inject(authtestkeeper.AppConfig, &txCfg) - require.NoError(t, err) - clientCtx := client.Context{} - clientCtx = clientCtx.WithTxConfig(txCfg) - - ctx := context.Background() - ctx = context.WithValue(ctx, client.ClientContextKey, &clientCtx) - - cmd := authcli.GetBroadcastCommand() - _, out := testutil.ApplyMockIO(cmd) - - // Create new file with tx - builder := txCfg.NewTxBuilder() - builder.SetGasLimit(200000) - err = builder.SetMsgs(banktypes.NewMsgSend("cosmos1cxlt8kznps92fwu3j6npahx4mjfutydyene2qw", "cosmos1cxlt8kznps92fwu3j6npahx4mjfutydyene2qw", sdk.Coins{sdk.NewInt64Coin("stake", 10000)})) - require.NoError(t, err) - txContents, err := txCfg.TxJSONEncoder()(builder.GetTx()) - require.NoError(t, err) - txFile := testutil.WriteToNewTempFile(t, string(txContents)) - defer txFile.Close() - - cmd.SetArgs([]string{txFile.Name()}) - err = cmd.ExecuteContext(ctx) - require.Error(t, err) - require.Contains(t, err.Error(), "connect: connection refused") - require.Contains(t, out.String(), "connect: connection refused") -} - -// TestTxWithoutPublicKey makes sure sending a proto tx message without the -// public key doesn't cause any error in the RPC layer (broadcast). -// See https://github.com/cosmos/cosmos-sdk/issues/7585 for more details. -func (s *E2ETestSuite) TestTxWithoutPublicKey() { - val1 := s.network.GetValidators()[0] - clientCtx := val1.GetClientCtx() - txCfg := clientCtx.TxConfig - - // Create a txBuilder with an unsigned tx. - txBuilder := txCfg.NewTxBuilder() - msg := banktypes.NewMsgSend(val1.GetAddress().String(), val1.GetAddress().String(), sdk.NewCoins( - sdk.NewCoin(s.cfg.BondDenom, math.NewInt(10)), - )) - err := txBuilder.SetMsgs(msg) - s.Require().NoError(err) - txBuilder.SetFeeAmount(sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, math.NewInt(150)))) - txBuilder.SetGasLimit(testdata.NewTestGasLimit()) - // Set empty signature to set signer infos. - sigV2 := signing.SignatureV2{ - PubKey: val1.GetPubKey(), - Data: &signing.SingleSignatureData{ - SignMode: signing.SignMode_SIGN_MODE_DIRECT, - Signature: nil, - }, - } - err = txBuilder.SetSignatures(sigV2) - s.Require().NoError(err) - - // Create a file with the unsigned tx. - txJSON, err := txCfg.TxJSONEncoder()(txBuilder.GetTx()) - s.Require().NoError(err) - unsignedTxFile := testutil.WriteToNewTempFile(s.T(), string(txJSON)) - defer unsignedTxFile.Close() - - // Sign the file with the unsignedTx. - signedTx, err := authclitestutil.TxSignExec(clientCtx, val1.GetAddress(), unsignedTxFile.Name(), fmt.Sprintf("--%s=true", cli.FlagOverwrite)) - s.Require().NoError(err) - - // Remove the signerInfo's `public_key` field manually from the signedTx. - // Note: this method is only used for test purposes! In general, one should - // use txBuilder and TxEncoder/TxDecoder to manipulate txs. - var tx tx.Tx - err = clientCtx.Codec.UnmarshalJSON(signedTx.Bytes(), &tx) - s.Require().NoError(err) - tx.AuthInfo.SignerInfos[0].PublicKey = nil - // Re-encode the tx again, to another file. - txJSON, err = clientCtx.Codec.MarshalJSON(&tx) - s.Require().NoError(err) - signedTxFile := testutil.WriteToNewTempFile(s.T(), string(txJSON)) - defer signedTxFile.Close() - s.Require().True(strings.Contains(string(txJSON), "\"public_key\":null")) - - // Broadcast tx, test that it shouldn't panic. - clientCtx.BroadcastMode = flags.BroadcastSync - out, err := authclitestutil.TxBroadcastExec(clientCtx, signedTxFile.Name()) - s.Require().NoError(err) - var res sdk.TxResponse - s.Require().NoError(clientCtx.Codec.UnmarshalJSON(out.Bytes(), &res)) - s.Require().NotEqual(0, res.Code) -} - -// TestSignWithMultiSignersAminoJSON tests the case where a transaction with 2 -// messages which has to be signed with 2 different keys. Sign and append the -// signatures using the CLI with Amino signing mode. Finally, send the -// transaction to the blockchain. -func (s *E2ETestSuite) TestSignWithMultiSignersAminoJSON() { - require := s.Require() - val0, val1 := s.network.GetValidators()[0], s.network.GetValidators()[1] - val0Coin := sdk.NewCoin(fmt.Sprintf("%stoken", val0.GetMoniker()), math.NewInt(10)) - val1Coin := sdk.NewCoin(fmt.Sprintf("%stoken", val1.GetMoniker()), math.NewInt(10)) - _, _, addr1 := testdata.KeyTestPubAddr() - - // Creating a tx with 2 msgs from 2 signers: val0 and val1. - // The validators need to sign with SIGN_MODE_LEGACY_AMINO_JSON, - // because DIRECT doesn't support multi signers via the CLI. - // Since we use amino, we don't need to pre-populate signer_infos. - txBuilder := val0.GetClientCtx().TxConfig.NewTxBuilder() - val0Str, err := s.ac.BytesToString(val0.GetAddress()) - s.Require().NoError(err) - val1Str, err := s.ac.BytesToString(val1.GetAddress()) - s.Require().NoError(err) - addrStr, err := s.ac.BytesToString(addr1) - s.Require().NoError(err) - err = txBuilder.SetMsgs( - banktypes.NewMsgSend(val0Str, addrStr, sdk.NewCoins(val0Coin)), - banktypes.NewMsgSend(val1Str, addrStr, sdk.NewCoins(val1Coin)), - ) - require.NoError(err) - txBuilder.SetFeeAmount(sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, math.NewInt(10)))) - txBuilder.SetGasLimit(testdata.NewTestGasLimit() * 2) - signers, err := txBuilder.GetTx().GetSigners() - require.NoError(err) - require.Equal([][]byte{val0.GetAddress(), val1.GetAddress()}, signers) - - // Write the unsigned tx into a file. - txJSON, err := val0.GetClientCtx().TxConfig.TxJSONEncoder()(txBuilder.GetTx()) - require.NoError(err) - unsignedTxFile := testutil.WriteToNewTempFile(s.T(), string(txJSON)) - defer unsignedTxFile.Close() - - // Let val0 sign first the file with the unsignedTx. - signedByVal0, err := authclitestutil.TxSignExec(val0.GetClientCtx(), val0.GetAddress(), unsignedTxFile.Name(), "--overwrite", "--sign-mode=amino-json") - require.NoError(err) - signedByVal0File := testutil.WriteToNewTempFile(s.T(), signedByVal0.String()) - defer signedByVal0File.Close() - - // Then let val1 sign the file with signedByVal0. - val1AccNum, val1Seq, err := val0.GetClientCtx().AccountRetriever.GetAccountNumberSequence(val0.GetClientCtx(), val1.GetAddress()) - require.NoError(err) - - signedTx, err := authclitestutil.TxSignExec( - val1.GetClientCtx(), - val1.GetAddress(), - signedByVal0File.Name(), - "--offline", - fmt.Sprintf("--account-number=%d", val1AccNum), - fmt.Sprintf("--sequence=%d", val1Seq), - "--sign-mode=amino-json", - ) - require.NoError(err) - signedTxFile := testutil.WriteToNewTempFile(s.T(), signedTx.String()) - defer signedTxFile.Close() - - res, err := authclitestutil.TxBroadcastExec( - val0.GetClientCtx(), - signedTxFile.Name(), - fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), - ) - require.NoError(err) - require.NoError(s.network.WaitForNextBlock()) - - var txRes sdk.TxResponse - require.NoError(val0.GetClientCtx().Codec.UnmarshalJSON(res.Bytes(), &txRes)) - require.Equal(uint32(0), txRes.Code, txRes.RawLog) - - // Make sure the addr1's balance got funded. - resp, err := testutil.GetRequest(fmt.Sprintf("%s/cosmos/bank/v1beta1/balances/%s", val0.GetAPIAddress(), addr1)) - s.Require().NoError(err) - var queryRes banktypes.QueryAllBalancesResponse - err = val0.GetClientCtx().Codec.UnmarshalJSON(resp, &queryRes) - require.NoError(err) - require.Equal(sdk.NewCoins(val0Coin, val1Coin), queryRes.Balances) -} - -func (s *E2ETestSuite) TestAuxSigner() { - s.T().Skip("re-enable this when we bring back sign mode aux client testing") - require := s.Require() - val := s.network.GetValidators()[0] - val0Coin := sdk.NewCoin(fmt.Sprintf("%stoken", val.GetMoniker()), math.NewInt(10)) - - testCases := []struct { - name string - args []string - expectErr bool - }{ - { - "error with SIGN_MODE_DIRECT_AUX and --aux unset", - []string{ - fmt.Sprintf("--%s=%s", flags.FlagSignMode, flags.SignModeDirectAux), - }, - true, - }, - { - "no error with SIGN_MDOE_DIRECT_AUX mode and generate-only set (ignores generate-only)", - []string{ - fmt.Sprintf("--%s=%s", flags.FlagSignMode, flags.SignModeDirectAux), - fmt.Sprintf("--%s=true", flags.FlagGenerateOnly), - }, - false, - }, - { - "no error with SIGN_MDOE_DIRECT_AUX mode and generate-only, tip flag set", - []string{ - fmt.Sprintf("--%s=%s", flags.FlagSignMode, flags.SignModeDirectAux), - fmt.Sprintf("--%s=true", flags.FlagGenerateOnly), - fmt.Sprintf("--%s=%s", flags.FlagTip, val0Coin.String()), - }, - false, - }, - } - - for _, tc := range testCases { - s.Run(tc.name, func() { - _, err := govtestutil.MsgSubmitLegacyProposal( - val.GetClientCtx(), - val.GetAddress().String(), - "test", - "test desc", - govtypes.ProposalTypeText, - tc.args..., - ) - if tc.expectErr { - require.Error(err) - } else { - require.NoError(err) - } - }) - } -} - -func (s *E2ETestSuite) TestAuxToFeeWithTips() { - // Skipping this test as it needs a simapp with the TipDecorator in post handler. - s.T().Skip() - - require := s.Require() - val := s.network.GetValidators()[0] - - kb := s.network.GetValidators()[0].GetClientCtx().Keyring - acc, _, err := kb.NewMnemonic("tipperAccount", keyring.English, sdk.FullFundraiserPath, keyring.DefaultBIP39Passphrase, hd.Secp256k1) - require.NoError(err) - - tipper, err := acc.GetAddress() - require.NoError(err) - tipperInitialBal := sdk.NewCoin(fmt.Sprintf("%stoken", val.GetMoniker()), math.NewInt(10000)) - - feePayer := val.GetAddress() - fee := sdk.NewCoin(s.cfg.BondDenom, math.NewInt(1000)) - tip := sdk.NewCoin(fmt.Sprintf("%stoken", val.GetMoniker()), math.NewInt(1000)) - - require.NoError(s.network.WaitForNextBlock()) - _, err = s.createBankMsg( - val, - tipper, - sdk.NewCoins(tipperInitialBal), - clitestutil.TestTxConfig{}, - ) - require.NoError(err) - require.NoError(s.network.WaitForNextBlock()) - - bal := s.getBalances(val.GetClientCtx(), tipper, tip.Denom) - require.True(bal.Equal(tipperInitialBal.Amount)) - - testCases := []struct { - name string - tipper sdk.AccAddress - feePayer sdk.AccAddress - tip sdk.Coin - expectErrAux bool - expectErrBroadCast bool - errMsg string - tipperArgs []string - feePayerArgs []string - }{ - { - name: "when --aux and --sign-mode = direct set: error", - tipper: tipper, - feePayer: feePayer, - tip: tip, - tipperArgs: []string{ - fmt.Sprintf("--%s=%s", flags.FlagSignMode, flags.SignModeDirect), - fmt.Sprintf("--%s=%s", flags.FlagTip, tip), - fmt.Sprintf("--%s=true", flags.FlagAux), - }, - expectErrAux: true, - feePayerArgs: []string{ - fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), - fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), - fmt.Sprintf("--%s=%s", flags.FlagFrom, feePayer), - fmt.Sprintf("--%s=%s", flags.FlagFees, fee.String()), - }, - }, - { - name: "both tipper, fee payer uses AMINO: no error", - tipper: tipper, - feePayer: feePayer, - tip: tip, - tipperArgs: []string{ - fmt.Sprintf("--%s=%s", flags.FlagSignMode, flags.SignModeLegacyAminoJSON), - fmt.Sprintf("--%s=%s", flags.FlagTip, tip), - fmt.Sprintf("--%s=true", flags.FlagAux), - }, - feePayerArgs: []string{ - fmt.Sprintf("--%s=%s", flags.FlagSignMode, flags.SignModeLegacyAminoJSON), - fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), - fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), - fmt.Sprintf("--%s=%s", flags.FlagFrom, feePayer), - fmt.Sprintf("--%s=%s", flags.FlagFees, fee.String()), - }, - }, - { - name: "tipper uses DIRECT_AUX, fee payer uses AMINO: no error", - tipper: tipper, - feePayer: feePayer, - tip: tip, - tipperArgs: []string{ - fmt.Sprintf("--%s=%s", flags.FlagSignMode, flags.SignModeDirectAux), - fmt.Sprintf("--%s=%s", flags.FlagTip, tip), - fmt.Sprintf("--%s=true", flags.FlagAux), - }, - feePayerArgs: []string{ - fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), - fmt.Sprintf("--%s=%s", flags.FlagSignMode, flags.SignModeLegacyAminoJSON), - fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), - fmt.Sprintf("--%s=%s", flags.FlagFrom, feePayer), - fmt.Sprintf("--%s=%s", flags.FlagFees, fee.String()), - }, - }, - { - name: "--tip flag unset: no error", - tipper: tipper, - feePayer: feePayer, - tip: sdk.Coin{Denom: fmt.Sprintf("%stoken", val.GetMoniker()), Amount: math.NewInt(0)}, - tipperArgs: []string{ - fmt.Sprintf("--%s=%s", flags.FlagSignMode, flags.SignModeDirectAux), - fmt.Sprintf("--%s=true", flags.FlagAux), - }, - feePayerArgs: []string{ - fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), - fmt.Sprintf("--%s=%s", flags.FlagSignMode, flags.SignModeLegacyAminoJSON), - fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), - fmt.Sprintf("--%s=%s", flags.FlagFrom, feePayer), - fmt.Sprintf("--%s=%s", flags.FlagFees, fee.String()), - }, - }, - { - name: "legacy amino json: no error", - tipper: tipper, - feePayer: feePayer, - tip: tip, - tipperArgs: []string{ - fmt.Sprintf("--%s=%s", flags.FlagSignMode, flags.SignModeLegacyAminoJSON), - fmt.Sprintf("--%s=%s", flags.FlagTip, tip), - fmt.Sprintf("--%s=true", flags.FlagAux), - }, - feePayerArgs: []string{ - fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), - fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), - fmt.Sprintf("--%s=%s", flags.FlagFrom, feePayer), - fmt.Sprintf("--%s=%s", flags.FlagFees, fee.String()), - }, - }, - { - name: "tipper uses direct aux, fee payer uses direct: happy case", - tipper: tipper, - feePayer: feePayer, - tip: tip, - tipperArgs: []string{ - fmt.Sprintf("--%s=%s", flags.FlagSignMode, flags.SignModeDirectAux), - fmt.Sprintf("--%s=%s", flags.FlagTip, tip), - fmt.Sprintf("--%s=true", flags.FlagAux), - }, - feePayerArgs: []string{ - fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), - fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), - fmt.Sprintf("--%s=%s", flags.FlagFrom, feePayer), - fmt.Sprintf("--%s=%s", flags.FlagFees, fee.String()), - }, - }, - { - name: "chain-id mismatch: error", - tipper: tipper, - feePayer: feePayer, - tip: tip, - tipperArgs: []string{ - fmt.Sprintf("--%s=%s", flags.FlagSignMode, flags.SignModeDirectAux), - fmt.Sprintf("--%s=%s", flags.FlagTip, tip), - fmt.Sprintf("--%s=true", flags.FlagAux), - }, - expectErrAux: false, - feePayerArgs: []string{ - fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), - fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), - fmt.Sprintf("--%s=%s", flags.FlagFrom, feePayer), - fmt.Sprintf("--%s=%s", flags.FlagFees, fee.String()), - fmt.Sprintf("--%s=%s", flags.FlagChainID, "foobar"), - }, - expectErrBroadCast: true, - }, - { - name: "wrong denom in tip: error", - tipper: tipper, - feePayer: feePayer, - tip: sdk.Coin{Denom: fmt.Sprintf("%stoken", val.GetMoniker()), Amount: math.NewInt(0)}, - tipperArgs: []string{ - fmt.Sprintf("--%s=%s", flags.FlagTip, "1000wrongDenom"), - fmt.Sprintf("--%s=%s", flags.FlagSignMode, flags.SignModeDirectAux), - fmt.Sprintf("--%s=true", flags.FlagAux), - }, - feePayerArgs: []string{ - fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), - fmt.Sprintf("--%s=%s", flags.FlagSignMode, flags.SignModeDirect), - fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), - fmt.Sprintf("--%s=%s", flags.FlagFrom, feePayer), - fmt.Sprintf("--%s=%s", flags.FlagFees, fee.String()), - }, - errMsg: "insufficient funds", - }, - { - name: "insufficient fees: error", - tipper: tipper, - feePayer: feePayer, - tip: sdk.Coin{Denom: fmt.Sprintf("%stoken", val.GetMoniker()), Amount: math.NewInt(0)}, - tipperArgs: []string{ - fmt.Sprintf("--%s=%s", flags.FlagTip, tip), - fmt.Sprintf("--%s=%s", flags.FlagSignMode, flags.SignModeDirectAux), - fmt.Sprintf("--%s=true", flags.FlagAux), - }, - feePayerArgs: []string{ - fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), - fmt.Sprintf("--%s=%s", flags.FlagSignMode, flags.SignModeDirect), - fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), - fmt.Sprintf("--%s=%s", flags.FlagFrom, feePayer), - }, - errMsg: "insufficient fees", - }, - } - - for _, tc := range testCases { - s.Run(tc.name, func() { - res, err := govtestutil.MsgSubmitLegacyProposal( - val.GetClientCtx(), - tipper.String(), - "test", - "test desc", - govtypes.ProposalTypeText, - tc.tipperArgs..., - ) - - if tc.expectErrAux { - require.Error(err) - } else { - require.NoError(err) - genTxFile := testutil.WriteToNewTempFile(s.T(), string(res.Bytes())) - defer genTxFile.Close() - - s.Require().NoError(s.network.WaitForNextBlock()) - - switch { - case tc.expectErrBroadCast: - require.Error(err) - - case tc.errMsg != "": - require.NoError(err) - - var txRes sdk.TxResponse - require.NoError(val.GetClientCtx().Codec.UnmarshalJSON(res.Bytes(), &txRes)) - - require.Contains(txRes.RawLog, tc.errMsg) - - default: - require.NoError(err) - - var txRes sdk.TxResponse - require.NoError(val.GetClientCtx().Codec.UnmarshalJSON(res.Bytes(), &txRes)) - - require.Equal(uint32(0), txRes.Code) - require.NotNil(int64(0), txRes.Height) - - bal = s.getBalances(val.GetClientCtx(), tipper, tc.tip.Denom) - tipperInitialBal = tipperInitialBal.Sub(tc.tip) - require.True(bal.Equal(tipperInitialBal.Amount)) - } - } - }) - } -} - -func (s *E2ETestSuite) createBankMsg(val network.ValidatorI, toAddr sdk.AccAddress, amount sdk.Coins, config clitestutil.TestTxConfig) (testutil.BufferWriter, error) { - msgSend := &banktypes.MsgSend{ - FromAddress: val.GetAddress().String(), - ToAddress: toAddr.String(), - Amount: amount, - } - - return clitestutil.SubmitTestTx(val.GetClientCtx(), msgSend, val.GetAddress(), config) -} - -func (s *E2ETestSuite) getBalances(clientCtx client.Context, addr sdk.AccAddress, denom string) math.Int { - resp, err := testutil.GetRequest(fmt.Sprintf("%s/cosmos/bank/v1beta1/balances/%s/by_denom?denom=%s", s.cfg.APIAddress, addr.String(), denom)) - s.Require().NoError(err) - - var balRes banktypes.QueryAllBalancesResponse - err = clientCtx.Codec.UnmarshalJSON(resp, &balRes) - s.Require().NoError(err) - startTokens := balRes.Balances.AmountOf(denom) - return startTokens -} - -func (s *E2ETestSuite) deepContains(events []abci.Event, value string) bool { - for _, e := range events { - for _, attr := range e.Attributes { - if strings.Contains(attr.Value, value) { - return true - } - } - } - return false -} diff --git a/tests/systemtests/auth_test.go b/tests/systemtests/auth_test.go new file mode 100644 index 000000000000..c45652f3cf8a --- /dev/null +++ b/tests/systemtests/auth_test.go @@ -0,0 +1,508 @@ +//go:build system_test + +package systemtests + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/tidwall/gjson" + "github.com/tidwall/sjson" +) + +const ( + authTestDenom = "stake" +) + +func TestAuthSignAndBroadcastTxCmd(t *testing.T) { + // scenario: test auth sign and broadcast commands + // given a running chain + + sut.ResetChain(t) + require.GreaterOrEqual(t, sut.NodesCount(), 2) + + cli := NewCLIWrapper(t, sut, verbose) + + // get validator addresses + val1Addr := cli.GetKeyAddr("node0") + require.NotEmpty(t, val1Addr) + + val2Addr := cli.GetKeyAddr("node1") + require.NotEmpty(t, val2Addr) + + sut.StartChain(t) + + var transferAmount, feeAmount int64 = 1000, 1 + + // test sign tx command + + // run bank tx send with --generate-only flag + sendTx := generateBankSendTx(t, cli, val1Addr, val2Addr, transferAmount, feeAmount, "") + txFile := StoreTempFile(t, []byte(fmt.Sprintf("%s\n", sendTx))) + + // query node0 account details + signTxCmd := []string{"tx", "sign", txFile.Name(), "--from=" + val1Addr, "--chain-id=" + cli.chainID} + testSignTxBroadcast(t, cli, signTxCmd, "sign tx", val1Addr, val2Addr, transferAmount, feeAmount) + + // test broadcast with empty public key in signed tx + rsp := cli.RunCommandWithArgs(cli.withTXFlags("tx", "sign", txFile.Name(), "--from="+val1Addr)...) + updated, err := sjson.Set(rsp, "auth_info.signer_infos.0.public_key", nil) + require.NoError(t, err) + newSignFile := StoreTempFile(t, []byte(updated)) + + broadcastCmd := []string{"tx", "broadcast", newSignFile.Name()} + rsp = cli.RunCommandWithArgs(cli.withTXFlags(broadcastCmd...)...) + RequireTxFailure(t, rsp) + + // test sign-batch tx command + + // generate another bank send tx with less amount + newAmount := int64(100) + sendTx2 := generateBankSendTx(t, cli, val1Addr, val2Addr, newAmount, feeAmount, "") + tx2File := StoreTempFile(t, []byte(fmt.Sprintf("%s\n", sendTx2))) + + signBatchCmd := []string{"tx", "sign-batch", txFile.Name(), tx2File.Name(), "--from=" + val1Addr, "--chain-id=" + cli.chainID} + sendAmount := transferAmount + newAmount + fees := feeAmount * 2 + + // TODO: remove below block code once v2 supports multi messages + // ref: https://github.com/cosmos/cosmos-sdk/issues/22215 + if isV2() { + sendAmount = transferAmount + fees = feeAmount + } + + testSignTxBroadcast(t, cli, signBatchCmd, "sign-batch tx", val1Addr, val2Addr, sendAmount, fees) +} + +func testSignTxBroadcast(t *testing.T, cli *CLIWrapper, txCmd []string, prefix, fromAddr, toAddr string, amount, fees int64) { + t.Helper() + + fromAddrBal := cli.QueryBalance(fromAddr, authTestDenom) + toAddrBal := cli.QueryBalance(toAddr, authTestDenom) + + // query from account details + rsp := cli.CustomQuery("q", "auth", "accounts") + details := gjson.Get(rsp, fmt.Sprintf("accounts.#(value.address==%s).value", fromAddr)).String() + accSeq := gjson.Get(details, "sequence").Int() + accNum := gjson.Get(details, "account_number").Int() + + testCases := []struct { + name string + extraArgs []string + }{ + { + "valid tx sign with offline mode", + []string{ + "--offline", + fmt.Sprintf("--account-number=%d", accNum), + fmt.Sprintf("--sequence=%d", accSeq), + }, + }, + { + "valid tx sign", + []string{}, + }, + { + "valid tx sign with sign-mode", + []string{"--sign-mode=amino-json"}, + }, + } + + for _, tc := range testCases { + t.Run(prefix+"-"+tc.name, func(t *testing.T) { + cmd := append(txCmd, tc.extraArgs...) + + // run tx sign command and verify signatures count + rsp = cli.RunCommandWithArgs(cli.withKeyringFlags(cmd...)...) + + signatures := gjson.Get(rsp, "signatures").Array() + require.Len(t, signatures, 1) + + signFile := StoreTempFile(t, []byte(rsp)) + + // validate signature + rsp = cli.RunCommandWithArgs(cli.withKeyringFlags("tx", "validate-signatures", signFile.Name(), "--from="+fromAddr, "--chain-id="+cli.chainID)...) + require.Contains(t, rsp, "[OK]") + + // run broadcast tx command + broadcastCmd := []string{"tx", "broadcast", signFile.Name()} + rsp = cli.RunAndWait(broadcastCmd...) + RequireTxSuccess(t, rsp) + + // query balance and confirm transaction + expVal1Bal := fromAddrBal - amount - fees + fromAddrBal = cli.QueryBalance(fromAddr, authTestDenom) + require.Equal(t, expVal1Bal, fromAddrBal) + + expVal2Bal := toAddrBal + amount + toAddrBal = cli.QueryBalance(toAddr, authTestDenom) + require.Equal(t, expVal2Bal, toAddrBal) + }) + } +} + +func TestAuthQueryTxCmds(t *testing.T) { + // scenario: test query tx and txs commands + // given a running chain + + sut.ResetChain(t) + require.GreaterOrEqual(t, sut.NodesCount(), 2) + + cli := NewCLIWrapper(t, sut, verbose) + + // get validator addresses + val1Addr := cli.GetKeyAddr("node0") + require.NotEmpty(t, val1Addr) + + val2Addr := cli.GetKeyAddr("node1") + require.NotEmpty(t, val2Addr) + + sut.StartChain(t) + + // do a bank transfer and use it for query txs + feeAmount := "2stake" + rsp := cli.RunAndWait("tx", "bank", "send", val1Addr, val2Addr, "10000stake", "--fees="+feeAmount) + RequireTxSuccess(t, rsp) + + // parse values from above tx + height := gjson.Get(rsp, "height").String() + txHash := gjson.Get(rsp, "txhash").String() + accSeq := fmt.Sprintf("%s/%d", val1Addr, gjson.Get(rsp, "tx.auth_info.signer_infos.0.sequence").Int()) + signature := gjson.Get(rsp, "tx.signatures.0").String() + + // test query tx command + testCases := []struct { + name string + args []string + }{ + { + "valid query with txhash", + []string{txHash}, + }, + { + "valid query with addr+seq filter", + []string{"--type=acc_seq", accSeq}, + }, + { + "valid query with signature filter", + []string{"--type=signature", signature}, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + cmd := []string{"q", "tx"} + rsp = cli.CustomQuery(append(cmd, tc.args...)...) + txHeight := gjson.Get(rsp, "height").String() + require.Equal(t, height, txHeight) + }) + } + + // test query txs command + txsTestCases := []struct { + name string + query string + expLen int + }{ + { + "fee event happy case", + fmt.Sprintf("--query=tx.fee='%s'", feeAmount), + 1, + }, + { + "no matching fee event", + "--query=tx.fee='120stake'", + 0, + }, + { + "query with tx height", + fmt.Sprintf("--query=tx.height='%s'", height), + 1, + }, + } + + for _, tc := range txsTestCases { + t.Run(tc.name, func(t *testing.T) { + cmd := []string{"q", "txs", tc.query} + rsp = cli.CustomQuery(cmd...) + txs := gjson.Get(rsp, "txs").Array() + require.Equal(t, tc.expLen, len(txs)) + }) + } +} + +func TestAuthMultisigTxCmds(t *testing.T) { + // scenario: test auth multisig related tx commands + // given a running chain + + sut.ResetChain(t) + cli := NewCLIWrapper(t, sut, verbose) + + // get validator address + valAddr := cli.GetKeyAddr("node0") + require.NotEmpty(t, valAddr) + + // add new keys for multisig + acc1Addr := cli.AddKey("acc1") + require.NotEmpty(t, acc1Addr) + + acc2Addr := cli.AddKey("acc2") + require.NotEqual(t, acc1Addr, acc2Addr) + + acc3Addr := cli.AddKey("acc3") + require.NotEqual(t, acc1Addr, acc3Addr) + + out := cli.RunCommandWithArgs(cli.withKeyringFlags("keys", "add", "multi", "--multisig=acc1,acc2,acc3", "--multisig-threshold=2")...) + multiAddr := gjson.Get(out, "address").String() + require.NotEmpty(t, multiAddr) + + sut.StartChain(t) + + // fund multisig address some amount + var initialAmount, transferAmount, feeAmount int64 = 10000, 100, 1 + _ = cli.FundAddress(multiAddr, fmt.Sprintf("%d%s", initialAmount, authTestDenom)) + + multiAddrBal := cli.QueryBalance(multiAddr, authTestDenom) + require.Equal(t, initialAmount, multiAddrBal) + + // test multisign tx command + + // run bank tx send with --generate-only flag + sendTx := generateBankSendTx(t, cli, multiAddr, valAddr, transferAmount, feeAmount, "") + txFile := StoreTempFile(t, []byte(sendTx)) + + signTxCmd := cli.withKeyringFlags("tx", "sign", txFile.Name(), "--multisig="+multiAddr, "--chain-id="+cli.chainID) + multiSignTxCmd := cli.withKeyringFlags("tx", "multisign", txFile.Name(), "multi", "--chain-id="+cli.chainID) + + testMultisigTxBroadcast(t, cli, multiSigTxInput{ + "multisign", + multiAddr, + valAddr, + acc1Addr, + acc2Addr, + acc3Addr, + signTxCmd, + multiSignTxCmd, + transferAmount, + feeAmount, + }) + + // test multisign-batch tx command + + // generate two send transactions in single file + multiSendTx := fmt.Sprintf("%s\n%s", sendTx, sendTx) + multiTxFile := StoreTempFile(t, []byte(multiSendTx)) + + signBatchTxCmd := cli.withKeyringFlags("tx", "sign-batch", multiTxFile.Name(), "--multisig="+multiAddr, "--signature-only", "--chain-id="+cli.chainID) + multiSignBatchTxCmd := cli.withKeyringFlags("tx", "multisign-batch", multiTxFile.Name(), "multi", "--chain-id="+cli.chainID) + + // as we done couple of bank transactions as batch, + // transferred amount will be twice + sendAmount := transferAmount * 2 + fees := feeAmount * 2 + + // TODO: remove below block code once v2 supports multi messages + // ref: https://github.com/cosmos/cosmos-sdk/issues/22215 + if isV2() { + sendAmount = transferAmount + fees = feeAmount + } + + testMultisigTxBroadcast(t, cli, multiSigTxInput{ + "multisign-batch", + multiAddr, + valAddr, + acc1Addr, + acc2Addr, + acc3Addr, + signBatchTxCmd, + multiSignBatchTxCmd, + sendAmount, + fees, + }) +} + +func generateBankSendTx(t *testing.T, cli *CLIWrapper, fromAddr, toAddr string, amount, fees int64, memo string) string { + t.Helper() + + bankSendGenCmd := []string{ + "tx", "bank", "send", fromAddr, toAddr, + fmt.Sprintf("%d%s", amount, authTestDenom), + fmt.Sprintf("--fees=%d%s", fees, authTestDenom), + "--generate-only", + "--note=" + memo, + } + + return cli.RunCommandWithArgs(cli.withTXFlags(bankSendGenCmd...)...) +} + +type multiSigTxInput struct { + prefix string + multiAddr string + toAddr string + acc1Addr string + acc2Addr string + acc3Addr string + signCmd []string + multiSignCmd []string + transferAmount int64 + feeAmount int64 +} + +func testMultisigTxBroadcast(t *testing.T, cli *CLIWrapper, i multiSigTxInput) { + t.Helper() + + multiAddrBal := cli.QueryBalance(i.multiAddr, authTestDenom) + toAddrBal := cli.QueryBalance(i.toAddr, authTestDenom) + + testCases := []struct { + name string + signingAccs []string + expErrMsg string + }{ + { + "minimum threshold not reached", + []string{i.acc1Addr}, + "signature size is incorrect", + }, + { + "valid tx with two signers", + []string{i.acc1Addr, i.acc2Addr}, + "", + }, + { + "valid tx with three signed files", + []string{i.acc1Addr, i.acc2Addr, i.acc3Addr}, + "", + }, + } + + for _, tc := range testCases { + t.Run(i.prefix+"-"+tc.name, func(t *testing.T) { + // append signed files to multisign command + cmd := i.multiSignCmd + for _, acc := range tc.signingAccs { + rsp := cli.RunCommandWithArgs(append(i.signCmd, "--from="+acc)...) + signFile := StoreTempFile(t, []byte(rsp)) + cmd = append(cmd, signFile.Name()) + } + rsp := cli.RunCommandWithArgs(cmd...) + multiSignFile := StoreTempFile(t, []byte(rsp)) + + // run broadcast tx command + broadcastCmd := []string{"tx", "broadcast", multiSignFile.Name()} + if tc.expErrMsg != "" { + rsp = cli.RunCommandWithArgs(cli.withTXFlags(broadcastCmd...)...) + RequireTxFailure(t, rsp) + require.Contains(t, rsp, tc.expErrMsg) + return + } + + rsp = cli.RunAndWait(broadcastCmd...) + RequireTxSuccess(t, rsp) + + // query balance and confirm transaction + expMultiBal := multiAddrBal - i.transferAmount - i.feeAmount + multiAddrBal = cli.QueryBalance(i.multiAddr, authTestDenom) + require.Equal(t, expMultiBal, multiAddrBal) + + expVal2Bal := toAddrBal + i.transferAmount + toAddrBal = cli.QueryBalance(i.toAddr, authTestDenom) + require.Equal(t, expVal2Bal, toAddrBal) + }) + } +} + +func TestAuxSigner(t *testing.T) { + // scenario: test tx with direct aux sign mode + // given a running chain + + sut.ResetChain(t) + require.GreaterOrEqual(t, sut.NodesCount(), 2) + + cli := NewCLIWrapper(t, sut, verbose) + + // get validator addresses + val1Addr := cli.GetKeyAddr("node0") + require.NotEmpty(t, val1Addr) + + val2Addr := cli.GetKeyAddr("node1") + require.NotEmpty(t, val2Addr) + + sut.StartChain(t) + + bankSendCmd := []string{"tx", "bank", "send", val1Addr, val2Addr, "10000stake", "--from=" + val1Addr} + + testCases := []struct { + name string + args []string + expErrMsg string + }{ + { + "error with SIGN_MODE_DIRECT_AUX and --aux unset", + []string{ + "--sign-mode=direct-aux", + }, + "cannot sign with SIGN_MODE_DIRECT_AUX", + }, + { + "no error with SIGN_MDOE_DIRECT_AUX mode and generate-only set (ignores generate-only)", + []string{ + "--sign-mode=direct-aux", + "--generate-only", + }, + "", + }, + { + "no error with SIGN_MDOE_DIRECT_AUX mode and generate-only, tip flag set", + []string{ + "--sign-mode=direct-aux", + "--generate-only", + "--tip=10stake", + }, + "", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + cmd := append(bankSendCmd, tc.args...) + assertTxOutput := func(_ assert.TestingT, gotErr error, gotOutputs ...interface{}) bool { + require.Len(t, gotOutputs, 1) + output := gotOutputs[0].(string) + if tc.expErrMsg != "" { + require.Contains(t, output, tc.expErrMsg) + } else { + require.Len(t, gjson.Get(output, "body.messages").Array(), 1) + } + return false + } + + _ = cli.WithRunErrorMatcher(assertTxOutput).Run(cli.withTXFlags(cmd...)...) + }) + } +} + +func TestTxEncodeandDecode(t *testing.T) { + // scenario: test tx encode and decode commands + + cli := NewCLIWrapper(t, sut, verbose) + + // get validator address + val1Addr := cli.GetKeyAddr("node0") + require.NotEmpty(t, val1Addr) + + memoText := "testmemo" + sendTx := generateBankSendTx(t, cli, val1Addr, val1Addr, 100, 1, memoText) + txFile := StoreTempFile(t, []byte(sendTx)) + + // run encode command + encodedText := cli.RunCommandWithArgs("tx", "encode", txFile.Name()) + + // check transaction decodes as expected + decodedTx := cli.RunCommandWithArgs("tx", "decode", encodedText) + require.Equal(t, gjson.Get(decodedTx, "body.memo").String(), memoText) +} diff --git a/tests/systemtests/cli.go b/tests/systemtests/cli.go index d7ac2368ae79..6c8dffc4bbd5 100644 --- a/tests/systemtests/cli.go +++ b/tests/systemtests/cli.go @@ -1,8 +1,6 @@ package systemtests import ( - "bufio" - "bytes" "fmt" "io" "os" @@ -243,26 +241,10 @@ func (c CLIWrapper) runWithInput(args []string, input io.Reader) (output string, cmd.Stdin = input return cmd.CombinedOutput() }() - gotOut = filterProtoNoise(gotOut) ok = c.assertErrorFn(c.t, gotErr, string(gotOut)) return strings.TrimSpace(string(gotOut)), ok } -func filterProtoNoise(in []byte) []byte { - // temporary hack to get rid of all the noise on the stderr - var out bytes.Buffer - scanner := bufio.NewScanner(bytes.NewReader(in)) - for scanner.Scan() { - if !strings.Contains(scanner.Text(), " proto: duplicate proto type registered") { - _, _ = out.Write(scanner.Bytes()) - } - } - if err := scanner.Err(); err != nil { - panic(err) - } - return out.Bytes() -} - func (c CLIWrapper) withQueryFlags(args ...string) []string { args = append(args, "--output", "json") return c.withChainFlags(args...) diff --git a/tests/systemtests/distribution_test.go b/tests/systemtests/distribution_test.go index 6d8eaf2ae0c0..e074b89e7cf3 100644 --- a/tests/systemtests/distribution_test.go +++ b/tests/systemtests/distribution_test.go @@ -89,8 +89,8 @@ func TestWithdrawAllRewardsCmd(t *testing.T) { t.Run(tc.name, func(t *testing.T) { assertGenOnlyOutput := func(_ assert.TestingT, gotErr error, gotOutputs ...interface{}) bool { require.Len(t, gotOutputs, 1) - // gets output combining two objects without any space or new line - splitOutput := strings.Split(gotOutputs[0].(string), "}{") + // split txs with new line + splitOutput := strings.Split(strings.Trim(gotOutputs[0].(string), "\n"), "\n") require.Len(t, splitOutput, tc.expTxLen) return false } diff --git a/x/auth/client/cli/tx_sign.go b/x/auth/client/cli/tx_sign.go index 07d8f5330f41..bf0fb453d102 100644 --- a/x/auth/client/cli/tx_sign.go +++ b/x/auth/client/cli/tx_sign.go @@ -98,12 +98,16 @@ func makeSignBatchCmd() func(cmd *cobra.Command, args []string) error { return err } - if !clientCtx.Offline && multisigKey == "" { + if !clientCtx.Offline { from, err := cmd.Flags().GetString(flags.FlagFrom) if err != nil { return err } + if multisigKey != "" { + from = multisigKey + } + fromAddr, _, _, err := client.GetFromFields(clientCtx, txFactory.Keybase(), from) if err != nil { return err @@ -267,7 +271,7 @@ func multisigSign(clientCtx client.Context, txBuilder client.TxBuilder, txFactor multisigAddr, clientCtx.FromName, txBuilder, - clientCtx.Offline, + true, true, ); err != nil { return err