From d01aa5b4a8776f09656c9d64586dfd7f924d69eb Mon Sep 17 00:00:00 2001 From: Likhita Polavarapu <78951027+likhita-809@users.noreply.github.com> Date: Fri, 14 Oct 2022 16:07:08 +0530 Subject: [PATCH] refactor(authz): CLI tests using Tendermint Mock (#13517) ## Description Closes: #13487 --- ### Author Checklist *All items are required. Please add a note to the item if the item is not applicable and please add links to any relevant follow up issues.* I have... - [ ] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] added `!` to the type prefix if API or client breaking change - [ ] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/main/CONTRIBUTING.md#pr-targeting)) - [ ] provided a link to the relevant issue or specification - [ ] followed the guidelines for [building modules](https://github.com/cosmos/cosmos-sdk/blob/main/docs/building-modules) - [ ] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/main/CONTRIBUTING.md#testing) - [ ] added a changelog entry to `CHANGELOG.md` - [ ] included comments for [documenting Go code](https://blog.golang.org/godoc) - [ ] updated the relevant documentation or specification - [ ] reviewed "Files changed" and left comments if necessary - [ ] confirmed all CI checks have passed ### Reviewers Checklist *All items are required. Please add a note if the item is not applicable and please add your handle next to the items reviewed if you only reviewed selected items.* I have... - [ ] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] confirmed `!` in the type prefix if API or client breaking change - [ ] confirmed all author checklist items have been addressed - [ ] reviewed state machine logic - [ ] reviewed API design and naming - [ ] reviewed documentation is accurate - [ ] reviewed tests and test coverage - [ ] manually tested (if applicable) --- .../authz/{client/testutil => }/cli_test.go | 6 +- .../testutil => tests/e2e/authz}/grpc.go | 2 +- .../testutil => tests/e2e/authz}/query.go | 2 +- .../e2e/authz}/test_helpers.go | 2 +- .../client/testutil => tests/e2e/authz}/tx.go | 2 +- x/authz/client/cli/query_test.go | 238 +++++ x/authz/client/cli/tx_test.go | 919 ++++++++++++++++++ 7 files changed, 1164 insertions(+), 7 deletions(-) rename tests/e2e/authz/{client/testutil => }/cli_test.go (66%) rename {x/authz/client/testutil => tests/e2e/authz}/grpc.go (99%) rename {x/authz/client/testutil => tests/e2e/authz}/query.go (99%) rename {x/authz/client/testutil => tests/e2e/authz}/test_helpers.go (96%) rename {x/authz/client/testutil => tests/e2e/authz}/tx.go (99%) create mode 100644 x/authz/client/cli/query_test.go create mode 100644 x/authz/client/cli/tx_test.go diff --git a/tests/e2e/authz/client/testutil/cli_test.go b/tests/e2e/authz/cli_test.go similarity index 66% rename from tests/e2e/authz/client/testutil/cli_test.go rename to tests/e2e/authz/cli_test.go index 137e61dd0..2a0ca2984 100644 --- a/tests/e2e/authz/client/testutil/cli_test.go +++ b/tests/e2e/authz/cli_test.go @@ -1,7 +1,7 @@ //go:build e2e // +build e2e -package testutil +package authz import ( "testing" @@ -9,12 +9,12 @@ import ( "github.com/stretchr/testify/suite" "cosmossdk.io/simapp" + "github.com/cosmos/cosmos-sdk/testutil/network" - clienttestutil "github.com/cosmos/cosmos-sdk/x/authz/client/testutil" ) func TestIntegrationTestSuite(t *testing.T) { cfg := network.DefaultConfig(simapp.NewTestNetworkFixture) cfg.NumValidators = 1 - suite.Run(t, clienttestutil.NewIntegrationTestSuite(cfg)) + suite.Run(t, NewIntegrationTestSuite(cfg)) } diff --git a/x/authz/client/testutil/grpc.go b/tests/e2e/authz/grpc.go similarity index 99% rename from x/authz/client/testutil/grpc.go rename to tests/e2e/authz/grpc.go index 4a3b5e793..48b9c45cf 100644 --- a/x/authz/client/testutil/grpc.go +++ b/tests/e2e/authz/grpc.go @@ -1,4 +1,4 @@ -package testutil +package authz import ( "fmt" diff --git a/x/authz/client/testutil/query.go b/tests/e2e/authz/query.go similarity index 99% rename from x/authz/client/testutil/query.go rename to tests/e2e/authz/query.go index 42584a599..52a4e2318 100644 --- a/x/authz/client/testutil/query.go +++ b/tests/e2e/authz/query.go @@ -1,4 +1,4 @@ -package testutil +package authz import ( "fmt" diff --git a/x/authz/client/testutil/test_helpers.go b/tests/e2e/authz/test_helpers.go similarity index 96% rename from x/authz/client/testutil/test_helpers.go rename to tests/e2e/authz/test_helpers.go index f1a990c54..56c70434f 100644 --- a/x/authz/client/testutil/test_helpers.go +++ b/tests/e2e/authz/test_helpers.go @@ -1,4 +1,4 @@ -package testutil +package authz import ( "github.com/cosmos/cosmos-sdk/testutil" diff --git a/x/authz/client/testutil/tx.go b/tests/e2e/authz/tx.go similarity index 99% rename from x/authz/client/testutil/tx.go rename to tests/e2e/authz/tx.go index 7474d4a75..bc362115e 100644 --- a/x/authz/client/testutil/tx.go +++ b/tests/e2e/authz/tx.go @@ -1,4 +1,4 @@ -package testutil +package authz import ( "fmt" diff --git a/x/authz/client/cli/query_test.go b/x/authz/client/cli/query_test.go new file mode 100644 index 000000000..297db96c6 --- /dev/null +++ b/x/authz/client/cli/query_test.go @@ -0,0 +1,238 @@ +package cli_test + +import ( + "fmt" + "time" + + tmcli "github.com/tendermint/tendermint/libs/cli" + + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/testutil" + clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/authz" + "github.com/cosmos/cosmos-sdk/x/authz/client/cli" +) + +func (s *CLITestSuite) TestQueryAuthorizations() { + val := testutil.CreateKeyringAccounts(s.T(), s.kr, 1) + + grantee := s.grantee[0] + twoHours := time.Now().Add(time.Minute * time.Duration(120)).Unix() + + _, err := s.createGrant( + []string{ + grantee.String(), + "send", + fmt.Sprintf("--%s=100stake", cli.FlagSpendLimit), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagFrom, val[0].Address), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), + fmt.Sprintf("--%s=%d", cli.FlagExpiration, twoHours), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(10))).String()), + }, + ) + s.Require().NoError(err) + + testCases := []struct { + name string + args []string + expectErr bool + expErrMsg string + }{ + { + "Error: Invalid grantee", + []string{ + val[0].Address.String(), + "invalid grantee", + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + true, + "decoding bech32 failed: invalid character in string: ' '", + }, + { + "Error: Invalid granter", + []string{ + "invalid granter", + grantee.String(), + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + true, + "decoding bech32 failed: invalid character in string: ' '", + }, + { + "Valid txn (json)", + []string{ + val[0].Address.String(), + grantee.String(), + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + false, + ``, + }, + } + for _, tc := range testCases { + tc := tc + + s.Run(tc.name, func() { + cmd := cli.GetCmdQueryGrants() + resp, err := clitestutil.ExecTestCLICmd(s.clientCtx, cmd, tc.args) + if tc.expectErr { + s.Require().Error(err) + s.Require().Contains(string(resp.Bytes()), tc.expErrMsg) + } else { + s.Require().NoError(err) + var grants authz.QueryGrantsResponse + err = s.clientCtx.Codec.UnmarshalJSON(resp.Bytes(), &grants) + s.Require().NoError(err) + } + }) + } +} + +func (s *CLITestSuite) TestQueryAuthorization() { + val := testutil.CreateKeyringAccounts(s.T(), s.kr, 1) + + grantee := s.grantee[0] + twoHours := time.Now().Add(time.Minute * time.Duration(120)).Unix() + + _, err := s.createGrant( + []string{ + grantee.String(), + "send", + fmt.Sprintf("--%s=100stake", cli.FlagSpendLimit), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagFrom, val[0].Address), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), + fmt.Sprintf("--%s=%d", cli.FlagExpiration, twoHours), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(10))).String()), + }, + ) + s.Require().NoError(err) + + testCases := []struct { + name string + args []string + expectErr bool + }{ + { + "Error: Invalid grantee", + []string{ + val[0].Address.String(), + "invalid grantee", + typeMsgSend, + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + true, + }, + { + "Error: Invalid granter", + []string{ + "invalid granter", + grantee.String(), + typeMsgSend, + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + true, + }, + { + "Valid txn (json)", + []string{ + val[0].Address.String(), + grantee.String(), + typeMsgSend, + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + false, + }, + { + "Valid txn with allowed list (json)", + []string{ + val[0].Address.String(), + s.grantee[3].String(), + typeMsgSend, + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + false, + }, + } + for _, tc := range testCases { + tc := tc + + s.Run(tc.name, func() { + cmd := cli.GetCmdQueryGrants() + _, err := clitestutil.ExecTestCLICmd(s.clientCtx, cmd, tc.args) + if tc.expectErr { + s.Require().Error(err) + } else { + s.Require().NoError(err) + } + }) + } +} + +func (s *CLITestSuite) TestQueryGranterGrants() { + val := testutil.CreateKeyringAccounts(s.T(), s.kr, 1) + + grantee := s.grantee[0] + require := s.Require() + + testCases := []struct { + name string + args []string + expectErr bool + expectedErr string + }{ + { + "invalid address", + []string{ + "invalid-address", + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + true, + "decoding bech32 failed", + }, + { + "no authorization found", + []string{ + grantee.String(), + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + false, + "", + }, + { + "valid case", + []string{ + val[0].Address.String(), + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + false, + "", + }, + { + "valid case with pagination", + []string{ + val[0].Address.String(), + "--limit=2", + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + false, + "", + }, + } + for _, tc := range testCases { + s.Run(tc.name, func() { + cmd := cli.GetQueryGranterGrants() + out, err := clitestutil.ExecTestCLICmd(s.clientCtx, cmd, tc.args) + if tc.expectErr { + require.Error(err) + require.Contains(out.String(), tc.expectedErr) + } else { + require.NoError(err) + var grants authz.QueryGranterGrantsResponse + require.NoError(s.clientCtx.Codec.UnmarshalJSON(out.Bytes(), &grants)) + } + }) + } +} diff --git a/x/authz/client/cli/tx_test.go b/x/authz/client/cli/tx_test.go new file mode 100644 index 000000000..a0bf682f4 --- /dev/null +++ b/x/authz/client/cli/tx_test.go @@ -0,0 +1,919 @@ +package cli_test + +import ( + "bytes" + "context" + "fmt" + "io" + "testing" + "time" + + "github.com/cosmos/gogoproto/proto" + "github.com/stretchr/testify/suite" + abci "github.com/tendermint/tendermint/abci/types" + tmbytes "github.com/tendermint/tendermint/libs/bytes" + rpcclient "github.com/tendermint/tendermint/rpc/client" + rpcclientmock "github.com/tendermint/tendermint/rpc/client/mock" + coretypes "github.com/tendermint/tendermint/rpc/core/types" + tmtypes "github.com/tendermint/tendermint/types" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/crypto/hd" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + "github.com/cosmos/cosmos-sdk/testutil" + clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" + sdk "github.com/cosmos/cosmos-sdk/types" + testutilmod "github.com/cosmos/cosmos-sdk/types/module/testutil" + "github.com/cosmos/cosmos-sdk/x/authz/client/cli" + "github.com/cosmos/cosmos-sdk/x/bank" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + "github.com/cosmos/cosmos-sdk/x/gov" + govcli "github.com/cosmos/cosmos-sdk/x/gov/client/cli" + govtestutil "github.com/cosmos/cosmos-sdk/x/gov/client/testutil" + govv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" + govv1beta1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" +) + +var ( + typeMsgSend = banktypes.SendAuthorization{}.MsgTypeURL() + typeMsgVote = sdk.MsgTypeURL(&govv1.MsgVote{}) + typeMsgSubmitProposal = sdk.MsgTypeURL(&govv1.MsgSubmitProposal{}) +) + +var _ client.TendermintRPC = (*mockTendermintRPC)(nil) + +type mockTendermintRPC struct { + rpcclientmock.Client + + responseQuery abci.ResponseQuery +} + +func newMockTendermintRPC(respQuery abci.ResponseQuery) mockTendermintRPC { + return mockTendermintRPC{responseQuery: respQuery} +} + +func (mockTendermintRPC) BroadcastTxSync(context.Context, tmtypes.Tx) (*coretypes.ResultBroadcastTx, error) { + return &coretypes.ResultBroadcastTx{Code: 0}, nil +} + +func (m mockTendermintRPC) ABCIQueryWithOptions( + _ context.Context, + _ string, _ tmbytes.HexBytes, + _ rpcclient.ABCIQueryOptions, +) (*coretypes.ResultABCIQuery, error) { + return &coretypes.ResultABCIQuery{Response: m.responseQuery}, nil +} + +type CLITestSuite struct { + suite.Suite + + kr keyring.Keyring + encCfg testutilmod.TestEncodingConfig + baseCtx client.Context + clientCtx client.Context + grantee []sdk.AccAddress + addrs []sdk.AccAddress +} + +func TestCLITestSuite(t *testing.T) { + suite.Run(t, new(CLITestSuite)) +} + +func (s *CLITestSuite) SetupSuite() { + s.encCfg = testutilmod.MakeTestEncodingConfig(gov.AppModuleBasic{}, bank.AppModuleBasic{}) + s.kr = keyring.NewInMemory(s.encCfg.Codec) + s.baseCtx = client.Context{}. + WithKeyring(s.kr). + WithTxConfig(s.encCfg.TxConfig). + WithCodec(s.encCfg.Codec). + WithClient(mockTendermintRPC{Client: rpcclientmock.Client{}}). + WithAccountRetriever(client.MockAccountRetriever{}). + WithOutput(io.Discard). + WithChainID("test-chain") + + var outBuf bytes.Buffer + ctxGen := func() client.Context { + bz, _ := s.encCfg.Codec.Marshal(&sdk.TxResponse{}) + c := newMockTendermintRPC(abci.ResponseQuery{ + Value: bz, + }) + return s.baseCtx.WithClient(c) + } + s.clientCtx = ctxGen().WithOutput(&outBuf) + + val := testutil.CreateKeyringAccounts(s.T(), s.kr, 1) + s.grantee = make([]sdk.AccAddress, 6) + + s.addrs = make([]sdk.AccAddress, 1) + s.addrs[0] = s.createAccount("validator address") + + // Send some funds to the new account. + // Create new account in the keyring. + s.grantee[0] = s.createAccount("grantee1") + s.msgSendExec(s.grantee[0]) + + // create a proposal with deposit + _, err := govtestutil.MsgSubmitLegacyProposal(s.clientCtx, val[0].Address.String(), + "Text Proposal 1", "Where is the title!?", govv1beta1.ProposalTypeText, + fmt.Sprintf("--%s=%s", govcli.FlagDeposit, sdk.NewCoin("stake", govv1.DefaultMinDepositTokens).String())) + s.Require().NoError(err) + + // Create new account in the keyring. + s.grantee[1] = s.createAccount("grantee2") + // Send some funds to the new account. + s.msgSendExec(s.grantee[1]) + + // grant send authorization to grantee2 + out, err := s.createGrant([]string{ + s.grantee[1].String(), + "send", + fmt.Sprintf("--%s=100stake", cli.FlagSpendLimit), + fmt.Sprintf("--%s=%s", flags.FlagFrom, val[0].Address.String()), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(10))).String()), + fmt.Sprintf("--%s=%d", cli.FlagExpiration, time.Now().Add(time.Minute*time.Duration(120)).Unix()), + }) + s.Require().NoError(err) + + var response sdk.TxResponse + s.Require().NoError(s.clientCtx.Codec.UnmarshalJSON(out.Bytes(), &response), out.String()) + + // Create new account in the keyring. + s.grantee[2] = s.createAccount("grantee3") + + // grant send authorization to grantee3 + _, err = s.createGrant([]string{ + s.grantee[2].String(), + "send", + fmt.Sprintf("--%s=100stake", cli.FlagSpendLimit), + fmt.Sprintf("--%s=%s", flags.FlagFrom, val[0].Address.String()), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(10))).String()), + fmt.Sprintf("--%s=%d", cli.FlagExpiration, time.Now().Add(time.Minute*time.Duration(120)).Unix()), + }) + s.Require().NoError(err) + + // Create new accounts in the keyring. + s.grantee[3] = s.createAccount("grantee4") + s.msgSendExec(s.grantee[3]) + + s.grantee[4] = s.createAccount("grantee5") + s.grantee[5] = s.createAccount("grantee6") + + // grant send authorization with allow list to grantee4 + out, err = s.createGrant( + []string{ + s.grantee[3].String(), + "send", + fmt.Sprintf("--%s=100stake", cli.FlagSpendLimit), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagFrom, val[0].Address.String()), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), + fmt.Sprintf("--%s=%d", cli.FlagExpiration, time.Now().Add(time.Minute*time.Duration(120)).Unix()), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(10))).String()), + fmt.Sprintf("--%s=%s", cli.FlagAllowList, s.grantee[4]), + }, + ) + s.Require().NoError(err) + + s.Require().NoError(s.clientCtx.Codec.UnmarshalJSON(out.Bytes(), &response), out.String()) +} + +func (s *CLITestSuite) createAccount(uid string) sdk.AccAddress { + // Create new account in the keyring. + k, _, err := s.clientCtx.Keyring.NewMnemonic(uid, keyring.English, sdk.FullFundraiserPath, keyring.DefaultBIP39Passphrase, hd.Secp256k1) + s.Require().NoError(err) + + addr, err := k.GetAddress() + s.Require().NoError(err) + + return addr +} + +func (s *CLITestSuite) msgSendExec(grantee sdk.AccAddress) { + val := testutil.CreateKeyringAccounts(s.T(), s.kr, 1) + // Send some funds to the new account. + out, err := clitestutil.MsgSendExec( + s.clientCtx, + val[0].Address, + grantee, + sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(200))), fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(10))).String()), + ) + s.Require().NoError(err) + s.Require().Contains(out.String(), `"code":0`) +} + +func (s *CLITestSuite) createGrant(args []string) (testutil.BufferWriter, error) { + cmd := cli.NewCmdGrantAuthorization() + return clitestutil.ExecTestCLICmd(s.clientCtx, cmd, args) +} + +func (s *CLITestSuite) TestCLITxGrantAuthorization() { + val := testutil.CreateKeyringAccounts(s.T(), s.kr, 1) + + grantee := s.grantee[0] + + twoHours := time.Now().Add(time.Minute * 120).Unix() + pastHour := time.Now().Add(-time.Minute * 60).Unix() + + testCases := []struct { + name string + args []string + expectErr bool + expErrMsg string + }{ + { + "Invalid granter Address", + []string{ + "grantee_addr", + "send", + fmt.Sprintf("--%s=100stake", cli.FlagSpendLimit), + fmt.Sprintf("--%s=%s", flags.FlagFrom, "granter"), + fmt.Sprintf("--%s=true", flags.FlagGenerateOnly), + fmt.Sprintf("--%s=%d", cli.FlagExpiration, twoHours), + }, + true, + "key not found", + }, + { + "Invalid grantee Address", + []string{ + "grantee_addr", + "send", + fmt.Sprintf("--%s=100stake", cli.FlagSpendLimit), + fmt.Sprintf("--%s=%s", flags.FlagFrom, val[0].Address.String()), + fmt.Sprintf("--%s=true", flags.FlagGenerateOnly), + fmt.Sprintf("--%s=%d", cli.FlagExpiration, twoHours), + }, + true, + "invalid separator index", + }, + { + "Invalid expiration time", + []string{ + grantee.String(), + "send", + fmt.Sprintf("--%s=100stake", cli.FlagSpendLimit), + fmt.Sprintf("--%s=%s", flags.FlagFrom, val[0].Address.String()), + fmt.Sprintf("--%s=true", flags.FlagBroadcastMode), + fmt.Sprintf("--%s=%d", cli.FlagExpiration, pastHour), + }, + true, + "", + }, + { + "fail with error invalid msg-type", + []string{ + grantee.String(), + "generic", + fmt.Sprintf("--%s=invalid-msg-type", cli.FlagMsgType), + fmt.Sprintf("--%s=%s", flags.FlagFrom, val[0].Address.String()), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(10))).String()), + fmt.Sprintf("--%s=%d", cli.FlagExpiration, twoHours), + }, + false, + "", + }, + { + "invalid bond denom for tx delegate authorization allowed validators", + []string{ + grantee.String(), + "delegate", + fmt.Sprintf("--%s=100xyz", cli.FlagSpendLimit), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagFrom, val[0].Address.String()), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), + fmt.Sprintf("--%s=%d", cli.FlagExpiration, twoHours), + fmt.Sprintf("--%s=%s", cli.FlagAllowedValidators, sdk.ValAddress(s.addrs[0]).String()), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(10))).String()), + }, + true, + "invalid denom", + }, + { + "invalid bond denom for tx delegate authorization deny validators", + []string{ + grantee.String(), + "delegate", + fmt.Sprintf("--%s=100xyz", cli.FlagSpendLimit), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagFrom, val[0].Address.String()), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), + fmt.Sprintf("--%s=%d", cli.FlagExpiration, twoHours), + fmt.Sprintf("--%s=%s", cli.FlagDenyValidators, sdk.ValAddress(s.addrs[0]).String()), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(10))).String()), + }, + true, + "invalid denom", + }, + { + "invalid bond denom for tx undelegate authorization", + []string{ + grantee.String(), + "unbond", + fmt.Sprintf("--%s=100xyz", cli.FlagSpendLimit), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagFrom, val[0].Address.String()), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), + fmt.Sprintf("--%s=%d", cli.FlagExpiration, twoHours), + fmt.Sprintf("--%s=%s", cli.FlagAllowedValidators, sdk.ValAddress(s.addrs[0]).String()), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(10))).String()), + }, + true, + "invalid denom", + }, + { + "invalid bond denon for tx redelegate authorization", + []string{ + grantee.String(), + "redelegate", + fmt.Sprintf("--%s=100xyz", cli.FlagSpendLimit), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagFrom, val[0].Address.String()), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), + fmt.Sprintf("--%s=%d", cli.FlagExpiration, twoHours), + fmt.Sprintf("--%s=%s", cli.FlagAllowedValidators, sdk.ValAddress(s.addrs[0]).String()), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(10))).String()), + }, + true, + "invalid denom", + }, + { + "invalid decimal coin expression with more than single coin", + []string{ + grantee.String(), + "delegate", + fmt.Sprintf("--%s=100stake,20xyz", cli.FlagSpendLimit), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagFrom, val[0].Address.String()), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), + fmt.Sprintf("--%s=%d", cli.FlagExpiration, twoHours), + fmt.Sprintf("--%s=%s", cli.FlagAllowedValidators, sdk.ValAddress(s.addrs[0]).String()), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(10))).String()), + }, + true, + "invalid decimal coin expression", + }, + { + "Valid tx send authorization", + []string{ + grantee.String(), + "send", + fmt.Sprintf("--%s=100stake", cli.FlagSpendLimit), + fmt.Sprintf("--%s=%s", flags.FlagFrom, val[0].Address.String()), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), + fmt.Sprintf("--%s=%d", cli.FlagExpiration, twoHours), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(10))).String()), + }, + false, + "", + }, + { + "Valid tx send authorization with allow list", + []string{ + grantee.String(), + "send", + fmt.Sprintf("--%s=100stake", cli.FlagSpendLimit), + fmt.Sprintf("--%s=%s", flags.FlagFrom, val[0].Address.String()), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), + fmt.Sprintf("--%s=%d", cli.FlagExpiration, twoHours), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(10))).String()), + fmt.Sprintf("--%s=%s", cli.FlagAllowList, s.grantee[1]), + }, + false, + "", + }, + { + "Invalid tx send authorization with duplicate allow list", + []string{ + grantee.String(), + "send", + fmt.Sprintf("--%s=100stake", cli.FlagSpendLimit), + fmt.Sprintf("--%s=%s", flags.FlagFrom, val[0].Address.String()), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), + fmt.Sprintf("--%s=%d", cli.FlagExpiration, twoHours), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(10))).String()), + fmt.Sprintf("--%s=%s", cli.FlagAllowList, fmt.Sprintf("%s,%s", s.grantee[1], s.grantee[1])), + }, + true, + "duplicate entry", + }, + { + "Valid tx generic authorization", + []string{ + grantee.String(), + "generic", + fmt.Sprintf("--%s=%s", cli.FlagMsgType, typeMsgVote), + fmt.Sprintf("--%s=%s", flags.FlagFrom, val[0].Address.String()), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), + fmt.Sprintf("--%s=%d", cli.FlagExpiration, twoHours), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(10))).String()), + }, + false, + "", + }, + { + "fail when granter = grantee", + []string{ + grantee.String(), + "generic", + fmt.Sprintf("--%s=%s", cli.FlagMsgType, typeMsgVote), + fmt.Sprintf("--%s=%s", flags.FlagFrom, grantee.String()), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), + fmt.Sprintf("--%s=%d", cli.FlagExpiration, twoHours), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(10))).String()), + }, + true, + "grantee and granter should be different", + }, + { + "Valid tx with amino", + []string{ + grantee.String(), + "generic", + fmt.Sprintf("--%s=%s", cli.FlagMsgType, typeMsgVote), + fmt.Sprintf("--%s=%s", flags.FlagFrom, val[0].Address.String()), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), + fmt.Sprintf("--%s=%d", cli.FlagExpiration, twoHours), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(10))).String()), + fmt.Sprintf("--%s=%s", flags.FlagSignMode, flags.SignModeLegacyAminoJSON), + }, + false, + "", + }, + } + + for _, tc := range testCases { + s.Run(tc.name, func() { + out, err := s.createGrant( + tc.args, + ) + if tc.expectErr { + s.Require().Error(err, out) + s.Require().Contains(err.Error(), tc.expErrMsg) + } else { + var txResp sdk.TxResponse + s.Require().NoError(err) + s.Require().NoError(s.clientCtx.Codec.UnmarshalJSON(out.Bytes(), &txResp), out.String()) + } + }) + } +} + +func (s *CLITestSuite) TestCmdRevokeAuthorizations() { + val := testutil.CreateKeyringAccounts(s.T(), s.kr, 1) + + grantee := s.grantee[0] + twoHours := time.Now().Add(time.Minute * time.Duration(120)).Unix() + + // send-authorization + _, err := s.createGrant( + []string{ + grantee.String(), + "send", + fmt.Sprintf("--%s=100stake", cli.FlagSpendLimit), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagFrom, val[0].Address), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), + fmt.Sprintf("--%s=%d", cli.FlagExpiration, twoHours), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(10))).String()), + }, + ) + s.Require().NoError(err) + + // generic-authorization + _, err = s.createGrant( + []string{ + grantee.String(), + "generic", + fmt.Sprintf("--%s=%s", cli.FlagMsgType, typeMsgVote), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagFrom, val[0].Address), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), + fmt.Sprintf("--%s=%d", cli.FlagExpiration, twoHours), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(10))).String()), + }, + ) + s.Require().NoError(err) + + // generic-authorization used for amino testing + _, err = s.createGrant( + []string{ + grantee.String(), + "generic", + fmt.Sprintf("--%s=%s", cli.FlagMsgType, typeMsgSubmitProposal), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagFrom, val[0].Address), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), + fmt.Sprintf("--%s=%d", cli.FlagExpiration, twoHours), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(10))).String()), + fmt.Sprintf("--%s=%s", flags.FlagSignMode, flags.SignModeLegacyAminoJSON), + }, + ) + s.Require().NoError(err) + testCases := []struct { + name string + args []string + respType proto.Message + expectErr bool + }{ + { + "invalid grantee address", + []string{ + "invalid grantee", + typeMsgSend, + fmt.Sprintf("--%s=%s", flags.FlagFrom, val[0].Address.String()), + fmt.Sprintf("--%s=true", flags.FlagGenerateOnly), + }, + nil, + true, + }, + { + "invalid granter address", + []string{ + grantee.String(), + typeMsgSend, + fmt.Sprintf("--%s=%s", flags.FlagFrom, "granter"), + fmt.Sprintf("--%s=true", flags.FlagGenerateOnly), + }, + nil, + true, + }, + { + "Valid tx send authorization", + []string{ + grantee.String(), + typeMsgSend, + fmt.Sprintf("--%s=%s", flags.FlagFrom, val[0].Address.String()), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(10))).String()), + }, + &sdk.TxResponse{}, + false, + }, + { + "Valid tx generic authorization", + []string{ + grantee.String(), + typeMsgVote, + fmt.Sprintf("--%s=%s", flags.FlagFrom, val[0].Address.String()), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(10))).String()), + }, + &sdk.TxResponse{}, + false, + }, + { + "Valid tx with amino", + []string{ + grantee.String(), + typeMsgSubmitProposal, + fmt.Sprintf("--%s=%s", flags.FlagFrom, val[0].Address.String()), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(10))).String()), + fmt.Sprintf("--%s=%s", flags.FlagSignMode, flags.SignModeLegacyAminoJSON), + }, + &sdk.TxResponse{}, + false, + }, + } + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + cmd := cli.NewCmdRevokeAuthorization() + + out, err := clitestutil.ExecTestCLICmd(s.clientCtx, cmd, tc.args) + if tc.expectErr { + s.Require().Error(err) + } else { + s.Require().NoError(err) + s.Require().NoError(s.clientCtx.Codec.UnmarshalJSON(out.Bytes(), tc.respType), out.String()) + } + }) + } +} + +func (s *CLITestSuite) TestExecAuthorizationWithExpiration() { + val := testutil.CreateKeyringAccounts(s.T(), s.kr, 1) + + grantee := s.grantee[0] + tenSeconds := time.Now().Add(time.Second * time.Duration(10)).Unix() + + _, err := s.createGrant( + []string{ + grantee.String(), + "generic", + fmt.Sprintf("--%s=%s", cli.FlagMsgType, typeMsgVote), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagFrom, val[0].Address.String()), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), + fmt.Sprintf("--%s=%d", cli.FlagExpiration, tenSeconds), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(10))).String()), + }, + ) + s.Require().NoError(err) + // msg vote + voteTx := fmt.Sprintf(`{"body":{"messages":[{"@type":"/cosmos.gov.v1.MsgVote","proposal_id":"1","voter":"%s","option":"VOTE_OPTION_YES"}],"memo":"","timeout_height":"0","extension_options":[],"non_critical_extension_options":[]},"auth_info":{"signer_infos":[],"fee":{"amount":[],"gas_limit":"200000","payer":"","granter":""}},"signatures":[]}`, val[0].Address.String()) + execMsg := testutil.WriteToNewTempFile(s.T(), voteTx) + defer execMsg.Close() + + // waiting for authorization to expire + time.Sleep(12 * time.Second) + + cmd := cli.NewCmdExecAuthorization() + + out, err := clitestutil.ExecTestCLICmd(s.clientCtx, cmd, []string{ + execMsg.Name(), + fmt.Sprintf("--%s=%s", flags.FlagFrom, grantee.String()), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(10))).String()), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + }) + s.Require().NoError(err) + var response sdk.TxResponse + s.Require().NoError(s.clientCtx.Codec.UnmarshalJSON(out.Bytes(), &response), out.String()) +} + +func (s *CLITestSuite) TestNewExecGenericAuthorized() { + val := testutil.CreateKeyringAccounts(s.T(), s.kr, 1) + grantee := s.grantee[0] + twoHours := time.Now().Add(time.Minute * time.Duration(120)).Unix() + + _, err := s.createGrant( + []string{ + grantee.String(), + "generic", + fmt.Sprintf("--%s=%s", cli.FlagMsgType, typeMsgVote), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagFrom, val[0].Address.String()), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), + fmt.Sprintf("--%s=%d", cli.FlagExpiration, twoHours), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(10))).String()), + }, + ) + s.Require().NoError(err) + + // msg vote + voteTx := fmt.Sprintf(`{"body":{"messages":[{"@type":"/cosmos.gov.v1.MsgVote","proposal_id":"1","voter":"%s","option":"VOTE_OPTION_YES"}],"memo":"","timeout_height":"0","extension_options":[],"non_critical_extension_options":[]},"auth_info":{"signer_infos":[],"fee":{"amount":[],"gas_limit":"200000","payer":"","granter":""}},"signatures":[]}`, val[0].Address.String()) + execMsg := testutil.WriteToNewTempFile(s.T(), voteTx) + defer execMsg.Close() + + testCases := []struct { + name string + args []string + respType proto.Message + expectErr bool + }{ + { + "fail invalid grantee", + []string{ + execMsg.Name(), + fmt.Sprintf("--%s=%s", flags.FlagFrom, "grantee"), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), + fmt.Sprintf("--%s=true", flags.FlagGenerateOnly), + }, + nil, + true, + }, + { + "fail invalid json path", + []string{ + "/invalid/file.txt", + fmt.Sprintf("--%s=%s", flags.FlagFrom, grantee.String()), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), + }, + nil, + true, + }, + { + "valid txn", + []string{ + execMsg.Name(), + fmt.Sprintf("--%s=%s", flags.FlagFrom, grantee.String()), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(10))).String()), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + }, + &sdk.TxResponse{}, + false, + }, + { + "valid tx with amino", + []string{ + execMsg.Name(), + fmt.Sprintf("--%s=%s", flags.FlagFrom, grantee.String()), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(10))).String()), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagSignMode, flags.SignModeLegacyAminoJSON), + }, + &sdk.TxResponse{}, + false, + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + cmd := cli.NewCmdExecAuthorization() + + out, err := clitestutil.ExecTestCLICmd(s.clientCtx, cmd, tc.args) + if tc.expectErr { + s.Require().Error(err) + } else { + s.Require().NoError(err) + s.Require().NoError(s.clientCtx.Codec.UnmarshalJSON(out.Bytes(), tc.respType), out.String()) + } + }) + } +} + +func (s *CLITestSuite) TestNewExecGrantAuthorized() { + val := testutil.CreateKeyringAccounts(s.T(), s.kr, 1) + grantee := s.grantee[0] + twoHours := time.Now().Add(time.Minute * time.Duration(120)).Unix() + + _, err := s.createGrant( + []string{ + grantee.String(), + "send", + fmt.Sprintf("--%s=12testtoken", cli.FlagSpendLimit), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagFrom, val[0].Address.String()), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), + fmt.Sprintf("--%s=%d", cli.FlagExpiration, twoHours), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(10))).String()), + }, + ) + s.Require().NoError(err) + + tokens := sdk.NewCoins( + sdk.NewCoin("testtoken", sdk.NewInt(12)), + ) + normalGeneratedTx, err := clitestutil.MsgSendExec( + s.clientCtx, + val[0].Address, + grantee, + tokens, + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(10))).String()), + fmt.Sprintf("--%s=true", flags.FlagGenerateOnly), + ) + s.Require().NoError(err) + execMsg := testutil.WriteToNewTempFile(s.T(), normalGeneratedTx.String()) + defer execMsg.Close() + + testCases := []struct { + name string + args []string + expectErr bool + expectErrMsg string + }{ + { + "valid txn", + []string{ + execMsg.Name(), + fmt.Sprintf("--%s=%s", flags.FlagFrom, grantee.String()), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(10))).String()), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + }, + false, + "", + }, + { + "error over spent", + []string{ + execMsg.Name(), + fmt.Sprintf("--%s=%s", flags.FlagFrom, grantee.String()), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(10))).String()), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + }, + false, + "", + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + cmd := cli.NewCmdExecAuthorization() + clientCtx := s.clientCtx + + var response sdk.TxResponse + out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) + switch { + case tc.expectErrMsg != "": + s.Require().NoError(clientCtx.Codec.UnmarshalJSON(out.Bytes(), &response), out.String()) + s.Require().Contains(response.RawLog, tc.expectErrMsg) + + case tc.expectErr: + s.Require().Error(err) + + default: + s.Require().NoError(err) + s.Require().NoError(clientCtx.Codec.UnmarshalJSON(out.Bytes(), &response), out.String()) + } + }) + } +} + +func (s *CLITestSuite) TestExecSendAuthzWithAllowList() { + val := testutil.CreateKeyringAccounts(s.T(), s.kr, 1) + grantee := s.grantee[3] + + allowedAddr := s.grantee[4] + notAllowedAddr := s.grantee[5] + twoHours := time.Now().Add(time.Minute * time.Duration(120)).Unix() + + _, err := s.createGrant( + []string{ + grantee.String(), + "send", + fmt.Sprintf("--%s=100stake", cli.FlagSpendLimit), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagFrom, val[0].Address.String()), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), + fmt.Sprintf("--%s=%d", cli.FlagExpiration, twoHours), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(10))).String()), + fmt.Sprintf("--%s=%s", cli.FlagAllowList, allowedAddr), + }, + ) + s.Require().NoError(err) + + tokens := sdk.NewCoins( + sdk.NewCoin("stake", sdk.NewInt(12)), + ) + + validGeneratedTx, err := clitestutil.MsgSendExec( + s.clientCtx, + val[0].Address, + allowedAddr, + tokens, + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(10))).String()), + fmt.Sprintf("--%s=true", flags.FlagGenerateOnly), + ) + s.Require().NoError(err) + execMsg := testutil.WriteToNewTempFile(s.T(), validGeneratedTx.String()) + defer execMsg.Close() + + invalidGeneratedTx, err := clitestutil.MsgSendExec( + s.clientCtx, + val[0].Address, + notAllowedAddr, + tokens, + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(10))).String()), + fmt.Sprintf("--%s=true", flags.FlagGenerateOnly), + ) + s.Require().NoError(err) + execMsg1 := testutil.WriteToNewTempFile(s.T(), invalidGeneratedTx.String()) + defer execMsg1.Close() + + // test sending to allowed address + args := []string{ + execMsg.Name(), + fmt.Sprintf("--%s=%s", flags.FlagFrom, grantee.String()), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(10))).String()), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + } + var response sdk.TxResponse + cmd := cli.NewCmdExecAuthorization() + out, err := clitestutil.ExecTestCLICmd(s.clientCtx, cmd, args) + s.Require().NoError(err) + s.Require().NoError(s.clientCtx.Codec.UnmarshalJSON(out.Bytes(), &response), out.String()) + + // test sending to not allowed address + args = []string{ + execMsg1.Name(), + fmt.Sprintf("--%s=%s", flags.FlagFrom, grantee.String()), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(10))).String()), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + } + out, err = clitestutil.ExecTestCLICmd(s.clientCtx, cmd, args) + s.Require().NoError(err) + s.Require().NoError(s.clientCtx.Codec.UnmarshalJSON(out.Bytes(), &response), out.String()) +}