chore: x/gov v1 Completeness audit (#11567)

## Description

Closes: https://github.com/cosmos/cosmos-sdk/issues/11086



---

### 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...

- [x] 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
- [x] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#pr-targeting))
- [x] provided a link to the relevant issue or specification
- [ ] followed the guidelines for [building modules](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules)
- [ ] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/master/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)
This commit is contained in:
Marie Gauthier 2022-04-08 12:03:43 +02:00 committed by GitHub
parent 47d74779ac
commit eb607ae954
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 847 additions and 572 deletions

View File

@ -171,7 +171,6 @@ Ref: https://keepachangelog.com/en/1.0.0/
* [\#9780](https://github.com/cosmos/cosmos-sdk/pull/9780) Use sigs.k8s.io for yaml, which might lead to minor YAML output changes
* [\#10625](https://github.com/cosmos/cosmos-sdk/pull/10625) Rename `--fee-account` CLI flag to `--fee-granter`
* [\#10684](https://github.com/cosmos/cosmos-sdk/pull/10684) Rename `edit-validator` command's `--moniker` flag to `--new-moniker`
* [\#11116](https://github.com/cosmos/cosmos-sdk/pull/11116) `software-upgrade` and `cancel-software-upgrade` gov proposal commands have changed to `legacy-software-upgrade` and `legacy-cancel-software-upgrade`.
* (authz)[\#11060](https://github.com/cosmos/cosmos-sdk/pull/11060) Changed the default value of the `--expiration` `tx grant` CLI Flag: was now + 1year, update: null (no expire date).
### Improvements

View File

@ -40,12 +40,6 @@ func parseSubmitLegacyProposalFlags(fs *pflag.FlagSet) (*legacyProposal, error)
if proposalFile == "" {
proposalType, _ := fs.GetString(FlagProposalType)
title, _ := fs.GetString(FlagTitle)
description, _ := fs.GetString(FlagDescription)
if proposalType == "" && title == "" && description == "" {
return nil, fmt.Errorf("one of the --proposal or (--title, --description and --type) flags are required")
}
proposal.Title, _ = fs.GetString(FlagTitle)
proposal.Description, _ = fs.GetString(FlagDescription)
proposal.Type = govutils.NormalizeProposalType(proposalType)

View File

@ -60,17 +60,57 @@ func TestParseSubmitLegacyProposalFlags(t *testing.T) {
// no --proposal, only flags
fs.Set(FlagProposal, "")
fs.Set(FlagTitle, proposal1.Title)
fs.Set(FlagDescription, proposal1.Description)
fs.Set(FlagProposalType, proposal1.Type)
fs.Set(FlagDeposit, proposal1.Deposit)
proposal2, err := parseSubmitLegacyProposalFlags(fs)
flagTestCases := map[string]struct {
pTitle string
pDescription string
pType string
expErr bool
errMsg string
}{
"valid flags": {
pTitle: proposal1.Title,
pDescription: proposal1.Description,
pType: proposal1.Type,
},
"empty type": {
pTitle: proposal1.Title,
pDescription: proposal1.Description,
expErr: true,
errMsg: "proposal type is required",
},
"empty title": {
pDescription: proposal1.Description,
pType: proposal1.Type,
expErr: true,
errMsg: "proposal title is required",
},
"empty description": {
pTitle: proposal1.Title,
pType: proposal1.Type,
expErr: true,
errMsg: "proposal description is required",
},
}
for name, tc := range flagTestCases {
t.Run(name, func(t *testing.T) {
fs.Set(FlagTitle, tc.pTitle)
fs.Set(FlagDescription, tc.pDescription)
fs.Set(FlagProposalType, tc.pType)
fs.Set(FlagDeposit, proposal1.Deposit)
proposal2, err := parseSubmitLegacyProposalFlags(fs)
require.Nil(t, err, "unexpected error")
require.Equal(t, proposal1.Title, proposal2.Title)
require.Equal(t, proposal1.Description, proposal2.Description)
require.Equal(t, proposal1.Type, proposal2.Type)
require.Equal(t, proposal1.Deposit, proposal2.Deposit)
if tc.expErr {
require.Error(t, err)
require.Contains(t, err.Error(), tc.errMsg)
} else {
require.NoError(t, err)
require.Equal(t, proposal1.Title, proposal2.Title)
require.Equal(t, proposal1.Description, proposal2.Description)
require.Equal(t, proposal1.Type, proposal2.Type)
require.Equal(t, proposal1.Deposit, proposal2.Deposit)
}
})
}
err = okJSON.Close()
require.Nil(t, err, "unexpected error")

View File

@ -35,7 +35,7 @@ const (
FlagProposal = "proposal"
)
// ProposalFlags defines the core required fields of a proposal. It is used to
// ProposalFlags defines the core required fields of a legacy proposal. It is used to
// verify that these values are not provided in conjunction with a JSON proposal
// file.
var ProposalFlags = []string{
@ -47,7 +47,7 @@ var ProposalFlags = []string{
// NewTxCmd returns the transaction commands for this module
// governance ModuleClient is slightly different from other ModuleClients in that
// it contains a slice of "proposal" child commands. These commands are respective
// it contains a slice of legacy "proposal" child commands. These commands are respective
// to the proposal type handlers that are implemented in other modules but are mounted
// under the governance CLI (eg. parameter change proposals).
func NewTxCmd(legacyPropCmds []*cobra.Command) *cobra.Command {
@ -81,12 +81,12 @@ func NewTxCmd(legacyPropCmds []*cobra.Command) *cobra.Command {
// NewCmdSubmitProposal implements submitting a proposal transaction command.
func NewCmdSubmitProposal() *cobra.Command {
cmd := &cobra.Command{
Use: "submit-proposal",
Short: "Submit a proposal along with some messages and metadata",
Use: "submit-proposal [path/to/proposal.json]",
Short: "Submit a proposal along with some messages, metadata and deposit",
Args: cobra.ExactArgs(1),
Long: strings.TrimSpace(
fmt.Sprintf(`Submit a proposal along with some messages and metadata.
Messages, metadata and deposit are defined in a JSON file.
fmt.Sprintf(`Submit a proposal along with some messages, metadata and deposit.
They should be defined in a JSON file.
Example:
$ %s tx gov submit-proposal path/to/proposal.json

View File

@ -17,20 +17,6 @@ var commonArgs = []string{
fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(10))).String()),
}
// MsgSubmitProposal creates a tx for submit proposal
func MsgSubmitProposal(clientCtx client.Context, from, title, description, proposalType string, extraArgs ...string) (testutil.BufferWriter, error) {
args := append([]string{
fmt.Sprintf("--%s=%s", govcli.FlagTitle, title),
fmt.Sprintf("--%s=%s", govcli.FlagDescription, description),
fmt.Sprintf("--%s=%s", govcli.FlagProposalType, proposalType),
fmt.Sprintf("--%s=%s", flags.FlagFrom, from),
}, commonArgs...)
args = append(args, extraArgs...)
return clitestutil.ExecTestCLICmd(clientCtx, govcli.NewCmdSubmitProposal(), args)
}
// MsgSubmitLegacyProposal creates a tx for submit legacy proposal
func MsgSubmitLegacyProposal(clientCtx client.Context, from, title, description, proposalType string, extraArgs ...string) (testutil.BufferWriter, error) {
args := append([]string{

View File

@ -0,0 +1,536 @@
package testutil
import (
"fmt"
"strings"
tmcli "github.com/tendermint/tendermint/libs/cli"
clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/gov/client/cli"
v1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1"
"github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1"
)
func (s *IntegrationTestSuite) TestCmdParams() {
val := s.network.Validators[0]
testCases := []struct {
name string
args []string
expectedOutput string
}{
{
"json output",
[]string{fmt.Sprintf("--%s=json", tmcli.OutputFlag)},
`{"voting_params":{"voting_period":"172800000000000"},"tally_params":{"quorum":"0.334000000000000000","threshold":"0.500000000000000000","veto_threshold":"0.334000000000000000"},"deposit_params":{"min_deposit":[{"denom":"stake","amount":"10000000"}],"max_deposit_period":"172800000000000"}}`,
},
{
"text output",
[]string{},
`
deposit_params:
max_deposit_period: "172800000000000"
min_deposit:
- amount: "10000000"
denom: stake
tally_params:
quorum: "0.334000000000000000"
threshold: "0.500000000000000000"
veto_threshold: "0.334000000000000000"
voting_params:
voting_period: "172800000000000"
`,
},
}
for _, tc := range testCases {
tc := tc
s.Run(tc.name, func() {
cmd := cli.GetCmdQueryParams()
clientCtx := val.ClientCtx
out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args)
s.Require().NoError(err)
s.Require().Equal(strings.TrimSpace(tc.expectedOutput), strings.TrimSpace(out.String()))
})
}
}
func (s *IntegrationTestSuite) TestCmdParam() {
val := s.network.Validators[0]
testCases := []struct {
name string
args []string
expectedOutput string
}{
{
"voting params",
[]string{
"voting",
fmt.Sprintf("--%s=json", tmcli.OutputFlag),
},
`{"voting_period":"172800000000000"}`,
},
{
"tally params",
[]string{
"tallying",
fmt.Sprintf("--%s=json", tmcli.OutputFlag),
},
`{"quorum":"0.334000000000000000","threshold":"0.500000000000000000","veto_threshold":"0.334000000000000000"}`,
},
{
"deposit params",
[]string{
"deposit",
fmt.Sprintf("--%s=json", tmcli.OutputFlag),
},
`{"min_deposit":[{"denom":"stake","amount":"10000000"}],"max_deposit_period":"172800000000000"}`,
},
}
for _, tc := range testCases {
tc := tc
s.Run(tc.name, func() {
cmd := cli.GetCmdQueryParam()
clientCtx := val.ClientCtx
out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args)
s.Require().NoError(err)
s.Require().Equal(strings.TrimSpace(tc.expectedOutput), strings.TrimSpace(out.String()))
})
}
}
func (s *IntegrationTestSuite) TestCmdProposer() {
val := s.network.Validators[0]
testCases := []struct {
name string
args []string
expectErr bool
expectedOutput string
}{
{
"without proposal id",
[]string{
fmt.Sprintf("--%s=json", tmcli.OutputFlag),
},
true,
``,
},
{
"json output",
[]string{
"1",
fmt.Sprintf("--%s=json", tmcli.OutputFlag),
},
false,
fmt.Sprintf("{\"proposal_id\":\"%s\",\"proposer\":\"%s\"}", "1", val.Address.String()),
},
}
for _, tc := range testCases {
tc := tc
s.Run(tc.name, func() {
cmd := cli.GetCmdQueryProposer()
clientCtx := val.ClientCtx
out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args)
if tc.expectErr {
s.Require().Error(err)
} else {
s.Require().NoError(err)
s.Require().Equal(strings.TrimSpace(tc.expectedOutput), strings.TrimSpace(out.String()))
}
})
}
}
func (s *IntegrationTestSuite) TestCmdTally() {
val := s.network.Validators[0]
testCases := []struct {
name string
args []string
expectErr bool
expectedOutput v1.TallyResult
}{
{
"without proposal id",
[]string{
fmt.Sprintf("--%s=json", tmcli.OutputFlag),
},
true,
v1.TallyResult{},
},
{
"json output",
[]string{
"2",
fmt.Sprintf("--%s=json", tmcli.OutputFlag),
},
false,
v1.NewTallyResult(sdk.NewInt(0), sdk.NewInt(0), sdk.NewInt(0), sdk.NewInt(0)),
},
{
"json output",
[]string{
"1",
fmt.Sprintf("--%s=json", tmcli.OutputFlag),
},
false,
v1.NewTallyResult(s.cfg.BondedTokens, sdk.NewInt(0), sdk.NewInt(0), sdk.NewInt(0)),
},
}
for _, tc := range testCases {
tc := tc
s.Run(tc.name, func() {
cmd := cli.GetCmdQueryTally()
clientCtx := val.ClientCtx
out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args)
if tc.expectErr {
s.Require().Error(err)
} else {
var tally v1.TallyResult
s.Require().NoError(clientCtx.Codec.UnmarshalJSON(out.Bytes(), &tally), out.String())
s.Require().Equal(tally, tc.expectedOutput)
}
})
}
}
func (s *IntegrationTestSuite) TestCmdGetProposal() {
val := s.network.Validators[0]
title := "Text Proposal 1"
testCases := []struct {
name string
args []string
expectErr bool
}{
{
"get non existing proposal",
[]string{
"10",
fmt.Sprintf("--%s=json", tmcli.OutputFlag),
},
true,
},
{
"get proposal with json response",
[]string{
"1",
fmt.Sprintf("--%s=json", tmcli.OutputFlag),
},
false,
},
}
for _, tc := range testCases {
tc := tc
s.Run(tc.name, func() {
cmd := cli.GetCmdQueryProposal()
clientCtx := val.ClientCtx
out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args)
if tc.expectErr {
s.Require().Error(err)
} else {
s.Require().NoError(err)
var proposal v1.Proposal
s.Require().NoError(clientCtx.Codec.UnmarshalJSON(out.Bytes(), &proposal), out.String())
s.Require().Equal(title, proposal.Messages[0].GetCachedValue().(*v1.MsgExecLegacyContent).Content.GetCachedValue().(v1beta1.Content).GetTitle())
}
})
}
}
func (s *IntegrationTestSuite) TestCmdGetProposals() {
val := s.network.Validators[0]
testCases := []struct {
name string
args []string
expectErr bool
}{
{
"get proposals as json response",
[]string{
fmt.Sprintf("--%s=json", tmcli.OutputFlag),
},
false,
},
{
"get proposals with invalid status",
[]string{
"--status=unknown",
fmt.Sprintf("--%s=json", tmcli.OutputFlag),
},
true,
},
}
for _, tc := range testCases {
tc := tc
s.Run(tc.name, func() {
cmd := cli.GetCmdQueryProposals()
clientCtx := val.ClientCtx
out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args)
if tc.expectErr {
s.Require().Error(err)
} else {
s.Require().NoError(err)
var proposals v1.QueryProposalsResponse
s.Require().NoError(clientCtx.Codec.UnmarshalJSON(out.Bytes(), &proposals), out.String())
s.Require().Len(proposals.Proposals, 3)
}
})
}
}
func (s *IntegrationTestSuite) TestCmdQueryDeposits() {
val := s.network.Validators[0]
testCases := []struct {
name string
args []string
expectErr bool
}{
{
"get deposits of non existing proposal",
[]string{
"10",
},
true,
},
{
"get deposits(valid req)",
[]string{
"1",
fmt.Sprintf("--%s=json", tmcli.OutputFlag),
},
false,
},
}
for _, tc := range testCases {
tc := tc
s.Run(tc.name, func() {
cmd := cli.GetCmdQueryDeposits()
clientCtx := val.ClientCtx
out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args)
if tc.expectErr {
s.Require().Error(err)
} else {
s.Require().NoError(err)
var deposits v1.QueryDepositsResponse
s.Require().NoError(clientCtx.Codec.UnmarshalJSON(out.Bytes(), &deposits), out.String())
s.Require().Len(deposits.Deposits, 1)
}
})
}
}
func (s *IntegrationTestSuite) TestCmdQueryDeposit() {
val := s.network.Validators[0]
depositAmount := sdk.NewCoin(s.cfg.BondDenom, v1.DefaultMinDepositTokens)
testCases := []struct {
name string
args []string
expectErr bool
}{
{
"get deposit with no depositer",
[]string{
"1",
},
true,
},
{
"get deposit with wrong deposit address",
[]string{
"1",
"wrong address",
},
true,
},
{
"get deposit (valid req)",
[]string{
"1",
val.Address.String(),
fmt.Sprintf("--%s=json", tmcli.OutputFlag),
},
false,
},
}
for _, tc := range testCases {
tc := tc
s.Run(tc.name, func() {
cmd := cli.GetCmdQueryDeposit()
clientCtx := val.ClientCtx
out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args)
if tc.expectErr {
s.Require().Error(err)
} else {
s.Require().NoError(err)
var deposit v1.Deposit
s.Require().NoError(clientCtx.Codec.UnmarshalJSON(out.Bytes(), &deposit), out.String())
s.Require().Equal(depositAmount.String(), sdk.Coins(deposit.Amount).String())
}
})
}
}
func (s *IntegrationTestSuite) TestCmdQueryVotes() {
val := s.network.Validators[0]
testCases := []struct {
name string
args []string
expectErr bool
}{
{
"get votes with no proposal id",
[]string{},
true,
},
{
"get votes of non existed proposal",
[]string{
"10",
},
true,
},
{
"vote for invalid proposal",
[]string{
"1",
fmt.Sprintf("--%s=json", tmcli.OutputFlag),
},
false,
},
}
for _, tc := range testCases {
tc := tc
s.Run(tc.name, func() {
cmd := cli.GetCmdQueryVotes()
clientCtx := val.ClientCtx
out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args)
if tc.expectErr {
s.Require().Error(err)
} else {
s.Require().NoError(err)
var votes v1.QueryVotesResponse
s.Require().NoError(clientCtx.Codec.UnmarshalJSON(out.Bytes(), &votes), out.String())
s.Require().Len(votes.Votes, 1)
}
})
}
}
func (s *IntegrationTestSuite) TestCmdQueryVote() {
val := s.network.Validators[0]
testCases := []struct {
name string
args []string
expectErr bool
expVoteOptions v1.WeightedVoteOptions
}{
{
"get vote of non existing proposal",
[]string{
"10",
val.Address.String(),
},
true,
v1.NewNonSplitVoteOption(v1.OptionYes),
},
{
"get vote by wrong voter",
[]string{
"1",
"wrong address",
},
true,
v1.NewNonSplitVoteOption(v1.OptionYes),
},
{
"vote for valid proposal",
[]string{
"1",
val.Address.String(),
fmt.Sprintf("--%s=json", tmcli.OutputFlag),
},
false,
v1.NewNonSplitVoteOption(v1.OptionYes),
},
{
"split vote for valid proposal",
[]string{
"3",
val.Address.String(),
fmt.Sprintf("--%s=json", tmcli.OutputFlag),
},
false,
v1.WeightedVoteOptions{
&v1.WeightedVoteOption{Option: v1.OptionYes, Weight: sdk.NewDecWithPrec(60, 2).String()},
&v1.WeightedVoteOption{Option: v1.OptionNo, Weight: sdk.NewDecWithPrec(30, 2).String()},
&v1.WeightedVoteOption{Option: v1.OptionAbstain, Weight: sdk.NewDecWithPrec(5, 2).String()},
&v1.WeightedVoteOption{Option: v1.OptionNoWithVeto, Weight: sdk.NewDecWithPrec(5, 2).String()},
},
},
}
for _, tc := range testCases {
tc := tc
s.Run(tc.name, func() {
cmd := cli.GetCmdQueryVote()
clientCtx := val.ClientCtx
out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args)
if tc.expectErr {
s.Require().Error(err)
} else {
s.Require().NoError(err)
var vote v1.Vote
s.Require().NoError(clientCtx.Codec.UnmarshalJSON(out.Bytes(), &vote), out.String())
s.Require().Equal(len(vote.Options), len(tc.expVoteOptions))
for i, option := range tc.expVoteOptions {
s.Require().Equal(option.Option, vote.Options[i].Option)
s.Require().Equal(option.Weight, vote.Options[i].Weight)
}
}
})
}
}

View File

@ -3,15 +3,12 @@ package testutil
import (
"encoding/base64"
"fmt"
"strings"
"github.com/cosmos/cosmos-sdk/testutil"
"github.com/gogo/protobuf/proto"
"github.com/stretchr/testify/suite"
tmcli "github.com/tendermint/tendermint/libs/cli"
"github.com/cosmos/cosmos-sdk/client/flags"
clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli"
"github.com/cosmos/cosmos-sdk/testutil/network"
@ -83,202 +80,6 @@ func (s *IntegrationTestSuite) TearDownSuite() {
s.network.Cleanup()
}
func (s *IntegrationTestSuite) TestCmdParams() {
val := s.network.Validators[0]
testCases := []struct {
name string
args []string
expectedOutput string
}{
{
"json output",
[]string{fmt.Sprintf("--%s=json", tmcli.OutputFlag)},
`{"voting_params":{"voting_period":"172800000000000"},"tally_params":{"quorum":"0.334000000000000000","threshold":"0.500000000000000000","veto_threshold":"0.334000000000000000"},"deposit_params":{"min_deposit":[{"denom":"stake","amount":"10000000"}],"max_deposit_period":"172800000000000"}}`,
},
{
"text output",
[]string{},
`
deposit_params:
max_deposit_period: "172800000000000"
min_deposit:
- amount: "10000000"
denom: stake
tally_params:
quorum: "0.334000000000000000"
threshold: "0.500000000000000000"
veto_threshold: "0.334000000000000000"
voting_params:
voting_period: "172800000000000"
`,
},
}
for _, tc := range testCases {
tc := tc
s.Run(tc.name, func() {
cmd := cli.GetCmdQueryParams()
clientCtx := val.ClientCtx
out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args)
s.Require().NoError(err)
s.Require().Equal(strings.TrimSpace(tc.expectedOutput), strings.TrimSpace(out.String()))
})
}
}
func (s *IntegrationTestSuite) TestCmdParam() {
val := s.network.Validators[0]
testCases := []struct {
name string
args []string
expectedOutput string
}{
{
"voting params",
[]string{
"voting",
fmt.Sprintf("--%s=json", tmcli.OutputFlag),
},
`{"voting_period":"172800000000000"}`,
},
{
"tally params",
[]string{
"tallying",
fmt.Sprintf("--%s=json", tmcli.OutputFlag),
},
`{"quorum":"0.334000000000000000","threshold":"0.500000000000000000","veto_threshold":"0.334000000000000000"}`,
},
{
"deposit params",
[]string{
"deposit",
fmt.Sprintf("--%s=json", tmcli.OutputFlag),
},
`{"min_deposit":[{"denom":"stake","amount":"10000000"}],"max_deposit_period":"172800000000000"}`,
},
}
for _, tc := range testCases {
tc := tc
s.Run(tc.name, func() {
cmd := cli.GetCmdQueryParam()
clientCtx := val.ClientCtx
out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args)
s.Require().NoError(err)
s.Require().Equal(strings.TrimSpace(tc.expectedOutput), strings.TrimSpace(out.String()))
})
}
}
func (s *IntegrationTestSuite) TestCmdProposer() {
val := s.network.Validators[0]
testCases := []struct {
name string
args []string
expectErr bool
expectedOutput string
}{
{
"without proposal id",
[]string{
fmt.Sprintf("--%s=json", tmcli.OutputFlag),
},
true,
``,
},
{
"json output",
[]string{
"1",
fmt.Sprintf("--%s=json", tmcli.OutputFlag),
},
false,
fmt.Sprintf("{\"proposal_id\":\"%s\",\"proposer\":\"%s\"}", "1", val.Address.String()),
},
}
for _, tc := range testCases {
tc := tc
s.Run(tc.name, func() {
cmd := cli.GetCmdQueryProposer()
clientCtx := val.ClientCtx
out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args)
if tc.expectErr {
s.Require().Error(err)
} else {
s.Require().NoError(err)
s.Require().Equal(strings.TrimSpace(tc.expectedOutput), strings.TrimSpace(out.String()))
}
})
}
}
func (s *IntegrationTestSuite) TestCmdTally() {
val := s.network.Validators[0]
testCases := []struct {
name string
args []string
expectErr bool
expectedOutput v1.TallyResult
}{
{
"without proposal id",
[]string{
fmt.Sprintf("--%s=json", tmcli.OutputFlag),
},
true,
v1.TallyResult{},
},
{
"json output",
[]string{
"2",
fmt.Sprintf("--%s=json", tmcli.OutputFlag),
},
false,
v1.NewTallyResult(sdk.NewInt(0), sdk.NewInt(0), sdk.NewInt(0), sdk.NewInt(0)),
},
{
"json output",
[]string{
"1",
fmt.Sprintf("--%s=json", tmcli.OutputFlag),
},
false,
v1.NewTallyResult(s.cfg.BondedTokens, sdk.NewInt(0), sdk.NewInt(0), sdk.NewInt(0)),
},
}
for _, tc := range testCases {
tc := tc
s.Run(tc.name, func() {
cmd := cli.GetCmdQueryTally()
clientCtx := val.ClientCtx
out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args)
if tc.expectErr {
s.Require().Error(err)
} else {
var tally v1.TallyResult
s.Require().NoError(clientCtx.Codec.UnmarshalJSON(out.Bytes(), &tally), out.String())
s.Require().Equal(tally, tc.expectedOutput)
}
})
}
}
func (s *IntegrationTestSuite) TestNewCmdSubmitProposal() {
val := s.network.Validators[0]
@ -453,202 +254,6 @@ func (s *IntegrationTestSuite) TestNewCmdSubmitLegacyProposal() {
}
}
func (s *IntegrationTestSuite) TestCmdGetProposal() {
val := s.network.Validators[0]
title := "Text Proposal 1"
testCases := []struct {
name string
args []string
expectErr bool
}{
{
"get non existing proposal",
[]string{
"10",
fmt.Sprintf("--%s=json", tmcli.OutputFlag),
},
true,
},
{
"get proposal with json response",
[]string{
"1",
fmt.Sprintf("--%s=json", tmcli.OutputFlag),
},
false,
},
}
for _, tc := range testCases {
tc := tc
s.Run(tc.name, func() {
cmd := cli.GetCmdQueryProposal()
clientCtx := val.ClientCtx
out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args)
if tc.expectErr {
s.Require().Error(err)
} else {
s.Require().NoError(err)
var proposal v1.Proposal
s.Require().NoError(clientCtx.Codec.UnmarshalJSON(out.Bytes(), &proposal), out.String())
s.Require().Equal(title, proposal.Messages[0].GetCachedValue().(*v1.MsgExecLegacyContent).Content.GetCachedValue().(v1beta1.Content).GetTitle())
}
})
}
}
func (s *IntegrationTestSuite) TestCmdGetProposals() {
val := s.network.Validators[0]
testCases := []struct {
name string
args []string
expectErr bool
}{
{
"get proposals as json response",
[]string{
fmt.Sprintf("--%s=json", tmcli.OutputFlag),
},
false,
},
{
"get proposals with invalid status",
[]string{
"--status=unknown",
fmt.Sprintf("--%s=json", tmcli.OutputFlag),
},
true,
},
}
for _, tc := range testCases {
tc := tc
s.Run(tc.name, func() {
cmd := cli.GetCmdQueryProposals()
clientCtx := val.ClientCtx
out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args)
if tc.expectErr {
s.Require().Error(err)
} else {
s.Require().NoError(err)
var proposals v1.QueryProposalsResponse
s.Require().NoError(clientCtx.Codec.UnmarshalJSON(out.Bytes(), &proposals), out.String())
s.Require().Len(proposals.Proposals, 3)
}
})
}
}
func (s *IntegrationTestSuite) TestCmdQueryDeposits() {
val := s.network.Validators[0]
testCases := []struct {
name string
args []string
expectErr bool
}{
{
"get deposits of non existing proposal",
[]string{
"10",
},
true,
},
{
"get deposits(valid req)",
[]string{
"1",
fmt.Sprintf("--%s=json", tmcli.OutputFlag),
},
false,
},
}
for _, tc := range testCases {
tc := tc
s.Run(tc.name, func() {
cmd := cli.GetCmdQueryDeposits()
clientCtx := val.ClientCtx
out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args)
if tc.expectErr {
s.Require().Error(err)
} else {
s.Require().NoError(err)
var deposits v1.QueryDepositsResponse
s.Require().NoError(clientCtx.Codec.UnmarshalJSON(out.Bytes(), &deposits), out.String())
s.Require().Len(deposits.Deposits, 1)
}
})
}
}
func (s *IntegrationTestSuite) TestCmdQueryDeposit() {
val := s.network.Validators[0]
depositAmount := sdk.NewCoin(s.cfg.BondDenom, v1.DefaultMinDepositTokens)
testCases := []struct {
name string
args []string
expectErr bool
}{
{
"get deposit with no depositer",
[]string{
"1",
},
true,
},
{
"get deposit with wrong deposit address",
[]string{
"1",
"wrong address",
},
true,
},
{
"get deposit (valid req)",
[]string{
"1",
val.Address.String(),
fmt.Sprintf("--%s=json", tmcli.OutputFlag),
},
false,
},
}
for _, tc := range testCases {
tc := tc
s.Run(tc.name, func() {
cmd := cli.GetCmdQueryDeposit()
clientCtx := val.ClientCtx
out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args)
if tc.expectErr {
s.Require().Error(err)
} else {
s.Require().NoError(err)
var deposit v1.Deposit
s.Require().NoError(clientCtx.Codec.UnmarshalJSON(out.Bytes(), &deposit), out.String())
s.Require().Equal(depositAmount.String(), sdk.Coins(deposit.Amount).String())
}
})
}
}
func (s *IntegrationTestSuite) TestNewCmdDeposit() {
val := s.network.Validators[0]
@ -727,136 +332,6 @@ func (s *IntegrationTestSuite) TestNewCmdDeposit() {
}
}
func (s *IntegrationTestSuite) TestCmdQueryVotes() {
val := s.network.Validators[0]
testCases := []struct {
name string
args []string
expectErr bool
}{
{
"get votes with no proposal id",
[]string{},
true,
},
{
"get votes of non existed proposal",
[]string{
"10",
},
true,
},
{
"vote for invalid proposal",
[]string{
"1",
fmt.Sprintf("--%s=json", tmcli.OutputFlag),
},
false,
},
}
for _, tc := range testCases {
tc := tc
s.Run(tc.name, func() {
cmd := cli.GetCmdQueryVotes()
clientCtx := val.ClientCtx
out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args)
if tc.expectErr {
s.Require().Error(err)
} else {
s.Require().NoError(err)
var votes v1.QueryVotesResponse
s.Require().NoError(clientCtx.Codec.UnmarshalJSON(out.Bytes(), &votes), out.String())
s.Require().Len(votes.Votes, 1)
}
})
}
}
func (s *IntegrationTestSuite) TestCmdQueryVote() {
val := s.network.Validators[0]
testCases := []struct {
name string
args []string
expectErr bool
expVoteOptions v1.WeightedVoteOptions
}{
{
"get vote of non existing proposal",
[]string{
"10",
val.Address.String(),
},
true,
v1.NewNonSplitVoteOption(v1.OptionYes),
},
{
"get vote by wrong voter",
[]string{
"1",
"wrong address",
},
true,
v1.NewNonSplitVoteOption(v1.OptionYes),
},
{
"vote for valid proposal",
[]string{
"1",
val.Address.String(),
fmt.Sprintf("--%s=json", tmcli.OutputFlag),
},
false,
v1.NewNonSplitVoteOption(v1.OptionYes),
},
{
"split vote for valid proposal",
[]string{
"3",
val.Address.String(),
fmt.Sprintf("--%s=json", tmcli.OutputFlag),
},
false,
v1.WeightedVoteOptions{
&v1.WeightedVoteOption{Option: v1.OptionYes, Weight: sdk.NewDecWithPrec(60, 2).String()},
&v1.WeightedVoteOption{Option: v1.OptionNo, Weight: sdk.NewDecWithPrec(30, 2).String()},
&v1.WeightedVoteOption{Option: v1.OptionAbstain, Weight: sdk.NewDecWithPrec(5, 2).String()},
&v1.WeightedVoteOption{Option: v1.OptionNoWithVeto, Weight: sdk.NewDecWithPrec(5, 2).String()},
},
},
}
for _, tc := range testCases {
tc := tc
s.Run(tc.name, func() {
cmd := cli.GetCmdQueryVote()
clientCtx := val.ClientCtx
out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args)
if tc.expectErr {
s.Require().Error(err)
} else {
s.Require().NoError(err)
var vote v1.Vote
s.Require().NoError(clientCtx.Codec.UnmarshalJSON(out.Bytes(), &vote), out.String())
s.Require().Equal(len(vote.Options), len(tc.expVoteOptions))
for i, option := range tc.expVoteOptions {
s.Require().Equal(option.Option, vote.Options[i].Option)
s.Require().Equal(option.Weight, vote.Options[i].Weight)
}
}
})
}
}
func (s *IntegrationTestSuite) TestNewCmdVote() {
val := s.network.Validators[0]

View File

@ -0,0 +1,175 @@
package v046_test
import (
"testing"
"time"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/tx"
v046 "github.com/cosmos/cosmos-sdk/x/gov/migrations/v046"
v1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1"
"github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1"
"github.com/stretchr/testify/require"
)
func TestConvertToLegacyProposal(t *testing.T) {
propTime := time.Unix(1e9, 0)
legacyContentMsg, err := v1.NewLegacyContent(v1beta1.NewTextProposal("title", "description"), "cosmos1fl48vsnmsdzcv85q5d2q4z5ajdha8yu34mf0eh")
require.NoError(t, err)
msgs := []sdk.Msg{legacyContentMsg}
msgsAny, err := tx.SetMsgs(msgs)
require.NoError(t, err)
proposal := v1.Proposal{
Id: 1,
Status: v1.StatusDepositPeriod,
Messages: msgsAny,
SubmitTime: &propTime,
DepositEndTime: &propTime,
VotingStartTime: &propTime,
VotingEndTime: &propTime,
Metadata: "proposal metadata",
}
testCases := map[string]struct {
tallyResult v1.TallyResult
expErr bool
}{
"valid": {
tallyResult: v1.EmptyTallyResult(),
},
"invalid final tally result": {
tallyResult: v1.TallyResult{},
expErr: true,
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
proposal.FinalTallyResult = &tc.tallyResult
v1beta1Proposal, err := v046.ConvertToLegacyProposal(proposal)
if tc.expErr {
require.Error(t, err)
} else {
require.NoError(t, err)
require.Equal(t, v1beta1Proposal.ProposalId, proposal.Id)
require.Equal(t, v1beta1Proposal.VotingStartTime, *proposal.VotingStartTime)
require.Equal(t, v1beta1Proposal.VotingEndTime, *proposal.VotingEndTime)
require.Equal(t, v1beta1Proposal.SubmitTime, *proposal.SubmitTime)
require.Equal(t, v1beta1Proposal.DepositEndTime, *proposal.DepositEndTime)
require.Equal(t, v1beta1Proposal.FinalTallyResult.Yes, sdk.NewInt(0))
require.Equal(t, v1beta1Proposal.FinalTallyResult.No, sdk.NewInt(0))
require.Equal(t, v1beta1Proposal.FinalTallyResult.NoWithVeto, sdk.NewInt(0))
require.Equal(t, v1beta1Proposal.FinalTallyResult.Abstain, sdk.NewInt(0))
}
})
}
}
func TestConvertToLegacyTallyResult(t *testing.T) {
tallyResult := v1.EmptyTallyResult()
testCases := map[string]struct {
tallyResult v1.TallyResult
expErr bool
}{
"valid": {
tallyResult: tallyResult,
},
"invalid yes count": {
tallyResult: v1.TallyResult{
YesCount: "invalid",
NoCount: tallyResult.NoCount,
AbstainCount: tallyResult.AbstainCount,
NoWithVetoCount: tallyResult.NoWithVetoCount,
},
expErr: true,
},
"invalid no count": {
tallyResult: v1.TallyResult{
YesCount: tallyResult.YesCount,
NoCount: "invalid",
AbstainCount: tallyResult.AbstainCount,
NoWithVetoCount: tallyResult.NoWithVetoCount,
},
expErr: true,
},
"invalid abstain count": {
tallyResult: v1.TallyResult{
YesCount: tallyResult.YesCount,
NoCount: tallyResult.NoCount,
AbstainCount: "invalid",
NoWithVetoCount: tallyResult.NoWithVetoCount,
},
expErr: true,
},
"invalid no with veto count": {
tallyResult: v1.TallyResult{
YesCount: tallyResult.YesCount,
NoCount: tallyResult.NoCount,
AbstainCount: tallyResult.AbstainCount,
NoWithVetoCount: "invalid",
},
expErr: true,
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
_, err := v046.ConvertToLegacyTallyResult(&tc.tallyResult)
if tc.expErr {
require.Error(t, err)
} else {
require.NoError(t, err)
}
})
}
}
func TestConvertToLegacyVote(t *testing.T) {
vote := v1.Vote{
ProposalId: 1,
Voter: "cosmos1fl48vsnmsdzcv85q5d2q4z5ajdha8yu34mf0eh",
Metadata: "vote metadata",
}
testCases := map[string]struct {
options []*v1.WeightedVoteOption
expErr bool
}{
"valid": {
options: v1.NewNonSplitVoteOption(v1.OptionYes),
},
"invalid options": {
options: []*v1.WeightedVoteOption{{Option: 1, Weight: "invalid"}},
expErr: true,
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
vote.Options = tc.options
v1beta1Vote, err := v046.ConvertToLegacyVote(vote)
if tc.expErr {
require.Error(t, err)
} else {
require.NoError(t, err)
require.Equal(t, v1beta1Vote.ProposalId, vote.ProposalId)
require.Equal(t, v1beta1Vote.Voter, vote.Voter)
require.Equal(t, v1beta1Vote.Options[0].Option, v1beta1.OptionYes)
require.Equal(t, v1beta1Vote.Options[0].Weight, sdk.NewDec(1))
}
})
}
}
func TestConvertToLegacyDeposit(t *testing.T) {
deposit := v1.Deposit{
ProposalId: 1,
Depositor: "cosmos1fl48vsnmsdzcv85q5d2q4z5ajdha8yu34mf0eh",
Amount: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(1))),
}
v1beta1Deposit := v046.ConvertToLegacyDeposit(&deposit)
require.Equal(t, v1beta1Deposit.ProposalId, deposit.ProposalId)
require.Equal(t, v1beta1Deposit.Depositor, deposit.Depositor)
require.Equal(t, v1beta1Deposit.Amount[0], deposit.Amount[0])
}

