Co-authored-by: son trinh <trinhleson2000@gmail.com> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2323 lines
61 KiB
Go
2323 lines
61 KiB
Go
package keeper_test
|
|
|
|
import (
|
|
"strings"
|
|
"time"
|
|
|
|
sdkmath "cosmossdk.io/math"
|
|
banktypes "cosmossdk.io/x/bank/types"
|
|
v1 "cosmossdk.io/x/gov/types/v1"
|
|
"cosmossdk.io/x/gov/types/v1beta1"
|
|
|
|
codectestutil "github.com/cosmos/cosmos-sdk/codec/testutil"
|
|
"github.com/cosmos/cosmos-sdk/codec/types"
|
|
simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims"
|
|
"github.com/cosmos/cosmos-sdk/testutil/testdata"
|
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
)
|
|
|
|
const (
|
|
abc = "abc"
|
|
o1 = "-0.1"
|
|
)
|
|
|
|
var longAddressError = "address max length is 255"
|
|
|
|
func (suite *KeeperTestSuite) TestMsgSubmitProposal() {
|
|
suite.reset()
|
|
addrs := suite.addrs
|
|
proposerAddr, err := suite.acctKeeper.AddressCodec().BytesToString(addrs[0])
|
|
suite.Require().NoError(err)
|
|
|
|
govStrAcct, err := suite.acctKeeper.AddressCodec().BytesToString(suite.govKeeper.GetGovernanceAccount(suite.ctx).GetAddress())
|
|
suite.Require().NoError(err)
|
|
|
|
coins := sdk.NewCoins(sdk.NewCoin("stake", sdkmath.NewInt(100000)))
|
|
initialDeposit := coins
|
|
params, _ := suite.govKeeper.Params.Get(suite.ctx)
|
|
minDeposit := params.MinDeposit
|
|
bankMsg := &banktypes.MsgSend{
|
|
FromAddress: govStrAcct,
|
|
ToAddress: proposerAddr,
|
|
Amount: coins,
|
|
}
|
|
|
|
cases := map[string]struct {
|
|
preRun func() (*v1.MsgSubmitProposal, error)
|
|
expErr bool
|
|
expErrMsg string
|
|
}{
|
|
"invalid addr": {
|
|
preRun: func() (*v1.MsgSubmitProposal, error) {
|
|
return v1.NewMsgSubmitProposal(
|
|
[]sdk.Msg{bankMsg},
|
|
initialDeposit,
|
|
"",
|
|
strings.Repeat("1", 100),
|
|
"Proposal",
|
|
"description of proposal",
|
|
v1.ProposalType_PROPOSAL_TYPE_STANDARD,
|
|
)
|
|
},
|
|
expErr: true,
|
|
expErrMsg: "invalid proposer address",
|
|
},
|
|
"empty msgs and metadata": {
|
|
preRun: func() (*v1.MsgSubmitProposal, error) {
|
|
return v1.NewMsgSubmitProposal(
|
|
nil,
|
|
initialDeposit,
|
|
proposerAddr,
|
|
"",
|
|
"Proposal",
|
|
"description of proposal",
|
|
v1.ProposalType_PROPOSAL_TYPE_STANDARD,
|
|
)
|
|
},
|
|
expErr: true,
|
|
expErrMsg: "no messages proposed",
|
|
},
|
|
"empty title": {
|
|
preRun: func() (*v1.MsgSubmitProposal, error) {
|
|
return v1.NewMsgSubmitProposal(
|
|
[]sdk.Msg{bankMsg},
|
|
initialDeposit,
|
|
proposerAddr,
|
|
"",
|
|
"",
|
|
"description of proposal",
|
|
v1.ProposalType_PROPOSAL_TYPE_STANDARD,
|
|
)
|
|
},
|
|
expErr: true,
|
|
expErrMsg: "proposal title cannot be empty",
|
|
},
|
|
"empty description": {
|
|
preRun: func() (*v1.MsgSubmitProposal, error) {
|
|
return v1.NewMsgSubmitProposal(
|
|
[]sdk.Msg{bankMsg},
|
|
initialDeposit,
|
|
proposerAddr,
|
|
"",
|
|
"Proposal",
|
|
"",
|
|
v1.ProposalType_PROPOSAL_TYPE_STANDARD,
|
|
)
|
|
},
|
|
expErr: true,
|
|
expErrMsg: "proposal summary cannot be empty",
|
|
},
|
|
"title != metadata.title": {
|
|
preRun: func() (*v1.MsgSubmitProposal, error) {
|
|
return v1.NewMsgSubmitProposal(
|
|
[]sdk.Msg{bankMsg},
|
|
initialDeposit,
|
|
proposerAddr,
|
|
"{\"title\":\"Proposal\", \"description\":\"description of proposal\"}",
|
|
"Proposal2",
|
|
"description of proposal",
|
|
v1.ProposalType_PROPOSAL_TYPE_STANDARD,
|
|
)
|
|
},
|
|
expErr: true,
|
|
expErrMsg: "metadata title 'Proposal' must equal proposal title 'Proposal2",
|
|
},
|
|
"summary != metadata.summary": {
|
|
preRun: func() (*v1.MsgSubmitProposal, error) {
|
|
return v1.NewMsgSubmitProposal(
|
|
[]sdk.Msg{bankMsg},
|
|
initialDeposit,
|
|
proposerAddr,
|
|
"{\"title\":\"Proposal\", \"description\":\"description of proposal\"}",
|
|
"Proposal",
|
|
"description",
|
|
v1.ProposalType_PROPOSAL_TYPE_STANDARD,
|
|
)
|
|
},
|
|
expErr: true,
|
|
expErrMsg: "metadata summary '' must equal proposal summary 'description'",
|
|
},
|
|
"title too long": {
|
|
preRun: func() (*v1.MsgSubmitProposal, error) {
|
|
return v1.NewMsgSubmitProposal(
|
|
[]sdk.Msg{bankMsg},
|
|
initialDeposit,
|
|
proposerAddr,
|
|
"Metadata",
|
|
strings.Repeat("1", 256),
|
|
"description of proposal",
|
|
v1.ProposalType_PROPOSAL_TYPE_STANDARD,
|
|
)
|
|
},
|
|
expErr: true,
|
|
expErrMsg: "title too long",
|
|
},
|
|
"metadata too long": {
|
|
preRun: func() (*v1.MsgSubmitProposal, error) {
|
|
return v1.NewMsgSubmitProposal(
|
|
[]sdk.Msg{bankMsg},
|
|
initialDeposit,
|
|
proposerAddr,
|
|
strings.Repeat("1", 257),
|
|
"Proposal",
|
|
"description of proposal",
|
|
v1.ProposalType_PROPOSAL_TYPE_STANDARD,
|
|
)
|
|
},
|
|
expErr: true,
|
|
expErrMsg: "metadata too long",
|
|
},
|
|
"summary too long": {
|
|
preRun: func() (*v1.MsgSubmitProposal, error) {
|
|
return v1.NewMsgSubmitProposal(
|
|
[]sdk.Msg{bankMsg},
|
|
initialDeposit,
|
|
proposerAddr,
|
|
"",
|
|
"Proposal",
|
|
strings.Repeat("1", 10201),
|
|
v1.ProposalType_PROPOSAL_TYPE_STANDARD,
|
|
)
|
|
},
|
|
expErr: true,
|
|
expErrMsg: "summary too long",
|
|
},
|
|
"many signers": {
|
|
preRun: func() (*v1.MsgSubmitProposal, error) {
|
|
return v1.NewMsgSubmitProposal(
|
|
[]sdk.Msg{testdata.NewTestMsg(govAcct, addrs[0])},
|
|
initialDeposit,
|
|
proposerAddr,
|
|
"",
|
|
"Proposal",
|
|
"description of proposal",
|
|
v1.ProposalType_PROPOSAL_TYPE_STANDARD,
|
|
)
|
|
},
|
|
expErr: true,
|
|
expErrMsg: "expected authority account as only signer for proposal message",
|
|
},
|
|
"signer isn't gov account": {
|
|
preRun: func() (*v1.MsgSubmitProposal, error) {
|
|
return v1.NewMsgSubmitProposal(
|
|
[]sdk.Msg{testdata.NewTestMsg(addrs[0])},
|
|
initialDeposit,
|
|
proposerAddr,
|
|
"",
|
|
"Proposal",
|
|
"description of proposal",
|
|
v1.ProposalType_PROPOSAL_TYPE_STANDARD,
|
|
)
|
|
},
|
|
expErr: true,
|
|
expErrMsg: "expected authority account as only signer for proposal message",
|
|
},
|
|
"invalid msg handler": {
|
|
preRun: func() (*v1.MsgSubmitProposal, error) {
|
|
return v1.NewMsgSubmitProposal(
|
|
[]sdk.Msg{testdata.NewTestMsg(govAcct)},
|
|
initialDeposit,
|
|
proposerAddr,
|
|
"",
|
|
"Proposal",
|
|
"description of proposal",
|
|
v1.ProposalType_PROPOSAL_TYPE_STANDARD,
|
|
)
|
|
},
|
|
expErr: true,
|
|
expErrMsg: "proposal message not recognized by router",
|
|
},
|
|
"invalid deposited coin": {
|
|
preRun: func() (*v1.MsgSubmitProposal, error) {
|
|
return v1.NewMsgSubmitProposal(
|
|
[]sdk.Msg{bankMsg},
|
|
[]sdk.Coin{sdk.NewCoin("invalid", sdkmath.NewInt(100))},
|
|
proposerAddr,
|
|
"",
|
|
"Proposal",
|
|
"description of proposal",
|
|
v1.ProposalType_PROPOSAL_TYPE_STANDARD,
|
|
)
|
|
},
|
|
expErr: true,
|
|
expErrMsg: "deposited 100invalid, but gov accepts only the following denom(s): [stake]: invalid deposit denom",
|
|
},
|
|
"invalid deposited coin (multiple)": {
|
|
preRun: func() (*v1.MsgSubmitProposal, error) {
|
|
return v1.NewMsgSubmitProposal(
|
|
[]sdk.Msg{bankMsg},
|
|
initialDeposit.Add(sdk.NewCoin("invalid", sdkmath.NewInt(100))),
|
|
proposerAddr,
|
|
"",
|
|
"Proposal",
|
|
"description of proposal",
|
|
v1.ProposalType_PROPOSAL_TYPE_STANDARD,
|
|
)
|
|
},
|
|
expErr: true,
|
|
expErrMsg: "deposited 100invalid,100000stake, but gov accepts only the following denom(s): [stake]: invalid deposit denom",
|
|
},
|
|
"all good": {
|
|
preRun: func() (*v1.MsgSubmitProposal, error) {
|
|
return v1.NewMsgSubmitProposal(
|
|
[]sdk.Msg{bankMsg},
|
|
initialDeposit,
|
|
proposerAddr,
|
|
"",
|
|
"Proposal",
|
|
"description of proposal",
|
|
v1.ProposalType_PROPOSAL_TYPE_STANDARD,
|
|
)
|
|
},
|
|
expErr: false,
|
|
},
|
|
"all good with min deposit": {
|
|
preRun: func() (*v1.MsgSubmitProposal, error) {
|
|
return v1.NewMsgSubmitProposal(
|
|
[]sdk.Msg{bankMsg},
|
|
minDeposit,
|
|
proposerAddr,
|
|
"",
|
|
"Proposal",
|
|
"description of proposal",
|
|
v1.ProposalType_PROPOSAL_TYPE_STANDARD,
|
|
)
|
|
},
|
|
expErr: false,
|
|
},
|
|
}
|
|
|
|
for name, tc := range cases {
|
|
suite.Run(name, func() {
|
|
msg, err := tc.preRun()
|
|
suite.Require().NoError(err)
|
|
res, err := suite.msgSrvr.SubmitProposal(suite.ctx, msg)
|
|
if tc.expErr {
|
|
suite.Require().Error(err)
|
|
suite.Require().Contains(err.Error(), tc.expErrMsg)
|
|
} else {
|
|
suite.Require().NoError(err)
|
|
suite.Require().NotNil(res.ProposalId)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestSubmitMultipleChoiceProposal tests only multiple choice proposal specific logic.
|
|
// Internally the message uses MsgSubmitProposal, which is tested above.
|
|
func (suite *KeeperTestSuite) TestSubmitMultipleChoiceProposal() {
|
|
suite.reset()
|
|
addrs := suite.addrs
|
|
proposerAddr, err := suite.acctKeeper.AddressCodec().BytesToString(addrs[0])
|
|
suite.Require().NoError(err)
|
|
initialDeposit := sdk.NewCoins(sdk.NewCoin("stake", sdkmath.NewInt(100000)))
|
|
|
|
cases := map[string]struct {
|
|
preRun func() (*v1.MsgSubmitMultipleChoiceProposal, error)
|
|
expErr bool
|
|
expErrMsg string
|
|
}{
|
|
"empty options": {
|
|
preRun: func() (*v1.MsgSubmitMultipleChoiceProposal, error) {
|
|
return v1.NewMultipleChoiceMsgSubmitProposal(
|
|
initialDeposit,
|
|
proposerAddr,
|
|
"mandatory metadata",
|
|
"Proposal",
|
|
"description of proposal",
|
|
&v1.ProposalVoteOptions{},
|
|
)
|
|
},
|
|
expErr: true,
|
|
expErrMsg: "vote options cannot be empty, two or more options must be provided",
|
|
},
|
|
"invalid options": {
|
|
preRun: func() (*v1.MsgSubmitMultipleChoiceProposal, error) {
|
|
return v1.NewMultipleChoiceMsgSubmitProposal(
|
|
initialDeposit,
|
|
proposerAddr,
|
|
"mandatory metadata",
|
|
"Proposal",
|
|
"description of proposal",
|
|
&v1.ProposalVoteOptions{
|
|
OptionOne: "Vote for me",
|
|
OptionFour: "Vote for them",
|
|
},
|
|
)
|
|
},
|
|
expErr: true,
|
|
expErrMsg: "if a vote option is provided, the previous one must also be provided",
|
|
},
|
|
"valid proposal": {
|
|
preRun: func() (*v1.MsgSubmitMultipleChoiceProposal, error) {
|
|
return v1.NewMultipleChoiceMsgSubmitProposal(
|
|
initialDeposit,
|
|
proposerAddr,
|
|
"mandatory metadata",
|
|
"Proposal",
|
|
"description of proposal",
|
|
&v1.ProposalVoteOptions{
|
|
OptionOne: "Vote for me",
|
|
OptionTwo: "Vote for them",
|
|
},
|
|
)
|
|
},
|
|
},
|
|
}
|
|
|
|
for name, tc := range cases {
|
|
suite.Run(name, func() {
|
|
msg, err := tc.preRun()
|
|
suite.Require().NoError(err)
|
|
res, err := suite.msgSrvr.SubmitMultipleChoiceProposal(suite.ctx, msg)
|
|
if tc.expErr {
|
|
suite.Require().Error(err)
|
|
suite.Require().Contains(err.Error(), tc.expErrMsg)
|
|
} else {
|
|
suite.Require().NoError(err)
|
|
suite.Require().NotNil(res.ProposalId)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func (suite *KeeperTestSuite) TestMsgCancelProposal() {
|
|
govAcct := suite.govKeeper.GetGovernanceAccount(suite.ctx).GetAddress()
|
|
addrs := suite.addrs
|
|
proposer := addrs[0]
|
|
proposerAddr, err := suite.acctKeeper.AddressCodec().BytesToString(proposer)
|
|
suite.Require().NoError(err)
|
|
|
|
govStrAcct, err := suite.acctKeeper.AddressCodec().BytesToString(govAcct)
|
|
suite.Require().NoError(err)
|
|
|
|
coins := sdk.NewCoins(sdk.NewCoin("stake", sdkmath.NewInt(100000)))
|
|
bankMsg := &banktypes.MsgSend{
|
|
FromAddress: govStrAcct,
|
|
ToAddress: proposerAddr,
|
|
Amount: coins,
|
|
}
|
|
|
|
msg, err := v1.NewMsgSubmitProposal(
|
|
[]sdk.Msg{bankMsg},
|
|
coins,
|
|
proposerAddr,
|
|
"", "title", "summary",
|
|
v1.ProposalType_PROPOSAL_TYPE_STANDARD,
|
|
)
|
|
suite.Require().NoError(err)
|
|
|
|
res, err := suite.msgSrvr.SubmitProposal(suite.ctx, msg)
|
|
suite.Require().NoError(err)
|
|
suite.Require().NotNil(res.ProposalId)
|
|
proposalID := res.ProposalId
|
|
|
|
cases := map[string]struct {
|
|
preRun func() uint64
|
|
expErr bool
|
|
expErrMsg string
|
|
proposalID uint64
|
|
depositor sdk.AccAddress
|
|
}{
|
|
"wrong proposal id": {
|
|
preRun: func() uint64 {
|
|
return 0
|
|
},
|
|
depositor: proposer,
|
|
expErr: true,
|
|
expErrMsg: "proposal 0 doesn't exist",
|
|
},
|
|
"valid proposal but invalid proposer": {
|
|
preRun: func() uint64 {
|
|
return res.ProposalId
|
|
},
|
|
depositor: addrs[1],
|
|
expErr: true,
|
|
expErrMsg: "invalid proposer",
|
|
},
|
|
"empty proposer": {
|
|
preRun: func() uint64 {
|
|
return proposalID
|
|
},
|
|
depositor: sdk.AccAddress{},
|
|
expErr: true,
|
|
expErrMsg: "invalid proposer address: empty address string is not allowed",
|
|
},
|
|
"all good": {
|
|
preRun: func() uint64 {
|
|
msg, err := v1.NewMsgSubmitProposal(
|
|
[]sdk.Msg{bankMsg},
|
|
coins,
|
|
proposerAddr,
|
|
"",
|
|
"Proposal",
|
|
"description of proposal",
|
|
v1.ProposalType_PROPOSAL_TYPE_STANDARD,
|
|
)
|
|
suite.Require().NoError(err)
|
|
|
|
res, err := suite.msgSrvr.SubmitProposal(suite.ctx, msg)
|
|
suite.Require().NoError(err)
|
|
suite.Require().NotNil(res.ProposalId)
|
|
return res.ProposalId
|
|
},
|
|
depositor: proposer,
|
|
expErr: false,
|
|
},
|
|
}
|
|
|
|
for name, tc := range cases {
|
|
suite.Run(name, func() {
|
|
proposalID := tc.preRun()
|
|
depositor, err := suite.acctKeeper.AddressCodec().BytesToString(tc.depositor)
|
|
suite.Require().NoError(err)
|
|
cancelProposalReq := v1.NewMsgCancelProposal(proposalID, depositor)
|
|
_, err = suite.msgSrvr.CancelProposal(suite.ctx, cancelProposalReq)
|
|
if tc.expErr {
|
|
suite.Require().Error(err)
|
|
suite.Require().Contains(err.Error(), tc.expErrMsg)
|
|
} else {
|
|
suite.Require().NoError(err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func (suite *KeeperTestSuite) TestMsgVote() {
|
|
suite.reset()
|
|
addrs := suite.addrs
|
|
proposer := addrs[0]
|
|
proposerAddr, err := suite.acctKeeper.AddressCodec().BytesToString(proposer)
|
|
suite.Require().NoError(err)
|
|
govStrAcct, err := suite.acctKeeper.AddressCodec().BytesToString(suite.govKeeper.GetGovernanceAccount(suite.ctx).GetAddress())
|
|
suite.Require().NoError(err)
|
|
|
|
coins := sdk.NewCoins(sdk.NewCoin("stake", sdkmath.NewInt(100000)))
|
|
params, _ := suite.govKeeper.Params.Get(suite.ctx)
|
|
minDeposit := params.MinDeposit
|
|
bankMsg := &banktypes.MsgSend{
|
|
FromAddress: govStrAcct,
|
|
ToAddress: proposerAddr,
|
|
Amount: coins,
|
|
}
|
|
|
|
msg, err := v1.NewMsgSubmitProposal(
|
|
[]sdk.Msg{bankMsg},
|
|
minDeposit,
|
|
proposerAddr,
|
|
"",
|
|
"Proposal",
|
|
"description of proposal",
|
|
v1.ProposalType_PROPOSAL_TYPE_STANDARD,
|
|
)
|
|
suite.Require().NoError(err)
|
|
|
|
res, err := suite.msgSrvr.SubmitProposal(suite.ctx, msg)
|
|
suite.Require().NoError(err)
|
|
suite.Require().NotNil(res.ProposalId)
|
|
proposalID := res.ProposalId
|
|
|
|
cases := map[string]struct {
|
|
preRun func() uint64
|
|
expErr bool
|
|
expErrMsg string
|
|
expAddrErr bool
|
|
expAddrErrMsg string
|
|
option v1.VoteOption
|
|
metadata string
|
|
voter sdk.AccAddress
|
|
}{
|
|
"empty voter": {
|
|
preRun: func() uint64 {
|
|
return proposalID
|
|
},
|
|
option: v1.VoteOption_VOTE_OPTION_ONE,
|
|
voter: sdk.AccAddress{},
|
|
metadata: "",
|
|
expErr: true,
|
|
expErrMsg: "invalid voter address",
|
|
},
|
|
"wrong vote option": {
|
|
preRun: func() uint64 {
|
|
return proposalID
|
|
},
|
|
option: v1.VoteOption(0x13),
|
|
voter: proposer,
|
|
metadata: "",
|
|
expErr: true,
|
|
expErrMsg: "invalid vote option",
|
|
},
|
|
"optimistic proposal: wrong vote option": {
|
|
preRun: func() uint64 {
|
|
msg, err := v1.NewMsgSubmitProposal(
|
|
[]sdk.Msg{bankMsg},
|
|
minDeposit,
|
|
proposerAddr,
|
|
"",
|
|
"Proposal",
|
|
"description of proposal",
|
|
v1.ProposalType_PROPOSAL_TYPE_OPTIMISTIC,
|
|
)
|
|
suite.Require().NoError(err)
|
|
|
|
res, err := suite.msgSrvr.SubmitProposal(suite.ctx, msg)
|
|
suite.Require().NoError(err)
|
|
suite.Require().NotNil(res.ProposalId)
|
|
return res.ProposalId
|
|
},
|
|
option: v1.VoteOption_VOTE_OPTION_ONE,
|
|
voter: proposer,
|
|
metadata: "",
|
|
expErr: true,
|
|
expErrMsg: "optimistic proposals can only be rejected: invalid vote option",
|
|
},
|
|
"vote on inactive proposal": {
|
|
preRun: func() uint64 {
|
|
msg, err := v1.NewMsgSubmitProposal(
|
|
[]sdk.Msg{bankMsg},
|
|
coins,
|
|
proposerAddr,
|
|
"",
|
|
"Proposal",
|
|
"description of proposal",
|
|
v1.ProposalType_PROPOSAL_TYPE_STANDARD,
|
|
)
|
|
suite.Require().NoError(err)
|
|
|
|
res, err := suite.msgSrvr.SubmitProposal(suite.ctx, msg)
|
|
suite.Require().NoError(err)
|
|
suite.Require().NotNil(res.ProposalId)
|
|
return res.ProposalId
|
|
},
|
|
option: v1.VoteOption_VOTE_OPTION_ONE,
|
|
voter: proposer,
|
|
metadata: "",
|
|
expErr: true,
|
|
expErrMsg: "inactive proposal",
|
|
},
|
|
"metadata too long": {
|
|
preRun: func() uint64 {
|
|
return proposalID
|
|
},
|
|
option: v1.VoteOption_VOTE_OPTION_ONE,
|
|
voter: proposer,
|
|
metadata: strings.Repeat("a", 300),
|
|
expErr: true,
|
|
expErrMsg: "metadata too long",
|
|
},
|
|
"voter error": {
|
|
preRun: func() uint64 {
|
|
return proposalID
|
|
},
|
|
option: v1.VoteOption_VOTE_OPTION_ONE,
|
|
voter: sdk.AccAddress(strings.Repeat("a", 300)),
|
|
metadata: "",
|
|
expErr: true,
|
|
expErrMsg: "invalid voter address: empty address string is not allowed",
|
|
expAddrErr: true,
|
|
expAddrErrMsg: longAddressError,
|
|
},
|
|
"all good": {
|
|
preRun: func() uint64 {
|
|
msg, err := v1.NewMsgSubmitProposal(
|
|
[]sdk.Msg{bankMsg},
|
|
minDeposit,
|
|
proposerAddr,
|
|
"",
|
|
"Proposal",
|
|
"description of proposal",
|
|
v1.ProposalType_PROPOSAL_TYPE_STANDARD,
|
|
)
|
|
suite.Require().NoError(err)
|
|
|
|
res, err := suite.msgSrvr.SubmitProposal(suite.ctx, msg)
|
|
suite.Require().NoError(err)
|
|
suite.Require().NotNil(res.ProposalId)
|
|
return res.ProposalId
|
|
},
|
|
option: v1.VoteOption_VOTE_OPTION_ONE,
|
|
voter: proposer,
|
|
metadata: "",
|
|
expErr: false,
|
|
},
|
|
}
|
|
|
|
for name, tc := range cases {
|
|
suite.Run(name, func() {
|
|
pID := tc.preRun()
|
|
voter, err := suite.acctKeeper.AddressCodec().BytesToString(tc.voter)
|
|
if tc.expAddrErr {
|
|
suite.Require().Error(err)
|
|
suite.Require().Contains(err.Error(), tc.expAddrErrMsg)
|
|
} else {
|
|
suite.Require().NoError(err)
|
|
}
|
|
voteReq := v1.NewMsgVote(voter, pID, tc.option, tc.metadata)
|
|
_, err = suite.msgSrvr.Vote(suite.ctx, voteReq)
|
|
if tc.expErr {
|
|
suite.Require().Error(err)
|
|
suite.Require().Contains(err.Error(), tc.expErrMsg)
|
|
} else {
|
|
suite.Require().NoError(err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func (suite *KeeperTestSuite) TestMsgVoteWeighted() {
|
|
suite.reset()
|
|
govAcct := suite.govKeeper.GetGovernanceAccount(suite.ctx).GetAddress()
|
|
govStrAcct, err := suite.acctKeeper.AddressCodec().BytesToString(govAcct)
|
|
suite.Require().NoError(err)
|
|
|
|
proposer := simtestutil.AddTestAddrsIncremental(suite.bankKeeper, suite.stakingKeeper, suite.ctx, 1, sdkmath.NewInt(50000000))[0]
|
|
proposerAddr, err := suite.acctKeeper.AddressCodec().BytesToString(proposer)
|
|
suite.Require().NoError(err)
|
|
|
|
coins := sdk.NewCoins(sdk.NewCoin("stake", sdkmath.NewInt(100000)))
|
|
params, _ := suite.govKeeper.Params.Get(suite.ctx)
|
|
minDeposit := params.MinDeposit
|
|
bankMsg := &banktypes.MsgSend{
|
|
FromAddress: govStrAcct,
|
|
ToAddress: proposerAddr,
|
|
Amount: coins,
|
|
}
|
|
|
|
msg, err := v1.NewMsgSubmitProposal(
|
|
[]sdk.Msg{bankMsg},
|
|
minDeposit,
|
|
proposerAddr,
|
|
"",
|
|
"Proposal",
|
|
"description of proposal",
|
|
v1.ProposalType_PROPOSAL_TYPE_STANDARD,
|
|
)
|
|
suite.Require().NoError(err)
|
|
|
|
res, err := suite.msgSrvr.SubmitProposal(suite.ctx, msg)
|
|
suite.Require().NoError(err)
|
|
suite.Require().NotNil(res.ProposalId)
|
|
proposalID := res.ProposalId
|
|
|
|
cases := map[string]struct {
|
|
preRun func() uint64
|
|
vote *v1.MsgVote
|
|
expErr bool
|
|
expErrMsg string
|
|
expAddrErr bool
|
|
expAddrErrMsg string
|
|
option v1.WeightedVoteOptions
|
|
metadata string
|
|
voter sdk.AccAddress
|
|
}{
|
|
"empty voter": {
|
|
preRun: func() uint64 {
|
|
return proposalID
|
|
},
|
|
option: v1.WeightedVoteOptions{
|
|
v1.NewWeightedVoteOption(v1.OptionYes, sdkmath.LegacyNewDec(1)),
|
|
},
|
|
voter: sdk.AccAddress{},
|
|
metadata: "",
|
|
expErr: true,
|
|
expErrMsg: "invalid voter address",
|
|
},
|
|
"weights sum > 1": {
|
|
preRun: func() uint64 {
|
|
return proposalID
|
|
},
|
|
option: v1.WeightedVoteOptions{
|
|
v1.NewWeightedVoteOption(v1.OptionYes, sdkmath.LegacyNewDec(1)),
|
|
v1.NewWeightedVoteOption(v1.OptionAbstain, sdkmath.LegacyNewDec(1)),
|
|
},
|
|
voter: proposer,
|
|
metadata: "",
|
|
expErr: true,
|
|
expErrMsg: "total weight overflow 1.00: invalid vote option",
|
|
},
|
|
"duplicate vote options": {
|
|
preRun: func() uint64 {
|
|
return proposalID
|
|
},
|
|
option: v1.WeightedVoteOptions{
|
|
v1.NewWeightedVoteOption(v1.OptionYes, sdkmath.LegacyNewDecWithPrec(5, 1)),
|
|
v1.NewWeightedVoteOption(v1.OptionYes, sdkmath.LegacyNewDecWithPrec(5, 1)),
|
|
},
|
|
voter: proposer,
|
|
metadata: "",
|
|
expErr: true,
|
|
expErrMsg: "duplicated vote option",
|
|
},
|
|
"zero weight": {
|
|
preRun: func() uint64 {
|
|
return proposalID
|
|
},
|
|
option: v1.WeightedVoteOptions{
|
|
v1.NewWeightedVoteOption(v1.OptionYes, sdkmath.LegacyNewDec(0)),
|
|
},
|
|
voter: proposer,
|
|
metadata: "",
|
|
expErr: true,
|
|
expErrMsg: `option:VOTE_OPTION_YES weight:"0.000000000000000000" : invalid vote option`,
|
|
},
|
|
"negative weight": {
|
|
preRun: func() uint64 {
|
|
return proposalID
|
|
},
|
|
option: v1.WeightedVoteOptions{
|
|
v1.NewWeightedVoteOption(v1.OptionYes, sdkmath.LegacyNewDec(-1)),
|
|
},
|
|
voter: proposer,
|
|
metadata: "",
|
|
expErr: true,
|
|
expErrMsg: `option:VOTE_OPTION_YES weight:"-1.000000000000000000" : invalid vote option`,
|
|
},
|
|
"individual weight > 1 but weights sum == 1": {
|
|
preRun: func() uint64 {
|
|
return proposalID
|
|
},
|
|
option: v1.WeightedVoteOptions{
|
|
v1.NewWeightedVoteOption(v1.OptionYes, sdkmath.LegacyNewDec(2)),
|
|
v1.NewWeightedVoteOption(v1.OptionNo, sdkmath.LegacyNewDec(-1)),
|
|
},
|
|
voter: proposer,
|
|
metadata: "",
|
|
expErr: true,
|
|
expErrMsg: `option:VOTE_OPTION_YES weight:"2.000000000000000000" : invalid vote option`,
|
|
},
|
|
"empty options": {
|
|
preRun: func() uint64 {
|
|
return proposalID
|
|
},
|
|
option: v1.WeightedVoteOptions{},
|
|
voter: proposer,
|
|
metadata: "",
|
|
expErr: true,
|
|
expErrMsg: "invalid request",
|
|
},
|
|
"invalid vote option": {
|
|
preRun: func() uint64 {
|
|
return proposalID
|
|
},
|
|
option: v1.NewNonSplitVoteOption(v1.VoteOption(0x13)),
|
|
voter: proposer,
|
|
metadata: "",
|
|
expErr: true,
|
|
expErrMsg: "invalid vote option",
|
|
},
|
|
"optimistic proposal: wrong vote option": {
|
|
preRun: func() uint64 {
|
|
msg, err := v1.NewMsgSubmitProposal(
|
|
[]sdk.Msg{bankMsg},
|
|
minDeposit,
|
|
proposerAddr,
|
|
"",
|
|
"Proposal",
|
|
"description of proposal",
|
|
v1.ProposalType_PROPOSAL_TYPE_OPTIMISTIC,
|
|
)
|
|
suite.Require().NoError(err)
|
|
|
|
res, err := suite.msgSrvr.SubmitProposal(suite.ctx, msg)
|
|
suite.Require().NoError(err)
|
|
suite.Require().NotNil(res.ProposalId)
|
|
return res.ProposalId
|
|
},
|
|
option: v1.NewNonSplitVoteOption(v1.VoteOption_VOTE_OPTION_ONE), // vote yes
|
|
voter: proposer,
|
|
metadata: "",
|
|
expErr: true,
|
|
expErrMsg: "optimistic proposals can only be rejected: invalid vote option",
|
|
},
|
|
"weights sum < 1": {
|
|
preRun: func() uint64 {
|
|
return proposalID
|
|
},
|
|
option: v1.WeightedVoteOptions{ // weight sum <1
|
|
v1.NewWeightedVoteOption(v1.OptionYes, sdkmath.LegacyNewDecWithPrec(5, 1)),
|
|
},
|
|
voter: proposer,
|
|
metadata: "",
|
|
expErr: true,
|
|
expErrMsg: "total weight lower than 1.00: invalid vote option",
|
|
},
|
|
"vote on inactive proposal": {
|
|
preRun: func() uint64 {
|
|
msg, err := v1.NewMsgSubmitProposal(
|
|
[]sdk.Msg{bankMsg},
|
|
coins,
|
|
proposerAddr,
|
|
"",
|
|
"Proposal",
|
|
"description of proposal",
|
|
v1.ProposalType_PROPOSAL_TYPE_STANDARD,
|
|
)
|
|
suite.Require().NoError(err)
|
|
|
|
res, err := suite.msgSrvr.SubmitProposal(suite.ctx, msg)
|
|
suite.Require().NoError(err)
|
|
suite.Require().NotNil(res.ProposalId)
|
|
return res.ProposalId
|
|
},
|
|
option: v1.NewNonSplitVoteOption(v1.VoteOption_VOTE_OPTION_ONE),
|
|
voter: proposer,
|
|
metadata: "",
|
|
expErr: true,
|
|
expErrMsg: "inactive proposal",
|
|
},
|
|
"metadata too long": {
|
|
preRun: func() uint64 {
|
|
return proposalID
|
|
},
|
|
option: v1.NewNonSplitVoteOption(v1.VoteOption_VOTE_OPTION_ONE),
|
|
voter: proposer,
|
|
metadata: strings.Repeat("a", 300),
|
|
expErr: true,
|
|
expErrMsg: "metadata too long",
|
|
},
|
|
"voter error": {
|
|
preRun: func() uint64 {
|
|
return proposalID
|
|
},
|
|
option: v1.NewNonSplitVoteOption(v1.VoteOption_VOTE_OPTION_ONE),
|
|
voter: sdk.AccAddress(strings.Repeat("a", 300)),
|
|
metadata: "",
|
|
expErr: true,
|
|
expErrMsg: "invalid voter address: empty address string is not allowed",
|
|
expAddrErr: true,
|
|
expAddrErrMsg: longAddressError,
|
|
},
|
|
"all good": {
|
|
preRun: func() uint64 {
|
|
msg, err := v1.NewMsgSubmitProposal(
|
|
[]sdk.Msg{bankMsg},
|
|
minDeposit,
|
|
proposerAddr,
|
|
"",
|
|
"Proposal",
|
|
"description of proposal",
|
|
v1.ProposalType_PROPOSAL_TYPE_STANDARD,
|
|
)
|
|
suite.Require().NoError(err)
|
|
|
|
res, err := suite.msgSrvr.SubmitProposal(suite.ctx, msg)
|
|
suite.Require().NoError(err)
|
|
suite.Require().NotNil(res.ProposalId)
|
|
return res.ProposalId
|
|
},
|
|
option: v1.NewNonSplitVoteOption(v1.VoteOption_VOTE_OPTION_ONE),
|
|
voter: proposer,
|
|
metadata: "",
|
|
expErr: false,
|
|
},
|
|
"all good with split votes": {
|
|
preRun: func() uint64 {
|
|
msg, err := v1.NewMsgSubmitProposal(
|
|
[]sdk.Msg{bankMsg},
|
|
minDeposit,
|
|
proposerAddr,
|
|
"",
|
|
"Proposal",
|
|
"description of proposal",
|
|
v1.ProposalType_PROPOSAL_TYPE_STANDARD,
|
|
)
|
|
suite.Require().NoError(err)
|
|
|
|
res, err := suite.msgSrvr.SubmitProposal(suite.ctx, msg)
|
|
suite.Require().NoError(err)
|
|
suite.Require().NotNil(res.ProposalId)
|
|
return res.ProposalId
|
|
},
|
|
option: v1.WeightedVoteOptions{
|
|
v1.NewWeightedVoteOption(v1.OptionYes, sdkmath.LegacyNewDecWithPrec(5, 1)),
|
|
v1.NewWeightedVoteOption(v1.OptionAbstain, sdkmath.LegacyNewDecWithPrec(5, 1)),
|
|
},
|
|
voter: proposer,
|
|
metadata: "",
|
|
expErr: false,
|
|
},
|
|
}
|
|
|
|
for name, tc := range cases {
|
|
suite.Run(name, func() {
|
|
pID := tc.preRun()
|
|
voter, err := suite.acctKeeper.AddressCodec().BytesToString(tc.voter)
|
|
if tc.expAddrErr {
|
|
suite.Require().Error(err)
|
|
suite.Require().Contains(err.Error(), tc.expAddrErrMsg)
|
|
} else {
|
|
suite.Require().NoError(err)
|
|
}
|
|
voteReq := v1.NewMsgVoteWeighted(voter, pID, tc.option, tc.metadata)
|
|
_, err = suite.msgSrvr.VoteWeighted(suite.ctx, voteReq)
|
|
if tc.expErr {
|
|
suite.Require().Error(err)
|
|
suite.Require().Contains(err.Error(), tc.expErrMsg)
|
|
} else {
|
|
suite.Require().NoError(err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func (suite *KeeperTestSuite) TestMsgDeposit() {
|
|
suite.reset()
|
|
govStrAcct, err := suite.acctKeeper.AddressCodec().BytesToString(suite.govKeeper.GetGovernanceAccount(suite.ctx).GetAddress())
|
|
suite.Require().NoError(err)
|
|
addrs := suite.addrs
|
|
proposer := addrs[0]
|
|
proposerAddr, err := suite.acctKeeper.AddressCodec().BytesToString(proposer)
|
|
suite.Require().NoError(err)
|
|
|
|
coins := sdk.NewCoins(sdk.NewCoin("stake", sdkmath.NewInt(100000)))
|
|
params, _ := suite.govKeeper.Params.Get(suite.ctx)
|
|
minDeposit := sdk.Coins(params.MinDeposit)
|
|
bankMsg := &banktypes.MsgSend{
|
|
FromAddress: govStrAcct,
|
|
ToAddress: proposerAddr,
|
|
Amount: coins,
|
|
}
|
|
|
|
msg, err := v1.NewMsgSubmitProposal(
|
|
[]sdk.Msg{bankMsg},
|
|
coins,
|
|
proposerAddr,
|
|
"",
|
|
"Proposal",
|
|
"description of proposal",
|
|
v1.ProposalType_PROPOSAL_TYPE_STANDARD,
|
|
)
|
|
suite.Require().NoError(err)
|
|
|
|
res, err := suite.msgSrvr.SubmitProposal(suite.ctx, msg)
|
|
suite.Require().NoError(err)
|
|
suite.Require().NotNil(res.ProposalId)
|
|
pID := res.ProposalId
|
|
|
|
cases := map[string]struct {
|
|
preRun func() uint64
|
|
expErr bool
|
|
proposalID uint64
|
|
depositor sdk.AccAddress
|
|
deposit sdk.Coins
|
|
expErrMsg string
|
|
}{
|
|
"wrong proposal id": {
|
|
preRun: func() uint64 {
|
|
return 0
|
|
},
|
|
depositor: proposer,
|
|
deposit: coins,
|
|
expErr: true,
|
|
expErrMsg: "not found",
|
|
},
|
|
"empty depositor": {
|
|
preRun: func() uint64 {
|
|
return pID
|
|
},
|
|
depositor: sdk.AccAddress{},
|
|
deposit: minDeposit,
|
|
expErr: true,
|
|
expErrMsg: "invalid depositor address",
|
|
},
|
|
"invalid deposited coin ": {
|
|
preRun: func() uint64 {
|
|
return pID
|
|
},
|
|
depositor: proposer,
|
|
deposit: []sdk.Coin{sdk.NewCoin("ibc/badcoin", sdkmath.NewInt(1000))},
|
|
expErr: true,
|
|
expErrMsg: "deposited 1000ibc/badcoin, but gov accepts only the following denom(s): [stake]",
|
|
},
|
|
"invalid deposited coin (multiple)": {
|
|
preRun: func() uint64 {
|
|
return pID
|
|
},
|
|
depositor: proposer,
|
|
deposit: minDeposit.Add(sdk.NewCoin("ibc/badcoin", sdkmath.NewInt(1000))),
|
|
expErr: true,
|
|
expErrMsg: "deposited 1000ibc/badcoin,10000000stake, but gov accepts only the following denom(s): [stake]: invalid deposit denom",
|
|
},
|
|
"all good": {
|
|
preRun: func() uint64 {
|
|
return pID
|
|
},
|
|
depositor: proposer,
|
|
deposit: minDeposit,
|
|
expErr: false,
|
|
},
|
|
}
|
|
|
|
for name, tc := range cases {
|
|
suite.Run(name, func() {
|
|
proposalID := tc.preRun()
|
|
depositor, err := suite.acctKeeper.AddressCodec().BytesToString(tc.depositor)
|
|
suite.Require().NoError(err)
|
|
depositReq := v1.NewMsgDeposit(depositor, proposalID, tc.deposit)
|
|
_, err = suite.msgSrvr.Deposit(suite.ctx, depositReq)
|
|
if tc.expErr {
|
|
suite.Require().Error(err)
|
|
suite.Require().Contains(err.Error(), tc.expErrMsg)
|
|
} else {
|
|
suite.Require().NoError(err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// legacy msg server tests
|
|
func (suite *KeeperTestSuite) TestLegacyMsgSubmitProposal() {
|
|
proposer, err := codectestutil.CodecOptions{}.GetAddressCodec().BytesToString(simtestutil.AddTestAddrsIncremental(suite.bankKeeper, suite.stakingKeeper, suite.ctx, 1, sdkmath.NewInt(50000000))[0])
|
|
suite.Require().NoError(err)
|
|
coins := sdk.NewCoins(sdk.NewCoin("stake", sdkmath.NewInt(100000)))
|
|
initialDeposit := coins
|
|
params, _ := suite.govKeeper.Params.Get(suite.ctx)
|
|
minDeposit := params.MinDeposit
|
|
address, err := codectestutil.CodecOptions{}.GetAddressCodec().BytesToString(sdk.AccAddress{})
|
|
suite.Require().NoError(err)
|
|
cases := map[string]struct {
|
|
preRun func() (*v1beta1.MsgSubmitProposal, error)
|
|
expErr bool
|
|
expErrMsg string
|
|
}{
|
|
"empty title": {
|
|
preRun: func() (*v1beta1.MsgSubmitProposal, error) {
|
|
content := v1beta1.NewTextProposal("", "I am test")
|
|
return v1beta1.NewMsgSubmitProposal(
|
|
content,
|
|
initialDeposit,
|
|
proposer,
|
|
)
|
|
},
|
|
expErr: true,
|
|
expErrMsg: "proposal title cannot be blank",
|
|
},
|
|
"empty description": {
|
|
preRun: func() (*v1beta1.MsgSubmitProposal, error) {
|
|
content := v1beta1.NewTextProposal("test", "")
|
|
return v1beta1.NewMsgSubmitProposal(
|
|
content,
|
|
initialDeposit,
|
|
proposer,
|
|
)
|
|
},
|
|
expErr: true,
|
|
expErrMsg: "proposal description cannot be blank",
|
|
},
|
|
"empty proposer": {
|
|
preRun: func() (*v1beta1.MsgSubmitProposal, error) {
|
|
content := v1beta1.NewTextProposal("test", "I am test")
|
|
return v1beta1.NewMsgSubmitProposal(
|
|
content,
|
|
initialDeposit,
|
|
address,
|
|
)
|
|
},
|
|
expErr: true,
|
|
expErrMsg: "invalid proposer address: empty address string is not allowed",
|
|
},
|
|
"title text length > max limit allowed": {
|
|
preRun: func() (*v1beta1.MsgSubmitProposal, error) {
|
|
content := v1beta1.NewTextProposal(strings.Repeat("#", v1beta1.MaxTitleLength*2), "I am test")
|
|
return v1beta1.NewMsgSubmitProposal(
|
|
content,
|
|
initialDeposit,
|
|
proposer,
|
|
)
|
|
},
|
|
expErr: true,
|
|
expErrMsg: "proposal title is longer than max length of 140: invalid proposal content",
|
|
},
|
|
"description text length > max limit allowed": {
|
|
preRun: func() (*v1beta1.MsgSubmitProposal, error) {
|
|
content := v1beta1.NewTextProposal("test", strings.Repeat("#", v1beta1.MaxDescriptionLength*2))
|
|
return v1beta1.NewMsgSubmitProposal(
|
|
content,
|
|
initialDeposit,
|
|
proposer,
|
|
)
|
|
},
|
|
expErr: true,
|
|
expErrMsg: "proposal description is longer than max length of 10000: invalid proposal content",
|
|
},
|
|
"all good": {
|
|
preRun: func() (*v1beta1.MsgSubmitProposal, error) {
|
|
return v1beta1.NewMsgSubmitProposal(
|
|
v1beta1.NewTextProposal("test", "I am test"),
|
|
initialDeposit,
|
|
proposer,
|
|
)
|
|
},
|
|
expErr: false,
|
|
},
|
|
"all good with min deposit": {
|
|
preRun: func() (*v1beta1.MsgSubmitProposal, error) {
|
|
return v1beta1.NewMsgSubmitProposal(
|
|
v1beta1.NewTextProposal("test", "I am test"),
|
|
minDeposit,
|
|
proposer,
|
|
)
|
|
},
|
|
expErr: false,
|
|
},
|
|
}
|
|
|
|
for name, c := range cases {
|
|
suite.Run(name, func() {
|
|
msg, err := c.preRun()
|
|
suite.Require().NoError(err)
|
|
res, err := suite.legacyMsgSrvr.SubmitProposal(suite.ctx, msg)
|
|
if c.expErr {
|
|
suite.Require().Error(err)
|
|
suite.Require().Contains(err.Error(), c.expErrMsg)
|
|
} else {
|
|
suite.Require().NoError(err)
|
|
suite.Require().NotNil(res.ProposalId)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func (suite *KeeperTestSuite) TestLegacyMsgVote() {
|
|
govStrAcct, err := suite.acctKeeper.AddressCodec().BytesToString(suite.govKeeper.GetGovernanceAccount(suite.ctx).GetAddress())
|
|
suite.Require().NoError(err)
|
|
addrs := suite.addrs
|
|
proposer := addrs[0]
|
|
proposerAddr, err := suite.acctKeeper.AddressCodec().BytesToString(proposer)
|
|
suite.Require().NoError(err)
|
|
|
|
coins := sdk.NewCoins(sdk.NewCoin("stake", sdkmath.NewInt(100000)))
|
|
params, _ := suite.govKeeper.Params.Get(suite.ctx)
|
|
minDeposit := params.MinDeposit
|
|
bankMsg := &banktypes.MsgSend{
|
|
FromAddress: govStrAcct,
|
|
ToAddress: proposerAddr,
|
|
Amount: coins,
|
|
}
|
|
|
|
msg, err := v1.NewMsgSubmitProposal(
|
|
[]sdk.Msg{bankMsg},
|
|
minDeposit,
|
|
proposerAddr,
|
|
"",
|
|
"Proposal",
|
|
"description of proposal",
|
|
v1.ProposalType_PROPOSAL_TYPE_STANDARD,
|
|
)
|
|
suite.Require().NoError(err)
|
|
|
|
res, err := suite.msgSrvr.SubmitProposal(suite.ctx, msg)
|
|
suite.Require().NoError(err)
|
|
suite.Require().NotNil(res.ProposalId)
|
|
proposalID := res.ProposalId
|
|
|
|
cases := map[string]struct {
|
|
preRun func() uint64
|
|
expErr bool
|
|
expAddrErr bool
|
|
expErrMsg string
|
|
expAddrErrMsg string
|
|
option v1beta1.VoteOption
|
|
metadata string
|
|
voter sdk.AccAddress
|
|
}{
|
|
"empty voter": {
|
|
preRun: func() uint64 {
|
|
return proposalID
|
|
},
|
|
option: v1beta1.OptionYes,
|
|
voter: sdk.AccAddress{},
|
|
metadata: "",
|
|
expErr: true,
|
|
expErrMsg: "invalid voter address",
|
|
},
|
|
"wrong vote option": {
|
|
preRun: func() uint64 {
|
|
return proposalID
|
|
},
|
|
option: v1beta1.VoteOption(0x13),
|
|
voter: proposer,
|
|
metadata: "",
|
|
expErr: true,
|
|
expErrMsg: "invalid vote option",
|
|
},
|
|
"vote on inactive proposal": {
|
|
preRun: func() uint64 {
|
|
msg, err := v1.NewMsgSubmitProposal(
|
|
[]sdk.Msg{bankMsg},
|
|
coins,
|
|
proposerAddr,
|
|
"",
|
|
"Proposal",
|
|
"description of proposal",
|
|
v1.ProposalType_PROPOSAL_TYPE_STANDARD,
|
|
)
|
|
suite.Require().NoError(err)
|
|
|
|
res, err := suite.msgSrvr.SubmitProposal(suite.ctx, msg)
|
|
suite.Require().NoError(err)
|
|
suite.Require().NotNil(res.ProposalId)
|
|
return res.ProposalId
|
|
},
|
|
option: v1beta1.OptionYes,
|
|
voter: proposer,
|
|
metadata: "",
|
|
expErr: true,
|
|
expErrMsg: "inactive proposal",
|
|
},
|
|
"voter error": {
|
|
preRun: func() uint64 {
|
|
return proposalID
|
|
},
|
|
option: v1beta1.OptionYes,
|
|
voter: sdk.AccAddress(strings.Repeat("a", 300)),
|
|
metadata: "",
|
|
expErr: true,
|
|
expErrMsg: "invalid voter address: empty address string is not allowed",
|
|
expAddrErr: true,
|
|
expAddrErrMsg: longAddressError,
|
|
},
|
|
"all good": {
|
|
preRun: func() uint64 {
|
|
msg, err := v1.NewMsgSubmitProposal(
|
|
[]sdk.Msg{bankMsg},
|
|
minDeposit,
|
|
proposerAddr,
|
|
"",
|
|
"Proposal",
|
|
"description of proposal",
|
|
v1.ProposalType_PROPOSAL_TYPE_STANDARD,
|
|
)
|
|
suite.Require().NoError(err)
|
|
|
|
res, err := suite.msgSrvr.SubmitProposal(suite.ctx, msg)
|
|
suite.Require().NoError(err)
|
|
suite.Require().NotNil(res.ProposalId)
|
|
return res.ProposalId
|
|
},
|
|
option: v1beta1.OptionYes,
|
|
voter: proposer,
|
|
metadata: "",
|
|
expErr: false,
|
|
},
|
|
}
|
|
|
|
for name, tc := range cases {
|
|
suite.Run(name, func() {
|
|
pID := tc.preRun()
|
|
voter, err := suite.acctKeeper.AddressCodec().BytesToString(tc.voter)
|
|
if tc.expAddrErr {
|
|
suite.Require().Error(err)
|
|
suite.Require().Contains(err.Error(), tc.expAddrErrMsg)
|
|
} else {
|
|
suite.Require().NoError(err)
|
|
}
|
|
voteReq := v1beta1.NewMsgVote(voter, pID, tc.option)
|
|
_, err = suite.legacyMsgSrvr.Vote(suite.ctx, voteReq)
|
|
if tc.expErr {
|
|
suite.Require().Error(err)
|
|
suite.Require().Contains(err.Error(), tc.expErrMsg)
|
|
} else {
|
|
suite.Require().NoError(err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func (suite *KeeperTestSuite) TestLegacyVoteWeighted() {
|
|
suite.reset()
|
|
govStrAcct, err := suite.acctKeeper.AddressCodec().BytesToString(suite.govKeeper.GetGovernanceAccount(suite.ctx).GetAddress())
|
|
suite.Require().NoError(err)
|
|
addrs := suite.addrs
|
|
proposer := addrs[0]
|
|
proposerAddr, err := suite.acctKeeper.AddressCodec().BytesToString(proposer)
|
|
suite.Require().NoError(err)
|
|
|
|
coins := sdk.NewCoins(sdk.NewCoin("stake", sdkmath.NewInt(100000)))
|
|
params, _ := suite.govKeeper.Params.Get(suite.ctx)
|
|
minDeposit := params.MinDeposit
|
|
bankMsg := &banktypes.MsgSend{
|
|
FromAddress: govStrAcct,
|
|
ToAddress: proposerAddr,
|
|
Amount: coins,
|
|
}
|
|
|
|
msg, err := v1.NewMsgSubmitProposal(
|
|
[]sdk.Msg{bankMsg},
|
|
minDeposit,
|
|
proposerAddr,
|
|
"",
|
|
"Proposal",
|
|
"description of proposal",
|
|
v1.ProposalType_PROPOSAL_TYPE_STANDARD,
|
|
)
|
|
suite.Require().NoError(err)
|
|
|
|
res, err := suite.msgSrvr.SubmitProposal(suite.ctx, msg)
|
|
suite.Require().NoError(err)
|
|
suite.Require().NotNil(res.ProposalId)
|
|
proposalID := res.ProposalId
|
|
|
|
cases := map[string]struct {
|
|
preRun func() uint64
|
|
vote *v1beta1.MsgVote
|
|
expErr bool
|
|
expAddrErr bool
|
|
expErrMsg string
|
|
expAddrErrMsg string
|
|
option v1beta1.WeightedVoteOptions
|
|
metadata string
|
|
voter sdk.AccAddress
|
|
}{
|
|
"empty voter": {
|
|
preRun: func() uint64 {
|
|
return proposalID
|
|
},
|
|
option: v1beta1.WeightedVoteOptions{
|
|
v1beta1.WeightedVoteOption{
|
|
Option: v1beta1.OptionYes,
|
|
Weight: sdkmath.LegacyNewDec(1),
|
|
},
|
|
},
|
|
voter: sdk.AccAddress{},
|
|
metadata: "",
|
|
expErr: true,
|
|
expErrMsg: "invalid voter address",
|
|
},
|
|
"weights sum > 1": {
|
|
preRun: func() uint64 {
|
|
return proposalID
|
|
},
|
|
option: v1beta1.WeightedVoteOptions{
|
|
v1beta1.WeightedVoteOption{
|
|
Option: v1beta1.OptionYes,
|
|
Weight: sdkmath.LegacyNewDec(1),
|
|
},
|
|
v1beta1.WeightedVoteOption{
|
|
Option: v1beta1.OptionAbstain,
|
|
Weight: sdkmath.LegacyNewDec(1),
|
|
},
|
|
},
|
|
voter: proposer,
|
|
metadata: "",
|
|
expErr: true,
|
|
expErrMsg: "total weight overflow 1.00: invalid vote option",
|
|
},
|
|
"duplicate vote options": {
|
|
preRun: func() uint64 {
|
|
return proposalID
|
|
},
|
|
option: v1beta1.WeightedVoteOptions{
|
|
v1beta1.WeightedVoteOption{
|
|
Option: v1beta1.OptionYes,
|
|
Weight: sdkmath.LegacyNewDecWithPrec(5, 1),
|
|
},
|
|
v1beta1.WeightedVoteOption{
|
|
Option: v1beta1.OptionYes,
|
|
Weight: sdkmath.LegacyNewDecWithPrec(5, 1),
|
|
},
|
|
},
|
|
voter: proposer,
|
|
metadata: "",
|
|
expErr: true,
|
|
expErrMsg: "duplicated vote option",
|
|
},
|
|
"zero weight": {
|
|
preRun: func() uint64 {
|
|
return proposalID
|
|
},
|
|
option: v1beta1.WeightedVoteOptions{
|
|
v1beta1.WeightedVoteOption{
|
|
Option: v1beta1.OptionYes,
|
|
Weight: sdkmath.LegacyNewDec(0),
|
|
},
|
|
},
|
|
voter: proposer,
|
|
metadata: "",
|
|
expErr: true,
|
|
expErrMsg: `option:VOTE_OPTION_YES weight:"0.000000000000000000" : invalid vote option`,
|
|
},
|
|
"negative weight": {
|
|
preRun: func() uint64 {
|
|
return proposalID
|
|
},
|
|
option: v1beta1.WeightedVoteOptions{
|
|
v1beta1.WeightedVoteOption{
|
|
Option: v1beta1.OptionYes,
|
|
Weight: sdkmath.LegacyNewDec(-1),
|
|
},
|
|
},
|
|
voter: proposer,
|
|
metadata: "",
|
|
expErr: true,
|
|
expErrMsg: `option:VOTE_OPTION_YES weight:"-1.000000000000000000" : invalid vote option`,
|
|
},
|
|
"empty options": {
|
|
preRun: func() uint64 {
|
|
return proposalID
|
|
},
|
|
option: v1beta1.WeightedVoteOptions{},
|
|
voter: proposer,
|
|
metadata: "",
|
|
expErr: true,
|
|
expErrMsg: "invalid request",
|
|
},
|
|
"invalid vote option": {
|
|
preRun: func() uint64 {
|
|
return proposalID
|
|
},
|
|
option: v1beta1.WeightedVoteOptions{
|
|
v1beta1.WeightedVoteOption{
|
|
Option: v1beta1.VoteOption(0x13),
|
|
Weight: sdkmath.LegacyNewDecWithPrec(5, 1),
|
|
},
|
|
},
|
|
voter: proposer,
|
|
metadata: "",
|
|
expErr: true,
|
|
expErrMsg: "invalid vote option",
|
|
},
|
|
"weight sum < 1": {
|
|
preRun: func() uint64 {
|
|
return proposalID
|
|
},
|
|
option: v1beta1.WeightedVoteOptions{
|
|
v1beta1.WeightedVoteOption{
|
|
Option: v1beta1.OptionYes,
|
|
Weight: sdkmath.LegacyNewDecWithPrec(5, 1),
|
|
},
|
|
},
|
|
voter: proposer,
|
|
metadata: "",
|
|
expErr: true,
|
|
expErrMsg: "total weight lower than 1.00: invalid vote option",
|
|
},
|
|
"vote on inactive proposal": {
|
|
preRun: func() uint64 {
|
|
msg, err := v1.NewMsgSubmitProposal(
|
|
[]sdk.Msg{bankMsg},
|
|
coins,
|
|
proposerAddr,
|
|
"",
|
|
"Proposal",
|
|
"description of proposal",
|
|
v1.ProposalType_PROPOSAL_TYPE_STANDARD,
|
|
)
|
|
suite.Require().NoError(err)
|
|
|
|
res, err := suite.msgSrvr.SubmitProposal(suite.ctx, msg)
|
|
suite.Require().NoError(err)
|
|
suite.Require().NotNil(res.ProposalId)
|
|
return res.ProposalId
|
|
},
|
|
option: v1beta1.WeightedVoteOptions{
|
|
v1beta1.WeightedVoteOption{
|
|
Option: v1beta1.OptionYes,
|
|
Weight: sdkmath.LegacyNewDec(1),
|
|
},
|
|
},
|
|
voter: proposer,
|
|
metadata: "",
|
|
expErr: true,
|
|
expErrMsg: "inactive proposal",
|
|
},
|
|
"voter error": {
|
|
preRun: func() uint64 {
|
|
return proposalID
|
|
},
|
|
option: v1beta1.WeightedVoteOptions{
|
|
v1beta1.WeightedVoteOption{
|
|
Option: v1beta1.OptionYes,
|
|
Weight: sdkmath.LegacyNewDec(1),
|
|
},
|
|
},
|
|
voter: sdk.AccAddress(strings.Repeat("a", 300)),
|
|
metadata: "",
|
|
expErr: true,
|
|
expErrMsg: "invalid voter address: empty address string is not allowed",
|
|
expAddrErr: true,
|
|
expAddrErrMsg: longAddressError,
|
|
},
|
|
"all good": {
|
|
preRun: func() uint64 {
|
|
msg, err := v1.NewMsgSubmitProposal(
|
|
[]sdk.Msg{bankMsg},
|
|
minDeposit,
|
|
proposerAddr,
|
|
"",
|
|
"Proposal",
|
|
"description of proposal",
|
|
v1.ProposalType_PROPOSAL_TYPE_STANDARD,
|
|
)
|
|
suite.Require().NoError(err)
|
|
|
|
res, err := suite.msgSrvr.SubmitProposal(suite.ctx, msg)
|
|
suite.Require().NoError(err)
|
|
suite.Require().NotNil(res.ProposalId)
|
|
return res.ProposalId
|
|
},
|
|
option: v1beta1.WeightedVoteOptions{
|
|
v1beta1.WeightedVoteOption{
|
|
Option: v1beta1.OptionYes,
|
|
Weight: sdkmath.LegacyNewDec(1),
|
|
},
|
|
},
|
|
voter: proposer,
|
|
metadata: "",
|
|
expErr: false,
|
|
},
|
|
}
|
|
|
|
for name, tc := range cases {
|
|
suite.Run(name, func() {
|
|
pID := tc.preRun()
|
|
voter, err := suite.acctKeeper.AddressCodec().BytesToString(tc.voter)
|
|
if tc.expAddrErr {
|
|
suite.Require().Error(err)
|
|
suite.Require().Contains(err.Error(), tc.expAddrErrMsg)
|
|
} else {
|
|
suite.Require().NoError(err)
|
|
}
|
|
voteReq := v1beta1.NewMsgVoteWeighted(voter, pID, tc.option)
|
|
_, err = suite.legacyMsgSrvr.VoteWeighted(suite.ctx, voteReq)
|
|
if tc.expErr {
|
|
suite.Require().Error(err)
|
|
suite.Require().Contains(err.Error(), tc.expErrMsg)
|
|
} else {
|
|
suite.Require().NoError(err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func (suite *KeeperTestSuite) TestLegacyMsgDeposit() {
|
|
govStrAcct, err := suite.acctKeeper.AddressCodec().BytesToString(suite.govKeeper.GetGovernanceAccount(suite.ctx).GetAddress())
|
|
suite.Require().NoError(err)
|
|
addrs := suite.addrs
|
|
proposer := addrs[0]
|
|
proposerAddr, err := suite.acctKeeper.AddressCodec().BytesToString(proposer)
|
|
suite.Require().NoError(err)
|
|
|
|
coins := sdk.NewCoins(sdk.NewCoin("stake", sdkmath.NewInt(100000)))
|
|
params, _ := suite.govKeeper.Params.Get(suite.ctx)
|
|
minDeposit := params.MinDeposit
|
|
bankMsg := &banktypes.MsgSend{
|
|
FromAddress: govStrAcct,
|
|
ToAddress: proposerAddr,
|
|
Amount: coins,
|
|
}
|
|
|
|
msg, err := v1.NewMsgSubmitProposal(
|
|
[]sdk.Msg{bankMsg},
|
|
coins,
|
|
proposerAddr,
|
|
"",
|
|
"Proposal",
|
|
"description of proposal",
|
|
v1.ProposalType_PROPOSAL_TYPE_STANDARD,
|
|
)
|
|
suite.Require().NoError(err)
|
|
|
|
res, err := suite.msgSrvr.SubmitProposal(suite.ctx, msg)
|
|
suite.Require().NoError(err)
|
|
suite.Require().NotNil(res.ProposalId)
|
|
pID := res.ProposalId
|
|
|
|
cases := map[string]struct {
|
|
preRun func() uint64
|
|
expErr bool
|
|
expErrMsg string
|
|
proposalID uint64
|
|
depositor sdk.AccAddress
|
|
deposit sdk.Coins
|
|
}{
|
|
"wrong proposal id": {
|
|
preRun: func() uint64 {
|
|
return 0
|
|
},
|
|
depositor: proposer,
|
|
deposit: coins,
|
|
expErr: true,
|
|
expErrMsg: "not found",
|
|
},
|
|
"empty depositer": {
|
|
preRun: func() uint64 {
|
|
return pID
|
|
},
|
|
depositor: sdk.AccAddress{},
|
|
deposit: coins,
|
|
expErr: true,
|
|
expErrMsg: "invalid depositor address: empty address string is not allowed",
|
|
},
|
|
"all good": {
|
|
preRun: func() uint64 {
|
|
return pID
|
|
},
|
|
depositor: proposer,
|
|
deposit: minDeposit,
|
|
expErr: false,
|
|
},
|
|
}
|
|
|
|
for name, tc := range cases {
|
|
suite.Run(name, func() {
|
|
proposalID := tc.preRun()
|
|
depositor, err := suite.acctKeeper.AddressCodec().BytesToString(tc.depositor)
|
|
suite.Require().NoError(err)
|
|
depositReq := v1beta1.NewMsgDeposit(depositor, proposalID, tc.deposit)
|
|
_, err = suite.legacyMsgSrvr.Deposit(suite.ctx, depositReq)
|
|
if tc.expErr {
|
|
suite.Require().Error(err)
|
|
suite.Require().Contains(err.Error(), tc.expErrMsg)
|
|
} else {
|
|
suite.Require().NoError(err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func (suite *KeeperTestSuite) TestMsgUpdateParams() {
|
|
authority := suite.govKeeper.GetAuthority()
|
|
params := v1.DefaultParams()
|
|
testCases := []struct {
|
|
name string
|
|
input func() *v1.MsgUpdateParams
|
|
expErr bool
|
|
expErrMsg string
|
|
}{
|
|
{
|
|
name: "valid",
|
|
input: func() *v1.MsgUpdateParams {
|
|
return &v1.MsgUpdateParams{
|
|
Authority: authority,
|
|
Params: params,
|
|
}
|
|
},
|
|
expErr: false,
|
|
},
|
|
{
|
|
name: "invalid authority",
|
|
input: func() *v1.MsgUpdateParams {
|
|
return &v1.MsgUpdateParams{
|
|
Authority: "authority",
|
|
Params: params,
|
|
}
|
|
},
|
|
expErr: true,
|
|
expErrMsg: "invalid authority",
|
|
},
|
|
{
|
|
name: "invalid min deposit",
|
|
input: func() *v1.MsgUpdateParams {
|
|
params1 := params
|
|
params1.MinDeposit = nil
|
|
|
|
return &v1.MsgUpdateParams{
|
|
Authority: authority,
|
|
Params: params1,
|
|
}
|
|
},
|
|
expErr: true,
|
|
expErrMsg: "invalid minimum deposit",
|
|
},
|
|
{
|
|
name: "negative deposit",
|
|
input: func() *v1.MsgUpdateParams {
|
|
params1 := params
|
|
params1.MinDeposit = sdk.Coins{{
|
|
Denom: sdk.DefaultBondDenom,
|
|
Amount: sdkmath.NewInt(-100),
|
|
}}
|
|
|
|
return &v1.MsgUpdateParams{
|
|
Authority: authority,
|
|
Params: params1,
|
|
}
|
|
},
|
|
expErr: true,
|
|
expErrMsg: "invalid minimum deposit",
|
|
},
|
|
{
|
|
name: "invalid max deposit period",
|
|
input: func() *v1.MsgUpdateParams {
|
|
params1 := params
|
|
params1.MaxDepositPeriod = nil
|
|
|
|
return &v1.MsgUpdateParams{
|
|
Authority: authority,
|
|
Params: params1,
|
|
}
|
|
},
|
|
expErr: true,
|
|
expErrMsg: "maximum deposit period must not be nil",
|
|
},
|
|
{
|
|
name: "zero max deposit period",
|
|
input: func() *v1.MsgUpdateParams {
|
|
params1 := params
|
|
duration := time.Duration(0)
|
|
params1.MaxDepositPeriod = &duration
|
|
|
|
return &v1.MsgUpdateParams{
|
|
Authority: authority,
|
|
Params: params1,
|
|
}
|
|
},
|
|
expErr: true,
|
|
expErrMsg: "maximum deposit period must be positive",
|
|
},
|
|
{
|
|
name: "invalid quorum",
|
|
input: func() *v1.MsgUpdateParams {
|
|
params1 := params
|
|
params1.Quorum = abc
|
|
|
|
return &v1.MsgUpdateParams{
|
|
Authority: authority,
|
|
Params: params1,
|
|
}
|
|
},
|
|
expErr: true,
|
|
expErrMsg: "invalid quorum string",
|
|
},
|
|
{
|
|
name: "negative quorum",
|
|
input: func() *v1.MsgUpdateParams {
|
|
params1 := params
|
|
params1.Quorum = o1
|
|
|
|
return &v1.MsgUpdateParams{
|
|
Authority: authority,
|
|
Params: params1,
|
|
}
|
|
},
|
|
expErr: true,
|
|
expErrMsg: "quorum cannot be negative",
|
|
},
|
|
{
|
|
name: "quorum > 1",
|
|
input: func() *v1.MsgUpdateParams {
|
|
params1 := params
|
|
params1.Quorum = "2"
|
|
|
|
return &v1.MsgUpdateParams{
|
|
Authority: authority,
|
|
Params: params1,
|
|
}
|
|
},
|
|
expErr: true,
|
|
expErrMsg: "quorum too large",
|
|
},
|
|
{
|
|
name: "invalid yes quorum",
|
|
input: func() *v1.MsgUpdateParams {
|
|
params1 := params
|
|
params1.YesQuorum = abc
|
|
|
|
return &v1.MsgUpdateParams{
|
|
Authority: authority,
|
|
Params: params1,
|
|
}
|
|
},
|
|
expErr: true,
|
|
expErrMsg: "invalid yes_quorum string",
|
|
},
|
|
{
|
|
name: "negative yes quorum",
|
|
input: func() *v1.MsgUpdateParams {
|
|
params1 := params
|
|
params1.YesQuorum = o1
|
|
|
|
return &v1.MsgUpdateParams{
|
|
Authority: authority,
|
|
Params: params1,
|
|
}
|
|
},
|
|
expErr: true,
|
|
expErrMsg: "yes_quorum cannot be negative",
|
|
},
|
|
{
|
|
name: "yes quorum > 1",
|
|
input: func() *v1.MsgUpdateParams {
|
|
params1 := params
|
|
params1.YesQuorum = "2"
|
|
|
|
return &v1.MsgUpdateParams{
|
|
Authority: authority,
|
|
Params: params1,
|
|
}
|
|
},
|
|
expErr: true,
|
|
expErrMsg: "yes_quorum too large",
|
|
},
|
|
{
|
|
name: "invalid expedited quorum",
|
|
input: func() *v1.MsgUpdateParams {
|
|
params1 := params
|
|
params1.ExpeditedQuorum = abc
|
|
|
|
return &v1.MsgUpdateParams{
|
|
Authority: authority,
|
|
Params: params1,
|
|
}
|
|
},
|
|
expErr: true,
|
|
expErrMsg: "invalid expedited_quorum string",
|
|
},
|
|
{
|
|
name: "negative expedited quorum",
|
|
input: func() *v1.MsgUpdateParams {
|
|
params1 := params
|
|
params1.ExpeditedQuorum = o1
|
|
|
|
return &v1.MsgUpdateParams{
|
|
Authority: authority,
|
|
Params: params1,
|
|
}
|
|
},
|
|
expErr: true,
|
|
expErrMsg: "expedited_quorum cannot be negative",
|
|
},
|
|
{
|
|
name: "expedited quorum > 1",
|
|
input: func() *v1.MsgUpdateParams {
|
|
params1 := params
|
|
params1.ExpeditedQuorum = "2"
|
|
|
|
return &v1.MsgUpdateParams{
|
|
Authority: authority,
|
|
Params: params1,
|
|
}
|
|
},
|
|
expErr: true,
|
|
expErrMsg: "expedited_quorum too large",
|
|
},
|
|
{
|
|
name: "invalid threshold",
|
|
input: func() *v1.MsgUpdateParams {
|
|
params1 := params
|
|
params1.Threshold = abc
|
|
|
|
return &v1.MsgUpdateParams{
|
|
Authority: authority,
|
|
Params: params1,
|
|
}
|
|
},
|
|
expErr: true,
|
|
expErrMsg: "invalid threshold string",
|
|
},
|
|
{
|
|
name: "negative threshold",
|
|
input: func() *v1.MsgUpdateParams {
|
|
params1 := params
|
|
params1.Threshold = o1
|
|
|
|
return &v1.MsgUpdateParams{
|
|
Authority: authority,
|
|
Params: params1,
|
|
}
|
|
},
|
|
expErr: true,
|
|
expErrMsg: "vote threshold must be positive",
|
|
},
|
|
{
|
|
name: "threshold > 1",
|
|
input: func() *v1.MsgUpdateParams {
|
|
params1 := params
|
|
params1.Threshold = "2"
|
|
|
|
return &v1.MsgUpdateParams{
|
|
Authority: authority,
|
|
Params: params1,
|
|
}
|
|
},
|
|
expErr: true,
|
|
expErrMsg: "vote threshold too large",
|
|
},
|
|
{
|
|
name: "invalid veto threshold",
|
|
input: func() *v1.MsgUpdateParams {
|
|
params1 := params
|
|
params1.VetoThreshold = abc
|
|
|
|
return &v1.MsgUpdateParams{
|
|
Authority: authority,
|
|
Params: params1,
|
|
}
|
|
},
|
|
expErr: true,
|
|
expErrMsg: "invalid vetoThreshold string",
|
|
},
|
|
{
|
|
name: "negative veto threshold",
|
|
input: func() *v1.MsgUpdateParams {
|
|
params1 := params
|
|
params1.VetoThreshold = o1
|
|
|
|
return &v1.MsgUpdateParams{
|
|
Authority: authority,
|
|
Params: params1,
|
|
}
|
|
},
|
|
expErr: true,
|
|
expErrMsg: "veto threshold must be positive",
|
|
},
|
|
{
|
|
name: "veto threshold > 1",
|
|
input: func() *v1.MsgUpdateParams {
|
|
params1 := params
|
|
params1.VetoThreshold = "2"
|
|
|
|
return &v1.MsgUpdateParams{
|
|
Authority: authority,
|
|
Params: params1,
|
|
}
|
|
},
|
|
expErr: true,
|
|
expErrMsg: "veto threshold too large",
|
|
},
|
|
{
|
|
name: "invalid voting period",
|
|
input: func() *v1.MsgUpdateParams {
|
|
params1 := params
|
|
params1.VotingPeriod = nil
|
|
|
|
return &v1.MsgUpdateParams{
|
|
Authority: authority,
|
|
Params: params1,
|
|
}
|
|
},
|
|
expErr: true,
|
|
expErrMsg: "voting period must not be nil",
|
|
},
|
|
{
|
|
name: "zero voting period",
|
|
input: func() *v1.MsgUpdateParams {
|
|
params1 := params
|
|
duration := time.Duration(0)
|
|
params1.VotingPeriod = &duration
|
|
|
|
return &v1.MsgUpdateParams{
|
|
Authority: authority,
|
|
Params: params1,
|
|
}
|
|
},
|
|
expErr: true,
|
|
expErrMsg: "voting period must be positive",
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
tc := tc
|
|
suite.Run(tc.name, func() {
|
|
msg := tc.input()
|
|
exec := func(updateParams *v1.MsgUpdateParams) error {
|
|
if _, err := suite.msgSrvr.UpdateParams(suite.ctx, updateParams); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
err := exec(msg)
|
|
if tc.expErr {
|
|
suite.Require().Error(err)
|
|
suite.Require().Contains(err.Error(), tc.expErrMsg)
|
|
} else {
|
|
suite.Require().NoError(err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func (suite *KeeperTestSuite) TestMsgUpdateMessageParams() {
|
|
testCases := []struct {
|
|
name string
|
|
input *v1.MsgUpdateMessageParams
|
|
expErrMsg string
|
|
}{
|
|
{
|
|
name: "invalid authority",
|
|
input: &v1.MsgUpdateMessageParams{
|
|
Authority: "invalid",
|
|
MsgUrl: "",
|
|
Params: nil,
|
|
},
|
|
expErrMsg: "invalid authority",
|
|
},
|
|
{
|
|
name: "invalid msg url (valid as not checked in msg handler)",
|
|
input: &v1.MsgUpdateMessageParams{
|
|
Authority: suite.govKeeper.GetAuthority(),
|
|
MsgUrl: "invalid",
|
|
Params: nil,
|
|
},
|
|
expErrMsg: "",
|
|
},
|
|
{
|
|
name: "empty params (valid = deleting params)",
|
|
input: &v1.MsgUpdateMessageParams{
|
|
Authority: suite.govKeeper.GetAuthority(),
|
|
MsgUrl: "",
|
|
Params: nil,
|
|
},
|
|
expErrMsg: "",
|
|
},
|
|
{
|
|
name: "invalid quorum",
|
|
input: &v1.MsgUpdateMessageParams{
|
|
Authority: suite.govKeeper.GetAuthority(),
|
|
MsgUrl: sdk.MsgTypeURL(&v1.MsgUpdateParams{}),
|
|
Params: &v1.MessageBasedParams{
|
|
VotingPeriod: func() *time.Duration { d := time.Hour; return &d }(),
|
|
Quorum: "-0.334",
|
|
YesQuorum: "0.5",
|
|
Threshold: "0.5",
|
|
VetoThreshold: "0.334",
|
|
},
|
|
},
|
|
expErrMsg: "quorum cannot be negative",
|
|
},
|
|
{
|
|
name: "invalid yes quorum",
|
|
input: &v1.MsgUpdateMessageParams{
|
|
Authority: suite.govKeeper.GetAuthority(),
|
|
MsgUrl: sdk.MsgTypeURL(&v1.MsgUpdateParams{}),
|
|
Params: &v1.MessageBasedParams{
|
|
VotingPeriod: func() *time.Duration { d := time.Hour; return &d }(),
|
|
Quorum: "0.334",
|
|
YesQuorum: "-0.5",
|
|
Threshold: "0.5",
|
|
VetoThreshold: "0.334",
|
|
},
|
|
},
|
|
expErrMsg: "yes_quorum cannot be negative",
|
|
},
|
|
{
|
|
name: "invalid threshold",
|
|
input: &v1.MsgUpdateMessageParams{
|
|
Authority: suite.govKeeper.GetAuthority(),
|
|
MsgUrl: sdk.MsgTypeURL(&v1.MsgUpdateParams{}),
|
|
Params: &v1.MessageBasedParams{
|
|
VotingPeriod: func() *time.Duration { d := time.Hour; return &d }(),
|
|
Quorum: "0.334",
|
|
YesQuorum: "0.5",
|
|
Threshold: "-0.5",
|
|
VetoThreshold: "0.334",
|
|
},
|
|
},
|
|
expErrMsg: "vote threshold must be positive",
|
|
},
|
|
{
|
|
name: "invalid veto threshold",
|
|
input: &v1.MsgUpdateMessageParams{
|
|
Authority: suite.govKeeper.GetAuthority(),
|
|
MsgUrl: sdk.MsgTypeURL(&v1.MsgUpdateParams{}),
|
|
Params: &v1.MessageBasedParams{
|
|
VotingPeriod: func() *time.Duration { d := time.Hour; return &d }(),
|
|
Quorum: "0.334",
|
|
YesQuorum: "0.5",
|
|
Threshold: "0.5",
|
|
VetoThreshold: "-0.334",
|
|
},
|
|
},
|
|
expErrMsg: "veto threshold must be positive",
|
|
},
|
|
{
|
|
name: "invalid voting period",
|
|
input: &v1.MsgUpdateMessageParams{
|
|
Authority: suite.govKeeper.GetAuthority(),
|
|
MsgUrl: sdk.MsgTypeURL(&v1.MsgUpdateParams{}),
|
|
Params: &v1.MessageBasedParams{
|
|
VotingPeriod: func() *time.Duration { d := -time.Hour; return &d }(),
|
|
Quorum: "0.334",
|
|
YesQuorum: "0.5",
|
|
Threshold: "0.5",
|
|
VetoThreshold: "0.334",
|
|
},
|
|
},
|
|
expErrMsg: "voting period must be positive",
|
|
},
|
|
{
|
|
name: "valid",
|
|
input: &v1.MsgUpdateMessageParams{
|
|
Authority: suite.govKeeper.GetAuthority(),
|
|
MsgUrl: sdk.MsgTypeURL(&v1.MsgUpdateParams{}),
|
|
Params: &v1.MessageBasedParams{
|
|
VotingPeriod: func() *time.Duration { d := time.Hour; return &d }(),
|
|
Quorum: "0.334",
|
|
YesQuorum: "0",
|
|
Threshold: "0.5",
|
|
VetoThreshold: "0.334",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
tc := tc
|
|
suite.Run(tc.name, func() {
|
|
_, err := suite.msgSrvr.UpdateMessageParams(suite.ctx, tc.input)
|
|
if tc.expErrMsg != "" {
|
|
suite.Require().Error(err)
|
|
suite.Require().Contains(err.Error(), tc.expErrMsg)
|
|
} else {
|
|
suite.Require().NoError(err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func (suite *KeeperTestSuite) TestSubmitProposal_InitialDeposit() {
|
|
const meetsDepositValue = baseDepositTestAmount * baseDepositTestPercent / 100
|
|
baseDepositRatioDec := sdkmath.LegacyNewDec(baseDepositTestPercent).Quo(sdkmath.LegacyNewDec(100))
|
|
|
|
testcases := map[string]struct {
|
|
minDeposit sdk.Coins
|
|
minInitialDepositRatio sdkmath.LegacyDec
|
|
initialDeposit sdk.Coins
|
|
accountBalance sdk.Coins
|
|
|
|
expectError bool
|
|
}{
|
|
"meets initial deposit, enough balance - success": {
|
|
minDeposit: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdkmath.NewInt(baseDepositTestAmount))),
|
|
minInitialDepositRatio: baseDepositRatioDec,
|
|
initialDeposit: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdkmath.NewInt(meetsDepositValue))),
|
|
accountBalance: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdkmath.NewInt(meetsDepositValue))),
|
|
},
|
|
"does not meet initial deposit, enough balance - error": {
|
|
minDeposit: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdkmath.NewInt(baseDepositTestAmount))),
|
|
minInitialDepositRatio: baseDepositRatioDec,
|
|
initialDeposit: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdkmath.NewInt(meetsDepositValue-1))),
|
|
accountBalance: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdkmath.NewInt(meetsDepositValue))),
|
|
|
|
expectError: true,
|
|
},
|
|
"meets initial deposit, not enough balance - error": {
|
|
minDeposit: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdkmath.NewInt(baseDepositTestAmount))),
|
|
minInitialDepositRatio: baseDepositRatioDec,
|
|
initialDeposit: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdkmath.NewInt(meetsDepositValue))),
|
|
accountBalance: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdkmath.NewInt(meetsDepositValue-1))),
|
|
|
|
expectError: true,
|
|
},
|
|
"does not meet initial deposit and not enough balance - error": {
|
|
minDeposit: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdkmath.NewInt(baseDepositTestAmount))),
|
|
minInitialDepositRatio: baseDepositRatioDec,
|
|
initialDeposit: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdkmath.NewInt(meetsDepositValue-1))),
|
|
accountBalance: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdkmath.NewInt(meetsDepositValue-1))),
|
|
|
|
expectError: true,
|
|
},
|
|
}
|
|
|
|
for name, tc := range testcases {
|
|
suite.Run(name, func() {
|
|
// Setup
|
|
govKeeper, ctx := suite.govKeeper, suite.ctx
|
|
address, err := suite.acctKeeper.AddressCodec().BytesToString(simtestutil.AddTestAddrs(suite.bankKeeper, suite.stakingKeeper, ctx, 1, tc.accountBalance[0].Amount)[0])
|
|
suite.Require().NoError(err)
|
|
|
|
params := v1.DefaultParams()
|
|
params.MinDeposit = tc.minDeposit
|
|
params.MinInitialDepositRatio = tc.minInitialDepositRatio.String()
|
|
err = govKeeper.Params.Set(ctx, params)
|
|
suite.Require().NoError(err)
|
|
|
|
msg, err := v1.NewMsgSubmitProposal(TestProposal, tc.initialDeposit, address, "test", "Proposal", "description of proposal", v1.ProposalType_PROPOSAL_TYPE_STANDARD)
|
|
suite.Require().NoError(err)
|
|
|
|
// System under test
|
|
_, err = suite.msgSrvr.SubmitProposal(ctx, msg)
|
|
|
|
// Assertions
|
|
if tc.expectError {
|
|
suite.Require().Error(err)
|
|
return
|
|
}
|
|
suite.Require().NoError(err)
|
|
})
|
|
}
|
|
}
|
|
|
|
func (suite *KeeperTestSuite) TestMsgSudoExec() {
|
|
addr0Str, err := suite.acctKeeper.AddressCodec().BytesToString(suite.addrs[0])
|
|
suite.Require().NoError(err)
|
|
// setup for valid use case
|
|
params, _ := suite.govKeeper.Params.Get(suite.ctx)
|
|
minDeposit := params.MinDeposit
|
|
proposal, err := v1.NewMsgSubmitProposal([]sdk.Msg{}, minDeposit, addr0Str, "{\"title\":\"Proposal\", \"summary\":\"description of proposal\"}", "Proposal", "description of proposal", v1.ProposalType_PROPOSAL_TYPE_STANDARD)
|
|
suite.Require().NoError(err)
|
|
proposalResp, err := suite.msgSrvr.SubmitProposal(suite.ctx, proposal)
|
|
suite.Require().NoError(err)
|
|
|
|
// governance makes a random account vote on a proposal
|
|
// normally it isn't possible as governance isn't the signer.
|
|
// governance needs to sudo the vote.
|
|
validMsg := &v1.MsgSudoExec{Authority: suite.govKeeper.GetAuthority()}
|
|
_, err = validMsg.SetSudoedMsg(v1.NewMsgVote(addr0Str, proposalResp.ProposalId, v1.OptionYes, ""))
|
|
suite.Require().NoError(err)
|
|
|
|
invalidMsg := &v1.MsgSudoExec{Authority: suite.govKeeper.GetAuthority()}
|
|
_, err = invalidMsg.SetSudoedMsg(&types.Any{TypeUrl: "invalid"})
|
|
suite.Require().NoError(err)
|
|
|
|
testCases := []struct {
|
|
name string
|
|
input *v1.MsgSudoExec
|
|
expErrMsg string
|
|
}{
|
|
{
|
|
name: "empty msg",
|
|
input: nil,
|
|
expErrMsg: "sudo-ed message cannot be nil",
|
|
},
|
|
{
|
|
name: "empty sudoed msg",
|
|
input: &v1.MsgSudoExec{
|
|
Authority: suite.govKeeper.GetAuthority(),
|
|
Msg: nil,
|
|
},
|
|
expErrMsg: "sudo-ed message cannot be nil",
|
|
},
|
|
{
|
|
name: "invalid authority",
|
|
input: &v1.MsgSudoExec{
|
|
Authority: "invalid",
|
|
Msg: &types.Any{},
|
|
},
|
|
expErrMsg: "invalid authority",
|
|
},
|
|
{
|
|
name: "invalid msg (not proper sdk message)",
|
|
input: &v1.MsgSudoExec{
|
|
Authority: suite.govKeeper.GetAuthority(),
|
|
Msg: &types.Any{TypeUrl: "invalid"},
|
|
},
|
|
expErrMsg: "invalid sudo-ed message",
|
|
},
|
|
{
|
|
name: "invalid msg (not registered)",
|
|
input: invalidMsg,
|
|
expErrMsg: "unknown message",
|
|
},
|
|
{
|
|
name: "valid",
|
|
input: validMsg,
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
tc := tc
|
|
suite.Run(tc.name, func() {
|
|
_, err := suite.msgSrvr.SudoExec(suite.ctx, tc.input)
|
|
if tc.expErrMsg != "" {
|
|
suite.Require().Error(err)
|
|
suite.Require().Contains(err.Error(), tc.expErrMsg)
|
|
} else {
|
|
suite.Require().NoError(err)
|
|
}
|
|
})
|
|
}
|
|
}
|