From 400c3cb08e3c2dafca19029106e25f24f9fbddb3 Mon Sep 17 00:00:00 2001 From: Alexander Bezobchuk Date: Fri, 10 Jul 2020 10:45:46 -0400 Subject: [PATCH] Merge PR #6652: x/distribution: In-Process Testing & CLI Refactor --- client/cmd.go | 6 + client/context.go | 6 + client/flags/flags.go | 2 +- testutil/ioutil.go | 1 + testutil/network/network.go | 20 +- x/distribution/client/cli/cli_test.go | 944 +++++++++++++++++--- x/distribution/client/cli/query.go | 112 ++- x/distribution/client/cli/tx.go | 113 ++- x/distribution/client/cli/tx_test.go | 8 +- x/distribution/client/common/common.go | 16 +- x/distribution/client/common/common_test.go | 2 +- x/distribution/client/rest/query.go | 12 +- x/distribution/client/rest/rest.go | 4 +- x/distribution/client/rest/tx.go | 10 +- x/distribution/client/testutil/helpers.go | 120 +-- x/distribution/module.go | 8 +- x/gov/client/proposal_handler.go | 2 +- x/gov/module.go | 2 +- x/params/client/cli/tx.go | 14 +- x/upgrade/client/cli/tx.go | 11 +- 20 files changed, 1072 insertions(+), 341 deletions(-) diff --git a/client/cmd.go b/client/cmd.go index 5d1af5fe0f..72cb424c34 100644 --- a/client/cmd.go +++ b/client/cmd.go @@ -7,6 +7,7 @@ import ( "github.com/pkg/errors" "github.com/spf13/cobra" "github.com/spf13/pflag" + "github.com/tendermint/tendermint/libs/cli" "github.com/cosmos/cosmos-sdk/client/flags" sdk "github.com/cosmos/cosmos-sdk/types" @@ -79,6 +80,11 @@ func ValidateCmd(cmd *cobra.Command, args []string) error { // flags that do not necessarily change with context. These must be checked if // the caller explicitly changed the values. func ReadPersistentCommandFlags(clientCtx Context, flagSet *pflag.FlagSet) (Context, error) { + if flagSet.Changed(cli.OutputFlag) { + output, _ := flagSet.GetString(cli.OutputFlag) + clientCtx = clientCtx.WithOutputFormat(output) + } + if flagSet.Changed(flags.FlagHome) { homeDir, _ := flagSet.GetString(flags.FlagHome) clientCtx = clientCtx.WithHomeDir(homeDir) diff --git a/client/context.go b/client/context.go index 638ba1b997..2d74aa2776 100644 --- a/client/context.go +++ b/client/context.go @@ -219,6 +219,12 @@ func (ctx Context) WithTrustNode(trustNode bool) Context { return ctx } +// WithOutputFormat returns a copy of the context with an updated OutputFormat field. +func (ctx Context) WithOutputFormat(format string) Context { + ctx.OutputFormat = format + return ctx +} + // WithNodeURI returns a copy of the context with an updated node URI. func (ctx Context) WithNodeURI(nodeURI string) Context { ctx.NodeURI = nodeURI diff --git a/client/flags/flags.go b/client/flags/flags.go index 2f20ab6c22..85ac252d8f 100644 --- a/client/flags/flags.go +++ b/client/flags/flags.go @@ -6,7 +6,6 @@ import ( "github.com/spf13/cobra" "github.com/spf13/viper" - tmcli "github.com/tendermint/tendermint/libs/cli" "github.com/cosmos/cosmos-sdk/crypto/keyring" @@ -80,6 +79,7 @@ func GetCommands(cmds ...*cobra.Command) []*cobra.Command { c.Flags().String(FlagNode, "tcp://localhost:26657", ": to Tendermint RPC interface for this chain") c.Flags().Int64(FlagHeight, 0, "Use a specific height to query state at (this can error if the node is pruning state)") c.Flags().String(FlagKeyringBackend, DefaultKeyringBackend, "Select keyring's backend (os|file|kwallet|pass|test)") + c.Flags().StringP(tmcli.OutputFlag, "o", "text", "Output format (text|json)") // TODO: REMOVE VIPER CALLS! viper.BindPFlag(FlagTrustNode, c.Flags().Lookup(FlagTrustNode)) diff --git a/testutil/ioutil.go b/testutil/ioutil.go index c70d50ad6b..622806f25c 100644 --- a/testutil/ioutil.go +++ b/testutil/ioutil.go @@ -31,6 +31,7 @@ type BufferReader interface { type BufferWriter interface { io.Writer Reset() + Bytes() []byte String() string } diff --git a/testutil/network/network.go b/testutil/network/network.go index e029878bc9..d682e9c742 100644 --- a/testutil/network/network.go +++ b/testutil/network/network.go @@ -262,12 +262,15 @@ func New(t *testing.T, cfg Config) *Network { genBalances = append(genBalances, banktypes.Balance{Address: addr, Coins: balances.Sort()}) genAccounts = append(genAccounts, authtypes.NewBaseAccount(addr, nil, 0, 0)) + commission, err := sdk.NewDecFromStr("0.5") + require.NoError(t, err) + createValMsg := stakingtypes.NewMsgCreateValidator( sdk.ValAddress(addr), valPubKeys[i], sdk.NewCoin(sdk.DefaultBondDenom, cfg.BondedTokens), stakingtypes.NewDescription(nodeDirName, "", "", "", ""), - stakingtypes.NewCommissionRates(sdk.OneDec(), sdk.OneDec(), sdk.OneDec()), + stakingtypes.NewCommissionRates(commission, sdk.OneDec(), sdk.OneDec()), sdk.OneInt(), ) @@ -338,6 +341,21 @@ func (n *Network) WaitForHeight(h int64) (int64, error) { return n.WaitForHeightWithTimeout(h, 10*time.Second) } +// LatestHeight returns the latest height of the network or an error if the +// query fails or no validators exist. +func (n *Network) LatestHeight() (int64, error) { + if len(n.Validators) == 0 { + return 0, errors.New("no validators available") + } + + status, err := n.Validators[0].RPCClient.Status() + if err != nil { + return 0, err + } + + return status.SyncInfo.LatestBlockHeight, nil +} + // WaitForHeightWithTimeout is the same as WaitForHeight except the caller can // provide a custom timeout. func (n *Network) WaitForHeightWithTimeout(h int64, t time.Duration) (int64, error) { diff --git a/x/distribution/client/cli/cli_test.go b/x/distribution/client/cli/cli_test.go index 4a0c1b73c7..0a561f5c29 100644 --- a/x/distribution/client/cli/cli_test.go +++ b/x/distribution/client/cli/cli_test.go @@ -1,126 +1,842 @@ -// +build cli_test - package cli_test import ( - "path/filepath" + "context" + "fmt" + "io/ioutil" + "os" + "strings" "testing" + "time" - "github.com/stretchr/testify/require" - tmtypes "github.com/tendermint/tendermint/types" + "github.com/stretchr/testify/suite" + tmcli "github.com/tendermint/tendermint/libs/cli" - "github.com/cosmos/cosmos-sdk/tests" - "github.com/cosmos/cosmos-sdk/tests/cli" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/testutil" + testnet "github.com/cosmos/cosmos-sdk/testutil/network" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/distribution/client/testutil" + "github.com/cosmos/cosmos-sdk/x/distribution/client/cli" + distrtestutil "github.com/cosmos/cosmos-sdk/x/distribution/client/testutil" minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" ) -func TestCLIWithdrawRewards(t *testing.T) { - t.SkipNow() // TODO: Bring back once viper is refactored. - t.Parallel() - f := cli.InitFixtures(t) +type IntegrationTestSuite struct { + suite.Suite - genesisState := f.GenesisState() - inflationMin := sdk.MustNewDecFromStr("1.0") - var mintData minttypes.GenesisState - f.Cdc.UnmarshalJSON(genesisState[minttypes.ModuleName], &mintData) - mintData.Minter.Inflation = inflationMin - mintData.Params.InflationMin = inflationMin - mintData.Params.InflationMax = sdk.MustNewDecFromStr("1.0") - mintDataBz, err := f.Cdc.MarshalJSON(mintData) - require.NoError(t, err) - genesisState[minttypes.ModuleName] = mintDataBz - - genFile := filepath.Join(f.SimdHome, "config", "genesis.json") - genDoc, err := tmtypes.GenesisDocFromFile(genFile) - require.NoError(t, err) - genDoc.AppState, err = f.Cdc.MarshalJSON(genesisState) - require.NoError(t, genDoc.SaveAs(genFile)) - - // start simd server - proc := f.SDStart() - t.Cleanup(func() { proc.Stop(false) }) - - params := testutil.QueryParameters(f) - require.NotEmpty(t, params) - - fooAddr := f.KeyAddress(cli.KeyFoo) - barAddr := f.KeyAddress(cli.KeyBar) - fooVal := sdk.ValAddress(fooAddr) - - outstandingRewards := testutil.QueryValidatorOutstandingRewards(f, fooVal.String()) - require.NotEmpty(t, outstandingRewards) - require.False(t, outstandingRewards.Rewards.IsZero()) - - commission := testutil.QueryCommission(f, fooVal.String()) - require.NotEmpty(t, commission) - require.False(t, commission.Commission.IsZero()) - - rewards := testutil.QueryRewards(f, fooAddr) - require.Len(t, rewards.Rewards, 1) - require.NotEmpty(t, rewards.Total) - - // withdrawing rewards of a delegation for a single validator - success := testutil.TxWithdrawRewards(f, fooVal, fooAddr.String(), "-y") - require.True(t, success) - - rewards = testutil.QueryRewards(f, fooAddr) - require.Len(t, rewards.Rewards, 1) - require.Len(t, rewards.Total, 1) - - // Setting up a new withdraw address - success, stdout, stderr := testutil.TxSetWithdrawAddress(f, fooAddr.String(), barAddr.String(), "--generate-only") - require.True(t, success) - require.Empty(t, stderr) - - msg := cli.UnmarshalStdTx(t, f.Cdc, stdout) - require.NotZero(t, msg.Fee.Gas) - require.Len(t, msg.Msgs, 1) - require.Len(t, msg.GetSignatures(), 0) - - success, _, stderr = testutil.TxSetWithdrawAddress(f, cli.KeyFoo, barAddr.String(), "-y") - require.True(t, success) - require.Empty(t, stderr) - tests.WaitForNextNBlocksTM(1, f.Port) - - // Withdraw all delegation rewards from all validators - success, stdout, stderr = testutil.TxWithdrawAllRewards(f, fooAddr.String(), "--generate-only") - require.True(t, success) - require.Empty(t, stderr) - - msg = cli.UnmarshalStdTx(t, f.Cdc, stdout) - require.NotZero(t, msg.Fee.Gas) - require.Len(t, msg.Msgs, 1) - require.Len(t, msg.GetSignatures(), 0) - - success, _, stderr = testutil.TxWithdrawAllRewards(f, cli.KeyFoo, "-y") - require.True(t, success) - require.Empty(t, stderr) - tests.WaitForNextNBlocksTM(1, f.Port) - - newTokens := sdk.NewCoin(cli.Denom, sdk.TokensFromConsensusPower(1)) - - // Withdraw all delegation rewards from all validators - success, stdout, stderr = testutil.TxFundCommunityPool(f, fooAddr.String(), newTokens, "--generate-only") - require.True(t, success) - require.Empty(t, stderr) - - msg = cli.UnmarshalStdTx(t, f.Cdc, stdout) - require.NotZero(t, msg.Fee.Gas) - require.Len(t, msg.Msgs, 1) - require.Len(t, msg.GetSignatures(), 0) - - success, _, stderr = testutil.TxFundCommunityPool(f, cli.KeyFoo, newTokens, "-y") - require.True(t, success) - require.Empty(t, stderr) - tests.WaitForNextNBlocksTM(1, f.Port) - - amount := testutil.QueryCommunityPool(f) - require.False(t, amount.IsZero()) - - slashes := testutil.QuerySlashes(f, fooVal.String()) - require.Nil(t, slashes, nil) - - f.Cleanup() + cfg testnet.Config + network *testnet.Network +} + +// SetupTest creates a new network for _each_ integration test. We create a new +// network for each test because there are some state modifications that are +// needed to be made in order to make useful queries. However, we don't want +// these state changes to be present in other tests. +func (s *IntegrationTestSuite) SetupTest() { + s.T().Log("setting up integration test suite") + + cfg := testnet.DefaultConfig() + genesisState := cfg.GenesisState + cfg.NumValidators = 1 + + var mintData minttypes.GenesisState + s.Require().NoError(cfg.Codec.UnmarshalJSON(genesisState[minttypes.ModuleName], &mintData)) + + inflation := sdk.MustNewDecFromStr("1.0") + mintData.Minter.Inflation = inflation + mintData.Params.InflationMin = inflation + mintData.Params.InflationMax = inflation + + mintDataBz, err := cfg.Codec.MarshalJSON(mintData) + s.Require().NoError(err) + genesisState[minttypes.ModuleName] = mintDataBz + cfg.GenesisState = genesisState + + s.cfg = cfg + s.network = testnet.New(s.T(), cfg) + + _, err = s.network.WaitForHeight(1) + s.Require().NoError(err) +} + +// TearDownTest cleans up the curret test network after _each_ test. +func (s *IntegrationTestSuite) TearDownTest() { + s.T().Log("tearing down integration test suite") + s.network.Cleanup() +} + +func (s *IntegrationTestSuite) TestGetCmdQueryParams() { + val := s.network.Validators[0] + + testCases := []struct { + name string + args []string + expectedOutput string + }{ + { + "default output", + []string{}, + `{"community_tax":"0.020000000000000000","base_proposer_reward":"0.010000000000000000","bonus_proposer_reward":"0.040000000000000000","withdraw_addr_enabled":true}`, + }, + { + "text output", + []string{fmt.Sprintf("--%s=text", tmcli.OutputFlag)}, + `base_proposer_reward: "0.010000000000000000" +bonus_proposer_reward: "0.040000000000000000" +community_tax: "0.020000000000000000" +withdraw_addr_enabled: true`, + }, + } + + for _, tc := range testCases { + tc := tc + + s.Run(tc.name, func() { + cmd := flags.GetCommands(cli.GetCmdQueryParams())[0] + _, out := testutil.ApplyMockIO(cmd) + + clientCtx := val.ClientCtx.WithOutput(out) + + ctx := context.Background() + ctx = context.WithValue(ctx, client.ClientContextKey, &clientCtx) + + out.Reset() + cmd.SetArgs(tc.args) + + s.Require().NoError(cmd.ExecuteContext(ctx)) + s.Require().Equal(tc.expectedOutput, strings.TrimSpace(out.String())) + }) + } +} + +func (s *IntegrationTestSuite) TestGetCmdQueryValidatorOutstandingRewards() { + val := s.network.Validators[0] + + _, err := s.network.WaitForHeight(4) + s.Require().NoError(err) + + testCases := []struct { + name string + args []string + expectErr bool + expectedOutput string + }{ + { + "invalid validator address", + []string{ + fmt.Sprintf("--%s=3", flags.FlagHeight), + "foo", + }, + true, + "", + }, + { + "default output", + []string{ + fmt.Sprintf("--%s=3", flags.FlagHeight), + sdk.ValAddress(val.Address).String(), + }, + false, + `{"rewards":[{"denom":"stake","amount":"232.260000000000000000"}]}`, + }, + { + "text output", + []string{ + fmt.Sprintf("--%s=text", tmcli.OutputFlag), + fmt.Sprintf("--%s=3", flags.FlagHeight), + sdk.ValAddress(val.Address).String(), + }, + false, + `rewards: +- amount: "232.260000000000000000" + denom: stake`, + }, + } + + for _, tc := range testCases { + tc := tc + + s.Run(tc.name, func() { + cmd := flags.GetCommands(cli.GetCmdQueryValidatorOutstandingRewards())[0] + _, out := testutil.ApplyMockIO(cmd) + + clientCtx := val.ClientCtx.WithOutput(out) + + ctx := context.Background() + ctx = context.WithValue(ctx, client.ClientContextKey, &clientCtx) + + out.Reset() + cmd.SetArgs(tc.args) + + err := cmd.ExecuteContext(ctx) + if tc.expectErr { + s.Require().Error(err) + } else { + s.Require().NoError(err) + s.Require().Equal(tc.expectedOutput, strings.TrimSpace(out.String())) + } + }) + } +} + +func (s *IntegrationTestSuite) TestGetCmdQueryValidatorCommission() { + val := s.network.Validators[0] + + _, err := s.network.WaitForHeight(4) + s.Require().NoError(err) + + testCases := []struct { + name string + args []string + expectErr bool + expectedOutput string + }{ + { + "invalid validator address", + []string{ + fmt.Sprintf("--%s=3", flags.FlagHeight), + "foo", + }, + true, + "", + }, + { + "default output", + []string{ + fmt.Sprintf("--%s=3", flags.FlagHeight), + sdk.ValAddress(val.Address).String(), + }, + false, + `{"commission":[{"denom":"stake","amount":"116.130000000000000000"}]}`, + }, + { + "text output", + []string{ + fmt.Sprintf("--%s=text", tmcli.OutputFlag), + fmt.Sprintf("--%s=3", flags.FlagHeight), + sdk.ValAddress(val.Address).String(), + }, + false, + `commission: +- amount: "116.130000000000000000" + denom: stake`, + }, + } + + for _, tc := range testCases { + tc := tc + + s.Run(tc.name, func() { + cmd := flags.GetCommands(cli.GetCmdQueryValidatorCommission())[0] + _, out := testutil.ApplyMockIO(cmd) + + clientCtx := val.ClientCtx.WithOutput(out) + + ctx := context.Background() + ctx = context.WithValue(ctx, client.ClientContextKey, &clientCtx) + + out.Reset() + cmd.SetArgs(tc.args) + + err := cmd.ExecuteContext(ctx) + if tc.expectErr { + s.Require().Error(err) + } else { + s.Require().NoError(err) + s.Require().Equal(tc.expectedOutput, strings.TrimSpace(out.String())) + } + }) + } +} + +func (s *IntegrationTestSuite) TestGetCmdQueryValidatorSlashes() { + val := s.network.Validators[0] + + _, err := s.network.WaitForHeight(4) + s.Require().NoError(err) + + testCases := []struct { + name string + args []string + expectErr bool + expectedOutput string + }{ + { + "invalid validator address", + []string{ + fmt.Sprintf("--%s=3", flags.FlagHeight), + "foo", "1", "3", + }, + true, + "", + }, + { + "invalid start height", + []string{ + fmt.Sprintf("--%s=3", flags.FlagHeight), + sdk.ValAddress(val.Address).String(), "-1", "3", + }, + true, + "", + }, + { + "invalid end height", + []string{ + fmt.Sprintf("--%s=3", flags.FlagHeight), + sdk.ValAddress(val.Address).String(), "1", "-3", + }, + true, + "", + }, + { + "default output", + []string{ + fmt.Sprintf("--%s=3", flags.FlagHeight), + sdk.ValAddress(val.Address).String(), "1", "3", + }, + false, + "null", + }, + { + "text output", + []string{ + fmt.Sprintf("--%s=text", tmcli.OutputFlag), + fmt.Sprintf("--%s=3", flags.FlagHeight), + sdk.ValAddress(val.Address).String(), "1", "3", + }, + false, + "null", + }, + } + + for _, tc := range testCases { + tc := tc + + s.Run(tc.name, func() { + cmd := flags.GetCommands(cli.GetCmdQueryValidatorSlashes())[0] + _, out := testutil.ApplyMockIO(cmd) + + clientCtx := val.ClientCtx.WithOutput(out) + + ctx := context.Background() + ctx = context.WithValue(ctx, client.ClientContextKey, &clientCtx) + + out.Reset() + cmd.SetArgs(tc.args) + + err := cmd.ExecuteContext(ctx) + if tc.expectErr { + s.Require().Error(err) + } else { + s.Require().NoError(err) + s.Require().Equal(tc.expectedOutput, strings.TrimSpace(out.String())) + } + }) + } +} + +func (s *IntegrationTestSuite) TestGetCmdQueryDelegatorRewards() { + val := s.network.Validators[0] + addr := val.Address + valAddr := sdk.ValAddress(addr) + + _, err := s.network.WaitForHeightWithTimeout(11, time.Minute) + s.Require().NoError(err) + + testCases := []struct { + name string + args []string + expectErr bool + expectedOutput string + }{ + { + "invalid delegator address", + []string{ + fmt.Sprintf("--%s=5", flags.FlagHeight), + "foo", valAddr.String(), + }, + true, + "", + }, + { + "invalid validator address", + []string{ + fmt.Sprintf("--%s=5", flags.FlagHeight), + addr.String(), "foo", + }, + true, + "", + }, + { + "default output", + []string{ + fmt.Sprintf("--%s=10", flags.FlagHeight), + addr.String(), + }, + false, + fmt.Sprintf(`{"rewards":[{"validator_address":"%s","reward":[{"denom":"stake","amount":"387.100000000000000000"}]}],"total":[{"denom":"stake","amount":"387.100000000000000000"}]}`, valAddr.String()), + }, + { + "default output (specific validator)", + []string{ + fmt.Sprintf("--%s=10", flags.FlagHeight), + addr.String(), valAddr.String(), + }, + false, + `[{"denom":"stake","amount":"387.100000000000000000"}]`, + }, + { + "text output", + []string{ + fmt.Sprintf("--%s=text", tmcli.OutputFlag), + fmt.Sprintf("--%s=10", flags.FlagHeight), + addr.String(), + }, + false, + fmt.Sprintf(`rewards: +- reward: + - amount: "387.100000000000000000" + denom: stake + validator_address: %s +total: +- amount: "387.100000000000000000" + denom: stake`, valAddr.String()), + }, + { + "text output (specific validator)", + []string{ + fmt.Sprintf("--%s=text", tmcli.OutputFlag), + fmt.Sprintf("--%s=10", flags.FlagHeight), + addr.String(), valAddr.String(), + }, + false, + `- amount: "387.100000000000000000" + denom: stake`, + }, + } + + for _, tc := range testCases { + tc := tc + + s.Run(tc.name, func() { + cmd := flags.GetCommands(cli.GetCmdQueryDelegatorRewards())[0] + _, out := testutil.ApplyMockIO(cmd) + + clientCtx := val.ClientCtx.WithOutput(out) + + ctx := context.Background() + ctx = context.WithValue(ctx, client.ClientContextKey, &clientCtx) + + out.Reset() + cmd.SetArgs(tc.args) + + err := cmd.ExecuteContext(ctx) + if tc.expectErr { + s.Require().Error(err) + } else { + s.Require().NoError(err) + s.Require().Equal(tc.expectedOutput, strings.TrimSpace(out.String())) + } + }) + } +} + +func (s *IntegrationTestSuite) TestGetCmdQueryCommunityPool() { + val := s.network.Validators[0] + + _, err := s.network.WaitForHeight(3) + s.Require().NoError(err) + + testCases := []struct { + name string + args []string + expectedOutput string + }{ + { + "default output", + []string{fmt.Sprintf("--%s=3", flags.FlagHeight)}, + `[{"denom":"stake","amount":"4.740000000000000000"}]`, + }, + { + "text output", + []string{fmt.Sprintf("--%s=text", tmcli.OutputFlag), fmt.Sprintf("--%s=3", flags.FlagHeight)}, + `- amount: "4.740000000000000000" + denom: stake`, + }, + } + + for _, tc := range testCases { + tc := tc + + s.Run(tc.name, func() { + cmd := flags.GetCommands(cli.GetCmdQueryCommunityPool())[0] + _, out := testutil.ApplyMockIO(cmd) + + clientCtx := val.ClientCtx.WithOutput(out) + + ctx := context.Background() + ctx = context.WithValue(ctx, client.ClientContextKey, &clientCtx) + + out.Reset() + cmd.SetArgs(tc.args) + + s.Require().NoError(cmd.ExecuteContext(ctx)) + s.Require().Equal(tc.expectedOutput, strings.TrimSpace(out.String())) + }) + } +} + +func (s *IntegrationTestSuite) TestNewWithdrawRewardsCmd() { + val := s.network.Validators[0] + + testCases := []struct { + name string + valAddr fmt.Stringer + args []string + expectErr bool + respType fmt.Stringer + expectedCode uint32 + }{ + { + "invalid validator address", + val.Address, + []string{ + fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), + }, + true, nil, 0, + }, + { + "valid transaction", + sdk.ValAddress(val.Address), + []string{ + fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), + }, + false, &sdk.TxResponse{}, 0, + }, + { + "valid transaction (with commission)", + sdk.ValAddress(val.Address), + []string{ + fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=true", cli.FlagCommission), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), + }, + false, &sdk.TxResponse{}, 0, + }, + } + + for _, tc := range testCases { + tc := tc + + s.Run(tc.name, func() { + clientCtx := val.ClientCtx + + ctx := context.Background() + ctx = context.WithValue(ctx, client.ClientContextKey, &clientCtx) + + bz, err := distrtestutil.MsgWithdrawDelegatorRewardExec(clientCtx, tc.valAddr, tc.args...) + if tc.expectErr { + s.Require().Error(err) + } else { + s.Require().NoError(err) + s.Require().NoError(clientCtx.JSONMarshaler.UnmarshalJSON(bz, tc.respType), string(bz)) + + txResp := tc.respType.(*sdk.TxResponse) + s.Require().Equal(tc.expectedCode, txResp.Code) + } + }) + } +} + +func (s *IntegrationTestSuite) TestNewWithdrawAllRewardsCmd() { + val := s.network.Validators[0] + + testCases := []struct { + name string + args []string + expectErr bool + respType fmt.Stringer + expectedCode uint32 + }{ + { + "valid transaction (offline)", + []string{ + fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()), + fmt.Sprintf("--%s=true", flags.FlagOffline), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), + }, + true, nil, 0, + }, + { + "valid transaction", + []string{ + fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), + }, + false, &sdk.TxResponse{}, 0, + }, + } + + for _, tc := range testCases { + tc := tc + + s.Run(tc.name, func() { + cmd := flags.PostCommands(cli.NewWithdrawAllRewardsCmd())[0] + _, out := testutil.ApplyMockIO(cmd) + + clientCtx := val.ClientCtx.WithOutput(out) + + ctx := context.Background() + ctx = context.WithValue(ctx, client.ClientContextKey, &clientCtx) + + out.Reset() + cmd.SetArgs(tc.args) + + err := cmd.ExecuteContext(ctx) + if tc.expectErr { + s.Require().Error(err) + } else { + s.Require().NoError(err) + s.Require().NoError(clientCtx.JSONMarshaler.UnmarshalJSON(out.Bytes(), tc.respType), out.String()) + + txResp := tc.respType.(*sdk.TxResponse) + s.Require().Equal(tc.expectedCode, txResp.Code) + } + }) + } +} + +func (s *IntegrationTestSuite) TestNewSetWithdrawAddrCmd() { + val := s.network.Validators[0] + + testCases := []struct { + name string + args []string + expectErr bool + respType fmt.Stringer + expectedCode uint32 + }{ + { + "invalid withdraw address", + []string{ + "foo", + fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), + }, + true, nil, 0, + }, + { + "valid transaction", + []string{ + val.Address.String(), + fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), + }, + false, &sdk.TxResponse{}, 0, + }, + } + + for _, tc := range testCases { + tc := tc + + s.Run(tc.name, func() { + cmd := flags.PostCommands(cli.NewSetWithdrawAddrCmd())[0] + _, out := testutil.ApplyMockIO(cmd) + + clientCtx := val.ClientCtx.WithOutput(out) + + ctx := context.Background() + ctx = context.WithValue(ctx, client.ClientContextKey, &clientCtx) + + out.Reset() + cmd.SetArgs(tc.args) + + err := cmd.ExecuteContext(ctx) + if tc.expectErr { + s.Require().Error(err) + } else { + s.Require().NoError(err) + s.Require().NoError(clientCtx.JSONMarshaler.UnmarshalJSON(out.Bytes(), tc.respType), out.String()) + + txResp := tc.respType.(*sdk.TxResponse) + s.Require().Equal(tc.expectedCode, txResp.Code) + } + }) + } +} + +func (s *IntegrationTestSuite) TestNewFundCommunityPoolCmd() { + val := s.network.Validators[0] + + testCases := []struct { + name string + args []string + expectErr bool + respType fmt.Stringer + expectedCode uint32 + }{ + { + "invalid funding amount", + []string{ + "-43foocoin", + fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), + }, + true, nil, 0, + }, + { + "valid transaction", + []string{ + sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(5431))).String(), + fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), + }, + false, &sdk.TxResponse{}, 0, + }, + } + + for _, tc := range testCases { + tc := tc + + s.Run(tc.name, func() { + cmd := flags.PostCommands(cli.NewFundCommunityPoolCmd())[0] + _, out := testutil.ApplyMockIO(cmd) + + clientCtx := val.ClientCtx.WithOutput(out) + + ctx := context.Background() + ctx = context.WithValue(ctx, client.ClientContextKey, &clientCtx) + + out.Reset() + cmd.SetArgs(tc.args) + + err := cmd.ExecuteContext(ctx) + if tc.expectErr { + s.Require().Error(err) + } else { + s.Require().NoError(err) + s.Require().NoError(clientCtx.JSONMarshaler.UnmarshalJSON(out.Bytes(), tc.respType), out.String()) + + txResp := tc.respType.(*sdk.TxResponse) + s.Require().Equal(tc.expectedCode, txResp.Code) + } + }) + } +} + +func (s *IntegrationTestSuite) TestGetCmdSubmitProposal() { + val := s.network.Validators[0] + + invalidPropFile, err := ioutil.TempFile(os.TempDir(), "invalid_community_spend_proposal.*.json") + s.Require().NoError(err) + defer os.Remove(invalidPropFile.Name()) + + invalidProp := `{ + "title": "", + "description": "Pay me some Atoms!", + "recipient": "foo", + "amount": "-343foocoin", + "deposit": -324foocoin +}` + + _, err = invalidPropFile.WriteString(invalidProp) + s.Require().NoError(err) + + validPropFile, err := ioutil.TempFile(os.TempDir(), "valid_community_spend_proposal.*.json") + s.Require().NoError(err) + defer os.Remove(validPropFile.Name()) + + validProp := fmt.Sprintf(`{ + "title": "Community Pool Spend", + "description": "Pay me some Atoms!", + "recipient": "%s", + "amount": "%s", + "deposit": "%s" +}`, val.Address.String(), sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(5431)), sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(5431))) + + _, err = validPropFile.WriteString(validProp) + s.Require().NoError(err) + + testCases := []struct { + name string + args []string + expectErr bool + respType fmt.Stringer + expectedCode uint32 + }{ + { + "invalid proposal", + []string{ + invalidPropFile.Name(), + fmt.Sprintf("--%s=%s", flags.FlagFrom, val.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(s.cfg.BondDenom, sdk.NewInt(10))).String()), + }, + true, nil, 0, + }, + { + "valid transaction", + []string{ + validPropFile.Name(), + fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), // sync mode as there are no funds yet + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), + }, + false, &sdk.TxResponse{}, 0, + }, + } + + for _, tc := range testCases { + tc := tc + + s.Run(tc.name, func() { + cmd := flags.PostCommands(cli.GetCmdSubmitProposal())[0] + _, out := testutil.ApplyMockIO(cmd) + + clientCtx := val.ClientCtx.WithOutput(out) + + ctx := context.Background() + ctx = context.WithValue(ctx, client.ClientContextKey, &clientCtx) + + out.Reset() + cmd.SetArgs(tc.args) + + err := cmd.ExecuteContext(ctx) + if tc.expectErr { + s.Require().Error(err) + } else { + s.Require().NoError(err) + s.Require().NoError(clientCtx.JSONMarshaler.UnmarshalJSON(out.Bytes(), tc.respType), out.String()) + + txResp := tc.respType.(*sdk.TxResponse) + s.Require().Equal(tc.expectedCode, txResp.Code, out.String()) + } + }) + } +} + +func TestIntegrationTestSuite(t *testing.T) { + suite.Run(t, new(IntegrationTestSuite)) } diff --git a/x/distribution/client/cli/query.go b/x/distribution/client/cli/query.go index 5a9af1f5c2..8eeb8ae0a9 100644 --- a/x/distribution/client/cli/query.go +++ b/x/distribution/client/cli/query.go @@ -9,7 +9,6 @@ import ( "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/version" "github.com/cosmos/cosmos-sdk/x/distribution/client/common" @@ -17,7 +16,7 @@ import ( ) // GetQueryCmd returns the cli query commands for this module -func GetQueryCmd(queryRoute string, cdc *codec.Codec) *cobra.Command { +func GetQueryCmd() *cobra.Command { distQueryCmd := &cobra.Command{ Use: types.ModuleName, Short: "Querying commands for the distribution module", @@ -27,25 +26,29 @@ func GetQueryCmd(queryRoute string, cdc *codec.Codec) *cobra.Command { } distQueryCmd.AddCommand(flags.GetCommands( - GetCmdQueryParams(queryRoute, cdc), - GetCmdQueryValidatorOutstandingRewards(queryRoute, cdc), - GetCmdQueryValidatorCommission(queryRoute, cdc), - GetCmdQueryValidatorSlashes(queryRoute, cdc), - GetCmdQueryDelegatorRewards(queryRoute, cdc), - GetCmdQueryCommunityPool(queryRoute, cdc), + GetCmdQueryParams(), + GetCmdQueryValidatorOutstandingRewards(), + GetCmdQueryValidatorCommission(), + GetCmdQueryValidatorSlashes(), + GetCmdQueryDelegatorRewards(), + GetCmdQueryCommunityPool(), )...) return distQueryCmd } // GetCmdQueryParams implements the query params command. -func GetCmdQueryParams(queryRoute string, cdc *codec.Codec) *cobra.Command { +func GetCmdQueryParams() *cobra.Command { return &cobra.Command{ Use: "params", Args: cobra.NoArgs, Short: "Query distribution params", - RunE: func(cmd *cobra.Command, args []string) error { - clientCtx := client.NewContext().WithCodec(cdc).WithJSONMarshaler(cdc) + RunE: func(cmd *cobra.Command, _ []string) error { + clientCtx := client.GetClientContextFromCmd(cmd) + clientCtx, err := client.ReadQueryCommandFlags(clientCtx, cmd.Flags()) + if err != nil { + return err + } route := fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryParams) res, _, err := clientCtx.QueryWithData(route, nil) @@ -54,7 +57,7 @@ func GetCmdQueryParams(queryRoute string, cdc *codec.Codec) *cobra.Command { } var params types.Params - if err := cdc.UnmarshalJSON(res, ¶ms); err != nil { + if err := clientCtx.JSONMarshaler.UnmarshalJSON(res, ¶ms); err != nil { return fmt.Errorf("failed to unmarshal params: %w", err) } @@ -63,15 +66,15 @@ func GetCmdQueryParams(queryRoute string, cdc *codec.Codec) *cobra.Command { } } -// GetCmdQueryValidatorOutstandingRewards implements the query validator outstanding rewards command. -func GetCmdQueryValidatorOutstandingRewards(queryRoute string, cdc *codec.Codec) *cobra.Command { +// GetCmdQueryValidatorOutstandingRewards implements the query validator +// outstanding rewards command. +func GetCmdQueryValidatorOutstandingRewards() *cobra.Command { return &cobra.Command{ Use: "validator-outstanding-rewards [validator]", Args: cobra.ExactArgs(1), Short: "Query distribution outstanding (un-withdrawn) rewards for a validator and all their delegations", Long: strings.TrimSpace( - fmt.Sprintf(`Query distribution outstanding (un-withdrawn) rewards -for a validator and all their delegations. + fmt.Sprintf(`Query distribution outstanding (un-withdrawn) rewards for a validator and all their delegations. Example: $ %s query distribution validator-outstanding-rewards cosmosvaloper1lwjmdnks33xwnmfayc64ycprww49n33mtm92ne @@ -80,7 +83,11 @@ $ %s query distribution validator-outstanding-rewards cosmosvaloper1lwjmdnks33xw ), ), RunE: func(cmd *cobra.Command, args []string) error { - clientCtx := client.NewContext().WithCodec(cdc).WithJSONMarshaler(cdc) + clientCtx := client.GetClientContextFromCmd(cmd) + clientCtx, err := client.ReadQueryCommandFlags(clientCtx, cmd.Flags()) + if err != nil { + return err + } valAddr, err := sdk.ValAddressFromBech32(args[0]) if err != nil { @@ -88,13 +95,13 @@ $ %s query distribution validator-outstanding-rewards cosmosvaloper1lwjmdnks33xw } params := types.NewQueryValidatorOutstandingRewardsParams(valAddr) - bz, err := cdc.MarshalJSON(params) + bz, err := clientCtx.JSONMarshaler.MarshalJSON(params) if err != nil { return err } resp, _, err := clientCtx.QueryWithData( - fmt.Sprintf("custom/%s/%s", queryRoute, types.QueryValidatorOutstandingRewards), + fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryValidatorOutstandingRewards), bz, ) if err != nil { @@ -102,7 +109,7 @@ $ %s query distribution validator-outstanding-rewards cosmosvaloper1lwjmdnks33xw } var outstandingRewards types.ValidatorOutstandingRewards - if err := cdc.UnmarshalJSON(resp, &outstandingRewards); err != nil { + if err := clientCtx.JSONMarshaler.UnmarshalJSON(resp, &outstandingRewards); err != nil { return err } @@ -112,7 +119,7 @@ $ %s query distribution validator-outstanding-rewards cosmosvaloper1lwjmdnks33xw } // GetCmdQueryValidatorCommission implements the query validator commission command. -func GetCmdQueryValidatorCommission(queryRoute string, cdc *codec.Codec) *cobra.Command { +func GetCmdQueryValidatorCommission() *cobra.Command { return &cobra.Command{ Use: "commission [validator]", Args: cobra.ExactArgs(1), @@ -127,27 +134,34 @@ $ %s query distribution commission cosmosvaloper1gghjut3ccd8ay0zduzj64hwre2fxs9l ), ), RunE: func(cmd *cobra.Command, args []string) error { - clientCtx := client.NewContext().WithCodec(cdc).WithJSONMarshaler(cdc) + clientCtx := client.GetClientContextFromCmd(cmd) + clientCtx, err := client.ReadQueryCommandFlags(clientCtx, cmd.Flags()) + if err != nil { + return err + } validatorAddr, err := sdk.ValAddressFromBech32(args[0]) if err != nil { return err } - res, err := common.QueryValidatorCommission(clientCtx, queryRoute, validatorAddr) + res, err := common.QueryValidatorCommission(clientCtx, validatorAddr) if err != nil { return err } var valCom types.ValidatorAccumulatedCommission - cdc.MustUnmarshalJSON(res, &valCom) + if err := clientCtx.JSONMarshaler.UnmarshalJSON(res, &valCom); err != nil { + return err + } + return clientCtx.PrintOutput(valCom) }, } } // GetCmdQueryValidatorSlashes implements the query validator slashes command. -func GetCmdQueryValidatorSlashes(queryRoute string, cdc *codec.Codec) *cobra.Command { +func GetCmdQueryValidatorSlashes() *cobra.Command { return &cobra.Command{ Use: "slashes [validator] [start-height] [end-height]", Args: cobra.ExactArgs(3), @@ -162,7 +176,11 @@ $ %s query distribution slashes cosmosvaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ldmq ), ), RunE: func(cmd *cobra.Command, args []string) error { - clientCtx := client.NewContext().WithCodec(cdc).WithJSONMarshaler(cdc) + clientCtx := client.GetClientContextFromCmd(cmd) + clientCtx, err := client.ReadQueryCommandFlags(clientCtx, cmd.Flags()) + if err != nil { + return err + } validatorAddr, err := sdk.ValAddressFromBech32(args[0]) if err != nil { @@ -180,29 +198,30 @@ $ %s query distribution slashes cosmosvaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ldmq } params := types.NewQueryValidatorSlashesParams(validatorAddr, startHeight, endHeight) - bz, err := cdc.MarshalJSON(params) + bz, err := clientCtx.JSONMarshaler.MarshalJSON(params) if err != nil { return err } - res, _, err := clientCtx.QueryWithData(fmt.Sprintf("custom/%s/validator_slashes", queryRoute), bz) + res, _, err := clientCtx.QueryWithData(fmt.Sprintf("custom/%s/validator_slashes", types.QuerierRoute), bz) if err != nil { return err } var slashes []types.ValidatorSlashEvent - if err = cdc.UnmarshalJSON(res, &slashes); err != nil { + if err = clientCtx.JSONMarshaler.UnmarshalJSON(res, &slashes); err != nil { return fmt.Errorf("failed to unmarshal response: %w", err) } + return clientCtx.PrintOutput(slashes) }, } } // GetCmdQueryDelegatorRewards implements the query delegator rewards command. -func GetCmdQueryDelegatorRewards(queryRoute string, cdc *codec.Codec) *cobra.Command { +func GetCmdQueryDelegatorRewards() *cobra.Command { return &cobra.Command{ - Use: "rewards [delegator-addr] []", + Use: "rewards [delegator-addr] [validator-addr]", Args: cobra.RangeArgs(1, 2), Short: "Query all distribution delegator rewards or rewards from a particular validator", Long: strings.TrimSpace( @@ -216,17 +235,21 @@ $ %s query distribution rewards cosmos1gghjut3ccd8ay0zduzj64hwre2fxs9ld75ru9p co ), ), RunE: func(cmd *cobra.Command, args []string) error { - clientCtx := client.NewContext().WithCodec(cdc).WithJSONMarshaler(cdc) + clientCtx := client.GetClientContextFromCmd(cmd) + clientCtx, err := client.ReadQueryCommandFlags(clientCtx, cmd.Flags()) + if err != nil { + return err + } // query for rewards from a particular delegation if len(args) == 2 { - resp, _, err := common.QueryDelegationRewards(clientCtx, queryRoute, args[0], args[1]) + resp, _, err := common.QueryDelegationRewards(clientCtx, args[0], args[1]) if err != nil { return err } var result sdk.DecCoins - if err = cdc.UnmarshalJSON(resp, &result); err != nil { + if err = clientCtx.JSONMarshaler.UnmarshalJSON(resp, &result); err != nil { return fmt.Errorf("failed to unmarshal response: %w", err) } @@ -239,20 +262,20 @@ $ %s query distribution rewards cosmos1gghjut3ccd8ay0zduzj64hwre2fxs9ld75ru9p co } params := types.NewQueryDelegatorParams(delegatorAddr) - bz, err := cdc.MarshalJSON(params) + bz, err := clientCtx.JSONMarshaler.MarshalJSON(params) if err != nil { return fmt.Errorf("failed to marshal params: %w", err) } // query for delegator total rewards - route := fmt.Sprintf("custom/%s/%s", queryRoute, types.QueryDelegatorTotalRewards) + route := fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryDelegatorTotalRewards) res, _, err := clientCtx.QueryWithData(route, bz) if err != nil { return err } var result types.QueryDelegatorTotalRewardsResponse - if err = cdc.UnmarshalJSON(res, &result); err != nil { + if err = clientCtx.JSONMarshaler.UnmarshalJSON(res, &result); err != nil { return fmt.Errorf("failed to unmarshal response: %w", err) } @@ -262,7 +285,7 @@ $ %s query distribution rewards cosmos1gghjut3ccd8ay0zduzj64hwre2fxs9ld75ru9p co } // GetCmdQueryCommunityPool returns the command for fetching community pool info -func GetCmdQueryCommunityPool(queryRoute string, cdc *codec.Codec) *cobra.Command { +func GetCmdQueryCommunityPool() *cobra.Command { return &cobra.Command{ Use: "community-pool", Args: cobra.NoArgs, @@ -277,15 +300,22 @@ $ %s query distribution community-pool ), ), RunE: func(cmd *cobra.Command, args []string) error { - clientCtx := client.NewContext().WithCodec(cdc).WithJSONMarshaler(cdc) + clientCtx := client.GetClientContextFromCmd(cmd) + clientCtx, err := client.ReadQueryCommandFlags(clientCtx, cmd.Flags()) + if err != nil { + return err + } - res, _, err := clientCtx.QueryWithData(fmt.Sprintf("custom/%s/community_pool", queryRoute), nil) + res, _, err := clientCtx.QueryWithData(fmt.Sprintf("custom/%s/community_pool", types.QuerierRoute), nil) if err != nil { return err } var result sdk.DecCoins - cdc.MustUnmarshalJSON(res, &result) + if err := clientCtx.JSONMarshaler.UnmarshalJSON(res, &result); err != nil { + return err + } + return clientCtx.PrintOutput(result) }, } diff --git a/x/distribution/client/cli/tx.go b/x/distribution/client/cli/tx.go index 5d7f83e2c2..7a58d0261e 100644 --- a/x/distribution/client/cli/tx.go +++ b/x/distribution/client/cli/tx.go @@ -1,4 +1,3 @@ -// nolint package cli import ( @@ -6,6 +5,7 @@ import ( "strings" "github.com/spf13/cobra" + "github.com/spf13/pflag" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" @@ -17,11 +17,10 @@ import ( govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" ) +// Transaction flags for the x/distribution module var ( - flagOnlyFromValidator = "only-from-validator" - flagIsValidator = "is-validator" - flagCommission = "commission" - flagMaxMessagesPerTx = "max-msgs" + FlagCommission = "commission" + FlagMaxMessagesPerTx = "max-msgs" ) const ( @@ -29,7 +28,7 @@ const ( ) // NewTxCmd returns a root CLI command handler for all x/distribution transaction commands. -func NewTxCmd(clientCtx client.Context) *cobra.Command { +func NewTxCmd() *cobra.Command { distTxCmd := &cobra.Command{ Use: types.ModuleName, Short: "Distribution transactions subcommands", @@ -39,25 +38,24 @@ func NewTxCmd(clientCtx client.Context) *cobra.Command { } distTxCmd.AddCommand(flags.PostCommands( - NewWithdrawRewardsCmd(clientCtx), - NewWithdrawAllRewardsCmd(clientCtx), - NewSetWithdrawAddrCmd(clientCtx), - NewFundCommunityPoolCmd(clientCtx), + NewWithdrawRewardsCmd(), + NewWithdrawAllRewardsCmd(), + NewSetWithdrawAddrCmd(), + NewFundCommunityPoolCmd(), )...) return distTxCmd } -type newGenerateOrBroadcastFunc func(clientCtx client.Context, msgs ...sdk.Msg) error +type newGenerateOrBroadcastFunc func(client.Context, *pflag.FlagSet, ...sdk.Msg) error func newSplitAndApply( - newGenerateOrBroadcast newGenerateOrBroadcastFunc, - clientCtx client.Context, - msgs []sdk.Msg, - chunkSize int, + genOrBroadcastFn newGenerateOrBroadcastFunc, clientCtx client.Context, + fs *pflag.FlagSet, msgs []sdk.Msg, chunkSize int, ) error { + if chunkSize == 0 { - return newGenerateOrBroadcast(clientCtx, msgs...) + return genOrBroadcastFn(clientCtx, fs, msgs...) } // split messages into slices of length chunkSize @@ -70,7 +68,7 @@ func newSplitAndApply( } msgChunk := msgs[i:sliceEnd] - if err := newGenerateOrBroadcast(clientCtx, msgChunk...); err != nil { + if err := genOrBroadcastFn(clientCtx, fs, msgChunk...); err != nil { return err } } @@ -78,7 +76,7 @@ func newSplitAndApply( return nil } -func NewWithdrawRewardsCmd(clientCtx client.Context) *cobra.Command { +func NewWithdrawRewardsCmd() *cobra.Command { cmd := &cobra.Command{ Use: "withdraw-rewards [validator-addr]", Short: "Withdraw rewards from a given delegation address, and optionally withdraw validator commission if the delegation address given is a validator operator", @@ -95,7 +93,11 @@ $ %s tx distribution withdraw-rewards cosmosvaloper1gghjut3ccd8ay0zduzj64hwre2fx ), Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - clientCtx := clientCtx.InitWithInput(cmd.InOrStdin()) + clientCtx := client.GetClientContextFromCmd(cmd) + clientCtx, err := client.ReadTxCommandFlags(clientCtx, cmd.Flags()) + if err != nil { + return err + } delAddr := clientCtx.GetFromAddress() valAddr, err := sdk.ValAddressFromBech32(args[0]) @@ -105,7 +107,7 @@ $ %s tx distribution withdraw-rewards cosmosvaloper1gghjut3ccd8ay0zduzj64hwre2fx msgs := []sdk.Msg{types.NewMsgWithdrawDelegatorReward(delAddr, valAddr)} - if commission, _ := cmd.Flags().GetBool(flagCommission); commission { + if commission, _ := cmd.Flags().GetBool(FlagCommission); commission { msgs = append(msgs, types.NewMsgWithdrawValidatorCommission(valAddr)) } @@ -115,15 +117,15 @@ $ %s tx distribution withdraw-rewards cosmosvaloper1gghjut3ccd8ay0zduzj64hwre2fx } } - return tx.GenerateOrBroadcastTx(clientCtx, msgs...) + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msgs...) }, } - cmd.Flags().Bool(flagCommission, false, "also withdraw validator's commission") + cmd.Flags().Bool(FlagCommission, false, "Withdraw the validator's commission in addition to the rewards") return cmd } -func NewWithdrawAllRewardsCmd(clientCtx client.Context) *cobra.Command { +func NewWithdrawAllRewardsCmd() *cobra.Command { cmd := &cobra.Command{ Use: "withdraw-all-rewards", Short: "withdraw all delegations rewards for a delegator", @@ -137,8 +139,12 @@ $ %s tx distribution withdraw-all-rewards --from mykey ), ), Args: cobra.NoArgs, - RunE: func(cmd *cobra.Command, args []string) error { - clientCtx := clientCtx.InitWithInput(cmd.InOrStdin()) + RunE: func(cmd *cobra.Command, _ []string) error { + clientCtx := client.GetClientContextFromCmd(cmd) + clientCtx, err := client.ReadTxCommandFlags(clientCtx, cmd.Flags()) + if err != nil { + return err + } delAddr := clientCtx.GetFromAddress() @@ -148,22 +154,22 @@ $ %s tx distribution withdraw-all-rewards --from mykey return fmt.Errorf("cannot generate tx in offline mode") } - msgs, err := common.WithdrawAllDelegatorRewards(clientCtx, types.QuerierRoute, delAddr) + msgs, err := common.WithdrawAllDelegatorRewards(clientCtx, delAddr) if err != nil { return err } - chunkSize, _ := cmd.Flags().GetInt(flagMaxMessagesPerTx) - return newSplitAndApply(tx.GenerateOrBroadcastTx, clientCtx, msgs, chunkSize) + chunkSize, _ := cmd.Flags().GetInt(FlagMaxMessagesPerTx) + return newSplitAndApply(tx.GenerateOrBroadcastTxCLI, clientCtx, cmd.Flags(), msgs, chunkSize) }, } - cmd.Flags().Int(flagMaxMessagesPerTx, MaxMessagesPerTxDefault, "Limit the number of messages per tx (0 for unlimited)") + cmd.Flags().Int(FlagMaxMessagesPerTx, MaxMessagesPerTxDefault, "Limit the number of messages per tx (0 for unlimited)") return cmd } -func NewSetWithdrawAddrCmd(clientCtx client.Context) *cobra.Command { - cmd := &cobra.Command{ +func NewSetWithdrawAddrCmd() *cobra.Command { + return &cobra.Command{ Use: "set-withdraw-addr [withdraw-addr]", Short: "change the default withdraw address for rewards associated with an address", Long: strings.TrimSpace( @@ -177,7 +183,11 @@ $ %s tx distribution set-withdraw-addr cosmos1gghjut3ccd8ay0zduzj64hwre2fxs9ld75 ), Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - clientCtx := clientCtx.InitWithInput(cmd.InOrStdin()) + clientCtx := client.GetClientContextFromCmd(cmd) + clientCtx, err := client.ReadTxCommandFlags(clientCtx, cmd.Flags()) + if err != nil { + return err + } delAddr := clientCtx.GetFromAddress() withdrawAddr, err := sdk.AccAddressFromBech32(args[0]) @@ -190,14 +200,13 @@ $ %s tx distribution set-withdraw-addr cosmos1gghjut3ccd8ay0zduzj64hwre2fxs9ld75 return err } - return tx.GenerateOrBroadcastTx(clientCtx, msg) + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) }, } - return cmd } -func NewFundCommunityPoolCmd(clientCtx client.Context) *cobra.Command { - cmd := &cobra.Command{ +func NewFundCommunityPoolCmd() *cobra.Command { + return &cobra.Command{ Use: "fund-community-pool [amount]", Args: cobra.ExactArgs(1), Short: "Funds the community pool with the specified amount", @@ -211,7 +220,11 @@ $ %s tx distribution fund-community-pool 100uatom --from mykey ), ), RunE: func(cmd *cobra.Command, args []string) error { - clientCtx := clientCtx.InitWithInput(cmd.InOrStdin()) + clientCtx := client.GetClientContextFromCmd(cmd) + clientCtx, err := client.ReadTxCommandFlags(clientCtx, cmd.Flags()) + if err != nil { + return err + } depositorAddr := clientCtx.GetFromAddress() amount, err := sdk.ParseCoins(args[0]) @@ -224,16 +237,14 @@ $ %s tx distribution fund-community-pool 100uatom --from mykey return err } - return tx.GenerateOrBroadcastTx(clientCtx, msg) + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) }, } - - return cmd } // GetCmdSubmitProposal implements the command to submit a community-pool-spend proposal -func GetCmdSubmitProposal(clientCtx client.Context) *cobra.Command { - cmd := &cobra.Command{ +func GetCmdSubmitProposal() *cobra.Command { + return &cobra.Command{ Use: "community-pool-spend [proposal-file]", Args: cobra.ExactArgs(1), Short: "Submit a community pool spend proposal", @@ -258,36 +269,40 @@ Where proposal.json contains: ), ), RunE: func(cmd *cobra.Command, args []string) error { - clientCtx := clientCtx.InitWithInput(cmd.InOrStdin()) + clientCtx := client.GetClientContextFromCmd(cmd) + clientCtx, err := client.ReadTxCommandFlags(clientCtx, cmd.Flags()) + if err != nil { + return err + } proposal, err := ParseCommunityPoolSpendProposalJSON(clientCtx.JSONMarshaler, args[0]) if err != nil { return err } - from := clientCtx.GetFromAddress() - amount, err := sdk.ParseCoins(proposal.Amount) if err != nil { return err } - content := types.NewCommunityPoolSpendProposal(proposal.Title, proposal.Description, proposal.Recipient, amount) deposit, err := sdk.ParseCoins(proposal.Deposit) if err != nil { return err } + + from := clientCtx.GetFromAddress() + content := types.NewCommunityPoolSpendProposal(proposal.Title, proposal.Description, proposal.Recipient, amount) + msg, err := govtypes.NewMsgSubmitProposal(content, deposit, from) if err != nil { return err } + if err := msg.ValidateBasic(); err != nil { return err } - return tx.GenerateOrBroadcastTx(clientCtx, msg) + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) }, } - - return cmd } diff --git a/x/distribution/client/cli/tx_test.go b/x/distribution/client/cli/tx_test.go index 966eb73c61..06ec46db9b 100644 --- a/x/distribution/client/cli/tx_test.go +++ b/x/distribution/client/cli/tx_test.go @@ -3,6 +3,8 @@ package cli import ( "testing" + "github.com/spf13/pflag" + "github.com/cosmos/cosmos-sdk/codec/testdata" "github.com/cosmos/cosmos-sdk/testutil" @@ -19,7 +21,7 @@ import ( func Test_splitAndCall_NoMessages(t *testing.T) { clientCtx := client.Context{} - err := newSplitAndApply(nil, clientCtx, nil, 10) + err := newSplitAndApply(nil, clientCtx, nil, nil, 10) assert.NoError(t, err, "") } @@ -42,7 +44,7 @@ func Test_splitAndCall_Splitting(t *testing.T) { callCount := 0 err := newSplitAndApply( - func(clientCtx client.Context, msgs ...sdk.Msg) error { + func(clientCtx client.Context, fs *pflag.FlagSet, msgs ...sdk.Msg) error { callCount++ assert.NotNil(t, clientCtx) @@ -56,7 +58,7 @@ func Test_splitAndCall_Splitting(t *testing.T) { return nil }, - clientCtx, msgs, chunkSize) + clientCtx, nil, msgs, chunkSize) assert.NoError(t, err, "") assert.Equal(t, 3, callCount) diff --git a/x/distribution/client/common/common.go b/x/distribution/client/common/common.go index 312698bc22..f18f0d7756 100644 --- a/x/distribution/client/common/common.go +++ b/x/distribution/client/common/common.go @@ -10,7 +10,7 @@ import ( // QueryDelegationRewards queries a delegation rewards between a delegator and a // validator. -func QueryDelegationRewards(clientCtx client.Context, queryRoute, delAddr, valAddr string) ([]byte, int64, error) { +func QueryDelegationRewards(clientCtx client.Context, delAddr, valAddr string) ([]byte, int64, error) { delegatorAddr, err := sdk.AccAddressFromBech32(delAddr) if err != nil { return nil, 0, err @@ -27,24 +27,24 @@ func QueryDelegationRewards(clientCtx client.Context, queryRoute, delAddr, valAd return nil, 0, fmt.Errorf("failed to marshal params: %w", err) } - route := fmt.Sprintf("custom/%s/%s", queryRoute, types.QueryDelegationRewards) + route := fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryDelegationRewards) return clientCtx.QueryWithData(route, bz) } // QueryDelegatorValidators returns delegator's list of validators // it submitted delegations to. -func QueryDelegatorValidators(clientCtx client.Context, queryRoute string, delegatorAddr sdk.AccAddress) ([]byte, error) { +func QueryDelegatorValidators(clientCtx client.Context, delegatorAddr sdk.AccAddress) ([]byte, error) { res, _, err := clientCtx.QueryWithData( - fmt.Sprintf("custom/%s/%s", queryRoute, types.QueryDelegatorValidators), + fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryDelegatorValidators), clientCtx.JSONMarshaler.MustMarshalJSON(types.NewQueryDelegatorParams(delegatorAddr)), ) return res, err } // QueryValidatorCommission returns a validator's commission. -func QueryValidatorCommission(clientCtx client.Context, queryRoute string, validatorAddr sdk.ValAddress) ([]byte, error) { +func QueryValidatorCommission(clientCtx client.Context, validatorAddr sdk.ValAddress) ([]byte, error) { res, _, err := clientCtx.QueryWithData( - fmt.Sprintf("custom/%s/%s", queryRoute, types.QueryValidatorCommission), + fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryValidatorCommission), clientCtx.JSONMarshaler.MustMarshalJSON(types.NewQueryValidatorCommissionParams(validatorAddr)), ) return res, err @@ -52,10 +52,10 @@ func QueryValidatorCommission(clientCtx client.Context, queryRoute string, valid // WithdrawAllDelegatorRewards builds a multi-message slice to be used // to withdraw all delegations rewards for the given delegator. -func WithdrawAllDelegatorRewards(clientCtx client.Context, queryRoute string, delegatorAddr sdk.AccAddress) ([]sdk.Msg, error) { +func WithdrawAllDelegatorRewards(clientCtx client.Context, delegatorAddr sdk.AccAddress) ([]sdk.Msg, error) { // retrieve the comprehensive list of all validators which the // delegator had submitted delegations to - bz, err := QueryDelegatorValidators(clientCtx, queryRoute, delegatorAddr) + bz, err := QueryDelegatorValidators(clientCtx, delegatorAddr) if err != nil { return nil, err } diff --git a/x/distribution/client/common/common_test.go b/x/distribution/client/common/common_test.go index eb606cabb3..987d42f5d7 100644 --- a/x/distribution/client/common/common_test.go +++ b/x/distribution/client/common/common_test.go @@ -32,7 +32,7 @@ func TestQueryDelegationRewardsAddrValidation(t *testing.T) { for _, tt := range tests { tt := tt t.Run(tt.name, func(t *testing.T) { - _, _, err := QueryDelegationRewards(clientCtx, "", tt.args.delAddr, tt.args.valAddr) + _, _, err := QueryDelegationRewards(clientCtx, tt.args.delAddr, tt.args.valAddr) require.True(t, err != nil, tt.wantErr) }) } diff --git a/x/distribution/client/rest/query.go b/x/distribution/client/rest/query.go index 57b1293e8d..9f90e31f54 100644 --- a/x/distribution/client/rest/query.go +++ b/x/distribution/client/rest/query.go @@ -108,7 +108,7 @@ func delegationRewardsHandlerFn(clientCtx client.Context) http.HandlerFunc { valAddr := mux.Vars(r)["validatorAddr"] // query for rewards from a particular delegation - res, height, ok := checkResponseQueryDelegationRewards(w, clientCtx, types.QuerierRoute, delAddr, valAddr) + res, height, ok := checkResponseQueryDelegationRewards(w, clientCtx, delAddr, valAddr) if !ok { return } @@ -174,7 +174,7 @@ func validatorInfoHandlerFn(clientCtx client.Context) http.HandlerFunc { } // query commission - bz, err := common.QueryValidatorCommission(clientCtx, types.QuerierRoute, valAddr) + bz, err := common.QueryValidatorCommission(clientCtx, valAddr) if rest.CheckInternalServerError(w, err) { return } @@ -186,7 +186,7 @@ func validatorInfoHandlerFn(clientCtx client.Context) http.HandlerFunc { // self bond rewards delAddr := sdk.AccAddress(valAddr) - bz, height, ok := checkResponseQueryDelegationRewards(w, clientCtx, types.QuerierRoute, delAddr.String(), valAddr.String()) + bz, height, ok := checkResponseQueryDelegationRewards(w, clientCtx, delAddr.String(), valAddr.String()) if !ok { return } @@ -221,7 +221,7 @@ func validatorRewardsHandlerFn(clientCtx client.Context) http.HandlerFunc { } delAddr := sdk.AccAddress(validatorAddr).String() - bz, height, ok := checkResponseQueryDelegationRewards(w, clientCtx, types.QuerierRoute, delAddr, valAddr) + bz, height, ok := checkResponseQueryDelegationRewards(w, clientCtx, delAddr, valAddr) if !ok { return } @@ -297,10 +297,10 @@ func outstandingRewardsHandlerFn(clientCtx client.Context) http.HandlerFunc { } func checkResponseQueryDelegationRewards( - w http.ResponseWriter, clientCtx client.Context, queryRoute, delAddr, valAddr string, + w http.ResponseWriter, clientCtx client.Context, delAddr, valAddr string, ) (res []byte, height int64, ok bool) { - res, height, err := common.QueryDelegationRewards(clientCtx, queryRoute, delAddr, valAddr) + res, height, err := common.QueryDelegationRewards(clientCtx, delAddr, valAddr) if rest.CheckInternalServerError(w, err) { return nil, 0, false } diff --git a/x/distribution/client/rest/rest.go b/x/distribution/client/rest/rest.go index ef4af014a4..07c1038455 100644 --- a/x/distribution/client/rest/rest.go +++ b/x/distribution/client/rest/rest.go @@ -20,9 +20,9 @@ func RegisterHandlers(clientCtx client.Context, r *mux.Router) { } // RegisterRoutes register distribution REST routes. -func RegisterRoutes(clientCtx client.Context, r *mux.Router, queryRoute string) { +func RegisterRoutes(clientCtx client.Context, r *mux.Router) { registerQueryRoutes(clientCtx, r) - registerTxRoutes(clientCtx, r, queryRoute) + registerTxRoutes(clientCtx, r) } // TODO add proto compatible Handler after x/gov migration diff --git a/x/distribution/client/rest/tx.go b/x/distribution/client/rest/tx.go index f9c199b969..47e7300437 100644 --- a/x/distribution/client/rest/tx.go +++ b/x/distribution/client/rest/tx.go @@ -80,7 +80,7 @@ func newWithdrawDelegatorRewardsHandlerFn(clientCtx client.Context) http.Handler return } - msgs, err := common.WithdrawAllDelegatorRewards(clientCtx, types.QuerierRoute, delAddr) + msgs, err := common.WithdrawAllDelegatorRewards(clientCtx, delAddr) if rest.CheckInternalServerError(w, err) { return } @@ -207,11 +207,11 @@ func newFundCommunityPoolHandlerFn(clientCtx client.Context) http.HandlerFunc { // // TODO: Remove once client-side Protobuf migration has been completed. // --------------------------------------------------------------------------- -func registerTxRoutes(clientCtx client.Context, r *mux.Router, queryRoute string) { +func registerTxRoutes(clientCtx client.Context, r *mux.Router) { // Withdraw all delegator rewards r.HandleFunc( "/distribution/delegators/{delegatorAddr}/rewards", - withdrawDelegatorRewardsHandlerFn(clientCtx, queryRoute), + withdrawDelegatorRewardsHandlerFn(clientCtx), ).Methods("POST") // Withdraw delegation rewards @@ -241,7 +241,7 @@ func registerTxRoutes(clientCtx client.Context, r *mux.Router, queryRoute string } // Withdraw delegator rewards -func withdrawDelegatorRewardsHandlerFn(clientCtx client.Context, queryRoute string) http.HandlerFunc { +func withdrawDelegatorRewardsHandlerFn(clientCtx client.Context) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { var req withdrawRewardsReq if !rest.ReadRESTReq(w, r, clientCtx.Codec, &req) { @@ -259,7 +259,7 @@ func withdrawDelegatorRewardsHandlerFn(clientCtx client.Context, queryRoute stri return } - msgs, err := common.WithdrawAllDelegatorRewards(clientCtx, queryRoute, delAddr) + msgs, err := common.WithdrawAllDelegatorRewards(clientCtx, delAddr) if rest.CheckInternalServerError(w, err) { return } diff --git a/x/distribution/client/testutil/helpers.go b/x/distribution/client/testutil/helpers.go index 7b9d5d0df3..941a805f7d 100644 --- a/x/distribution/client/testutil/helpers.go +++ b/x/distribution/client/testutil/helpers.go @@ -1,103 +1,33 @@ package testutil import ( + "bytes" + "context" "fmt" - "github.com/stretchr/testify/require" - - clientkeys "github.com/cosmos/cosmos-sdk/client/keys" - "github.com/cosmos/cosmos-sdk/tests" - "github.com/cosmos/cosmos-sdk/tests/cli" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/distribution/types" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + distrcli "github.com/cosmos/cosmos-sdk/x/distribution/client/cli" ) -// TxWithdrawRewards raises a txn to withdraw rewards -func TxWithdrawRewards(f *cli.Fixtures, valAddr sdk.ValAddress, from string, flags ...string) bool { - cmd := fmt.Sprintf("%s tx distribution withdraw-rewards %s %v --keyring-backend=test --from=%s", f.SimdBinary, valAddr, f.Flags(), from) - return cli.ExecuteWrite(f.T, cli.AddFlags(cmd, flags), clientkeys.DefaultKeyPass) -} - -// TxSetWithdrawAddress helps to set the withdraw address for rewards associated with a delegator address -func TxSetWithdrawAddress(f *cli.Fixtures, from, withDrawAddr string, flags ...string) (bool, string, string) { - cmd := fmt.Sprintf("%s tx distribution set-withdraw-addr %s --from %s %v --keyring-backend=test", f.SimdBinary, withDrawAddr, from, f.Flags()) - return cli.ExecuteWriteRetStdStreams(f.T, cli.AddFlags(cmd, flags), clientkeys.DefaultKeyPass) -} - -// TxWithdrawAllRewards raises a txn to withdraw all rewards of a delegator address -func TxWithdrawAllRewards(f *cli.Fixtures, from string, flags ...string) (bool, string, string) { - cmd := fmt.Sprintf("%s tx distribution withdraw-all-rewards %v --keyring-backend=test --from=%s", f.SimdBinary, f.Flags(), from) - return cli.ExecuteWriteRetStdStreams(f.T, cli.AddFlags(cmd, flags), clientkeys.DefaultKeyPass) -} - -// TxFundCommunityPool Funds the community pool with the specified amount -func TxFundCommunityPool(f *cli.Fixtures, from string, amount sdk.Coin, flags ...string) (bool, string, string) { - cmd := fmt.Sprintf("%s tx distribution fund-community-pool %v %v --keyring-backend=test --from=%s", f.SimdBinary, amount, f.Flags(), from) - return cli.ExecuteWriteRetStdStreams(f.T, cli.AddFlags(cmd, flags), clientkeys.DefaultKeyPass) -} - -// QueryRewards returns the rewards of a delegator -func QueryRewards(f *cli.Fixtures, delAddr sdk.AccAddress, flags ...string) types.QueryDelegatorTotalRewardsResponse { - cmd := fmt.Sprintf("%s query distribution rewards %s %s", f.SimdBinary, delAddr, f.Flags()) - res, errStr := tests.ExecuteT(f.T, cmd, "") - require.Empty(f.T, errStr) - - var rewards types.QueryDelegatorTotalRewardsResponse - require.NoError(f.T, f.Cdc.UnmarshalJSON([]byte(res), &rewards)) - return rewards -} - -// QueryValidatorOutstandingRewards distribution outstanding (un-withdrawn) rewards -func QueryValidatorOutstandingRewards(f *cli.Fixtures, valAddr string) types.ValidatorOutstandingRewards { - cmd := fmt.Sprintf("%s query distribution validator-outstanding-rewards %s %v", f.SimdBinary, valAddr, f.Flags()) - res, errStr := tests.ExecuteT(f.T, cmd, "") - require.Empty(f.T, errStr) - - var outstandingRewards types.ValidatorOutstandingRewards - require.NoError(f.T, f.Cdc.UnmarshalJSON([]byte(res), &outstandingRewards)) - return outstandingRewards -} - -// QueryParameters is simcli query distribution parameters -func QueryParameters(f *cli.Fixtures, flags ...string) types.Params { - cmd := fmt.Sprintf("%s query distribution params %v", f.SimdBinary, f.Flags()) - out, errStr := tests.ExecuteT(f.T, cli.AddFlags(cmd, flags), "") - require.Empty(f.T, errStr) - - var params types.Params - require.NoError(f.T, f.Cdc.UnmarshalJSON([]byte(out), ¶ms)) - return params -} - -// QueryCommission returns validator commission rewards from delegators to that validator. -func QueryCommission(f *cli.Fixtures, valAddr string, flags ...string) types.ValidatorAccumulatedCommission { - cmd := fmt.Sprintf("%s query distribution commission %s %v", f.SimdBinary, valAddr, f.Flags()) - out, errStr := tests.ExecuteT(f.T, cli.AddFlags(cmd, flags), "") - require.Empty(f.T, errStr) - - var commission types.ValidatorAccumulatedCommission - require.NoError(f.T, f.Cdc.UnmarshalJSON([]byte(out), &commission)) - return commission -} - -// QuerySlashes returns all slashes of a validator for a given block range. -func QuerySlashes(f *cli.Fixtures, valAddr string, flags ...string) []types.ValidatorSlashEvent { - cmd := fmt.Sprintf("%s query distribution slashes %s 0 5 %v ", f.SimdBinary, valAddr, f.Flags()) - out, errStr := tests.ExecuteT(f.T, cli.AddFlags(cmd, flags), "") - require.Empty(f.T, errStr) - - var slashes []types.ValidatorSlashEvent - require.NoError(f.T, f.Cdc.UnmarshalJSON([]byte(out), &slashes)) - return slashes -} - -// QueryCommunityPool returns the amount of coins in the community pool -func QueryCommunityPool(f *cli.Fixtures, flags ...string) sdk.DecCoins { - cmd := fmt.Sprintf("%s query distribution community-pool %v ", f.SimdBinary, f.Flags()) - out, errStr := tests.ExecuteT(f.T, cli.AddFlags(cmd, flags), "") - require.Empty(f.T, errStr) - - var amount sdk.DecCoins - require.NoError(f.T, f.Cdc.UnmarshalJSON([]byte(out), &amount)) - return amount +func MsgWithdrawDelegatorRewardExec(clientCtx client.Context, valAddr fmt.Stringer, extraArgs ...string) ([]byte, error) { + buf := new(bytes.Buffer) + clientCtx = clientCtx.WithOutput(buf) + + ctx := context.Background() + ctx = context.WithValue(ctx, client.ClientContextKey, &clientCtx) + + args := []string{valAddr.String()} + args = append(args, extraArgs...) + + cmd := flags.PostCommands(distrcli.NewWithdrawRewardsCmd())[0] + cmd.SetErr(buf) + cmd.SetOut(buf) + cmd.SetArgs(args) + + if err := cmd.ExecuteContext(ctx); err != nil { + return nil, err + } + + return buf.Bytes(), nil } diff --git a/x/distribution/module.go b/x/distribution/module.go index c097e89d7c..ce887651b7 100644 --- a/x/distribution/module.go +++ b/x/distribution/module.go @@ -69,13 +69,13 @@ func (AppModuleBasic) RegisterRESTRoutes(clientCtx sdkclient.Context, rtr *mux.R } // GetTxCmd returns the root tx command for the distribution module. -func (AppModuleBasic) GetTxCmd(clientCtx sdkclient.Context) *cobra.Command { - return cli.NewTxCmd(clientCtx) +func (AppModuleBasic) GetTxCmd(_ sdkclient.Context) *cobra.Command { + return cli.NewTxCmd() } // GetQueryCmd returns the root query command for the distribution module. -func (AppModuleBasic) GetQueryCmd(clientCtx sdkclient.Context) *cobra.Command { - return cli.GetQueryCmd(types.StoreKey, clientCtx.Codec) +func (AppModuleBasic) GetQueryCmd(_ sdkclient.Context) *cobra.Command { + return cli.GetQueryCmd() } // RegisterInterfaceTypes implements InterfaceModule diff --git a/x/gov/client/proposal_handler.go b/x/gov/client/proposal_handler.go index 8cac280a59..61ee3d8c3b 100644 --- a/x/gov/client/proposal_handler.go +++ b/x/gov/client/proposal_handler.go @@ -11,7 +11,7 @@ import ( type RESTHandlerFn func(client.Context) rest.ProposalRESTHandler // function to create the cli handler -type CLIHandlerFn func(client.Context) *cobra.Command +type CLIHandlerFn func() *cobra.Command // The combined type for a proposal handler for both cli and rest type ProposalHandler struct { diff --git a/x/gov/module.go b/x/gov/module.go index b1f2ed6ffb..cc5b179511 100644 --- a/x/gov/module.go +++ b/x/gov/module.go @@ -88,7 +88,7 @@ func (a AppModuleBasic) RegisterRESTRoutes(clientCtx client.Context, rtr *mux.Ro func (a AppModuleBasic) GetTxCmd(clientCtx client.Context) *cobra.Command { proposalCLIHandlers := make([]*cobra.Command, 0, len(a.proposalHandlers)) for _, proposalHandler := range a.proposalHandlers { - proposalCLIHandlers = append(proposalCLIHandlers, proposalHandler.CLIHandler(clientCtx)) + proposalCLIHandlers = append(proposalCLIHandlers, proposalHandler.CLIHandler()) } return cli.NewTxCmd(clientCtx, proposalCLIHandlers) diff --git a/x/params/client/cli/tx.go b/x/params/client/cli/tx.go index 3b16ab1181..739fadd393 100644 --- a/x/params/client/cli/tx.go +++ b/x/params/client/cli/tx.go @@ -17,8 +17,8 @@ import ( // NewSubmitParamChangeProposalTxCmd returns a CLI command handler for creating // a parameter change proposal governance transaction. -func NewSubmitParamChangeProposalTxCmd(clientCtx client.Context) *cobra.Command { - cmd := &cobra.Command{ +func NewSubmitParamChangeProposalTxCmd() *cobra.Command { + return &cobra.Command{ Use: "param-change [proposal-file]", Args: cobra.ExactArgs(1), Short: "Submit a parameter change proposal", @@ -57,7 +57,11 @@ Where proposal.json contains: ), ), RunE: func(cmd *cobra.Command, args []string) error { - clientCtx := clientCtx.InitWithInput(cmd.InOrStdin()) + clientCtx := client.GetClientContextFromCmd(cmd) + clientCtx, err := client.ReadTxCommandFlags(clientCtx, cmd.Flags()) + if err != nil { + return err + } proposal, err := paramscutils.ParseParamChangeProposalJSON(clientCtx.JSONMarshaler, args[0]) if err != nil { @@ -82,9 +86,7 @@ Where proposal.json contains: return err } - return tx.GenerateOrBroadcastTx(clientCtx, msg) + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) }, } - - return cmd } diff --git a/x/upgrade/client/cli/tx.go b/x/upgrade/client/cli/tx.go index 4cb9e017d8..c509fd03ed 100644 --- a/x/upgrade/client/cli/tx.go +++ b/x/upgrade/client/cli/tx.go @@ -36,7 +36,7 @@ func GetTxCmd() *cobra.Command { } // NewCmdSubmitUpgradeProposal implements a command handler for submitting a software upgrade proposal transaction. -func NewCmdSubmitUpgradeProposal(clientCtx client.Context) *cobra.Command { +func NewCmdSubmitUpgradeProposal() *cobra.Command { cmd := &cobra.Command{ Use: "software-upgrade [name] (--upgrade-height [height] | --upgrade-time [time]) (--upgrade-info [info]) [flags]", Args: cobra.ExactArgs(1), @@ -45,13 +45,18 @@ func NewCmdSubmitUpgradeProposal(clientCtx client.Context) *cobra.Command { "Please specify a unique name and height OR time for the upgrade to take effect.\n" + "You may include info to reference a binary download link, in a format compatible with: https://github.com/regen-network/cosmosd", RunE: func(cmd *cobra.Command, args []string) error { + clientCtx := client.GetClientContextFromCmd(cmd) + clientCtx, err := client.ReadTxCommandFlags(clientCtx, cmd.Flags()) + if err != nil { + return err + } + name := args[0] content, err := parseArgsToContent(cmd, name) if err != nil { return err } - clientCtx := clientCtx.InitWithInput(cmd.InOrStdin()) from := clientCtx.GetFromAddress() depositStr, err := cmd.Flags().GetString(cli.FlagDeposit) @@ -72,7 +77,7 @@ func NewCmdSubmitUpgradeProposal(clientCtx client.Context) *cobra.Command { return err } - return tx.GenerateOrBroadcastTx(clientCtx, msg) + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) }, }