View File

@ -12,7 +12,7 @@ to resolve and then execute if the proposal passes. `Proposal`'s are identified
unique id and contains a series of timestamps: `submit_time`, `deposit_end_time`,
`voting_start_time`, `voting_end_time` which track the lifecycle of a proposal
+++ https://github.com/cosmos/cosmos-sdk/blob/4a129832eb16f37a89e97652a669f0cdc9196ca9/proto/cosmos/gov/v1beta2/gov.proto#L42-L52
+++ https://github.com/cosmos/cosmos-sdk/blob/5bde3686c4538ce53356af6e9fe40b34e4ce4a06/proto/cosmos/gov/v1/gov.proto#L42-L59
A proposal will generally require more than just a set of messages to explain its
purpose but need some greater justification and allow a means for interested participants

View File

@ -9,7 +9,7 @@ order: 3
Proposals can be submitted by any account via a `MsgSubmitProposal`
transaction.
+++ https://github.com/cosmos/cosmos-sdk/blob/ab9545527d630fe38761aa61cc5c95eabd68e0e6/proto/cosmos/gov/v1beta2/tx.proto#L34-L44
+++ https://github.com/cosmos/cosmos-sdk/blob/5bde3686c4538ce53356af6e9fe40b34e4ce4a06/proto/cosmos/gov/v1/tx.proto#L33-L43
All `sdk.Msgs` passed into the `messages` field of a `MsgSubmitProposal` message
must be registered in the app's `MsgServiceRouter`. Each of these messages must

View File

@ -392,10 +392,10 @@ Example:
simd tx gov submit-legacy-proposal --title="Test Proposal" --description="testing" --type="Text" --deposit="100000000stake" --from cosmos1..
```
Example (`legacy-cancel-software-upgrade`):
Example (`cancel-software-upgrade`):
```bash
simd tx gov submit-legacy-proposal legacy-cancel-software-upgrade --title="Test Proposal" --description="testing" --deposit="100000000stake" --from cosmos1..
simd tx gov submit-legacy-proposal cancel-software-upgrade --title="Test Proposal" --description="testing" --deposit="100000000stake" --from cosmos1..
```
Example (`community-pool-spend`):
@ -435,10 +435,10 @@ simd tx gov submit-legacy-proposal param-change proposal.json --from cosmos1..
}
```
Example (`legacy-software-upgrade`):
Example (`software-upgrade`):
```bash
simd tx gov submit-legacy-proposal legacy-software-upgrade v2 --title="Test Proposal" --description="testing, testing, 1, 2, 3" --upgrade-height 1000000 --from cosmos1..
simd tx gov submit-legacy-proposal software-upgrade v2 --title="Test Proposal" --description="testing, testing, 1, 2, 3" --upgrade-height 1000000 --from cosmos1..
```
#### vote

View File

@ -38,7 +38,7 @@ func (data GenesisState) Empty() bool {
// ValidateGenesis checks if parameters are within valid ranges
func ValidateGenesis(data *GenesisState) error {
if data.StartingProposalId == 0 {
return errors.New("Starting proposal id must be greater than 0")
return errors.New("starting proposal id must be greater than 0")
}
if err := validateTallyParams(*data.TallyParams); err != nil {

View File

@ -3,7 +3,7 @@ package v1_test
import (
"testing"
"github.com/cosmos/cosmos-sdk/x/gov/types/v1"
v1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1"
"github.com/stretchr/testify/require"
)
@ -14,3 +14,73 @@ func TestEmptyGenesis(t *testing.T) {
state2 := v1.DefaultGenesisState()
require.False(t, state2.Empty())
}
func TestValidateGenesis(t *testing.T) {
depositParams := v1.DefaultDepositParams()
votingParams := v1.DefaultVotingParams()
tallyParams := v1.DefaultTallyParams()
testCases := []struct {
name string
genesisState *v1.GenesisState
expErr bool
}{
{
name: "valid",
genesisState: v1.DefaultGenesisState(),
},
{
name: "invalid StartingProposalId",
genesisState: &v1.GenesisState{
StartingProposalId: 0,
DepositParams: &depositParams,
VotingParams: &votingParams,
TallyParams: &tallyParams,
},
expErr: true,
},
{
name: "invalid TallyParams",
genesisState: &v1.GenesisState{
StartingProposalId: v1.DefaultStartingProposalID,
DepositParams: &depositParams,
VotingParams: &votingParams,
TallyParams: &v1.TallyParams{},
},
expErr: true,
},
{
name: "invalid VotingParams",
genesisState: &v1.GenesisState{
StartingProposalId: v1.DefaultStartingProposalID,
DepositParams: &depositParams,
VotingParams: &v1.VotingParams{},
TallyParams: &tallyParams,
},
expErr: true,
},
{
name: "invalid DepositParams",
genesisState: &v1.GenesisState{
StartingProposalId: v1.DefaultStartingProposalID,
DepositParams: &v1.DepositParams{},
VotingParams: &votingParams,
TallyParams: &tallyParams,
},
expErr: true,
},
}
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
err := v1.ValidateGenesis(tc.genesisState)
if tc.expErr {
require.Error(t, err)
} else {
require.NoError(t, err)
}
})
}
}

View File

@ -38,7 +38,7 @@ func GetTxCmd() *cobra.Command {
// Deprecated: please use NewCmdSubmitUpgradeProposal instead.
func NewCmdSubmitLegacyUpgradeProposal() *cobra.Command {
cmd := &cobra.Command{
Use: "legacy-software-upgrade [name] (--upgrade-height [height]) (--upgrade-info [info]) [flags]",
Use: "software-upgrade [name] (--upgrade-height [height]) (--upgrade-info [info]) [flags]",
Args: cobra.ExactArgs(1),
Short: "Submit a software upgrade proposal",
Long: "Submit a software upgrade along with an initial deposit.\n" +
@ -108,7 +108,7 @@ func NewCmdSubmitLegacyUpgradeProposal() *cobra.Command {
// Deprecated: please use NewCmdSubmitCancelUpgradeProposal instead.
func NewCmdSubmitLegacyCancelUpgradeProposal() *cobra.Command {
cmd := &cobra.Command{
Use: "legacy-cancel-software-upgrade [flags]",
Use: "cancel-software-upgrade [flags]",
Args: cobra.ExactArgs(0),
Short: "Cancel the current software upgrade proposal",
Long: "Cancel a software upgrade along with an initial deposit.",