Merge pull request #39 from antstalepresh/gov_split_vote

Governance split vote
This commit is contained in:
antstalepresh 2020-11-04 10:31:55 +10:00 committed by GitHub
commit 267148291c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
34 changed files with 1620 additions and 274 deletions

View File

@ -29,6 +29,16 @@ enum VoteOption {
VOTE_OPTION_NO_WITH_VETO = 4 [(gogoproto.enumvalue_customname) = "OptionNoWithVeto"]; VOTE_OPTION_NO_WITH_VETO = 4 [(gogoproto.enumvalue_customname) = "OptionNoWithVeto"];
} }
// WeightedVoteOption defines a unit of vote for vote split.
message WeightedVoteOption {
VoteOption option = 1;
string weight = 2 [
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
(gogoproto.nullable) = false,
(gogoproto.moretags) = "yaml:\"weight\""
];
}
// TextProposal defines a standard text proposal whose changes need to be // TextProposal defines a standard text proposal whose changes need to be
// manually updated in case of approval. // manually updated in case of approval.
message TextProposal { message TextProposal {
@ -121,7 +131,7 @@ message Vote {
uint64 proposal_id = 1 [(gogoproto.moretags) = "yaml:\"proposal_id\""]; uint64 proposal_id = 1 [(gogoproto.moretags) = "yaml:\"proposal_id\""];
string voter = 2; string voter = 2;
VoteOption option = 3; repeated WeightedVoteOption options = 3 [(gogoproto.nullable) = false];
} }
// DepositParams defines the params for deposits on governance proposals. // DepositParams defines the params for deposits on governance proposals.

View File

@ -17,6 +17,9 @@ service Msg {
// Vote defines a method to add a vote on a specific proposal. // Vote defines a method to add a vote on a specific proposal.
rpc Vote(MsgVote) returns (MsgVoteResponse); rpc Vote(MsgVote) returns (MsgVoteResponse);
// WeightedVote defines a method to add a weighted vote on a specific proposal.
rpc WeightedVote(MsgWeightedVote) returns (MsgWeightedVoteResponse);
// Deposit defines a method to add deposit on a specific proposal. // Deposit defines a method to add deposit on a specific proposal.
rpc Deposit(MsgDeposit) returns (MsgDepositResponse); rpc Deposit(MsgDeposit) returns (MsgDepositResponse);
} }
@ -55,9 +58,24 @@ message MsgVote {
VoteOption option = 3; VoteOption option = 3;
} }
// MsgVote defines a message to cast a vote.
message MsgWeightedVote {
option (gogoproto.equal) = false;
option (gogoproto.goproto_stringer) = false;
option (gogoproto.stringer) = false;
option (gogoproto.goproto_getters) = false;
uint64 proposal_id = 1 [(gogoproto.jsontag) = "proposal_id", (gogoproto.moretags) = "yaml:\"proposal_id\""];
string voter = 2;
repeated WeightedVoteOption options = 3 [(gogoproto.nullable) = false];
}
// MsgVoteResponse defines the Msg/Vote response type. // MsgVoteResponse defines the Msg/Vote response type.
message MsgVoteResponse {} message MsgVoteResponse {}
// MsgWeightedVoteResponse defines the MsgWeightedVote response type.
message MsgWeightedVoteResponse {}
// MsgDeposit defines a message to submit a deposit to an existing proposal. // MsgDeposit defines a message to submit a deposit to an existing proposal.
message MsgDeposit { message MsgDeposit {
option (gogoproto.equal) = false; option (gogoproto.equal) = false;

View File

@ -10,6 +10,7 @@ const (
DefaultWeightMsgFundCommunityPool int = 50 DefaultWeightMsgFundCommunityPool int = 50
DefaultWeightMsgDeposit int = 100 DefaultWeightMsgDeposit int = 100
DefaultWeightMsgVote int = 67 DefaultWeightMsgVote int = 67
DefaultWeightMsgWeightedVote int = 33
DefaultWeightMsgUnjail int = 100 DefaultWeightMsgUnjail int = 100
DefaultWeightMsgCreateValidator int = 100 DefaultWeightMsgCreateValidator int = 100
DefaultWeightMsgEditValidator int = 5 DefaultWeightMsgEditValidator int = 5

View File

@ -307,7 +307,7 @@ func TestProposalPassedEndblocker(t *testing.T) {
deposits := initialModuleAccCoins.Add(proposal.TotalDeposit...).Add(proposalCoins...) deposits := initialModuleAccCoins.Add(proposal.TotalDeposit...).Add(proposalCoins...)
require.True(t, moduleAccCoins.IsEqual(deposits)) require.True(t, moduleAccCoins.IsEqual(deposits))
err = app.GovKeeper.AddVote(ctx, proposal.ProposalId, addrs[0], types.OptionYes) err = app.GovKeeper.AddVote(ctx, proposal.ProposalId, addrs[0], types.NewNonSplitVoteOption(types.OptionYes))
require.NoError(t, err) require.NoError(t, err)
newHeader := ctx.BlockHeader() newHeader := ctx.BlockHeader()
@ -348,7 +348,7 @@ func TestEndBlockerProposalHandlerFailed(t *testing.T) {
handleAndCheck(t, gov.NewHandler(app.GovKeeper), ctx, newDepositMsg) handleAndCheck(t, gov.NewHandler(app.GovKeeper), ctx, newDepositMsg)
err = app.GovKeeper.AddVote(ctx, proposal.ProposalId, addrs[0], types.OptionYes) err = app.GovKeeper.AddVote(ctx, proposal.ProposalId, addrs[0], types.NewNonSplitVoteOption(types.OptionYes))
require.NoError(t, err) require.NoError(t, err)
newHeader := ctx.BlockHeader() newHeader := ctx.BlockHeader()

View File

@ -60,6 +60,18 @@ func (s *IntegrationTestSuite) SetupSuite() {
s.Require().NoError(err) s.Require().NoError(err)
_, err = s.network.WaitForHeight(1) _, err = s.network.WaitForHeight(1)
s.Require().NoError(err) s.Require().NoError(err)
// create a proposal3 with deposit
_, err = govtestutil.MsgSubmitProposal(val.ClientCtx, val.Address.String(),
"Text Proposal 3", "Where is the title!?", types.ProposalTypeText,
fmt.Sprintf("--%s=%s", cli.FlagDeposit, sdk.NewCoin(s.cfg.BondDenom, types.DefaultMinDepositTokens).String()))
s.Require().NoError(err)
_, err = s.network.WaitForHeight(1)
s.Require().NoError(err)
// vote for proposal3 as val
_, err = govtestutil.MsgVote(val.ClientCtx, val.Address.String(), "3", "yes=0.6,no=0.3,abstain=0.05,no_with_veto=0.05")
s.Require().NoError(err)
} }
func (s *IntegrationTestSuite) TearDownSuite() { func (s *IntegrationTestSuite) TearDownSuite() {
@ -448,7 +460,7 @@ func (s *IntegrationTestSuite) TestCmdGetProposals() {
var proposals types.QueryProposalsResponse var proposals types.QueryProposalsResponse
s.Require().NoError(clientCtx.JSONMarshaler.UnmarshalJSON(out.Bytes(), &proposals), out.String()) s.Require().NoError(clientCtx.JSONMarshaler.UnmarshalJSON(out.Bytes(), &proposals), out.String())
s.Require().Len(proposals.Proposals, 2) s.Require().Len(proposals.Proposals, 3)
} }
}) })
} }
@ -692,6 +704,7 @@ func (s *IntegrationTestSuite) TestCmdQueryVote() {
name string name string
args []string args []string
expectErr bool expectErr bool
expVoteOptions types.WeightedVoteOptions
}{ }{
{ {
"get vote of non existing proposal", "get vote of non existing proposal",
@ -700,6 +713,7 @@ func (s *IntegrationTestSuite) TestCmdQueryVote() {
val.Address.String(), val.Address.String(),
}, },
true, true,
types.NewNonSplitVoteOption(types.OptionYes),
}, },
{ {
"get vote by wrong voter", "get vote by wrong voter",
@ -708,6 +722,7 @@ func (s *IntegrationTestSuite) TestCmdQueryVote() {
"wrong address", "wrong address",
}, },
true, true,
types.NewNonSplitVoteOption(types.OptionYes),
}, },
{ {
"vote for valid proposal", "vote for valid proposal",
@ -717,6 +732,22 @@ func (s *IntegrationTestSuite) TestCmdQueryVote() {
fmt.Sprintf("--%s=json", tmcli.OutputFlag), fmt.Sprintf("--%s=json", tmcli.OutputFlag),
}, },
false, false,
types.NewNonSplitVoteOption(types.OptionYes),
},
{
"split vote for valid proposal",
[]string{
"3",
val.Address.String(),
fmt.Sprintf("--%s=json", tmcli.OutputFlag),
},
false,
types.WeightedVoteOptions{
types.WeightedVoteOption{Option: types.OptionYes, Weight: sdk.NewDecWithPrec(60, 2)},
types.WeightedVoteOption{Option: types.OptionNo, Weight: sdk.NewDecWithPrec(30, 2)},
types.WeightedVoteOption{Option: types.OptionAbstain, Weight: sdk.NewDecWithPrec(5, 2)},
types.WeightedVoteOption{Option: types.OptionNoWithVeto, Weight: sdk.NewDecWithPrec(5, 2)},
},
}, },
} }
@ -735,7 +766,11 @@ func (s *IntegrationTestSuite) TestCmdQueryVote() {
var vote types.Vote var vote types.Vote
s.Require().NoError(clientCtx.JSONMarshaler.UnmarshalJSON(out.Bytes(), &vote), out.String()) s.Require().NoError(clientCtx.JSONMarshaler.UnmarshalJSON(out.Bytes(), &vote), out.String())
s.Require().Equal(types.OptionYes, vote.Option) 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().True(option.Weight.Equal(vote.Options[i].Weight))
}
} }
}) })
} }
@ -801,6 +836,90 @@ func (s *IntegrationTestSuite) TestNewCmdVote() {
} }
} }
func (s *IntegrationTestSuite) TestNewCmdWeightedVote() {
val := s.network.Validators[0]
testCases := []struct {
name string
args []string
expectErr bool
expectedCode uint32
}{
{
"invalid vote",
[]string{},
true, 0,
},
{
"vote for invalid proposal",
[]string{
"10",
fmt.Sprintf("%s", "yes"),
fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()),
fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation),
fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock),
fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()),
},
false, 2,
},
{
"valid vote",
[]string{
"1",
fmt.Sprintf("%s", "yes"),
fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()),
fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation),
fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock),
fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()),
},
false, 0,
},
{
"invalid valid split vote string",
[]string{
"1",
fmt.Sprintf("%s", "yes/0.6,no/0.3,abstain/0.05,no_with_veto/0.05"),
fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()),
fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation),
fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock),
fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()),
},
true, 0,
},
{
"valid split vote",
[]string{
"1",
fmt.Sprintf("%s", "yes=0.6,no=0.3,abstain=0.05,no_with_veto=0.05"),
fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()),
fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation),
fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock),
fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()),
},
false, 0,
},
}
for _, tc := range testCases {
tc := tc
s.Run(tc.name, func() {
cmd := cli.NewCmdWeightedVote()
clientCtx := val.ClientCtx
var txResp sdk.TxResponse
out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args)
if tc.expectErr {
s.Require().Error(err)
} else {
s.Require().NoError(err)
s.Require().NoError(clientCtx.JSONMarshaler.UnmarshalJSON(out.Bytes(), &txResp), out.String())
s.Require().Equal(tc.expectedCode, txResp.Code, out.String())
}
})
}
}
func TestIntegrationTestSuite(t *testing.T) { func TestIntegrationTestSuite(t *testing.T) {
suite.Run(t, new(IntegrationTestSuite)) suite.Run(t, new(IntegrationTestSuite))
} }

View File

@ -68,6 +68,7 @@ func NewTxCmd(propCmds []*cobra.Command) *cobra.Command {
govTxCmd.AddCommand( govTxCmd.AddCommand(
NewCmdDeposit(), NewCmdDeposit(),
NewCmdVote(), NewCmdVote(),
NewCmdWeightedVote(),
cmdSubmitProp, cmdSubmitProp,
) )
@ -207,7 +208,6 @@ func NewCmdVote() *cobra.Command {
fmt.Sprintf(`Submit a vote for an active proposal. You can fmt.Sprintf(`Submit a vote for an active proposal. You can
find the proposal-id by running "%s query gov proposals". find the proposal-id by running "%s query gov proposals".
Example: Example:
$ %s tx gov vote 1 yes --from mykey $ %s tx gov vote 1 yes --from mykey
`, `,
@ -251,3 +251,57 @@ $ %s tx gov vote 1 yes --from mykey
return cmd return cmd
} }
// NewCmdWeightedVote implements creating a new weighted vote command.
func NewCmdWeightedVote() *cobra.Command {
cmd := &cobra.Command{
Use: "weighted-vote [proposal-id] [weighted-options]",
Args: cobra.ExactArgs(2),
Short: "Vote for an active proposal, options: yes/no/no_with_veto/abstain",
Long: strings.TrimSpace(
fmt.Sprintf(`Submit a vote for an active proposal. You can
find the proposal-id by running "%s query gov proposals".
Example:
$ %s tx gov vote 1 yes=0.6,no=0.3,abstain=0.05,no_with_veto=0.05 --from mykey
`,
version.AppName, version.AppName,
),
),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx := client.GetClientContextFromCmd(cmd)
clientCtx, err := client.ReadTxCommandFlags(clientCtx, cmd.Flags())
if err != nil {
return err
}
// Get voting address
from := clientCtx.GetFromAddress()
// validate that the proposal id is a uint
proposalID, err := strconv.ParseUint(args[0], 10, 64)
if err != nil {
return fmt.Errorf("proposal-id %s not a valid int, please input a valid proposal-id", args[0])
}
// Figure out which vote options user chose
options, err := types.WeightedVoteOptionsFromString(govutils.NormalizeWeightedVoteOptions(args[1]))
if err != nil {
return err
}
// Build vote message and run basic validation
msg := types.NewMsgWeightedVote(from, proposalID, options)
err = msg.ValidateBasic()
if err != nil {
return err
}
return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
},
}
flags.AddTxFlagsToCmd(cmd)
return cmd
}

View File

@ -57,6 +57,18 @@ func (s *IntegrationTestSuite) SetupSuite() {
s.Require().NoError(err) s.Require().NoError(err)
_, err = s.network.WaitForHeight(1) _, err = s.network.WaitForHeight(1)
s.Require().NoError(err) s.Require().NoError(err)
// create a proposal3 with deposit
_, err = govtestutil.MsgSubmitProposal(val.ClientCtx, val.Address.String(),
"Text Proposal 3", "Where is the title!?", types.ProposalTypeText,
fmt.Sprintf("--%s=%s", cli.FlagDeposit, sdk.NewCoin(s.cfg.BondDenom, types.DefaultMinDepositTokens).String()))
s.Require().NoError(err)
_, err = s.network.WaitForHeight(1)
s.Require().NoError(err)
// vote for proposal3 as val
_, err = govtestutil.MsgVote(val.ClientCtx, val.Address.String(), "3", "yes=0.6,no=0.3,abstain=0.05,no_with_veto=0.05")
s.Require().NoError(err)
} }
func (s *IntegrationTestSuite) TestGetProposalGRPC() { func (s *IntegrationTestSuite) TestGetProposalGRPC() {
@ -141,7 +153,7 @@ func (s *IntegrationTestSuite) TestGetProposalsGRPC() {
s.Require().Empty(proposals.Proposals) s.Require().Empty(proposals.Proposals)
} else { } else {
s.Require().NoError(err) s.Require().NoError(err)
s.Require().Len(proposals.Proposals, 2) s.Require().Len(proposals.Proposals, 3)
} }
}) })
} }
@ -156,26 +168,42 @@ func (s *IntegrationTestSuite) TestGetProposalVoteGRPC() {
name string name string
url string url string
expErr bool expErr bool
expVoteOptions types.WeightedVoteOptions
}{ }{
{ {
"empty proposal", "empty proposal",
fmt.Sprintf("%s/cosmos/gov/v1beta1/proposals/%s/votes/%s", val.APIAddress, "", voterAddressBase64), fmt.Sprintf("%s/cosmos/gov/v1beta1/proposals/%s/votes/%s", val.APIAddress, "", voterAddressBase64),
true, true,
types.NewNonSplitVoteOption(types.OptionYes),
}, },
{ {
"get non existing proposal", "get non existing proposal",
fmt.Sprintf("%s/cosmos/gov/v1beta1/proposals/%s/votes/%s", val.APIAddress, "10", voterAddressBase64), fmt.Sprintf("%s/cosmos/gov/v1beta1/proposals/%s/votes/%s", val.APIAddress, "10", voterAddressBase64),
true, true,
types.NewNonSplitVoteOption(types.OptionYes),
}, },
{ {
"get proposal with wrong voter address", "get proposal with wrong voter address",
fmt.Sprintf("%s/cosmos/gov/v1beta1/proposals/%s/votes/%s", val.APIAddress, "1", "wrongVoterAddress"), fmt.Sprintf("%s/cosmos/gov/v1beta1/proposals/%s/votes/%s", val.APIAddress, "1", "wrongVoterAddress"),
true, true,
types.NewNonSplitVoteOption(types.OptionYes),
}, },
{ {
"get proposal with id", "get proposal with id",
fmt.Sprintf("%s/cosmos/gov/v1beta1/proposals/%s/votes/%s", val.APIAddress, "1", voterAddressBase64), fmt.Sprintf("%s/cosmos/gov/v1beta1/proposals/%s/votes/%s", val.APIAddress, "1", voterAddressBase64),
false, false,
types.NewNonSplitVoteOption(types.OptionYes),
},
{
"get proposal with id for split vote",
fmt.Sprintf("%s/cosmos/gov/v1beta1/proposals/%s/votes/%s", val.APIAddress, "3", voterAddressBase64),
false,
types.WeightedVoteOptions{
types.WeightedVoteOption{Option: types.OptionYes, Weight: sdk.NewDecWithPrec(60, 2)},
types.WeightedVoteOption{Option: types.OptionNo, Weight: sdk.NewDecWithPrec(30, 2)},
types.WeightedVoteOption{Option: types.OptionAbstain, Weight: sdk.NewDecWithPrec(5, 2)},
types.WeightedVoteOption{Option: types.OptionNoWithVeto, Weight: sdk.NewDecWithPrec(5, 2)},
},
}, },
} }
@ -193,7 +221,11 @@ func (s *IntegrationTestSuite) TestGetProposalVoteGRPC() {
} else { } else {
s.Require().NoError(err) s.Require().NoError(err)
s.Require().NotEmpty(vote.Vote) s.Require().NotEmpty(vote.Vote)
s.Require().Equal(types.OptionYes, vote.Vote.Option) s.Require().Equal(len(vote.Vote.Options), len(tc.expVoteOptions))
for i, option := range tc.expVoteOptions {
s.Require().Equal(option.Option, vote.Vote.Options[i].Option)
s.Require().True(option.Weight.Equal(vote.Vote.Options[i].Weight))
}
} }
}) })
} }

View File

@ -58,3 +58,10 @@ type VoteReq struct {
Voter sdk.AccAddress `json:"voter" yaml:"voter"` // address of the voter Voter sdk.AccAddress `json:"voter" yaml:"voter"` // address of the voter
Option string `json:"option" yaml:"option"` // option from OptionSet chosen by the voter Option string `json:"option" yaml:"option"` // option from OptionSet chosen by the voter
} }
// WeightedVoteReq defines the properties of a vote request's body.
type WeightedVoteReq struct {
BaseReq rest.BaseReq `json:"base_req" yaml:"base_req"`
Voter sdk.AccAddress `json:"voter" yaml:"voter"` // address of the voter
Options string `json:"options" yaml:"options"` // weighted options from OptionSet chosen by the voter
}

View File

@ -22,6 +22,7 @@ func registerTxHandlers(clientCtx client.Context, r *mux.Router, phs []ProposalR
r.HandleFunc("/gov/proposals", newPostProposalHandlerFn(clientCtx)).Methods("POST") r.HandleFunc("/gov/proposals", newPostProposalHandlerFn(clientCtx)).Methods("POST")
r.HandleFunc(fmt.Sprintf("/gov/proposals/{%s}/deposits", RestProposalID), newDepositHandlerFn(clientCtx)).Methods("POST") r.HandleFunc(fmt.Sprintf("/gov/proposals/{%s}/deposits", RestProposalID), newDepositHandlerFn(clientCtx)).Methods("POST")
r.HandleFunc(fmt.Sprintf("/gov/proposals/{%s}/votes", RestProposalID), newVoteHandlerFn(clientCtx)).Methods("POST") r.HandleFunc(fmt.Sprintf("/gov/proposals/{%s}/votes", RestProposalID), newVoteHandlerFn(clientCtx)).Methods("POST")
r.HandleFunc(fmt.Sprintf("/gov/proposals/{%s}/weightedvotes", RestProposalID), newWeightedVoteHandlerFn(clientCtx)).Methods("POST")
} }
func newPostProposalHandlerFn(clientCtx client.Context) http.HandlerFunc { func newPostProposalHandlerFn(clientCtx client.Context) http.HandlerFunc {
@ -125,3 +126,44 @@ func newVoteHandlerFn(clientCtx client.Context) http.HandlerFunc {
tx.WriteGeneratedTxResponse(clientCtx, w, req.BaseReq, msg) tx.WriteGeneratedTxResponse(clientCtx, w, req.BaseReq, msg)
} }
} }
func newWeightedVoteHandlerFn(clientCtx client.Context) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
strProposalID := vars[RestProposalID]
if len(strProposalID) == 0 {
rest.WriteErrorResponse(w, http.StatusBadRequest, "proposalId required but not specified")
return
}
proposalID, ok := rest.ParseUint64OrReturnBadRequest(w, strProposalID)
if !ok {
return
}
var req WeightedVoteReq
if !rest.ReadRESTReq(w, r, clientCtx.LegacyAmino, &req) {
return
}
req.BaseReq = req.BaseReq.Sanitize()
if !req.BaseReq.ValidateBasic(w) {
return
}
// Figure out which vote options user chose
options, err := types.WeightedVoteOptionsFromString(gcutils.NormalizeWeightedVoteOptions(req.Options))
if rest.CheckBadRequestError(w, err) {
return
}
// create the message
msg := types.NewMsgWeightedVote(req.Voter, proposalID, options)
if rest.CheckBadRequestError(w, msg.ValidateBasic()) {
return
}
tx.WriteGeneratedTxResponse(clientCtx, w, req.BaseReq, msg)
}
}

View File

@ -41,5 +41,5 @@ func MsgVote(clientCtx client.Context, from, id, vote string, extraArgs ...strin
args = append(args, extraArgs...) args = append(args, extraArgs...)
return clitestutil.ExecTestCLICmd(clientCtx, govcli.NewCmdVote(), args) return clitestutil.ExecTestCLICmd(clientCtx, govcli.NewCmdWeightedVote(), args)
} }

View File

@ -80,6 +80,7 @@ func QueryVotesByTxQuery(clientCtx client.Context, params types.QueryProposalVot
var ( var (
events = []string{ events = []string{
fmt.Sprintf("%s.%s='%s'", sdk.EventTypeMessage, sdk.AttributeKeyAction, types.TypeMsgVote), fmt.Sprintf("%s.%s='%s'", sdk.EventTypeMessage, sdk.AttributeKeyAction, types.TypeMsgVote),
fmt.Sprintf("%s.%s='%s'", sdk.EventTypeMessage, sdk.AttributeKeyAction, types.TypeMsgWeightedVote),
fmt.Sprintf("%s.%s='%s'", types.EventTypeProposalVote, types.AttributeKeyProposalID, []byte(fmt.Sprintf("%d", params.ProposalID))), fmt.Sprintf("%s.%s='%s'", types.EventTypeProposalVote, types.AttributeKeyProposalID, []byte(fmt.Sprintf("%d", params.ProposalID))),
} }
votes []types.Vote votes []types.Vote
@ -101,7 +102,15 @@ func QueryVotesByTxQuery(clientCtx client.Context, params types.QueryProposalVot
votes = append(votes, types.Vote{ votes = append(votes, types.Vote{
Voter: voteMsg.Voter, Voter: voteMsg.Voter,
ProposalId: params.ProposalID, ProposalId: params.ProposalID,
Option: voteMsg.Option, Options: types.NewNonSplitVoteOption(voteMsg.Option),
})
} else if msg.Type() == types.TypeMsgWeightedVote {
voteMsg := msg.(*types.MsgWeightedVote)
votes = append(votes, types.Vote{
Voter: voteMsg.Voter,
ProposalId: params.ProposalID,
Options: voteMsg.Options,
}) })
} }
} }
@ -148,7 +157,22 @@ func QueryVoteByTxQuery(clientCtx client.Context, params types.QueryVoteParams)
vote := types.Vote{ vote := types.Vote{
Voter: voteMsg.Voter, Voter: voteMsg.Voter,
ProposalId: params.ProposalID, ProposalId: params.ProposalID,
Option: voteMsg.Option, Options: types.NewNonSplitVoteOption(voteMsg.Option),
}
bz, err := clientCtx.JSONMarshaler.MarshalJSON(&vote)
if err != nil {
return nil, err
}
return bz, nil
} else if msg.Type() == types.TypeMsgWeightedVote {
voteMsg := msg.(*types.MsgWeightedVote)
vote := types.Vote{
Voter: voteMsg.Voter,
ProposalId: params.ProposalID,
Options: voteMsg.Options,
} }
bz, err := clientCtx.JSONMarshaler.MarshalJSON(&vote) bz, err := clientCtx.JSONMarshaler.MarshalJSON(&vote)

View File

@ -75,7 +75,7 @@ func TestGetPaginatedVotes(t *testing.T) {
} }
acc2Msgs := []sdk.Msg{ acc2Msgs := []sdk.Msg{
types.NewMsgVote(acc2, 0, types.OptionYes), types.NewMsgVote(acc2, 0, types.OptionYes),
types.NewMsgVote(acc2, 0, types.OptionYes), types.NewMsgWeightedVote(acc2, 0, types.NewNonSplitVoteOption(types.OptionYes)),
} }
for _, tc := range []testCase{ for _, tc := range []testCase{
{ {
@ -87,8 +87,8 @@ func TestGetPaginatedVotes(t *testing.T) {
acc2Msgs[:1], acc2Msgs[:1],
}, },
votes: []types.Vote{ votes: []types.Vote{
types.NewVote(0, acc1, types.OptionYes), types.NewVote(0, acc1, types.NewNonSplitVoteOption(types.OptionYes)),
types.NewVote(0, acc2, types.OptionYes)}, types.NewVote(0, acc2, types.NewNonSplitVoteOption(types.OptionYes))},
}, },
{ {
description: "2MsgPerTx1Chunk", description: "2MsgPerTx1Chunk",
@ -99,8 +99,9 @@ func TestGetPaginatedVotes(t *testing.T) {
acc2Msgs, acc2Msgs,
}, },
votes: []types.Vote{ votes: []types.Vote{
types.NewVote(0, acc1, types.OptionYes), types.NewVote(0, acc1, types.NewNonSplitVoteOption(types.OptionYes)),
types.NewVote(0, acc1, types.OptionYes)}, types.NewVote(0, acc1, types.NewNonSplitVoteOption(types.OptionYes)),
},
}, },
{ {
description: "2MsgPerTx2Chunk", description: "2MsgPerTx2Chunk",
@ -111,8 +112,9 @@ func TestGetPaginatedVotes(t *testing.T) {
acc2Msgs, acc2Msgs,
}, },
votes: []types.Vote{ votes: []types.Vote{
types.NewVote(0, acc2, types.OptionYes), types.NewVote(0, acc2, types.NewNonSplitVoteOption(types.OptionYes)),
types.NewVote(0, acc2, types.OptionYes)}, types.NewVote(0, acc2, types.NewNonSplitVoteOption(types.OptionYes)),
},
}, },
{ {
description: "IncompleteSearchTx", description: "IncompleteSearchTx",
@ -121,7 +123,7 @@ func TestGetPaginatedVotes(t *testing.T) {
msgs: [][]sdk.Msg{ msgs: [][]sdk.Msg{
acc1Msgs[:1], acc1Msgs[:1],
}, },
votes: []types.Vote{types.NewVote(0, acc1, types.OptionYes)}, votes: []types.Vote{types.NewVote(0, acc1, types.NewNonSplitVoteOption(types.OptionYes))},
}, },
{ {
description: "InvalidPage", description: "InvalidPage",

View File

@ -1,6 +1,10 @@
package utils package utils
import "github.com/cosmos/cosmos-sdk/x/gov/types" import (
"strings"
"github.com/cosmos/cosmos-sdk/x/gov/types"
)
// NormalizeVoteOption - normalize user specified vote option // NormalizeVoteOption - normalize user specified vote option
func NormalizeVoteOption(option string) string { func NormalizeVoteOption(option string) string {
@ -22,6 +26,20 @@ func NormalizeVoteOption(option string) string {
} }
} }
// NormalizeWeightedVoteOptions - normalize normalize vote options param string
func NormalizeWeightedVoteOptions(options string) string {
newOptions := []string{}
for _, option := range strings.Split(options, ",") {
fields := strings.Split(option, "=")
fields[0] = NormalizeVoteOption(fields[0])
if len(fields) < 2 {
fields = append(fields, "1")
}
newOptions = append(newOptions, strings.Join(fields, "="))
}
return strings.Join(newOptions, ",")
}
//NormalizeProposalType - normalize user specified proposal type //NormalizeProposalType - normalize user specified proposal type
func NormalizeProposalType(proposalType string) string { func NormalizeProposalType(proposalType string) string {
switch proposalType { switch proposalType {

View File

@ -27,6 +27,10 @@ func NewHandler(k keeper.Keeper) sdk.Handler {
res, err := msgServer.Vote(sdk.WrapSDKContext(ctx), msg) res, err := msgServer.Vote(sdk.WrapSDKContext(ctx), msg)
return sdk.WrapServiceResult(ctx, res, err) return sdk.WrapServiceResult(ctx, res, err)
case *types.MsgWeightedVote:
res, err := msgServer.WeightedVote(sdk.WrapSDKContext(ctx), msg)
return sdk.WrapServiceResult(ctx, res, err)
default: default:
return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized %s message type: %T", types.ModuleName, msg) return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized %s message type: %T", types.ModuleName, msg)
} }

View File

@ -183,7 +183,7 @@ func (suite *KeeperTestSuite) TestGRPCQueryProposals() {
func() { func() {
testProposals[1].Status = types.StatusVotingPeriod testProposals[1].Status = types.StatusVotingPeriod
app.GovKeeper.SetProposal(ctx, testProposals[1]) app.GovKeeper.SetProposal(ctx, testProposals[1])
suite.Require().NoError(app.GovKeeper.AddVote(ctx, testProposals[1].ProposalId, addrs[0], types.OptionAbstain)) suite.Require().NoError(app.GovKeeper.AddVote(ctx, testProposals[1].ProposalId, addrs[0], types.NewNonSplitVoteOption(types.OptionAbstain)))
req = &types.QueryProposalsRequest{ req = &types.QueryProposalsRequest{
Voter: addrs[0].String(), Voter: addrs[0].String(),
@ -291,14 +291,14 @@ func (suite *KeeperTestSuite) TestGRPCQueryVote() {
func() { func() {
proposal.Status = types.StatusVotingPeriod proposal.Status = types.StatusVotingPeriod
app.GovKeeper.SetProposal(ctx, proposal) app.GovKeeper.SetProposal(ctx, proposal)
suite.Require().NoError(app.GovKeeper.AddVote(ctx, proposal.ProposalId, addrs[0], types.OptionAbstain)) suite.Require().NoError(app.GovKeeper.AddVote(ctx, proposal.ProposalId, addrs[0], types.NewNonSplitVoteOption(types.OptionAbstain)))
req = &types.QueryVoteRequest{ req = &types.QueryVoteRequest{
ProposalId: proposal.ProposalId, ProposalId: proposal.ProposalId,
Voter: addrs[0].String(), Voter: addrs[0].String(),
} }
expRes = &types.QueryVoteResponse{Vote: types.NewVote(proposal.ProposalId, addrs[0], types.OptionAbstain)} expRes = &types.QueryVoteResponse{Vote: types.NewVote(proposal.ProposalId, addrs[0], types.NewNonSplitVoteOption(types.OptionAbstain))}
}, },
true, true,
}, },
@ -395,15 +395,15 @@ func (suite *KeeperTestSuite) TestGRPCQueryVotes() {
app.GovKeeper.SetProposal(ctx, proposal) app.GovKeeper.SetProposal(ctx, proposal)
votes = []types.Vote{ votes = []types.Vote{
{proposal.ProposalId, addrs[0].String(), types.OptionAbstain}, {proposal.ProposalId, addrs[0].String(), types.NewNonSplitVoteOption(types.OptionAbstain)},
{proposal.ProposalId, addrs[1].String(), types.OptionYes}, {proposal.ProposalId, addrs[1].String(), types.NewNonSplitVoteOption(types.OptionYes)},
} }
accAddr1, err1 := sdk.AccAddressFromBech32(votes[0].Voter) accAddr1, err1 := sdk.AccAddressFromBech32(votes[0].Voter)
accAddr2, err2 := sdk.AccAddressFromBech32(votes[1].Voter) accAddr2, err2 := sdk.AccAddressFromBech32(votes[1].Voter)
suite.Require().NoError(err1) suite.Require().NoError(err1)
suite.Require().NoError(err2) suite.Require().NoError(err2)
suite.Require().NoError(app.GovKeeper.AddVote(ctx, proposal.ProposalId, accAddr1, votes[0].Option)) suite.Require().NoError(app.GovKeeper.AddVote(ctx, proposal.ProposalId, accAddr1, votes[0].Options))
suite.Require().NoError(app.GovKeeper.AddVote(ctx, proposal.ProposalId, accAddr2, votes[1].Option)) suite.Require().NoError(app.GovKeeper.AddVote(ctx, proposal.ProposalId, accAddr2, votes[1].Options))
req = &types.QueryVotesRequest{ req = &types.QueryVotesRequest{
ProposalId: proposal.ProposalId, ProposalId: proposal.ProposalId,
@ -769,9 +769,9 @@ func (suite *KeeperTestSuite) TestGRPCQueryTally() {
proposal.Status = types.StatusVotingPeriod proposal.Status = types.StatusVotingPeriod
app.GovKeeper.SetProposal(ctx, proposal) app.GovKeeper.SetProposal(ctx, proposal)
suite.Require().NoError(app.GovKeeper.AddVote(ctx, proposal.ProposalId, addrs[0], types.OptionYes)) suite.Require().NoError(app.GovKeeper.AddVote(ctx, proposal.ProposalId, addrs[0], types.NewNonSplitVoteOption(types.OptionYes)))
suite.Require().NoError(app.GovKeeper.AddVote(ctx, proposal.ProposalId, addrs[1], types.OptionYes)) suite.Require().NoError(app.GovKeeper.AddVote(ctx, proposal.ProposalId, addrs[1], types.NewNonSplitVoteOption(types.OptionYes)))
suite.Require().NoError(app.GovKeeper.AddVote(ctx, proposal.ProposalId, addrs[2], types.OptionYes)) suite.Require().NoError(app.GovKeeper.AddVote(ctx, proposal.ProposalId, addrs[2], types.NewNonSplitVoteOption(types.OptionYes)))
req = &types.QueryTallyResultRequest{ProposalId: proposal.ProposalId} req = &types.QueryTallyResultRequest{ProposalId: proposal.ProposalId}

View File

@ -64,7 +64,7 @@ func (k msgServer) Vote(goCtx context.Context, msg *types.MsgVote) (*types.MsgVo
if accErr != nil { if accErr != nil {
return nil, accErr return nil, accErr
} }
err := k.Keeper.AddVote(ctx, msg.ProposalId, accAddr, msg.Option) err := k.Keeper.AddVote(ctx, msg.ProposalId, accAddr, types.NewNonSplitVoteOption(msg.Option))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -88,6 +88,36 @@ func (k msgServer) Vote(goCtx context.Context, msg *types.MsgVote) (*types.MsgVo
return &types.MsgVoteResponse{}, nil return &types.MsgVoteResponse{}, nil
} }
func (k msgServer) WeightedVote(goCtx context.Context, msg *types.MsgWeightedVote) (*types.MsgWeightedVoteResponse, error) {
ctx := sdk.UnwrapSDKContext(goCtx)
accAddr, accErr := sdk.AccAddressFromBech32(msg.Voter)
if accErr != nil {
return nil, accErr
}
err := k.Keeper.AddVote(ctx, msg.ProposalId, accAddr, msg.Options)
if err != nil {
return nil, err
}
defer telemetry.IncrCounterWithLabels(
[]string{types.ModuleName, "vote"},
1,
[]metrics.Label{
telemetry.NewLabel("proposal_id", strconv.Itoa(int(msg.ProposalId))),
},
)
ctx.EventManager().EmitEvent(
sdk.NewEvent(
sdk.EventTypeMessage,
sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory),
sdk.NewAttribute(sdk.AttributeKeySender, msg.Voter),
),
)
return &types.MsgWeightedVoteResponse{}, nil
}
func (k msgServer) Deposit(goCtx context.Context, msg *types.MsgDeposit) (*types.MsgDepositResponse, error) { func (k msgServer) Deposit(goCtx context.Context, msg *types.MsgDeposit) (*types.MsgDepositResponse, error) {
ctx := sdk.UnwrapSDKContext(goCtx) ctx := sdk.UnwrapSDKContext(goCtx)
accAddr, err := sdk.AccAddressFromBech32(msg.Depositor) accAddr, err := sdk.AccAddressFromBech32(msg.Depositor)

View File

@ -101,7 +101,7 @@ func TestGetProposalsFiltered(t *testing.T) {
if i%2 == 0 { if i%2 == 0 {
d := types.NewDeposit(proposalID, addr1, nil) d := types.NewDeposit(proposalID, addr1, nil)
v := types.NewVote(proposalID, addr1, types.OptionYes) v := types.NewVote(proposalID, addr1, types.NewNonSplitVoteOption(types.OptionYes))
app.GovKeeper.SetDeposit(ctx, d) app.GovKeeper.SetDeposit(ctx, d)
app.GovKeeper.SetVote(ctx, v) app.GovKeeper.SetVote(ctx, v)
} }

View File

@ -251,13 +251,13 @@ func TestQueries(t *testing.T) {
require.Equal(t, proposal3, proposals[1]) require.Equal(t, proposal3, proposals[1])
// Addrs[0] votes on proposals #2 & #3 // Addrs[0] votes on proposals #2 & #3
vote1 := types.NewVote(proposal2.ProposalId, TestAddrs[0], types.OptionYes) vote1 := types.NewVote(proposal2.ProposalId, TestAddrs[0], types.NewNonSplitVoteOption(types.OptionYes))
vote2 := types.NewVote(proposal3.ProposalId, TestAddrs[0], types.OptionYes) vote2 := types.NewVote(proposal3.ProposalId, TestAddrs[0], types.NewNonSplitVoteOption(types.OptionYes))
app.GovKeeper.SetVote(ctx, vote1) app.GovKeeper.SetVote(ctx, vote1)
app.GovKeeper.SetVote(ctx, vote2) app.GovKeeper.SetVote(ctx, vote2)
// Addrs[1] votes on proposal #3 // Addrs[1] votes on proposal #3
vote3 := types.NewVote(proposal3.ProposalId, TestAddrs[1], types.OptionYes) vote3 := types.NewVote(proposal3.ProposalId, TestAddrs[1], types.NewNonSplitVoteOption(types.OptionYes))
app.GovKeeper.SetVote(ctx, vote3) app.GovKeeper.SetVote(ctx, vote3)
// Test query voted by TestAddrs[0] // Test query voted by TestAddrs[0]
@ -323,7 +323,7 @@ func TestPaginatedVotesQuery(t *testing.T) {
vote := types.Vote{ vote := types.Vote{
ProposalId: proposal.ProposalId, ProposalId: proposal.ProposalId,
Voter: addr.String(), Voter: addr.String(),
Option: types.OptionYes, Options: types.NewNonSplitVoteOption(types.OptionYes),
} }
votes[i] = vote votes[i] = vote
app.GovKeeper.SetVote(ctx, vote) app.GovKeeper.SetVote(ctx, vote)

View File

@ -27,7 +27,7 @@ func (keeper Keeper) Tally(ctx sdk.Context, proposal types.Proposal) (passes boo
validator.GetBondedTokens(), validator.GetBondedTokens(),
validator.GetDelegatorShares(), validator.GetDelegatorShares(),
sdk.ZeroDec(), sdk.ZeroDec(),
types.OptionEmpty, types.WeightedVoteOptions{},
) )
return false return false
@ -43,7 +43,7 @@ func (keeper Keeper) Tally(ctx sdk.Context, proposal types.Proposal) (passes boo
valAddrStr := sdk.ValAddress(voter.Bytes()).String() valAddrStr := sdk.ValAddress(voter.Bytes()).String()
if val, ok := currValidators[valAddrStr]; ok { if val, ok := currValidators[valAddrStr]; ok {
val.Vote = vote.Option val.Vote = vote.Options
currValidators[valAddrStr] = val currValidators[valAddrStr] = val
} }
@ -60,7 +60,10 @@ func (keeper Keeper) Tally(ctx sdk.Context, proposal types.Proposal) (passes boo
// delegation shares * bonded / total shares // delegation shares * bonded / total shares
votingPower := delegation.GetShares().MulInt(val.BondedTokens).Quo(val.DelegatorShares) votingPower := delegation.GetShares().MulInt(val.BondedTokens).Quo(val.DelegatorShares)
results[vote.Option] = results[vote.Option].Add(votingPower) for _, option := range vote.Options {
subPower := votingPower.Mul(option.Weight)
results[option.Option] = results[option.Option].Add(subPower)
}
totalVotingPower = totalVotingPower.Add(votingPower) totalVotingPower = totalVotingPower.Add(votingPower)
} }
@ -73,14 +76,17 @@ func (keeper Keeper) Tally(ctx sdk.Context, proposal types.Proposal) (passes boo
// iterate over the validators again to tally their voting power // iterate over the validators again to tally their voting power
for _, val := range currValidators { for _, val := range currValidators {
if val.Vote == types.OptionEmpty { if len(val.Vote) == 0 {
continue continue
} }
sharesAfterDeductions := val.DelegatorShares.Sub(val.DelegatorDeductions) sharesAfterDeductions := val.DelegatorShares.Sub(val.DelegatorDeductions)
votingPower := sharesAfterDeductions.MulInt(val.BondedTokens).Quo(val.DelegatorShares) votingPower := sharesAfterDeductions.MulInt(val.BondedTokens).Quo(val.DelegatorShares)
results[val.Vote] = results[val.Vote].Add(votingPower) for _, option := range val.Vote {
subPower := votingPower.Mul(option.Weight)
results[option.Option] = results[option.Option].Add(subPower)
}
totalVotingPower = totalVotingPower.Add(votingPower) totalVotingPower = totalVotingPower.Add(votingPower)
} }

View File

@ -50,7 +50,7 @@ func TestTallyNoQuorum(t *testing.T) {
proposal.Status = types.StatusVotingPeriod proposal.Status = types.StatusVotingPeriod
app.GovKeeper.SetProposal(ctx, proposal) app.GovKeeper.SetProposal(ctx, proposal)
err = app.GovKeeper.AddVote(ctx, proposalID, addrs[0], types.OptionYes) err = app.GovKeeper.AddVote(ctx, proposalID, addrs[0], types.NewNonSplitVoteOption(types.OptionYes))
require.Nil(t, err) require.Nil(t, err)
proposal, ok := app.GovKeeper.GetProposal(ctx, proposalID) proposal, ok := app.GovKeeper.GetProposal(ctx, proposalID)
@ -73,9 +73,9 @@ func TestTallyOnlyValidatorsAllYes(t *testing.T) {
proposal.Status = types.StatusVotingPeriod proposal.Status = types.StatusVotingPeriod
app.GovKeeper.SetProposal(ctx, proposal) app.GovKeeper.SetProposal(ctx, proposal)
require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[0], types.OptionYes)) require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[0], types.NewNonSplitVoteOption(types.OptionYes)))
require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[1], types.OptionYes)) require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[1], types.NewNonSplitVoteOption(types.OptionYes)))
require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[2], types.OptionYes)) require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[2], types.NewNonSplitVoteOption(types.OptionYes)))
proposal, ok := app.GovKeeper.GetProposal(ctx, proposalID) proposal, ok := app.GovKeeper.GetProposal(ctx, proposalID)
require.True(t, ok) require.True(t, ok)
@ -99,8 +99,8 @@ func TestTallyOnlyValidators51No(t *testing.T) {
proposal.Status = types.StatusVotingPeriod proposal.Status = types.StatusVotingPeriod
app.GovKeeper.SetProposal(ctx, proposal) app.GovKeeper.SetProposal(ctx, proposal)
require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, valAccAddrs[0], types.OptionYes)) require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, valAccAddrs[0], types.NewNonSplitVoteOption(types.OptionYes)))
require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, valAccAddrs[1], types.OptionNo)) require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, valAccAddrs[1], types.NewNonSplitVoteOption(types.OptionNo)))
proposal, ok := app.GovKeeper.GetProposal(ctx, proposalID) proposal, ok := app.GovKeeper.GetProposal(ctx, proposalID)
require.True(t, ok) require.True(t, ok)
@ -123,8 +123,8 @@ func TestTallyOnlyValidators51Yes(t *testing.T) {
proposal.Status = types.StatusVotingPeriod proposal.Status = types.StatusVotingPeriod
app.GovKeeper.SetProposal(ctx, proposal) app.GovKeeper.SetProposal(ctx, proposal)
require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, valAccAddrs[0], types.OptionNo)) require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, valAccAddrs[0], types.NewNonSplitVoteOption(types.OptionNo)))
require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, valAccAddrs[1], types.OptionYes)) require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, valAccAddrs[1], types.NewNonSplitVoteOption(types.OptionYes)))
proposal, ok := app.GovKeeper.GetProposal(ctx, proposalID) proposal, ok := app.GovKeeper.GetProposal(ctx, proposalID)
require.True(t, ok) require.True(t, ok)
@ -148,9 +148,9 @@ func TestTallyOnlyValidatorsVetoed(t *testing.T) {
proposal.Status = types.StatusVotingPeriod proposal.Status = types.StatusVotingPeriod
app.GovKeeper.SetProposal(ctx, proposal) app.GovKeeper.SetProposal(ctx, proposal)
require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, valAccAddrs[0], types.OptionYes)) require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, valAccAddrs[0], types.NewNonSplitVoteOption(types.OptionYes)))
require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, valAccAddrs[1], types.OptionYes)) require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, valAccAddrs[1], types.NewNonSplitVoteOption(types.OptionYes)))
require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, valAccAddrs[2], types.OptionNoWithVeto)) require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, valAccAddrs[2], types.NewNonSplitVoteOption(types.OptionNoWithVeto)))
proposal, ok := app.GovKeeper.GetProposal(ctx, proposalID) proposal, ok := app.GovKeeper.GetProposal(ctx, proposalID)
require.True(t, ok) require.True(t, ok)
@ -174,9 +174,9 @@ func TestTallyOnlyValidatorsAbstainPasses(t *testing.T) {
proposal.Status = types.StatusVotingPeriod proposal.Status = types.StatusVotingPeriod
app.GovKeeper.SetProposal(ctx, proposal) app.GovKeeper.SetProposal(ctx, proposal)
require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, valAccAddrs[0], types.OptionAbstain)) require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, valAccAddrs[0], types.NewNonSplitVoteOption(types.OptionAbstain)))
require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, valAccAddrs[1], types.OptionNo)) require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, valAccAddrs[1], types.NewNonSplitVoteOption(types.OptionNo)))
require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, valAccAddrs[2], types.OptionYes)) require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, valAccAddrs[2], types.NewNonSplitVoteOption(types.OptionYes)))
proposal, ok := app.GovKeeper.GetProposal(ctx, proposalID) proposal, ok := app.GovKeeper.GetProposal(ctx, proposalID)
require.True(t, ok) require.True(t, ok)
@ -200,9 +200,9 @@ func TestTallyOnlyValidatorsAbstainFails(t *testing.T) {
proposal.Status = types.StatusVotingPeriod proposal.Status = types.StatusVotingPeriod
app.GovKeeper.SetProposal(ctx, proposal) app.GovKeeper.SetProposal(ctx, proposal)
require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, valAccAddrs[0], types.OptionAbstain)) require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, valAccAddrs[0], types.NewNonSplitVoteOption(types.OptionAbstain)))
require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, valAccAddrs[1], types.OptionYes)) require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, valAccAddrs[1], types.NewNonSplitVoteOption(types.OptionYes)))
require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, valAccAddrs[2], types.OptionNo)) require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, valAccAddrs[2], types.NewNonSplitVoteOption(types.OptionNo)))
proposal, ok := app.GovKeeper.GetProposal(ctx, proposalID) proposal, ok := app.GovKeeper.GetProposal(ctx, proposalID)
require.True(t, ok) require.True(t, ok)
@ -227,8 +227,8 @@ func TestTallyOnlyValidatorsNonVoter(t *testing.T) {
proposal.Status = types.StatusVotingPeriod proposal.Status = types.StatusVotingPeriod
app.GovKeeper.SetProposal(ctx, proposal) app.GovKeeper.SetProposal(ctx, proposal)
require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, valAccAddr1, types.OptionYes)) require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, valAccAddr1, types.NewNonSplitVoteOption(types.OptionYes)))
require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, valAccAddr2, types.OptionNo)) require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, valAccAddr2, types.NewNonSplitVoteOption(types.OptionNo)))
proposal, ok := app.GovKeeper.GetProposal(ctx, proposalID) proposal, ok := app.GovKeeper.GetProposal(ctx, proposalID)
require.True(t, ok) require.True(t, ok)
@ -261,10 +261,10 @@ func TestTallyDelgatorOverride(t *testing.T) {
proposal.Status = types.StatusVotingPeriod proposal.Status = types.StatusVotingPeriod
app.GovKeeper.SetProposal(ctx, proposal) app.GovKeeper.SetProposal(ctx, proposal)
require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[1], types.OptionYes)) require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[1], types.NewNonSplitVoteOption(types.OptionYes)))
require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[2], types.OptionYes)) require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[2], types.NewNonSplitVoteOption(types.OptionYes)))
require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[3], types.OptionYes)) require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[3], types.NewNonSplitVoteOption(types.OptionYes)))
require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[4], types.OptionNo)) require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[4], types.NewNonSplitVoteOption(types.OptionNo)))
proposal, ok := app.GovKeeper.GetProposal(ctx, proposalID) proposal, ok := app.GovKeeper.GetProposal(ctx, proposalID)
require.True(t, ok) require.True(t, ok)
@ -297,9 +297,9 @@ func TestTallyDelgatorInherit(t *testing.T) {
proposal.Status = types.StatusVotingPeriod proposal.Status = types.StatusVotingPeriod
app.GovKeeper.SetProposal(ctx, proposal) app.GovKeeper.SetProposal(ctx, proposal)
require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[0], types.OptionNo)) require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[0], types.NewNonSplitVoteOption(types.OptionNo)))
require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[1], types.OptionNo)) require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[1], types.NewNonSplitVoteOption(types.OptionNo)))
require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[2], types.OptionYes)) require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[2], types.NewNonSplitVoteOption(types.OptionYes)))
proposal, ok := app.GovKeeper.GetProposal(ctx, proposalID) proposal, ok := app.GovKeeper.GetProposal(ctx, proposalID)
require.True(t, ok) require.True(t, ok)
@ -336,10 +336,10 @@ func TestTallyDelgatorMultipleOverride(t *testing.T) {
proposal.Status = types.StatusVotingPeriod proposal.Status = types.StatusVotingPeriod
app.GovKeeper.SetProposal(ctx, proposal) app.GovKeeper.SetProposal(ctx, proposal)
require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[0], types.OptionYes)) require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[0], types.NewNonSplitVoteOption(types.OptionYes)))
require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[1], types.OptionYes)) require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[1], types.NewNonSplitVoteOption(types.OptionYes)))
require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[2], types.OptionYes)) require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[2], types.NewNonSplitVoteOption(types.OptionYes)))
require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[3], types.OptionNo)) require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[3], types.NewNonSplitVoteOption(types.OptionNo)))
proposal, ok := app.GovKeeper.GetProposal(ctx, proposalID) proposal, ok := app.GovKeeper.GetProposal(ctx, proposalID)
require.True(t, ok) require.True(t, ok)
@ -378,9 +378,9 @@ func TestTallyDelgatorMultipleInherit(t *testing.T) {
proposal.Status = types.StatusVotingPeriod proposal.Status = types.StatusVotingPeriod
app.GovKeeper.SetProposal(ctx, proposal) app.GovKeeper.SetProposal(ctx, proposal)
require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[0], types.OptionYes)) require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[0], types.NewNonSplitVoteOption(types.OptionYes)))
require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[1], types.OptionNo)) require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[1], types.NewNonSplitVoteOption(types.OptionNo)))
require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[2], types.OptionNo)) require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[2], types.NewNonSplitVoteOption(types.OptionNo)))
proposal, ok := app.GovKeeper.GetProposal(ctx, proposalID) proposal, ok := app.GovKeeper.GetProposal(ctx, proposalID)
require.True(t, ok) require.True(t, ok)
@ -421,9 +421,8 @@ func TestTallyJailedValidator(t *testing.T) {
proposal.Status = types.StatusVotingPeriod proposal.Status = types.StatusVotingPeriod
app.GovKeeper.SetProposal(ctx, proposal) app.GovKeeper.SetProposal(ctx, proposal)
require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[0], types.OptionYes)) require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[0], types.NewNonSplitVoteOption(types.OptionYes)))
require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[1], types.OptionNo)) require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[1], types.NewNonSplitVoteOption(types.OptionNo)))
require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[2], types.OptionNo))
proposal, ok := app.GovKeeper.GetProposal(ctx, proposalID) proposal, ok := app.GovKeeper.GetProposal(ctx, proposalID)
require.True(t, ok) require.True(t, ok)
@ -454,9 +453,9 @@ func TestTallyValidatorMultipleDelegations(t *testing.T) {
proposal.Status = types.StatusVotingPeriod proposal.Status = types.StatusVotingPeriod
app.GovKeeper.SetProposal(ctx, proposal) app.GovKeeper.SetProposal(ctx, proposal)
require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[0], types.OptionYes)) require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[0], types.NewNonSplitVoteOption(types.OptionYes)))
require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[1], types.OptionNo)) require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[1], types.NewNonSplitVoteOption(types.OptionNo)))
require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[2], types.OptionYes)) require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[2], types.NewNonSplitVoteOption(types.OptionYes)))
proposal, ok := app.GovKeeper.GetProposal(ctx, proposalID) proposal, ok := app.GovKeeper.GetProposal(ctx, proposalID)
require.True(t, ok) require.True(t, ok)

View File

@ -9,7 +9,7 @@ import (
) )
// AddVote adds a vote on a specific proposal // AddVote adds a vote on a specific proposal
func (keeper Keeper) AddVote(ctx sdk.Context, proposalID uint64, voterAddr sdk.AccAddress, option types.VoteOption) error { func (keeper Keeper) AddVote(ctx sdk.Context, proposalID uint64, voterAddr sdk.AccAddress, options types.WeightedVoteOptions) error {
proposal, ok := keeper.GetProposal(ctx, proposalID) proposal, ok := keeper.GetProposal(ctx, proposalID)
if !ok { if !ok {
return sdkerrors.Wrapf(types.ErrUnknownProposal, "%d", proposalID) return sdkerrors.Wrapf(types.ErrUnknownProposal, "%d", proposalID)
@ -18,17 +18,19 @@ func (keeper Keeper) AddVote(ctx sdk.Context, proposalID uint64, voterAddr sdk.A
return sdkerrors.Wrapf(types.ErrInactiveProposal, "%d", proposalID) return sdkerrors.Wrapf(types.ErrInactiveProposal, "%d", proposalID)
} }
if !types.ValidVoteOption(option) { for _, option := range options {
if !types.ValidWeightedVoteOption(option) {
return sdkerrors.Wrap(types.ErrInvalidVote, option.String()) return sdkerrors.Wrap(types.ErrInvalidVote, option.String())
} }
}
vote := types.NewVote(proposalID, voterAddr, option) vote := types.NewVote(proposalID, voterAddr, options)
keeper.SetVote(ctx, vote) keeper.SetVote(ctx, vote)
ctx.EventManager().EmitEvent( ctx.EventManager().EmitEvent(
sdk.NewEvent( sdk.NewEvent(
types.EventTypeProposalVote, types.EventTypeProposalVote,
sdk.NewAttribute(types.AttributeKeyOption, option.String()), sdk.NewAttribute(types.AttributeKeyOption, options.String()),
sdk.NewAttribute(types.AttributeKeyProposalID, fmt.Sprintf("%d", proposalID)), sdk.NewAttribute(types.AttributeKeyProposalID, fmt.Sprintf("%d", proposalID)),
), ),
) )

View File

@ -24,37 +24,52 @@ func TestVotes(t *testing.T) {
var invalidOption types.VoteOption = 0x10 var invalidOption types.VoteOption = 0x10
require.Error(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[0], types.OptionYes), "proposal not on voting period") require.Error(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[0], types.NewNonSplitVoteOption(types.OptionYes)), "proposal not on voting period")
require.Error(t, app.GovKeeper.AddVote(ctx, 10, addrs[0], types.OptionYes), "invalid proposal ID") require.Error(t, app.GovKeeper.AddVote(ctx, 10, addrs[0], types.NewNonSplitVoteOption(types.OptionYes)), "invalid proposal ID")
proposal.Status = types.StatusVotingPeriod proposal.Status = types.StatusVotingPeriod
app.GovKeeper.SetProposal(ctx, proposal) app.GovKeeper.SetProposal(ctx, proposal)
require.Error(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[0], invalidOption), "invalid option") require.Error(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[0], types.NewNonSplitVoteOption(invalidOption)), "invalid option")
// Test first vote // Test first vote
require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[0], types.OptionAbstain)) require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[0], types.NewNonSplitVoteOption(types.OptionAbstain)))
vote, found := app.GovKeeper.GetVote(ctx, proposalID, addrs[0]) vote, found := app.GovKeeper.GetVote(ctx, proposalID, addrs[0])
require.True(t, found) require.True(t, found)
require.Equal(t, addrs[0].String(), vote.Voter) require.Equal(t, addrs[0].String(), vote.Voter)
require.Equal(t, proposalID, vote.ProposalId) require.Equal(t, proposalID, vote.ProposalId)
require.Equal(t, types.OptionAbstain, vote.Option) require.True(t, len(vote.Options) == 1)
require.Equal(t, types.OptionAbstain, vote.Options[0].Option)
// Test change of vote // Test change of vote
require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[0], types.OptionYes)) require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[0], types.NewNonSplitVoteOption(types.OptionYes)))
vote, found = app.GovKeeper.GetVote(ctx, proposalID, addrs[0]) vote, found = app.GovKeeper.GetVote(ctx, proposalID, addrs[0])
require.True(t, found) require.True(t, found)
require.Equal(t, addrs[0].String(), vote.Voter) require.Equal(t, addrs[0].String(), vote.Voter)
require.Equal(t, proposalID, vote.ProposalId) require.Equal(t, proposalID, vote.ProposalId)
require.Equal(t, types.OptionYes, vote.Option) require.True(t, len(vote.Options) == 1)
require.Equal(t, types.OptionYes, vote.Options[0].Option)
// Test second vote // Test second vote
require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[1], types.OptionNoWithVeto)) require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[1], types.WeightedVoteOptions{
types.WeightedVoteOption{Option: types.OptionYes, Weight: sdk.NewDecWithPrec(60, 2)},
types.WeightedVoteOption{Option: types.OptionNo, Weight: sdk.NewDecWithPrec(30, 2)},
types.WeightedVoteOption{Option: types.OptionAbstain, Weight: sdk.NewDecWithPrec(5, 2)},
types.WeightedVoteOption{Option: types.OptionNoWithVeto, Weight: sdk.NewDecWithPrec(5, 2)},
}))
vote, found = app.GovKeeper.GetVote(ctx, proposalID, addrs[1]) vote, found = app.GovKeeper.GetVote(ctx, proposalID, addrs[1])
require.True(t, found) require.True(t, found)
require.Equal(t, addrs[1].String(), vote.Voter) require.Equal(t, addrs[1].String(), vote.Voter)
require.Equal(t, proposalID, vote.ProposalId) require.Equal(t, proposalID, vote.ProposalId)
require.Equal(t, types.OptionNoWithVeto, vote.Option) require.True(t, len(vote.Options) == 4)
require.Equal(t, types.OptionYes, vote.Options[0].Option)
require.Equal(t, types.OptionNo, vote.Options[1].Option)
require.Equal(t, types.OptionAbstain, vote.Options[2].Option)
require.Equal(t, types.OptionNoWithVeto, vote.Options[3].Option)
require.True(t, vote.Options[0].Weight.Equal(sdk.NewDecWithPrec(60, 2)))
require.True(t, vote.Options[1].Weight.Equal(sdk.NewDecWithPrec(30, 2)))
require.True(t, vote.Options[2].Weight.Equal(sdk.NewDecWithPrec(5, 2)))
require.True(t, vote.Options[3].Weight.Equal(sdk.NewDecWithPrec(5, 2)))
// Test vote iterator // Test vote iterator
// NOTE order of deposits is determined by the addresses // NOTE order of deposits is determined by the addresses
@ -63,8 +78,13 @@ func TestVotes(t *testing.T) {
require.Equal(t, votes, app.GovKeeper.GetVotes(ctx, proposalID)) require.Equal(t, votes, app.GovKeeper.GetVotes(ctx, proposalID))
require.Equal(t, addrs[0].String(), votes[0].Voter) require.Equal(t, addrs[0].String(), votes[0].Voter)
require.Equal(t, proposalID, votes[0].ProposalId) require.Equal(t, proposalID, votes[0].ProposalId)
require.Equal(t, types.OptionYes, votes[0].Option) require.True(t, len(votes[0].Options) == 1)
require.Equal(t, types.OptionYes, votes[0].Options[0].Option)
require.Equal(t, addrs[1].String(), votes[1].Voter) require.Equal(t, addrs[1].String(), votes[1].Voter)
require.Equal(t, proposalID, votes[1].ProposalId) require.Equal(t, proposalID, votes[1].ProposalId)
require.Equal(t, types.OptionNoWithVeto, votes[1].Option) require.True(t, len(votes[1].Options) == 4)
require.True(t, votes[1].Options[0].Weight.Equal(sdk.NewDecWithPrec(60, 2)))
require.True(t, votes[1].Options[1].Weight.Equal(sdk.NewDecWithPrec(30, 2)))
require.True(t, votes[1].Options[2].Weight.Equal(sdk.NewDecWithPrec(5, 2)))
require.True(t, votes[1].Options[3].Weight.Equal(sdk.NewDecWithPrec(5, 2)))
} }

View File

@ -118,7 +118,7 @@ func Migrate(oldGovState v036gov.GenesisState) *v040gov.GenesisState {
newVotes[i] = v040gov.Vote{ newVotes[i] = v040gov.Vote{
ProposalId: oldVote.ProposalID, ProposalId: oldVote.ProposalID,
Voter: oldVote.Voter.String(), Voter: oldVote.Voter.String(),
Option: migrateVoteOption(oldVote.Option), Options: v040gov.NewNonSplitVoteOption(migrateVoteOption(oldVote.Option)),
} }
} }

View File

@ -34,7 +34,7 @@ func TestDecodeStore(t *testing.T) {
proposalIDBz := make([]byte, 8) proposalIDBz := make([]byte, 8)
binary.LittleEndian.PutUint64(proposalIDBz, 1) binary.LittleEndian.PutUint64(proposalIDBz, 1)
deposit := types.NewDeposit(1, delAddr1, sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.OneInt()))) deposit := types.NewDeposit(1, delAddr1, sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.OneInt())))
vote := types.NewVote(1, delAddr1, types.OptionYes) vote := types.NewVote(1, delAddr1, types.NewNonSplitVoteOption(types.OptionYes))
proposalBz, err := cdc.MarshalBinaryBare(&proposal) proposalBz, err := cdc.MarshalBinaryBare(&proposal)
require.NoError(t, err) require.NoError(t, err)

View File

@ -22,6 +22,7 @@ var initialProposalID = uint64(100000000000000)
const ( const (
OpWeightMsgDeposit = "op_weight_msg_deposit" OpWeightMsgDeposit = "op_weight_msg_deposit"
OpWeightMsgVote = "op_weight_msg_vote" OpWeightMsgVote = "op_weight_msg_vote"
OpWeightMsgWeightedVote = "op_weight_msg_weighted_vote"
) )
// WeightedOperations returns all the operations from the module with their respective weights // WeightedOperations returns all the operations from the module with their respective weights
@ -33,6 +34,7 @@ func WeightedOperations(
var ( var (
weightMsgDeposit int weightMsgDeposit int
weightMsgVote int weightMsgVote int
weightMsgWeightedVote int
) )
appParams.GetOrGenerate(cdc, OpWeightMsgDeposit, &weightMsgDeposit, nil, appParams.GetOrGenerate(cdc, OpWeightMsgDeposit, &weightMsgDeposit, nil,
@ -47,6 +49,12 @@ func WeightedOperations(
}, },
) )
appParams.GetOrGenerate(cdc, OpWeightMsgWeightedVote, &weightMsgWeightedVote, nil,
func(_ *rand.Rand) {
weightMsgWeightedVote = simappparams.DefaultWeightMsgWeightedVote
},
)
// generate the weighted operations for the proposal contents // generate the weighted operations for the proposal contents
var wProposalOps simulation.WeightedOperations var wProposalOps simulation.WeightedOperations
@ -74,12 +82,16 @@ func WeightedOperations(
weightMsgVote, weightMsgVote,
SimulateMsgVote(ak, bk, k), SimulateMsgVote(ak, bk, k),
), ),
simulation.NewWeightedOperation(
weightMsgWeightedVote,
SimulateMsgWeightedVote(ak, bk, k),
),
} }
return append(wProposalOps, wGovOps...) return append(wProposalOps, wGovOps...)
} }
// SimulateSubmitProposal simulates creating a msg Submit Proposal // SimulateMsgSubmitProposal simulates creating a msg Submit Proposal
// voting on the proposal, and subsequently slashing the proposal. It is implemented using // voting on the proposal, and subsequently slashing the proposal. It is implemented using
// future operations. // future operations.
func SimulateMsgSubmitProposal( func SimulateMsgSubmitProposal(
@ -315,6 +327,69 @@ func operationSimulateMsgVote(ak types.AccountKeeper, bk types.BankKeeper, k kee
} }
} }
// SimulateMsgWeightedVote generates a MsgWeightedVote with random values.
func SimulateMsgWeightedVote(ak types.AccountKeeper, bk types.BankKeeper, k keeper.Keeper) simtypes.Operation {
return operationSimulateMsgWeightedVote(ak, bk, k, simtypes.Account{}, -1)
}
func operationSimulateMsgWeightedVote(ak types.AccountKeeper, bk types.BankKeeper, k keeper.Keeper,
simAccount simtypes.Account, proposalIDInt int64) simtypes.Operation {
return func(
r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context,
accs []simtypes.Account, chainID string,
) (simtypes.OperationMsg, []simtypes.FutureOperation, error) {
if simAccount.Equals(simtypes.Account{}) {
simAccount, _ = simtypes.RandomAcc(r, accs)
}
var proposalID uint64
switch {
case proposalIDInt < 0:
var ok bool
proposalID, ok = randomProposalID(r, k, ctx, types.StatusVotingPeriod)
if !ok {
return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgWeightedVote, "unable to generate proposalID"), nil, nil
}
default:
proposalID = uint64(proposalIDInt)
}
options := randomWeightedVotingOptions(r)
msg := types.NewMsgWeightedVote(simAccount.Address, proposalID, options)
account := ak.GetAccount(ctx, simAccount.Address)
spendable := bk.SpendableCoins(ctx, account.GetAddress())
fees, err := simtypes.RandomFees(r, ctx, spendable)
if err != nil {
return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "unable to generate fees"), nil, err
}
txGen := simappparams.MakeTestEncodingConfig().TxConfig
tx, err := helpers.GenTx(
txGen,
[]sdk.Msg{msg},
fees,
helpers.DefaultGenTxGas,
chainID,
[]uint64{account.GetAccountNumber()},
[]uint64{account.GetSequence()},
simAccount.PrivKey,
)
if err != nil {
return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "unable to generate mock tx"), nil, err
}
_, _, err = app.Deliver(txGen.TxEncoder(), tx)
if err != nil {
return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "unable to deliver tx"), nil, err
}
return simtypes.NewOperationMsg(msg, true, ""), nil, nil
}
}
// Pick a random deposit with a random denomination with a // Pick a random deposit with a random denomination with a
// deposit amount between (0, min(balance, minDepositAmount)) // deposit amount between (0, min(balance, minDepositAmount))
// This is to simulate multiple users depositing to get the // This is to simulate multiple users depositing to get the
@ -393,3 +468,37 @@ func randomVotingOption(r *rand.Rand) types.VoteOption {
panic("invalid vote option") panic("invalid vote option")
} }
} }
// Pick a random weighted voting options
func randomWeightedVotingOptions(r *rand.Rand) types.WeightedVoteOptions {
w1 := r.Intn(100 + 1)
w2 := r.Intn(100 - w1 + 1)
w3 := r.Intn(100 - w1 - w2 + 1)
w4 := 100 - w1 - w2 - w3
weightedVoteOptions := types.WeightedVoteOptions{}
if w1 > 0 {
weightedVoteOptions = append(weightedVoteOptions, types.WeightedVoteOption{
Option: types.OptionYes,
Weight: sdk.NewDecWithPrec(int64(w1), 2),
})
}
if w2 > 0 {
weightedVoteOptions = append(weightedVoteOptions, types.WeightedVoteOption{
Option: types.OptionAbstain,
Weight: sdk.NewDecWithPrec(int64(w2), 2),
})
}
if w3 > 0 {
weightedVoteOptions = append(weightedVoteOptions, types.WeightedVoteOption{
Option: types.OptionNo,
Weight: sdk.NewDecWithPrec(int64(w3), 2),
})
}
if w4 > 0 {
weightedVoteOptions = append(weightedVoteOptions, types.WeightedVoteOption{
Option: types.OptionNoWithVeto,
Weight: sdk.NewDecWithPrec(int64(w4), 2),
})
}
return weightedVoteOptions
}

View File

@ -80,6 +80,7 @@ func TestWeightedOperations(t *testing.T) {
{2, types.ModuleName, "submit_proposal"}, {2, types.ModuleName, "submit_proposal"},
{simappparams.DefaultWeightMsgDeposit, types.ModuleName, types.TypeMsgDeposit}, {simappparams.DefaultWeightMsgDeposit, types.ModuleName, types.TypeMsgDeposit},
{simappparams.DefaultWeightMsgVote, types.ModuleName, types.TypeMsgVote}, {simappparams.DefaultWeightMsgVote, types.ModuleName, types.TypeMsgVote},
{simappparams.DefaultWeightMsgWeightedVote, types.ModuleName, types.TypeMsgWeightedVote},
} }
for i, w := range weightesOps { for i, w := range weightesOps {
@ -205,7 +206,48 @@ func TestSimulateMsgVote(t *testing.T) {
require.Equal(t, types.OptionYes, msg.Option) require.Equal(t, types.OptionYes, msg.Option)
require.Equal(t, "gov", msg.Route()) require.Equal(t, "gov", msg.Route())
require.Equal(t, types.TypeMsgVote, msg.Type()) require.Equal(t, types.TypeMsgVote, msg.Type())
}
// TestSimulateMsgWeightedVote tests the normal scenario of a valid message of type TypeMsgWeightedVote.
// Abonormal scenarios, where the message is created by an errors are not tested here.
func TestSimulateMsgWeightedVote(t *testing.T) {
app, ctx := createTestApp(false)
blockTime := time.Now().UTC()
ctx = ctx.WithBlockTime(blockTime)
// setup 3 accounts
s := rand.NewSource(1)
r := rand.New(s)
accounts := getTestingAccounts(t, r, app, ctx, 3)
// setup a proposal
content := types.NewTextProposal("Test", "description")
submitTime := ctx.BlockHeader().Time
depositPeriod := app.GovKeeper.GetDepositParams(ctx).MaxDepositPeriod
proposal, err := types.NewProposal(content, 1, submitTime, submitTime.Add(depositPeriod))
require.NoError(t, err)
app.GovKeeper.ActivateVotingPeriod(ctx, proposal)
// begin a new block
app.BeginBlock(abci.RequestBeginBlock{Header: tmproto.Header{Height: app.LastBlockHeight() + 1, AppHash: app.LastCommitID().Hash, Time: blockTime}})
// execute operation
op := simulation.SimulateMsgWeightedVote(app.AccountKeeper, app.BankKeeper, app.GovKeeper)
operationMsg, _, err := op(r, app.BaseApp, ctx, accounts, "")
require.NoError(t, err)
var msg types.MsgWeightedVote
types.ModuleCdc.UnmarshalJSON(operationMsg.Msg, &msg)
require.True(t, operationMsg.OK)
require.Equal(t, uint64(1), msg.ProposalId)
require.Equal(t, "cosmos1ghekyjucln7y67ntx7cf27m9dpuxxemn4c8g4r", msg.Voter)
require.True(t, len(msg.Options) >= 1)
require.Equal(t, "gov", msg.Route())
require.Equal(t, types.TypeMsgWeightedVote, msg.Type())
} }
// returns context and an app with updated mint keeper // returns context and an app with updated mint keeper

View File

@ -41,6 +41,16 @@ The governance module emits the following events:
| message | action | vote | | message | action | vote |
| message | sender | {senderAddress} | | message | sender | {senderAddress} |
### MsgWeightedVote
| Type | Attribute Key | Attribute Value |
| ------------- | ------------- | ------------------------ |
| proposal_vote | option | {weightedVoteOptions} |
| proposal_vote | proposal_id | {proposalID} |
| message | module | governance |
| message | action | vote |
| message | sender | {senderAddress} |
### MsgDeposit ### MsgDeposit
| Type | Attribute Key | Attribute Value | | Type | Attribute Key | Attribute Value |

View File

@ -15,6 +15,7 @@ func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) {
cdc.RegisterConcrete(&MsgSubmitProposal{}, "cosmos-sdk/MsgSubmitProposal", nil) cdc.RegisterConcrete(&MsgSubmitProposal{}, "cosmos-sdk/MsgSubmitProposal", nil)
cdc.RegisterConcrete(&MsgDeposit{}, "cosmos-sdk/MsgDeposit", nil) cdc.RegisterConcrete(&MsgDeposit{}, "cosmos-sdk/MsgDeposit", nil)
cdc.RegisterConcrete(&MsgVote{}, "cosmos-sdk/MsgVote", nil) cdc.RegisterConcrete(&MsgVote{}, "cosmos-sdk/MsgVote", nil)
cdc.RegisterConcrete(&MsgWeightedVote{}, "cosmos-sdk/MsgWeightedVote", nil)
cdc.RegisterConcrete(&TextProposal{}, "cosmos-sdk/TextProposal", nil) cdc.RegisterConcrete(&TextProposal{}, "cosmos-sdk/TextProposal", nil)
} }
@ -22,6 +23,7 @@ func RegisterInterfaces(registry types.InterfaceRegistry) {
registry.RegisterImplementations((*sdk.Msg)(nil), registry.RegisterImplementations((*sdk.Msg)(nil),
&MsgSubmitProposal{}, &MsgSubmitProposal{},
&MsgVote{}, &MsgVote{},
&MsgWeightedVote{},
&MsgDeposit{}, &MsgDeposit{},
) )
registry.RegisterInterface( registry.RegisterInterface(

View File

@ -121,6 +121,44 @@ func (ProposalStatus) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_6e82113c1a9a4b7c, []int{1} return fileDescriptor_6e82113c1a9a4b7c, []int{1}
} }
// WeightedVoteOption defines a unit of vote for vote split.
type WeightedVoteOption struct {
Option VoteOption `protobuf:"varint,1,opt,name=option,proto3,enum=cosmos.gov.v1beta1.VoteOption" json:"option,omitempty"`
Weight github_com_cosmos_cosmos_sdk_types.Dec `protobuf:"bytes,2,opt,name=weight,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Dec" json:"weight" yaml:"weight"`
}
func (m *WeightedVoteOption) Reset() { *m = WeightedVoteOption{} }
func (*WeightedVoteOption) ProtoMessage() {}
func (*WeightedVoteOption) Descriptor() ([]byte, []int) {
return fileDescriptor_6e82113c1a9a4b7c, []int{0}
}
func (m *WeightedVoteOption) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *WeightedVoteOption) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_WeightedVoteOption.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *WeightedVoteOption) XXX_Merge(src proto.Message) {
xxx_messageInfo_WeightedVoteOption.Merge(m, src)
}
func (m *WeightedVoteOption) XXX_Size() int {
return m.Size()
}
func (m *WeightedVoteOption) XXX_DiscardUnknown() {
xxx_messageInfo_WeightedVoteOption.DiscardUnknown(m)
}
var xxx_messageInfo_WeightedVoteOption proto.InternalMessageInfo
// TextProposal defines a standard text proposal whose changes need to be // TextProposal defines a standard text proposal whose changes need to be
// manually updated in case of approval. // manually updated in case of approval.
type TextProposal struct { type TextProposal struct {
@ -131,7 +169,7 @@ type TextProposal struct {
func (m *TextProposal) Reset() { *m = TextProposal{} } func (m *TextProposal) Reset() { *m = TextProposal{} }
func (*TextProposal) ProtoMessage() {} func (*TextProposal) ProtoMessage() {}
func (*TextProposal) Descriptor() ([]byte, []int) { func (*TextProposal) Descriptor() ([]byte, []int) {
return fileDescriptor_6e82113c1a9a4b7c, []int{0} return fileDescriptor_6e82113c1a9a4b7c, []int{1}
} }
func (m *TextProposal) XXX_Unmarshal(b []byte) error { func (m *TextProposal) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b) return m.Unmarshal(b)
@ -171,7 +209,7 @@ type Deposit struct {
func (m *Deposit) Reset() { *m = Deposit{} } func (m *Deposit) Reset() { *m = Deposit{} }
func (*Deposit) ProtoMessage() {} func (*Deposit) ProtoMessage() {}
func (*Deposit) Descriptor() ([]byte, []int) { func (*Deposit) Descriptor() ([]byte, []int) {
return fileDescriptor_6e82113c1a9a4b7c, []int{1} return fileDescriptor_6e82113c1a9a4b7c, []int{2}
} }
func (m *Deposit) XXX_Unmarshal(b []byte) error { func (m *Deposit) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b) return m.Unmarshal(b)
@ -216,7 +254,7 @@ type Proposal struct {
func (m *Proposal) Reset() { *m = Proposal{} } func (m *Proposal) Reset() { *m = Proposal{} }
func (*Proposal) ProtoMessage() {} func (*Proposal) ProtoMessage() {}
func (*Proposal) Descriptor() ([]byte, []int) { func (*Proposal) Descriptor() ([]byte, []int) {
return fileDescriptor_6e82113c1a9a4b7c, []int{2} return fileDescriptor_6e82113c1a9a4b7c, []int{3}
} }
func (m *Proposal) XXX_Unmarshal(b []byte) error { func (m *Proposal) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b) return m.Unmarshal(b)
@ -256,7 +294,7 @@ type TallyResult struct {
func (m *TallyResult) Reset() { *m = TallyResult{} } func (m *TallyResult) Reset() { *m = TallyResult{} }
func (*TallyResult) ProtoMessage() {} func (*TallyResult) ProtoMessage() {}
func (*TallyResult) Descriptor() ([]byte, []int) { func (*TallyResult) Descriptor() ([]byte, []int) {
return fileDescriptor_6e82113c1a9a4b7c, []int{3} return fileDescriptor_6e82113c1a9a4b7c, []int{4}
} }
func (m *TallyResult) XXX_Unmarshal(b []byte) error { func (m *TallyResult) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b) return m.Unmarshal(b)
@ -290,13 +328,13 @@ var xxx_messageInfo_TallyResult proto.InternalMessageInfo
type Vote struct { type Vote struct {
ProposalId uint64 `protobuf:"varint,1,opt,name=proposal_id,json=proposalId,proto3" json:"proposal_id,omitempty" yaml:"proposal_id"` ProposalId uint64 `protobuf:"varint,1,opt,name=proposal_id,json=proposalId,proto3" json:"proposal_id,omitempty" yaml:"proposal_id"`
Voter string `protobuf:"bytes,2,opt,name=voter,proto3" json:"voter,omitempty"` Voter string `protobuf:"bytes,2,opt,name=voter,proto3" json:"voter,omitempty"`
Option VoteOption `protobuf:"varint,3,opt,name=option,proto3,enum=cosmos.gov.v1beta1.VoteOption" json:"option,omitempty"` Options []WeightedVoteOption `protobuf:"bytes,3,rep,name=options,proto3" json:"options"`
} }
func (m *Vote) Reset() { *m = Vote{} } func (m *Vote) Reset() { *m = Vote{} }
func (*Vote) ProtoMessage() {} func (*Vote) ProtoMessage() {}
func (*Vote) Descriptor() ([]byte, []int) { func (*Vote) Descriptor() ([]byte, []int) {
return fileDescriptor_6e82113c1a9a4b7c, []int{4} return fileDescriptor_6e82113c1a9a4b7c, []int{5}
} }
func (m *Vote) XXX_Unmarshal(b []byte) error { func (m *Vote) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b) return m.Unmarshal(b)
@ -337,7 +375,7 @@ type DepositParams struct {
func (m *DepositParams) Reset() { *m = DepositParams{} } func (m *DepositParams) Reset() { *m = DepositParams{} }
func (*DepositParams) ProtoMessage() {} func (*DepositParams) ProtoMessage() {}
func (*DepositParams) Descriptor() ([]byte, []int) { func (*DepositParams) Descriptor() ([]byte, []int) {
return fileDescriptor_6e82113c1a9a4b7c, []int{5} return fileDescriptor_6e82113c1a9a4b7c, []int{6}
} }
func (m *DepositParams) XXX_Unmarshal(b []byte) error { func (m *DepositParams) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b) return m.Unmarshal(b)
@ -375,7 +413,7 @@ type VotingParams struct {
func (m *VotingParams) Reset() { *m = VotingParams{} } func (m *VotingParams) Reset() { *m = VotingParams{} }
func (*VotingParams) ProtoMessage() {} func (*VotingParams) ProtoMessage() {}
func (*VotingParams) Descriptor() ([]byte, []int) { func (*VotingParams) Descriptor() ([]byte, []int) {
return fileDescriptor_6e82113c1a9a4b7c, []int{6} return fileDescriptor_6e82113c1a9a4b7c, []int{7}
} }
func (m *VotingParams) XXX_Unmarshal(b []byte) error { func (m *VotingParams) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b) return m.Unmarshal(b)
@ -419,7 +457,7 @@ type TallyParams struct {
func (m *TallyParams) Reset() { *m = TallyParams{} } func (m *TallyParams) Reset() { *m = TallyParams{} }
func (*TallyParams) ProtoMessage() {} func (*TallyParams) ProtoMessage() {}
func (*TallyParams) Descriptor() ([]byte, []int) { func (*TallyParams) Descriptor() ([]byte, []int) {
return fileDescriptor_6e82113c1a9a4b7c, []int{7} return fileDescriptor_6e82113c1a9a4b7c, []int{8}
} }
func (m *TallyParams) XXX_Unmarshal(b []byte) error { func (m *TallyParams) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b) return m.Unmarshal(b)
@ -451,6 +489,7 @@ var xxx_messageInfo_TallyParams proto.InternalMessageInfo
func init() { func init() {
proto.RegisterEnum("cosmos.gov.v1beta1.VoteOption", VoteOption_name, VoteOption_value) proto.RegisterEnum("cosmos.gov.v1beta1.VoteOption", VoteOption_name, VoteOption_value)
proto.RegisterEnum("cosmos.gov.v1beta1.ProposalStatus", ProposalStatus_name, ProposalStatus_value) proto.RegisterEnum("cosmos.gov.v1beta1.ProposalStatus", ProposalStatus_name, ProposalStatus_value)
proto.RegisterType((*WeightedVoteOption)(nil), "cosmos.gov.v1beta1.WeightedVoteOption")
proto.RegisterType((*TextProposal)(nil), "cosmos.gov.v1beta1.TextProposal") proto.RegisterType((*TextProposal)(nil), "cosmos.gov.v1beta1.TextProposal")
proto.RegisterType((*Deposit)(nil), "cosmos.gov.v1beta1.Deposit") proto.RegisterType((*Deposit)(nil), "cosmos.gov.v1beta1.Deposit")
proto.RegisterType((*Proposal)(nil), "cosmos.gov.v1beta1.Proposal") proto.RegisterType((*Proposal)(nil), "cosmos.gov.v1beta1.Proposal")
@ -464,94 +503,97 @@ func init() {
func init() { proto.RegisterFile("cosmos/gov/v1beta1/gov.proto", fileDescriptor_6e82113c1a9a4b7c) } func init() { proto.RegisterFile("cosmos/gov/v1beta1/gov.proto", fileDescriptor_6e82113c1a9a4b7c) }
var fileDescriptor_6e82113c1a9a4b7c = []byte{ var fileDescriptor_6e82113c1a9a4b7c = []byte{
// 1377 bytes of a gzipped FileDescriptorProto // 1438 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x57, 0x5f, 0x6c, 0xdb, 0xd4, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x57, 0x51, 0x68, 0xdb, 0x56,
0x17, 0x8e, 0xd3, 0xbf, 0xb9, 0x49, 0x5b, 0xef, 0x36, 0x6b, 0x53, 0xff, 0xf6, 0xb3, 0x8d, 0x41, 0x17, 0xb6, 0x1c, 0xc7, 0x89, 0xaf, 0x9d, 0x44, 0xbd, 0x49, 0x13, 0xc7, 0x7f, 0x7f, 0xc9, 0xd3,
0xa8, 0x9a, 0xb6, 0x74, 0x2b, 0x08, 0x44, 0x27, 0x21, 0x92, 0xc6, 0x63, 0x41, 0x53, 0x12, 0x39, 0x46, 0x09, 0xa5, 0x75, 0xda, 0x6c, 0x6c, 0x2c, 0x85, 0x6d, 0x56, 0xac, 0xac, 0x1e, 0xc5, 0x36,
0x5e, 0xa6, 0x8d, 0x07, 0xcb, 0x49, 0xee, 0x52, 0x43, 0xec, 0x1b, 0xe2, 0x9b, 0xd2, 0x88, 0x17, 0xb2, 0xea, 0xd0, 0xee, 0x41, 0x28, 0xf6, 0xad, 0xa3, 0xcd, 0xd2, 0xf5, 0xac, 0xeb, 0x34, 0x66,
0x1e, 0xa7, 0x20, 0xa1, 0xbd, 0x31, 0x09, 0x45, 0x9a, 0xc4, 0x1b, 0xcf, 0x3c, 0xf3, 0x5c, 0x21, 0x2f, 0x7b, 0x2c, 0x1e, 0x8c, 0x3e, 0x16, 0x86, 0xa1, 0x30, 0xc6, 0x60, 0xcf, 0x7b, 0xde, 0x73,
0x24, 0x26, 0x9e, 0x26, 0x90, 0x32, 0xd6, 0x49, 0x68, 0xea, 0x63, 0x1f, 0x78, 0x46, 0xf6, 0xbd, 0x18, 0x83, 0x95, 0x3d, 0x95, 0x0d, 0xdc, 0x35, 0x85, 0x51, 0xf2, 0x98, 0x87, 0x3d, 0x0f, 0xe9,
0x6e, 0x9c, 0xa4, 0xa2, 0x84, 0xa7, 0xd9, 0xe7, 0x9e, 0xef, 0xfb, 0xce, 0xfd, 0x7c, 0xce, 0xc9, 0x5e, 0xc5, 0xb2, 0x1d, 0x96, 0x79, 0x4f, 0xd1, 0x3d, 0xf7, 0x7c, 0xdf, 0x77, 0xce, 0xf1, 0x39,
0x0a, 0x2e, 0xd5, 0xb0, 0x6b, 0x63, 0x77, 0xab, 0x81, 0xf7, 0xb7, 0xf6, 0xaf, 0x57, 0x11, 0x31, 0x47, 0x0a, 0xb8, 0x54, 0xc5, 0x8e, 0x85, 0x9d, 0xf5, 0x3a, 0xde, 0x5f, 0xdf, 0xbf, 0xb1, 0x8b,
0xaf, 0x7b, 0xcf, 0xe9, 0x56, 0x1b, 0x13, 0x0c, 0x21, 0x3d, 0x4d, 0x7b, 0x11, 0x76, 0x2a, 0x88, 0x88, 0x71, 0xc3, 0x7d, 0xce, 0x34, 0x5b, 0x98, 0x60, 0x08, 0xe9, 0x6d, 0xc6, 0xb5, 0xb0, 0xdb,
0x0c, 0x51, 0x35, 0x5d, 0x74, 0x0a, 0xa9, 0x61, 0xcb, 0xa1, 0x18, 0x21, 0xd9, 0xc0, 0x0d, 0xec, 0x94, 0xc0, 0x10, 0xbb, 0x86, 0x83, 0x4e, 0x21, 0x55, 0x6c, 0xda, 0x14, 0x93, 0x5a, 0xaa, 0xe3,
0x3f, 0x6e, 0x79, 0x4f, 0x2c, 0xba, 0x41, 0x51, 0x06, 0x3d, 0x60, 0xb4, 0xf4, 0x48, 0x6a, 0x60, 0x3a, 0xf6, 0x1e, 0xd7, 0xdd, 0x27, 0x66, 0x5d, 0xa5, 0x28, 0x9d, 0x5e, 0x30, 0x5a, 0x7a, 0x25,
0xdc, 0x68, 0xa2, 0x2d, 0xff, 0xad, 0xda, 0x79, 0xb0, 0x45, 0x2c, 0x1b, 0xb9, 0xc4, 0xb4, 0x5b, 0xd6, 0x31, 0xae, 0x37, 0xd0, 0xba, 0x77, 0xda, 0x6d, 0xdf, 0x5f, 0x27, 0xa6, 0x85, 0x1c, 0x62,
0x01, 0x76, 0x3c, 0xc1, 0x74, 0xba, 0xec, 0x48, 0x1c, 0x3f, 0xaa, 0x77, 0xda, 0x26, 0xb1, 0x30, 0x58, 0x4d, 0x1f, 0x3b, 0xea, 0x60, 0xd8, 0x1d, 0x76, 0x25, 0x8c, 0x5e, 0xd5, 0xda, 0x2d, 0x83,
0x2b, 0x46, 0xb9, 0x0b, 0x12, 0x3a, 0x3a, 0x20, 0xa5, 0x36, 0x6e, 0x61, 0xd7, 0x6c, 0xc2, 0x24, 0x98, 0x98, 0x05, 0x23, 0x7d, 0xcb, 0x01, 0xb8, 0x83, 0xcc, 0xfa, 0x1e, 0x41, 0xb5, 0x0a, 0x26,
0x98, 0x23, 0x16, 0x69, 0xa2, 0x14, 0x27, 0x73, 0x9b, 0x31, 0x8d, 0xbe, 0x40, 0x19, 0xc4, 0xeb, 0xa8, 0xd8, 0x74, 0x2f, 0xe1, 0xdb, 0x20, 0x8a, 0xbd, 0xa7, 0x24, 0x97, 0xe6, 0xd6, 0xe6, 0x37,
0xc8, 0xad, 0xb5, 0xad, 0x96, 0x07, 0x4d, 0x45, 0xfd, 0xb3, 0x70, 0x68, 0x67, 0xe5, 0xd5, 0x13, 0x84, 0xcc, 0x78, 0xa2, 0x99, 0x81, 0xbf, 0xca, 0xbc, 0xe1, 0x0e, 0x88, 0x3e, 0xf0, 0xd8, 0x92,
0x89, 0xfb, 0xf5, 0x87, 0xab, 0x0b, 0xbb, 0xd8, 0x21, 0xc8, 0x21, 0xca, 0x2f, 0x1c, 0x58, 0xc8, 0xe1, 0x34, 0xb7, 0x16, 0x93, 0xdf, 0x3f, 0xec, 0x8b, 0xa1, 0xdf, 0xfa, 0xe2, 0xe5, 0xba, 0x49,
0xa1, 0x16, 0x76, 0x2d, 0x02, 0xdf, 0x05, 0xf1, 0x16, 0x13, 0x30, 0xac, 0xba, 0x4f, 0x3d, 0x9b, 0xf6, 0xda, 0xbb, 0x99, 0x2a, 0xb6, 0x58, 0x6e, 0xec, 0xcf, 0x35, 0xa7, 0xf6, 0xe9, 0x3a, 0xe9,
0x5d, 0x3b, 0x19, 0x48, 0xb0, 0x6b, 0xda, 0xcd, 0x1d, 0x25, 0x74, 0xa8, 0x68, 0x20, 0x78, 0xcb, 0x34, 0x91, 0x93, 0xc9, 0xa1, 0xea, 0x49, 0x5f, 0x9c, 0xeb, 0x18, 0x56, 0x63, 0x53, 0xa2, 0x2c,
0xd7, 0xe1, 0x25, 0x10, 0xab, 0x53, 0x0e, 0xdc, 0x66, 0xaa, 0xc3, 0x00, 0xac, 0x81, 0x79, 0xd3, 0x92, 0xca, 0xe8, 0xa4, 0x1d, 0x90, 0xd0, 0xd0, 0x01, 0x29, 0xb5, 0x70, 0x13, 0x3b, 0x46, 0x03,
0xc6, 0x1d, 0x87, 0xa4, 0x66, 0xe4, 0x99, 0xcd, 0xf8, 0xf6, 0x46, 0x9a, 0xd9, 0xe6, 0x39, 0x1f, 0x2e, 0x81, 0x69, 0x62, 0x92, 0x06, 0xf2, 0xe2, 0x8b, 0xa9, 0xf4, 0x00, 0xd3, 0x20, 0x5e, 0x43,
0x7c, 0x8e, 0xf4, 0x2e, 0xb6, 0x9c, 0xec, 0xb5, 0xc3, 0x81, 0x14, 0xf9, 0xfe, 0xb9, 0xb4, 0xd9, 0x4e, 0xb5, 0x65, 0xd2, 0xd8, 0xbd, 0x18, 0xd4, 0xa0, 0x69, 0x73, 0xe1, 0xd5, 0x13, 0x91, 0xfb,
0xb0, 0xc8, 0x5e, 0xa7, 0x9a, 0xae, 0x61, 0x9b, 0x79, 0xcc, 0xfe, 0xb9, 0xea, 0xd6, 0x3f, 0xdd, 0xf5, 0x87, 0x6b, 0x33, 0x5b, 0xd8, 0x26, 0xc8, 0x26, 0xd2, 0x2f, 0x1c, 0x98, 0xc9, 0xa1, 0x26,
0x22, 0xdd, 0x16, 0x72, 0x7d, 0x80, 0xab, 0x31, 0xea, 0x9d, 0xc5, 0x87, 0x4f, 0xa4, 0xc8, 0xab, 0x76, 0x4c, 0x02, 0xdf, 0x01, 0xf1, 0x26, 0x13, 0xd0, 0xcd, 0x9a, 0x47, 0x1d, 0x91, 0x97, 0x4f,
0x27, 0x52, 0x44, 0xf9, 0x6b, 0x1e, 0x2c, 0x9e, 0xfa, 0xf4, 0xf6, 0x59, 0x57, 0x5a, 0x3d, 0x1e, 0xfa, 0x22, 0xa4, 0x41, 0x05, 0x2e, 0x25, 0x15, 0xf8, 0xa7, 0x7c, 0x0d, 0x5e, 0x02, 0xb1, 0x1a,
0x48, 0x51, 0xab, 0x7e, 0x32, 0x90, 0x62, 0xf4, 0x62, 0xe3, 0xf7, 0xb9, 0x01, 0x16, 0x6a, 0xd4, 0xe5, 0xc0, 0x2d, 0xa6, 0x3a, 0x30, 0xc0, 0x2a, 0x88, 0x1a, 0x16, 0x6e, 0xdb, 0x24, 0x39, 0x95,
0x1f, 0xff, 0x36, 0xf1, 0xed, 0x64, 0x9a, 0x7e, 0x9f, 0x74, 0xf0, 0x7d, 0xd2, 0x19, 0xa7, 0x9b, 0x9e, 0x5a, 0x8b, 0x6f, 0xac, 0xfa, 0xc5, 0x74, 0x3b, 0xe4, 0xb4, 0x9a, 0x5b, 0xd8, 0xb4, 0xe5,
0x8d, 0xff, 0x34, 0x34, 0x52, 0x0b, 0x10, 0xb0, 0x02, 0xe6, 0x5d, 0x62, 0x92, 0x8e, 0x9b, 0x9a, 0xeb, 0x6e, 0xbd, 0xbe, 0x7f, 0x2e, 0xae, 0xfd, 0x8b, 0x7a, 0xb9, 0x00, 0x47, 0x65, 0xd4, 0x9b,
0x91, 0xb9, 0xcd, 0xe5, 0x6d, 0x25, 0x3d, 0xd9, 0x7c, 0xe9, 0xa0, 0xc0, 0xb2, 0x9f, 0x99, 0x15, 0xb3, 0x0f, 0x9f, 0x88, 0xa1, 0x57, 0x4f, 0xc4, 0x90, 0xf4, 0x57, 0x14, 0xcc, 0x9e, 0xd6, 0xe9,
0x4e, 0x06, 0xd2, 0xda, 0x98, 0xc9, 0x94, 0x44, 0xd1, 0x18, 0x1b, 0x6c, 0x01, 0xf8, 0xc0, 0x72, 0xad, 0xb3, 0x52, 0x5a, 0x3c, 0xee, 0x8b, 0x61, 0xb3, 0x76, 0xd2, 0x17, 0x63, 0x34, 0xb1, 0xd1,
0xcc, 0xa6, 0x41, 0xcc, 0x66, 0xb3, 0x6b, 0xb4, 0x91, 0xdb, 0x69, 0x92, 0xd4, 0xac, 0x5f, 0x9f, 0x7c, 0x6e, 0x82, 0x99, 0x2a, 0xad, 0x8f, 0x97, 0x4d, 0x7c, 0x63, 0x29, 0x43, 0xfb, 0x28, 0xe3,
0x74, 0x96, 0x86, 0xee, 0xe5, 0x69, 0x7e, 0x5a, 0xf6, 0x35, 0xcf, 0xd8, 0x93, 0x81, 0xb4, 0x41, 0xf7, 0x51, 0x26, 0x6b, 0x77, 0xe4, 0xf8, 0x4f, 0x83, 0x42, 0xaa, 0x3e, 0x02, 0x56, 0x40, 0xd4,
0x45, 0x26, 0x89, 0x14, 0x8d, 0xf7, 0x83, 0x21, 0x10, 0xfc, 0x18, 0xc4, 0xdd, 0x4e, 0xd5, 0xb6, 0x21, 0x06, 0x69, 0x3b, 0xc9, 0x29, 0xaf, 0x77, 0xa4, 0xb3, 0x7a, 0xc7, 0x0f, 0xb0, 0xec, 0x79,
0x88, 0xe1, 0x75, 0x72, 0x6a, 0xce, 0x97, 0x12, 0x26, 0xac, 0xd0, 0x83, 0x36, 0xcf, 0x8a, 0x4c, 0xca, 0xa9, 0x93, 0xbe, 0xb8, 0x3c, 0x52, 0x64, 0x4a, 0x22, 0xa9, 0x8c, 0x0d, 0x36, 0x01, 0xbc,
0x85, 0xf5, 0x4b, 0x08, 0xac, 0x3c, 0x7a, 0x2e, 0x71, 0x1a, 0xa0, 0x11, 0x0f, 0x00, 0x2d, 0xc0, 0x6f, 0xda, 0x46, 0x43, 0x27, 0x46, 0xa3, 0xd1, 0xd1, 0x5b, 0xc8, 0x69, 0x37, 0x48, 0x32, 0xe2,
0xb3, 0x16, 0x31, 0x90, 0x53, 0xa7, 0x0a, 0xf3, 0xe7, 0x2a, 0xbc, 0xce, 0x14, 0xd6, 0xa9, 0xc2, 0xc5, 0x27, 0x9e, 0xa5, 0xa1, 0xb9, 0x7e, 0xaa, 0xe7, 0x26, 0xbf, 0xe6, 0x16, 0xf6, 0xa4, 0x2f,
0x38, 0x03, 0x95, 0x59, 0x66, 0x61, 0xd5, 0xa9, 0xfb, 0x52, 0x0f, 0x39, 0xb0, 0x44, 0x30, 0x31, 0xae, 0x52, 0x91, 0x71, 0x22, 0x49, 0xe5, 0x3d, 0x63, 0x00, 0x04, 0x3f, 0x06, 0x71, 0xa7, 0xbd,
0x9b, 0x06, 0x3b, 0x48, 0x2d, 0x9c, 0xd7, 0x88, 0xb7, 0x98, 0x4e, 0x92, 0xea, 0x8c, 0xa0, 0x95, 0x6b, 0x99, 0x44, 0x77, 0x27, 0x2e, 0x39, 0xed, 0x49, 0xa5, 0xc6, 0x4a, 0xa1, 0xf9, 0xe3, 0x28,
0xa9, 0x1a, 0x34, 0xe1, 0x63, 0x83, 0x11, 0x6b, 0x82, 0x0b, 0xfb, 0x98, 0x58, 0x4e, 0xc3, 0xfb, 0x0b, 0x4c, 0x85, 0xf5, 0x4b, 0x00, 0x2c, 0x3d, 0x7a, 0x2e, 0x72, 0x2a, 0xa0, 0x16, 0x17, 0x00,
0xbc, 0x6d, 0x66, 0xec, 0xe2, 0xb9, 0xd7, 0x7e, 0x83, 0x95, 0x93, 0xa2, 0xe5, 0x4c, 0x50, 0xd0, 0x4d, 0xc0, 0xb3, 0x16, 0xd1, 0x91, 0x5d, 0xa3, 0x0a, 0xd1, 0x73, 0x15, 0x5e, 0x67, 0x0a, 0x2b,
0x7b, 0xaf, 0xd0, 0x78, 0xd9, 0x0b, 0xfb, 0x17, 0x7f, 0x00, 0x58, 0x68, 0x68, 0x71, 0xec, 0x5c, 0x54, 0x61, 0x94, 0x81, 0xca, 0xcc, 0x33, 0xb3, 0x62, 0xd7, 0x3c, 0xa9, 0x87, 0x1c, 0x98, 0x23,
0x2d, 0x85, 0x69, 0xad, 0x8d, 0x68, 0x8d, 0x3a, 0xbc, 0x44, 0xa3, 0xcc, 0xe0, 0x9d, 0x59, 0x6f, 0x98, 0x18, 0x0d, 0x9d, 0x5d, 0x24, 0x67, 0xce, 0x6b, 0xc4, 0x5b, 0x4c, 0x67, 0x89, 0xea, 0x0c,
0xab, 0x28, 0x87, 0x51, 0x10, 0x0f, 0xb7, 0xcf, 0x07, 0x60, 0xa6, 0x8b, 0x5c, 0xba, 0xa1, 0xb2, 0xa1, 0xa5, 0x89, 0x1a, 0x34, 0xe1, 0x61, 0xfd, 0x11, 0x6b, 0x80, 0x0b, 0xfb, 0x98, 0x98, 0x76,
0x69, 0x8f, 0xf5, 0xb7, 0x81, 0xf4, 0xe6, 0xbf, 0x30, 0x2e, 0xef, 0x10, 0xcd, 0x83, 0xc2, 0x5b, 0xdd, 0xfd, 0x79, 0x5b, 0xac, 0xb0, 0xb3, 0xe7, 0xa6, 0xfd, 0x06, 0x0b, 0x27, 0x49, 0xc3, 0x19,
0x60, 0xc1, 0xac, 0xba, 0xc4, 0xb4, 0xd8, 0x2e, 0x9b, 0x9a, 0x25, 0x80, 0xc3, 0xf7, 0x41, 0xd4, 0xa3, 0xa0, 0x79, 0x2f, 0x50, 0x7b, 0xd9, 0x35, 0x7b, 0x89, 0xdf, 0x07, 0xcc, 0x34, 0x28, 0x71,
0xc1, 0xfe, 0x40, 0x4e, 0x4f, 0x12, 0x75, 0x30, 0x6c, 0x80, 0x84, 0x83, 0x8d, 0xcf, 0x2d, 0xb2, 0xec, 0x5c, 0x2d, 0x89, 0x69, 0x2d, 0x0f, 0x69, 0x0d, 0x57, 0x78, 0x8e, 0x5a, 0x59, 0x81, 0x37,
0x67, 0xec, 0x23, 0x82, 0xfd, 0xb1, 0x8b, 0x65, 0xd5, 0xe9, 0x98, 0x4e, 0x06, 0xd2, 0x2a, 0x35, 0x23, 0xee, 0x56, 0x91, 0x0e, 0xc3, 0x20, 0x1e, 0x6c, 0x9f, 0x0f, 0xc0, 0x54, 0x07, 0x39, 0x74,
0x35, 0xcc, 0xa5, 0x68, 0xc0, 0xc1, 0x77, 0x2d, 0xb2, 0x57, 0x41, 0x04, 0x33, 0x2b, 0xbf, 0xe1, 0x43, 0xc9, 0x99, 0x09, 0x36, 0x61, 0xde, 0x26, 0xaa, 0x0b, 0x85, 0xb7, 0xc0, 0x8c, 0xb1, 0xeb,
0xc0, 0x6c, 0x05, 0x13, 0xf4, 0xdf, 0x57, 0x72, 0x12, 0xcc, 0xed, 0x63, 0x82, 0x82, 0x75, 0x4c, 0x10, 0xc3, 0x64, 0xbb, 0x6c, 0x62, 0x16, 0x1f, 0x0e, 0xdf, 0x03, 0x61, 0x1b, 0x7b, 0x03, 0x39,
0x5f, 0xe0, 0x3b, 0x60, 0x1e, 0xd3, 0xdf, 0x06, 0xba, 0x9b, 0xc4, 0xb3, 0xf6, 0x86, 0x27, 0x5c, 0x39, 0x49, 0xd8, 0xc6, 0xb0, 0x0e, 0x12, 0x36, 0xd6, 0x1f, 0x98, 0x64, 0x4f, 0xdf, 0x47, 0x04,
0xf4, 0xb3, 0x34, 0x96, 0xbd, 0xb3, 0xf8, 0x38, 0xd8, 0xae, 0x3f, 0x46, 0xc1, 0x12, 0x6b, 0xe6, 0x7b, 0x63, 0x17, 0x93, 0x95, 0xc9, 0x98, 0x4e, 0xfa, 0xe2, 0x22, 0x2d, 0x6a, 0x90, 0x4b, 0x52,
0x92, 0xd9, 0x36, 0x6d, 0x17, 0x7e, 0xcb, 0x81, 0xb8, 0x6d, 0x39, 0xa7, 0xb3, 0xc5, 0x9d, 0x37, 0x81, 0x8d, 0x77, 0x4c, 0xb2, 0x57, 0x41, 0x04, 0xb3, 0x52, 0x7e, 0xc7, 0x81, 0x88, 0xfb, 0x7a,
0x5b, 0x86, 0xe7, 0xda, 0xf1, 0x40, 0xba, 0x18, 0x42, 0x5d, 0xc1, 0xb6, 0x45, 0x90, 0xdd, 0x22, 0xf9, 0xef, 0x2b, 0x79, 0x09, 0x4c, 0xef, 0x63, 0x82, 0xfc, 0x75, 0x4c, 0x0f, 0x70, 0x1b, 0xcc,
0xdd, 0xe1, 0xdd, 0x42, 0xc7, 0xd3, 0x8d, 0x1c, 0xb0, 0x2d, 0x27, 0x18, 0xb8, 0xaf, 0x39, 0x00, 0xd0, 0x37, 0x95, 0xc3, 0x76, 0xf1, 0xe5, 0xb3, 0x16, 0xc7, 0xf8, 0x0b, 0x51, 0x8e, 0xb8, 0x99,
0x6d, 0xf3, 0x20, 0x20, 0x32, 0x5a, 0xa8, 0x6d, 0xe1, 0x3a, 0x5b, 0xeb, 0x1b, 0x13, 0x63, 0x90, 0xaa, 0x3e, 0x78, 0x73, 0xf6, 0xb1, 0xbf, 0x6d, 0x7f, 0x0c, 0x83, 0x39, 0xd6, 0xdc, 0x25, 0xa3,
0x63, 0x3f, 0xbb, 0xf4, 0xd3, 0x1e, 0x0f, 0xa4, 0x4b, 0x93, 0xe0, 0x91, 0x5a, 0xd9, 0x42, 0x9d, 0x65, 0x58, 0x0e, 0xfc, 0x9a, 0x03, 0x71, 0xcb, 0xb4, 0x4f, 0x67, 0x8d, 0x3b, 0x6f, 0xd6, 0x74,
0xcc, 0x52, 0x1e, 0x7b, 0x83, 0xc2, 0xdb, 0xe6, 0x41, 0x60, 0x17, 0x0d, 0x7f, 0xc5, 0x81, 0x44, 0x97, 0xfb, 0xb8, 0x2f, 0x5e, 0x0c, 0xa0, 0xae, 0x62, 0xcb, 0x24, 0xc8, 0x6a, 0x92, 0xce, 0x20,
0xc5, 0x9f, 0x1e, 0xe6, 0xdf, 0x17, 0x80, 0x4d, 0x53, 0x50, 0x1b, 0x77, 0x5e, 0x6d, 0x37, 0x58, 0xd7, 0xc0, 0xf5, 0x64, 0x23, 0x08, 0x2c, 0xd3, 0xf6, 0x07, 0xf0, 0x2b, 0x0e, 0x40, 0xcb, 0x38,
0x6d, 0xeb, 0x23, 0xb8, 0x91, 0xb2, 0x92, 0x23, 0xc3, 0x1b, 0xae, 0x28, 0x41, 0x63, 0xac, 0x9a, 0xf0, 0x89, 0xf4, 0x26, 0x6a, 0x99, 0xb8, 0xc6, 0xd6, 0xfc, 0xea, 0xd8, 0x58, 0xe4, 0xd8, 0xe7,
0xdf, 0x83, 0x99, 0x65, 0xc5, 0xdc, 0x07, 0xf3, 0x9f, 0x75, 0x70, 0xbb, 0x63, 0xfb, 0x55, 0x24, 0x02, 0xfd, 0xa9, 0x8f, 0xfb, 0xe2, 0xa5, 0x71, 0xf0, 0x50, 0xac, 0x6c, 0xc1, 0x8e, 0x7b, 0x49,
0xb2, 0xd9, 0x29, 0x3a, 0x3c, 0x87, 0x6a, 0xc7, 0x03, 0x89, 0xa7, 0xf8, 0x61, 0x35, 0x1a, 0x63, 0x8f, 0xdd, 0xc1, 0xe1, 0x2d, 0xe3, 0xc0, 0x2f, 0x17, 0x35, 0x7f, 0xc9, 0x81, 0x44, 0xc5, 0x9b,
0x84, 0x35, 0x10, 0x23, 0x7b, 0x6d, 0xe4, 0xee, 0xe1, 0x26, 0xfd, 0x00, 0x89, 0xa9, 0x06, 0x88, 0x26, 0x56, 0xbf, 0xcf, 0x01, 0x9b, 0x2e, 0x3f, 0x36, 0xee, 0xbc, 0xd8, 0x6e, 0xb2, 0xd8, 0x56,
0xd2, 0xaf, 0x9e, 0x52, 0x84, 0x14, 0x86, 0xbc, 0xb0, 0xc7, 0x81, 0x65, 0x6f, 0xaa, 0x8c, 0xa1, 0x86, 0x70, 0x43, 0x61, 0x2d, 0x0d, 0x0d, 0x73, 0x30, 0xa2, 0x04, 0xb5, 0xb1, 0x68, 0x7e, 0xf7,
0xd4, 0x8c, 0x2f, 0x55, 0x9b, 0x5a, 0x2a, 0x35, 0xca, 0x33, 0xe2, 0xef, 0x45, 0xe6, 0xef, 0x48, 0x67, 0x98, 0x05, 0x73, 0x0f, 0x44, 0x3f, 0x6b, 0xe3, 0x56, 0xdb, 0xf2, 0xa2, 0x48, 0xc8, 0xf2,
0x86, 0xa2, 0x2d, 0x79, 0x01, 0x3d, 0x78, 0xbf, 0xfc, 0x27, 0x07, 0xc0, 0x70, 0x9a, 0xe0, 0x15, 0x64, 0x1f, 0x34, 0xc7, 0x7d, 0x91, 0xa7, 0xf8, 0x41, 0x34, 0x2a, 0x63, 0x84, 0x55, 0x10, 0x23,
0xb0, 0x5e, 0x29, 0xea, 0xaa, 0x51, 0x2c, 0xe9, 0xf9, 0x62, 0xc1, 0xb8, 0x53, 0x28, 0x97, 0xd4, 0x7b, 0x2d, 0xe4, 0xec, 0xe1, 0x06, 0xfd, 0x01, 0x12, 0x13, 0x0d, 0x14, 0xa5, 0x5f, 0x3c, 0xa5,
0xdd, 0xfc, 0xcd, 0xbc, 0x9a, 0xe3, 0x23, 0xc2, 0x4a, 0xaf, 0x2f, 0xc7, 0x69, 0xa2, 0xea, 0x89, 0x08, 0x28, 0x0c, 0x78, 0x61, 0x97, 0x03, 0xf3, 0xee, 0x94, 0xe9, 0x03, 0xa9, 0x29, 0x4f, 0xaa,
0x40, 0x05, 0xac, 0x84, 0xb3, 0xef, 0xa9, 0x65, 0x9e, 0x13, 0x96, 0x7a, 0x7d, 0x39, 0x46, 0xb3, 0x3a, 0xb1, 0x54, 0x72, 0x98, 0x67, 0xa8, 0xbe, 0x17, 0x59, 0x7d, 0x87, 0x3c, 0x24, 0x75, 0xce,
0xee, 0x21, 0x17, 0x5e, 0x06, 0xab, 0xe1, 0x9c, 0x4c, 0xb6, 0xac, 0x67, 0xf2, 0x05, 0x3e, 0x2a, 0x35, 0x68, 0xfe, 0xf9, 0xca, 0x9f, 0x1c, 0x00, 0x81, 0xaf, 0xcc, 0xab, 0x60, 0xa5, 0x52, 0xd4,
0x5c, 0xe8, 0xf5, 0xe5, 0x25, 0x9a, 0x97, 0x61, 0x2b, 0x50, 0x06, 0xcb, 0xe1, 0xdc, 0x42, 0x91, 0x14, 0xbd, 0x58, 0xd2, 0xf2, 0xc5, 0x82, 0x7e, 0xa7, 0x50, 0x2e, 0x29, 0x5b, 0xf9, 0xed, 0xbc,
0x9f, 0x11, 0x12, 0xbd, 0xbe, 0xbc, 0x48, 0xd3, 0x0a, 0x18, 0x6e, 0x83, 0xd4, 0x68, 0x86, 0x71, 0x92, 0xe3, 0x43, 0xa9, 0x85, 0x6e, 0x2f, 0x1d, 0xa7, 0x8e, 0x8a, 0x2b, 0x02, 0x25, 0xb0, 0x10,
0x37, 0xaf, 0xdf, 0x32, 0x2a, 0xaa, 0x5e, 0xe4, 0x67, 0x85, 0x64, 0xaf, 0x2f, 0xf3, 0x41, 0x6e, 0xf4, 0xbe, 0xab, 0x94, 0x79, 0x2e, 0x35, 0xd7, 0xed, 0xa5, 0x63, 0xd4, 0xeb, 0x2e, 0x72, 0xe0,
0xb0, 0xaf, 0x84, 0xd9, 0x87, 0xdf, 0x89, 0x91, 0xcb, 0x3f, 0x47, 0xc1, 0xf2, 0xe8, 0x7f, 0x69, 0x15, 0xb0, 0x18, 0xf4, 0xc9, 0xca, 0x65, 0x2d, 0x9b, 0x2f, 0xf0, 0xe1, 0xd4, 0x85, 0x6e, 0x2f,
0x60, 0x1a, 0xfc, 0xaf, 0xa4, 0x15, 0x4b, 0xc5, 0x72, 0xe6, 0xb6, 0x51, 0xd6, 0x33, 0xfa, 0x9d, 0x3d, 0x47, 0xfd, 0xb2, 0x6c, 0x25, 0xa6, 0xc1, 0x7c, 0xd0, 0xb7, 0x50, 0xe4, 0xa7, 0x52, 0x89,
0xf2, 0xd8, 0x85, 0xfd, 0xab, 0xd0, 0xe4, 0x82, 0xd5, 0x84, 0x37, 0x80, 0x38, 0x9e, 0x9f, 0x53, 0x6e, 0x2f, 0x3d, 0x4b, 0xdd, 0x0a, 0x18, 0x6e, 0x80, 0xe4, 0xb0, 0x87, 0xbe, 0x93, 0xd7, 0x6e,
0x4b, 0xc5, 0x72, 0x5e, 0x37, 0x4a, 0xaa, 0x96, 0x2f, 0xe6, 0x78, 0x4e, 0x58, 0xef, 0xf5, 0xe5, 0xe9, 0x15, 0x45, 0x2b, 0xf2, 0x91, 0xd4, 0x52, 0xb7, 0x97, 0xe6, 0x7d, 0x5f, 0x7f, 0x7f, 0xa5,
0x55, 0x0a, 0x19, 0x19, 0x2a, 0xf8, 0x1e, 0xf8, 0xff, 0x38, 0xb8, 0x52, 0xd4, 0xf3, 0x85, 0x0f, 0x22, 0x0f, 0xbf, 0x11, 0x42, 0x57, 0x7e, 0x0e, 0x83, 0xf9, 0xe1, 0x4f, 0x1c, 0x98, 0x01, 0xff,
0x03, 0x6c, 0x54, 0x58, 0xeb, 0xf5, 0x65, 0x48, 0xb1, 0x95, 0xd0, 0x04, 0xc0, 0x2b, 0x60, 0x6d, 0x2b, 0xa9, 0xc5, 0x52, 0xb1, 0x9c, 0xbd, 0xad, 0x97, 0xb5, 0xac, 0x76, 0xa7, 0x3c, 0x92, 0xb0,
0x1c, 0x5a, 0xca, 0x94, 0xcb, 0x6a, 0x8e, 0x9f, 0x11, 0xf8, 0x5e, 0x5f, 0x4e, 0x50, 0x4c, 0xc9, 0x97, 0x0a, 0x75, 0x2e, 0x98, 0x0d, 0x78, 0x13, 0x08, 0xa3, 0xfe, 0x39, 0xa5, 0x54, 0x2c, 0xe7,
0x74, 0x5d, 0x54, 0x87, 0xd7, 0x40, 0x6a, 0x3c, 0x5b, 0x53, 0x3f, 0x52, 0x77, 0x75, 0x35, 0xc7, 0x35, 0xbd, 0xa4, 0xa8, 0xf9, 0x62, 0x8e, 0xe7, 0x52, 0x2b, 0xdd, 0x5e, 0x7a, 0x91, 0x42, 0x86,
0xcf, 0x0a, 0xb0, 0xd7, 0x97, 0x97, 0x69, 0xbe, 0x86, 0x3e, 0x41, 0x35, 0x82, 0xce, 0xe4, 0xbf, 0x86, 0x0a, 0xbe, 0x0b, 0xfe, 0x3f, 0x0a, 0xae, 0x14, 0xb5, 0x7c, 0xe1, 0x43, 0x1f, 0x1b, 0x4e,
0x99, 0xc9, 0xdf, 0x56, 0x73, 0xfc, 0x5c, 0x98, 0xff, 0xa6, 0x69, 0x35, 0x51, 0x9d, 0xda, 0x99, 0x2d, 0x77, 0x7b, 0x69, 0x48, 0xb1, 0x95, 0xc0, 0x04, 0xc0, 0xab, 0x60, 0x79, 0x14, 0x5a, 0xca,
0x2d, 0x1c, 0xbe, 0x10, 0x23, 0xcf, 0x5e, 0x88, 0x91, 0x2f, 0x8f, 0xc4, 0xc8, 0xe1, 0x91, 0xc8, 0x96, 0xcb, 0x4a, 0x8e, 0x9f, 0x4a, 0xf1, 0xdd, 0x5e, 0x3a, 0x41, 0x31, 0x25, 0xc3, 0x71, 0x50,
0x3d, 0x3d, 0x12, 0xb9, 0x3f, 0x8e, 0x44, 0xee, 0xd1, 0x4b, 0x31, 0xf2, 0xf4, 0xa5, 0x18, 0x79, 0x0d, 0x5e, 0x07, 0xc9, 0x51, 0x6f, 0x55, 0xf9, 0x48, 0xd9, 0xd2, 0x94, 0x1c, 0x1f, 0x49, 0xc1,
0xf6, 0x52, 0x8c, 0xdc, 0xff, 0xe7, 0x85, 0x78, 0xe0, 0xff, 0x29, 0xe4, 0xf7, 0x73, 0x75, 0xde, 0x6e, 0x2f, 0x3d, 0x4f, 0xfd, 0x55, 0xf4, 0x09, 0xaa, 0x12, 0x74, 0x26, 0xff, 0x76, 0x36, 0x7f,
0xdf, 0x21, 0x6f, 0xfd, 0x1d, 0x00, 0x00, 0xff, 0xff, 0xef, 0x91, 0x01, 0x67, 0x25, 0x0d, 0x00, 0x5b, 0xc9, 0xf1, 0xd3, 0x41, 0xfe, 0x6d, 0xc3, 0x6c, 0xa0, 0x1a, 0x2d, 0xa7, 0x5c, 0x38, 0x7c,
0x00, 0x21, 0x84, 0x9e, 0xbd, 0x10, 0x42, 0x5f, 0x1c, 0x09, 0xa1, 0xc3, 0x23, 0x81, 0x7b, 0x7a, 0x24,
0x70, 0x7f, 0x1c, 0x09, 0xdc, 0xa3, 0x97, 0x42, 0xe8, 0xe9, 0x4b, 0x21, 0xf4, 0xec, 0xa5, 0x10,
0xba, 0xf7, 0xcf, 0x0b, 0xf1, 0xc0, 0xfb, 0x17, 0xce, 0xeb, 0xe7, 0xdd, 0xa8, 0xb7, 0x43, 0xde,
0xfc, 0x3b, 0x00, 0x00, 0xff, 0xff, 0x2e, 0xc3, 0x29, 0x39, 0xdd, 0x0d, 0x00, 0x00,
} }
func (this *TextProposal) Equal(that interface{}) bool { func (this *TextProposal) Equal(that interface{}) bool {
@ -667,6 +709,44 @@ func (this *TallyResult) Equal(that interface{}) bool {
} }
return true return true
} }
func (m *WeightedVoteOption) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *WeightedVoteOption) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *WeightedVoteOption) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
{
size := m.Weight.Size()
i -= size
if _, err := m.Weight.MarshalTo(dAtA[i:]); err != nil {
return 0, err
}
i = encodeVarintGov(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x12
if m.Option != 0 {
i = encodeVarintGov(dAtA, i, uint64(m.Option))
i--
dAtA[i] = 0x8
}
return len(dAtA) - i, nil
}
func (m *TextProposal) Marshal() (dAtA []byte, err error) { func (m *TextProposal) Marshal() (dAtA []byte, err error) {
size := m.Size() size := m.Size()
dAtA = make([]byte, size) dAtA = make([]byte, size)
@ -937,10 +1017,19 @@ func (m *Vote) MarshalToSizedBuffer(dAtA []byte) (int, error) {
_ = i _ = i
var l int var l int
_ = l _ = l
if m.Option != 0 { if len(m.Options) > 0 {
i = encodeVarintGov(dAtA, i, uint64(m.Option)) for iNdEx := len(m.Options) - 1; iNdEx >= 0; iNdEx-- {
{
size, err := m.Options[iNdEx].MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintGov(dAtA, i, uint64(size))
}
i-- i--
dAtA[i] = 0x18 dAtA[i] = 0x1a
}
} }
if len(m.Voter) > 0 { if len(m.Voter) > 0 {
i -= len(m.Voter) i -= len(m.Voter)
@ -1097,6 +1186,20 @@ func encodeVarintGov(dAtA []byte, offset int, v uint64) int {
dAtA[offset] = uint8(v) dAtA[offset] = uint8(v)
return base return base
} }
func (m *WeightedVoteOption) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.Option != 0 {
n += 1 + sovGov(uint64(m.Option))
}
l = m.Weight.Size()
n += 1 + l + sovGov(uint64(l))
return n
}
func (m *TextProposal) Size() (n int) { func (m *TextProposal) Size() (n int) {
if m == nil { if m == nil {
return 0 return 0
@ -1201,8 +1304,11 @@ func (m *Vote) Size() (n int) {
if l > 0 { if l > 0 {
n += 1 + l + sovGov(uint64(l)) n += 1 + l + sovGov(uint64(l))
} }
if m.Option != 0 { if len(m.Options) > 0 {
n += 1 + sovGov(uint64(m.Option)) for _, e := range m.Options {
l = e.Size()
n += 1 + l + sovGov(uint64(l))
}
} }
return n return n
} }
@ -1256,6 +1362,112 @@ func sovGov(x uint64) (n int) {
func sozGov(x uint64) (n int) { func sozGov(x uint64) (n int) {
return sovGov(uint64((x << 1) ^ uint64((int64(x) >> 63)))) return sovGov(uint64((x << 1) ^ uint64((int64(x) >> 63))))
} }
func (m *WeightedVoteOption) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGov
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: WeightedVoteOption: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: WeightedVoteOption: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field Option", wireType)
}
m.Option = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGov
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.Option |= VoteOption(b&0x7F) << shift
if b < 0x80 {
break
}
}
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Weight", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGov
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthGov
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthGov
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if err := m.Weight.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipGov(dAtA[iNdEx:])
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthGov
}
if (iNdEx + skippy) < 0 {
return ErrInvalidLengthGov
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *TextProposal) Unmarshal(dAtA []byte) error { func (m *TextProposal) Unmarshal(dAtA []byte) error {
l := len(dAtA) l := len(dAtA)
iNdEx := 0 iNdEx := 0
@ -2107,10 +2319,10 @@ func (m *Vote) Unmarshal(dAtA []byte) error {
m.Voter = string(dAtA[iNdEx:postIndex]) m.Voter = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex iNdEx = postIndex
case 3: case 3:
if wireType != 0 { if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Option", wireType) return fmt.Errorf("proto: wrong wireType = %d for field Options", wireType)
} }
m.Option = 0 var msglen int
for shift := uint(0); ; shift += 7 { for shift := uint(0); ; shift += 7 {
if shift >= 64 { if shift >= 64 {
return ErrIntOverflowGov return ErrIntOverflowGov
@ -2120,11 +2332,26 @@ func (m *Vote) Unmarshal(dAtA []byte) error {
} }
b := dAtA[iNdEx] b := dAtA[iNdEx]
iNdEx++ iNdEx++
m.Option |= VoteOption(b&0x7F) << shift msglen |= int(b&0x7F) << shift
if b < 0x80 { if b < 0x80 {
break break
} }
} }
if msglen < 0 {
return ErrInvalidLengthGov
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthGov
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Options = append(m.Options, WeightedVoteOption{})
if err := m.Options[len(m.Options)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
default: default:
iNdEx = preIndex iNdEx = preIndex
skippy, err := skipGov(dAtA[iNdEx:]) skippy, err := skipGov(dAtA[iNdEx:])

View File

@ -16,11 +16,12 @@ import (
const ( const (
TypeMsgDeposit = "deposit" TypeMsgDeposit = "deposit"
TypeMsgVote = "vote" TypeMsgVote = "vote"
TypeMsgWeightedVote = "weighted_vote"
TypeMsgSubmitProposal = "submit_proposal" TypeMsgSubmitProposal = "submit_proposal"
) )
var ( var (
_, _, _ sdk.Msg = &MsgSubmitProposal{}, &MsgDeposit{}, &MsgVote{} _, _, _, _ sdk.Msg = &MsgSubmitProposal{}, &MsgDeposit{}, &MsgVote{}, &MsgWeightedVote{}
_ types.UnpackInterfacesMessage = &MsgSubmitProposal{} _ types.UnpackInterfacesMessage = &MsgSubmitProposal{}
) )
@ -192,6 +193,7 @@ func (msg MsgVote) ValidateBasic() error {
if msg.Voter == "" { if msg.Voter == "" {
return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, msg.Voter) return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, msg.Voter)
} }
if !ValidVoteOption(msg.Option) { if !ValidVoteOption(msg.Option) {
return sdkerrors.Wrap(ErrInvalidVote, msg.Option.String()) return sdkerrors.Wrap(ErrInvalidVote, msg.Option.String())
} }
@ -216,3 +218,63 @@ func (msg MsgVote) GetSigners() []sdk.AccAddress {
voter, _ := sdk.AccAddressFromBech32(msg.Voter) voter, _ := sdk.AccAddressFromBech32(msg.Voter)
return []sdk.AccAddress{voter} return []sdk.AccAddress{voter}
} }
// NewMsgWeightedVote creates a message to cast a vote on an active proposal
//nolint:interfacer
func NewMsgWeightedVote(voter sdk.AccAddress, proposalID uint64, options WeightedVoteOptions) *MsgWeightedVote {
return &MsgWeightedVote{proposalID, voter.String(), options}
}
// Route implements Msg
func (msg MsgWeightedVote) Route() string { return RouterKey }
// Type implements Msg
func (msg MsgWeightedVote) Type() string { return TypeMsgWeightedVote }
// ValidateBasic implements Msg
func (msg MsgWeightedVote) ValidateBasic() error {
if msg.Voter == "" {
return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, msg.Voter)
}
if len(msg.Options) == 0 {
return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, WeightedVoteOptions(msg.Options).String())
}
totalWeight := sdk.NewDec(0)
usedOptions := make(map[VoteOption]bool)
for _, option := range msg.Options {
if !ValidWeightedVoteOption(option) {
return sdkerrors.Wrap(ErrInvalidVote, option.String())
}
totalWeight = totalWeight.Add(option.Weight)
if usedOptions[option.Option] {
return sdkerrors.Wrap(ErrInvalidVote, "Duplicated vote option")
}
usedOptions[option.Option] = true
}
if totalWeight.GT(sdk.NewDec(1)) {
return sdkerrors.Wrap(ErrInvalidVote, "Total weight overflow 1.00")
}
return nil
}
// String implements the Stringer interface
func (msg MsgWeightedVote) String() string {
out, _ := yaml.Marshal(msg)
return string(out)
}
// GetSignBytes implements Msg
func (msg MsgWeightedVote) GetSignBytes() []byte {
bz := ModuleCdc.MustMarshalJSON(&msg)
return sdk.MustSortJSON(bz)
}
// GetSigners implements Msg
func (msg MsgWeightedVote) GetSigners() []sdk.AccAddress {
voter, _ := sdk.AccAddressFromBech32(msg.Voter)
return []sdk.AccAddress{voter}
}

View File

@ -92,7 +92,7 @@ func TestMsgDeposit(t *testing.T) {
} }
} }
// test ValidateBasic for MsgDeposit // test ValidateBasic for MsgVote
func TestMsgVote(t *testing.T) { func TestMsgVote(t *testing.T) {
tests := []struct { tests := []struct {
proposalID uint64 proposalID uint64
@ -118,6 +118,47 @@ func TestMsgVote(t *testing.T) {
} }
} }
// test ValidateBasic for MsgWeightedVote
func TestMsgWeightedVote(t *testing.T) {
tests := []struct {
proposalID uint64
voterAddr sdk.AccAddress
options WeightedVoteOptions
expectPass bool
}{
{0, addrs[0], NewNonSplitVoteOption(OptionYes), true},
{0, sdk.AccAddress{}, NewNonSplitVoteOption(OptionYes), false},
{0, addrs[0], NewNonSplitVoteOption(OptionNo), true},
{0, addrs[0], NewNonSplitVoteOption(OptionNoWithVeto), true},
{0, addrs[0], NewNonSplitVoteOption(OptionAbstain), true},
{0, addrs[0], WeightedVoteOptions{ // weight sum > 1
WeightedVoteOption{Option: OptionYes, Weight: sdk.NewDec(1)},
WeightedVoteOption{Option: OptionAbstain, Weight: sdk.NewDec(1)},
}, false},
{0, addrs[0], WeightedVoteOptions{ // duplicate option
WeightedVoteOption{Option: OptionYes, Weight: sdk.NewDecWithPrec(5, 1)},
WeightedVoteOption{Option: OptionYes, Weight: sdk.NewDecWithPrec(5, 1)},
}, false},
{0, addrs[0], WeightedVoteOptions{ // zero weight
WeightedVoteOption{Option: OptionYes, Weight: sdk.NewDec(0)},
}, false},
{0, addrs[0], WeightedVoteOptions{ // negative weight
WeightedVoteOption{Option: OptionYes, Weight: sdk.NewDec(-1)},
}, false},
{0, addrs[0], WeightedVoteOptions{}, false},
{0, addrs[0], NewNonSplitVoteOption(VoteOption(0x13)), false},
}
for i, tc := range tests {
msg := NewMsgWeightedVote(tc.voterAddr, tc.proposalID, tc.options)
if tc.expectPass {
require.Nil(t, msg.ValidateBasic(), "test: %v", i)
} else {
require.NotNil(t, msg.ValidateBasic(), "test: %v", i)
}
}
}
// this tests that Amino JSON MsgSubmitProposal.GetSignBytes() still works with Content as Any using the ModuleCdc // this tests that Amino JSON MsgSubmitProposal.GetSignBytes() still works with Content as Any using the ModuleCdc
func TestMsgSubmitProposal_GetSignBytes(t *testing.T) { func TestMsgSubmitProposal_GetSignBytes(t *testing.T) {
msg, err := NewMsgSubmitProposal(NewTextProposal("test", "abcd"), sdk.NewCoins(), sdk.AccAddress{}) msg, err := NewMsgSubmitProposal(NewTextProposal("test", "abcd"), sdk.NewCoins(), sdk.AccAddress{})

View File

@ -12,19 +12,19 @@ type ValidatorGovInfo struct {
BondedTokens sdk.Int // Power of a Validator BondedTokens sdk.Int // Power of a Validator
DelegatorShares sdk.Dec // Total outstanding delegator shares DelegatorShares sdk.Dec // Total outstanding delegator shares
DelegatorDeductions sdk.Dec // Delegator deductions from validator's delegators voting independently DelegatorDeductions sdk.Dec // Delegator deductions from validator's delegators voting independently
Vote VoteOption // Vote of the validator Vote WeightedVoteOptions // Vote of the validator
} }
// NewValidatorGovInfo creates a ValidatorGovInfo instance // NewValidatorGovInfo creates a ValidatorGovInfo instance
func NewValidatorGovInfo(address sdk.ValAddress, bondedTokens sdk.Int, delegatorShares, func NewValidatorGovInfo(address sdk.ValAddress, bondedTokens sdk.Int, delegatorShares,
delegatorDeductions sdk.Dec, vote VoteOption) ValidatorGovInfo { delegatorDeductions sdk.Dec, options WeightedVoteOptions) ValidatorGovInfo {
return ValidatorGovInfo{ return ValidatorGovInfo{
Address: address, Address: address,
BondedTokens: bondedTokens, BondedTokens: bondedTokens,
DelegatorShares: delegatorShares, DelegatorShares: delegatorShares,
DelegatorDeductions: delegatorDeductions, DelegatorDeductions: delegatorDeductions,
Vote: vote, Vote: options,
} }
} }

View File

@ -156,6 +156,45 @@ func (m *MsgVote) XXX_DiscardUnknown() {
var xxx_messageInfo_MsgVote proto.InternalMessageInfo var xxx_messageInfo_MsgVote proto.InternalMessageInfo
// MsgVote defines a message to cast a vote.
type MsgWeightedVote struct {
ProposalId uint64 `protobuf:"varint,1,opt,name=proposal_id,json=proposalId,proto3" json:"proposal_id" yaml:"proposal_id"`
Voter string `protobuf:"bytes,2,opt,name=voter,proto3" json:"voter,omitempty"`
Options []WeightedVoteOption `protobuf:"bytes,3,rep,name=options,proto3" json:"options"`
}
func (m *MsgWeightedVote) Reset() { *m = MsgWeightedVote{} }
func (*MsgWeightedVote) ProtoMessage() {}
func (*MsgWeightedVote) Descriptor() ([]byte, []int) {
return fileDescriptor_3c053992595e3dce, []int{3}
}
func (m *MsgWeightedVote) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *MsgWeightedVote) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_MsgWeightedVote.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *MsgWeightedVote) XXX_Merge(src proto.Message) {
xxx_messageInfo_MsgWeightedVote.Merge(m, src)
}
func (m *MsgWeightedVote) XXX_Size() int {
return m.Size()
}
func (m *MsgWeightedVote) XXX_DiscardUnknown() {
xxx_messageInfo_MsgWeightedVote.DiscardUnknown(m)
}
var xxx_messageInfo_MsgWeightedVote proto.InternalMessageInfo
// MsgVoteResponse defines the Msg/Vote response type. // MsgVoteResponse defines the Msg/Vote response type.
type MsgVoteResponse struct { type MsgVoteResponse struct {
} }
@ -164,7 +203,7 @@ func (m *MsgVoteResponse) Reset() { *m = MsgVoteResponse{} }
func (m *MsgVoteResponse) String() string { return proto.CompactTextString(m) } func (m *MsgVoteResponse) String() string { return proto.CompactTextString(m) }
func (*MsgVoteResponse) ProtoMessage() {} func (*MsgVoteResponse) ProtoMessage() {}
func (*MsgVoteResponse) Descriptor() ([]byte, []int) { func (*MsgVoteResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_3c053992595e3dce, []int{3} return fileDescriptor_3c053992595e3dce, []int{4}
} }
func (m *MsgVoteResponse) XXX_Unmarshal(b []byte) error { func (m *MsgVoteResponse) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b) return m.Unmarshal(b)
@ -193,6 +232,43 @@ func (m *MsgVoteResponse) XXX_DiscardUnknown() {
var xxx_messageInfo_MsgVoteResponse proto.InternalMessageInfo var xxx_messageInfo_MsgVoteResponse proto.InternalMessageInfo
// MsgWeightedVoteResponse defines the MsgWeightedVote response type.
type MsgWeightedVoteResponse struct {
}
func (m *MsgWeightedVoteResponse) Reset() { *m = MsgWeightedVoteResponse{} }
func (m *MsgWeightedVoteResponse) String() string { return proto.CompactTextString(m) }
func (*MsgWeightedVoteResponse) ProtoMessage() {}
func (*MsgWeightedVoteResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_3c053992595e3dce, []int{5}
}
func (m *MsgWeightedVoteResponse) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *MsgWeightedVoteResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_MsgWeightedVoteResponse.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *MsgWeightedVoteResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_MsgWeightedVoteResponse.Merge(m, src)
}
func (m *MsgWeightedVoteResponse) XXX_Size() int {
return m.Size()
}
func (m *MsgWeightedVoteResponse) XXX_DiscardUnknown() {
xxx_messageInfo_MsgWeightedVoteResponse.DiscardUnknown(m)
}
var xxx_messageInfo_MsgWeightedVoteResponse proto.InternalMessageInfo
// MsgDeposit defines a message to submit a deposit to an existing proposal. // MsgDeposit defines a message to submit a deposit to an existing proposal.
type MsgDeposit struct { type MsgDeposit struct {
ProposalId uint64 `protobuf:"varint,1,opt,name=proposal_id,json=proposalId,proto3" json:"proposal_id" yaml:"proposal_id"` ProposalId uint64 `protobuf:"varint,1,opt,name=proposal_id,json=proposalId,proto3" json:"proposal_id" yaml:"proposal_id"`
@ -203,7 +279,7 @@ type MsgDeposit struct {
func (m *MsgDeposit) Reset() { *m = MsgDeposit{} } func (m *MsgDeposit) Reset() { *m = MsgDeposit{} }
func (*MsgDeposit) ProtoMessage() {} func (*MsgDeposit) ProtoMessage() {}
func (*MsgDeposit) Descriptor() ([]byte, []int) { func (*MsgDeposit) Descriptor() ([]byte, []int) {
return fileDescriptor_3c053992595e3dce, []int{4} return fileDescriptor_3c053992595e3dce, []int{6}
} }
func (m *MsgDeposit) XXX_Unmarshal(b []byte) error { func (m *MsgDeposit) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b) return m.Unmarshal(b)
@ -240,7 +316,7 @@ func (m *MsgDepositResponse) Reset() { *m = MsgDepositResponse{} }
func (m *MsgDepositResponse) String() string { return proto.CompactTextString(m) } func (m *MsgDepositResponse) String() string { return proto.CompactTextString(m) }
func (*MsgDepositResponse) ProtoMessage() {} func (*MsgDepositResponse) ProtoMessage() {}
func (*MsgDepositResponse) Descriptor() ([]byte, []int) { func (*MsgDepositResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_3c053992595e3dce, []int{5} return fileDescriptor_3c053992595e3dce, []int{7}
} }
func (m *MsgDepositResponse) XXX_Unmarshal(b []byte) error { func (m *MsgDepositResponse) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b) return m.Unmarshal(b)
@ -273,7 +349,9 @@ func init() {
proto.RegisterType((*MsgSubmitProposal)(nil), "cosmos.gov.v1beta1.MsgSubmitProposal") proto.RegisterType((*MsgSubmitProposal)(nil), "cosmos.gov.v1beta1.MsgSubmitProposal")
proto.RegisterType((*MsgSubmitProposalResponse)(nil), "cosmos.gov.v1beta1.MsgSubmitProposalResponse") proto.RegisterType((*MsgSubmitProposalResponse)(nil), "cosmos.gov.v1beta1.MsgSubmitProposalResponse")
proto.RegisterType((*MsgVote)(nil), "cosmos.gov.v1beta1.MsgVote") proto.RegisterType((*MsgVote)(nil), "cosmos.gov.v1beta1.MsgVote")
proto.RegisterType((*MsgWeightedVote)(nil), "cosmos.gov.v1beta1.MsgWeightedVote")
proto.RegisterType((*MsgVoteResponse)(nil), "cosmos.gov.v1beta1.MsgVoteResponse") proto.RegisterType((*MsgVoteResponse)(nil), "cosmos.gov.v1beta1.MsgVoteResponse")
proto.RegisterType((*MsgWeightedVoteResponse)(nil), "cosmos.gov.v1beta1.MsgWeightedVoteResponse")
proto.RegisterType((*MsgDeposit)(nil), "cosmos.gov.v1beta1.MsgDeposit") proto.RegisterType((*MsgDeposit)(nil), "cosmos.gov.v1beta1.MsgDeposit")
proto.RegisterType((*MsgDepositResponse)(nil), "cosmos.gov.v1beta1.MsgDepositResponse") proto.RegisterType((*MsgDepositResponse)(nil), "cosmos.gov.v1beta1.MsgDepositResponse")
} }
@ -281,44 +359,48 @@ func init() {
func init() { proto.RegisterFile("cosmos/gov/v1beta1/tx.proto", fileDescriptor_3c053992595e3dce) } func init() { proto.RegisterFile("cosmos/gov/v1beta1/tx.proto", fileDescriptor_3c053992595e3dce) }
var fileDescriptor_3c053992595e3dce = []byte{ var fileDescriptor_3c053992595e3dce = []byte{
// 586 bytes of a gzipped FileDescriptorProto // 655 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x54, 0xbf, 0x6f, 0xd3, 0x40, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x55, 0xc1, 0x6b, 0x13, 0x4f,
0x14, 0xb6, 0x93, 0xd2, 0xd0, 0x8b, 0x94, 0xd2, 0x53, 0x84, 0x12, 0xb7, 0xb2, 0x23, 0xa3, 0xa2, 0x14, 0xde, 0x4d, 0xfa, 0x6b, 0x7e, 0x7d, 0x91, 0xd6, 0x2e, 0x41, 0x93, 0x6d, 0xd9, 0x0d, 0x2b,
0x2c, 0xb1, 0x69, 0x90, 0x18, 0xca, 0x44, 0x8a, 0x10, 0x20, 0x45, 0x80, 0x91, 0x18, 0x58, 0x22, 0x2d, 0x01, 0xe9, 0xae, 0x8d, 0xe0, 0xa1, 0x9e, 0x4c, 0xa5, 0xa8, 0x10, 0xd4, 0x15, 0x14, 0xbc,
0xdb, 0x71, 0x8d, 0x45, 0xe2, 0x67, 0xe5, 0x2e, 0x51, 0xb3, 0x31, 0x22, 0x06, 0x60, 0x64, 0xcc, 0xd4, 0x4d, 0xb2, 0x9d, 0x2e, 0x26, 0xfb, 0x96, 0xcc, 0x24, 0x34, 0x37, 0x8f, 0x9e, 0xd4, 0xa3,
0xcc, 0x86, 0xc4, 0x1f, 0x51, 0x31, 0x75, 0x64, 0x40, 0x01, 0x25, 0x0b, 0x30, 0xf6, 0x2f, 0x40, 0xc7, 0x9e, 0xbd, 0x09, 0xfe, 0x11, 0xd5, 0x53, 0x8f, 0x1e, 0x24, 0x4a, 0x7b, 0x51, 0xf1, 0xd4,
0xbe, 0x1f, 0x69, 0xd5, 0xa4, 0x01, 0xa4, 0x4e, 0xc9, 0x7b, 0xdf, 0xfb, 0x3e, 0xdd, 0xf7, 0xdd, 0xbf, 0x40, 0x76, 0x66, 0x36, 0x8d, 0x6d, 0x12, 0x2a, 0x14, 0x4f, 0xc9, 0x7b, 0xdf, 0xfb, 0xbe,
0xf3, 0xa1, 0x4d, 0x1f, 0x48, 0x17, 0x88, 0x1d, 0xc2, 0xc0, 0x1e, 0xec, 0x78, 0x01, 0x75, 0x77, 0xbc, 0xef, 0xcd, 0x9b, 0x09, 0x2c, 0xd4, 0x91, 0xb6, 0x90, 0x3a, 0x04, 0xbb, 0x4e, 0x77, 0xb5,
0x6c, 0x7a, 0x60, 0x25, 0x3d, 0xa0, 0x80, 0x31, 0x07, 0xad, 0x10, 0x06, 0x96, 0x00, 0x35, 0x5d, 0xe6, 0x33, 0x6f, 0xd5, 0x61, 0x3b, 0x76, 0xd4, 0x46, 0x86, 0x9a, 0x26, 0x40, 0x9b, 0x60, 0xd7,
0x10, 0x3c, 0x97, 0x04, 0x33, 0x86, 0x0f, 0x51, 0xcc, 0x39, 0xda, 0xd6, 0x02, 0xc1, 0x94, 0xcf, 0x96, 0xa0, 0x6e, 0x48, 0x42, 0xcd, 0xa3, 0xfe, 0x80, 0x51, 0xc7, 0x20, 0x14, 0x1c, 0x7d, 0x71,
0xd1, 0x32, 0x47, 0x5b, 0xac, 0xb2, 0x85, 0x3c, 0x87, 0x8a, 0x21, 0x84, 0xc0, 0xfb, 0xe9, 0x3f, 0x84, 0x60, 0xcc, 0x17, 0x68, 0x41, 0xa0, 0x9b, 0x3c, 0x72, 0xa4, 0xbc, 0x80, 0x72, 0x04, 0x09,
0x49, 0x08, 0x01, 0xc2, 0x4e, 0x60, 0xb3, 0xca, 0xeb, 0xef, 0xdb, 0x6e, 0x3c, 0xe4, 0x90, 0xf9, 0x8a, 0x7c, 0xfc, 0x2d, 0x21, 0x10, 0x44, 0xd2, 0xf4, 0x1d, 0x1e, 0xd5, 0x3a, 0x5b, 0x8e, 0x17,
0x2e, 0x83, 0x36, 0x9a, 0x24, 0x7c, 0xda, 0xf7, 0xba, 0x11, 0x7d, 0xdc, 0x83, 0x04, 0x88, 0xdb, 0xf6, 0x04, 0x64, 0xbd, 0x4e, 0xc1, 0x7c, 0x95, 0x92, 0x47, 0x9d, 0x5a, 0x2b, 0x60, 0x0f, 0xda,
0xc1, 0xb7, 0x51, 0xce, 0x87, 0x98, 0x06, 0x31, 0x2d, 0xa9, 0x15, 0xb5, 0x9a, 0xaf, 0x17, 0x2d, 0x18, 0x21, 0xf5, 0x9a, 0xda, 0x4d, 0xc8, 0xd4, 0x31, 0x64, 0x7e, 0xc8, 0xf2, 0x6a, 0x51, 0x2d,
0x2e, 0x61, 0x49, 0x09, 0xeb, 0x4e, 0x3c, 0x6c, 0xe4, 0xbf, 0x7c, 0xae, 0xe5, 0xf6, 0xf8, 0xa0, 0x65, 0xcb, 0x39, 0x5b, 0x48, 0xd8, 0x89, 0x84, 0x7d, 0x2b, 0xec, 0x55, 0xb2, 0x9f, 0x3e, 0xac,
0x23, 0x19, 0xf8, 0xad, 0x8a, 0xd6, 0xa3, 0x38, 0xa2, 0x91, 0xdb, 0x69, 0xb5, 0x83, 0x04, 0x48, 0x64, 0xd6, 0x45, 0xa1, 0x9b, 0x30, 0xb4, 0x57, 0x2a, 0xcc, 0x05, 0x61, 0xc0, 0x02, 0xaf, 0xb9,
0x44, 0x4b, 0x99, 0x4a, 0xb6, 0x9a, 0xaf, 0x97, 0x2d, 0x71, 0xd8, 0xd4, 0xb7, 0x0c, 0xc3, 0xda, 0xd9, 0xf0, 0x23, 0xa4, 0x01, 0xcb, 0xa7, 0x8a, 0xe9, 0x52, 0xb6, 0x5c, 0xb0, 0x65, 0xb3, 0xb1,
0x83, 0x28, 0x6e, 0x3c, 0x3c, 0x1c, 0x1b, 0xca, 0xf1, 0xd8, 0xb8, 0x3a, 0x74, 0xbb, 0x9d, 0x5d, 0xef, 0x64, 0x18, 0xf6, 0x3a, 0x06, 0x61, 0xe5, 0xde, 0x5e, 0xdf, 0x54, 0x8e, 0xfa, 0xe6, 0xa5,
0xf3, 0x0c, 0xdf, 0xfc, 0xf8, 0xdd, 0xa8, 0x86, 0x11, 0x7d, 0xd1, 0xf7, 0x2c, 0x1f, 0xba, 0xc2, 0x9e, 0xd7, 0x6a, 0xae, 0x59, 0x27, 0xf8, 0xd6, 0xbb, 0xaf, 0x66, 0x89, 0x04, 0x6c, 0xbb, 0x53,
0xb3, 0xf8, 0xa9, 0x91, 0xf6, 0x4b, 0x9b, 0x0e, 0x93, 0x80, 0x30, 0x29, 0xe2, 0x14, 0x04, 0xfb, 0xb3, 0xeb, 0xd8, 0x92, 0x9e, 0xe5, 0xc7, 0x0a, 0x6d, 0x3c, 0x77, 0x58, 0x2f, 0xf2, 0x29, 0x97,
0x2e, 0x27, 0x63, 0x0d, 0x5d, 0x4e, 0x98, 0xb3, 0xa0, 0x57, 0xca, 0x56, 0xd4, 0xea, 0x9a, 0x33, 0xa2, 0xee, 0xac, 0x64, 0xdf, 0x16, 0x64, 0x4d, 0x87, 0xff, 0x23, 0xee, 0xcc, 0x6f, 0xe7, 0xd3,
0xab, 0x77, 0xaf, 0xbc, 0x1e, 0x19, 0xca, 0x87, 0x91, 0xa1, 0xfc, 0x1c, 0x19, 0xca, 0xab, 0x6f, 0x45, 0xb5, 0x34, 0xe3, 0x0e, 0xe2, 0xb5, 0x8b, 0x2f, 0x77, 0x4d, 0xe5, 0xed, 0xae, 0xa9, 0x7c,
0x15, 0xc5, 0xf4, 0x51, 0x79, 0x2e, 0x10, 0x27, 0x20, 0x09, 0xc4, 0x24, 0xc0, 0xf7, 0x50, 0x3e, 0xdf, 0x35, 0x95, 0x17, 0x5f, 0x8a, 0x8a, 0x55, 0x87, 0xc2, 0xa9, 0x81, 0xb8, 0x3e, 0x8d, 0x30,
0x11, 0xbd, 0x56, 0xd4, 0x66, 0xe1, 0xac, 0x34, 0xb6, 0x7f, 0x8f, 0x8d, 0xd3, 0xed, 0xe3, 0xb1, 0xa4, 0xbe, 0xb6, 0x01, 0xd9, 0x48, 0xe6, 0x36, 0x83, 0x06, 0x1f, 0xce, 0x54, 0x65, 0xe9, 0x67,
0x81, 0xb9, 0x8d, 0x53, 0x4d, 0xd3, 0x41, 0xb2, 0x7a, 0xd0, 0x36, 0x3f, 0xa9, 0x28, 0xd7, 0x24, 0xdf, 0x1c, 0x4e, 0x1f, 0xf5, 0x4d, 0x4d, 0xd8, 0x18, 0x4a, 0x5a, 0x2e, 0x24, 0xd1, 0xdd, 0x86,
0xe1, 0x33, 0xa0, 0x17, 0xa6, 0x89, 0x8b, 0xe8, 0xd2, 0x00, 0x68, 0xd0, 0x2b, 0x65, 0x98, 0x47, 0xf5, 0x5e, 0x85, 0x4c, 0x95, 0x92, 0xc7, 0xc8, 0xce, 0x4d, 0x53, 0xcb, 0xc1, 0x7f, 0x5d, 0x64,
0x5e, 0xe0, 0x5b, 0x68, 0x15, 0x12, 0x1a, 0x41, 0xcc, 0xac, 0x17, 0xea, 0xba, 0x35, 0xbf, 0x8f, 0x7e, 0x3b, 0x9f, 0xe2, 0x1e, 0x45, 0xa0, 0xdd, 0x80, 0x69, 0x8c, 0x58, 0x80, 0x21, 0xb7, 0x3e,
0x56, 0x7a, 0x8e, 0x47, 0x6c, 0xca, 0x11, 0xd3, 0x0b, 0x82, 0xd9, 0x40, 0xeb, 0xe2, 0xc8, 0x32, 0x5b, 0x36, 0xec, 0xd3, 0xfb, 0x68, 0xc7, 0x7d, 0xdc, 0xe7, 0x55, 0xae, 0xac, 0x1e, 0x31, 0x98,
0x0e, 0xf3, 0x97, 0x8a, 0x50, 0x93, 0x84, 0x32, 0xe8, 0x8b, 0x72, 0xb2, 0x85, 0xd6, 0xc4, 0xc5, 0x8f, 0x2a, 0xcc, 0x55, 0x29, 0x79, 0xe2, 0x07, 0x64, 0x9b, 0xf9, 0x8d, 0x7f, 0xd0, 0xfb, 0x06,
0x83, 0x74, 0x73, 0xd2, 0xc0, 0x3e, 0x5a, 0x75, 0xbb, 0xd0, 0x8f, 0x69, 0x29, 0xfb, 0xb7, 0xad, 0x64, 0x44, 0x37, 0x34, 0x9f, 0xe6, 0x0b, 0xb4, 0x3c, 0xaa, 0xf9, 0xe1, 0x86, 0x84, 0x89, 0xca,
0xba, 0x91, 0x6e, 0xd5, 0x7f, 0xed, 0x8e, 0x90, 0x5e, 0x60, 0xbf, 0x88, 0xf0, 0x89, 0x55, 0x99, 0x54, 0xbc, 0x4d, 0x6e, 0x42, 0x1e, 0xe1, 0x65, 0x9e, 0x5b, 0x89, 0x19, 0xc9, 0xd1, 0x5a, 0x05,
0x40, 0xfd, 0x4d, 0x06, 0x65, 0x9b, 0x24, 0xc4, 0xfb, 0xa8, 0x70, 0xe6, 0x1b, 0xda, 0x5e, 0x14, 0xb8, 0x7c, 0xc2, 0xdd, 0x00, 0xfa, 0xa1, 0x02, 0x54, 0x29, 0x49, 0xf6, 0xe9, 0xbc, 0x4c, 0x2f,
0xf4, 0xdc, 0x66, 0x69, 0xb5, 0x7f, 0x1a, 0x9b, 0x2d, 0xe0, 0x7d, 0xb4, 0xc2, 0x96, 0x66, 0xf3, 0xc2, 0x8c, 0xdc, 0x6f, 0x4c, 0x8c, 0x1f, 0x27, 0xb4, 0x3a, 0x4c, 0x7b, 0x2d, 0xec, 0x84, 0x4c,
0x1c, 0x5a, 0x0a, 0x6a, 0xd7, 0x96, 0x80, 0x33, 0xa5, 0x27, 0x28, 0x27, 0xef, 0x4d, 0x3f, 0x67, 0x7a, 0x9f, 0x70, 0x79, 0xae, 0xc5, 0x76, 0xff, 0xea, 0x8a, 0x48, 0xe9, 0x11, 0x93, 0xc9, 0x81,
0x5e, 0xe0, 0xda, 0xf5, 0xe5, 0xb8, 0x94, 0x6c, 0x34, 0x0e, 0x27, 0xba, 0x7a, 0x34, 0xd1, 0xd5, 0x76, 0x6c, 0x35, 0x99, 0x40, 0xf9, 0x57, 0x0a, 0xd2, 0x55, 0x4a, 0xb4, 0x2d, 0x98, 0x3d, 0xf1,
0x1f, 0x13, 0x5d, 0x7d, 0x3f, 0xd5, 0x95, 0xa3, 0xa9, 0xae, 0x7c, 0x9d, 0xea, 0xca, 0xf3, 0xe5, 0x54, 0x2c, 0x8d, 0x3a, 0x92, 0x53, 0x17, 0x48, 0x5f, 0x39, 0x53, 0xd9, 0xe0, 0x9e, 0xdd, 0x81,
0x17, 0x70, 0xc0, 0x1e, 0x3a, 0x76, 0x0d, 0xde, 0x2a, 0x7b, 0x61, 0x6e, 0xfe, 0x09, 0x00, 0x00, 0x29, 0xbe, 0x5f, 0x0b, 0x63, 0x68, 0x31, 0xa8, 0x5f, 0x99, 0x00, 0x0e, 0x94, 0x9e, 0xc1, 0x85,
0xff, 0xff, 0x7f, 0x09, 0x3f, 0x57, 0x54, 0x05, 0x00, 0x00, 0x3f, 0x36, 0x76, 0x1c, 0x69, 0xb8, 0x48, 0xbf, 0x7a, 0x86, 0xa2, 0xc1, 0x2f, 0x3c, 0x84, 0x4c,
0xb2, 0x19, 0xc6, 0x18, 0x9e, 0xc4, 0xf5, 0xe5, 0xc9, 0x78, 0x22, 0x59, 0xa9, 0xec, 0x1d, 0x18,
0xea, 0xfe, 0x81, 0xa1, 0x7e, 0x3b, 0x30, 0xd4, 0x37, 0x87, 0x86, 0xb2, 0x7f, 0x68, 0x28, 0x9f,
0x0f, 0x0d, 0xe5, 0xe9, 0xe4, 0x23, 0xde, 0xe1, 0xff, 0x18, 0xfc, 0xa0, 0x6b, 0xd3, 0xfc, 0xa9,
0xbe, 0xfe, 0x3b, 0x00, 0x00, 0xff, 0xff, 0xc9, 0x6e, 0x76, 0x95, 0x9d, 0x06, 0x00, 0x00,
} }
// Reference imports to suppress errors if they are not otherwise used. // Reference imports to suppress errors if they are not otherwise used.
@ -337,6 +419,8 @@ type MsgClient interface {
SubmitProposal(ctx context.Context, in *MsgSubmitProposal, opts ...grpc.CallOption) (*MsgSubmitProposalResponse, error) SubmitProposal(ctx context.Context, in *MsgSubmitProposal, opts ...grpc.CallOption) (*MsgSubmitProposalResponse, error)
// Vote defines a method to add a vote on a specific proposal. // Vote defines a method to add a vote on a specific proposal.
Vote(ctx context.Context, in *MsgVote, opts ...grpc.CallOption) (*MsgVoteResponse, error) Vote(ctx context.Context, in *MsgVote, opts ...grpc.CallOption) (*MsgVoteResponse, error)
// WeightedVote defines a method to add a weighted vote on a specific proposal.
WeightedVote(ctx context.Context, in *MsgWeightedVote, opts ...grpc.CallOption) (*MsgWeightedVoteResponse, error)
// Deposit defines a method to add deposit on a specific proposal. // Deposit defines a method to add deposit on a specific proposal.
Deposit(ctx context.Context, in *MsgDeposit, opts ...grpc.CallOption) (*MsgDepositResponse, error) Deposit(ctx context.Context, in *MsgDeposit, opts ...grpc.CallOption) (*MsgDepositResponse, error)
} }
@ -367,6 +451,15 @@ func (c *msgClient) Vote(ctx context.Context, in *MsgVote, opts ...grpc.CallOpti
return out, nil return out, nil
} }
func (c *msgClient) WeightedVote(ctx context.Context, in *MsgWeightedVote, opts ...grpc.CallOption) (*MsgWeightedVoteResponse, error) {
out := new(MsgWeightedVoteResponse)
err := c.cc.Invoke(ctx, "/cosmos.gov.v1beta1.Msg/WeightedVote", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *msgClient) Deposit(ctx context.Context, in *MsgDeposit, opts ...grpc.CallOption) (*MsgDepositResponse, error) { func (c *msgClient) Deposit(ctx context.Context, in *MsgDeposit, opts ...grpc.CallOption) (*MsgDepositResponse, error) {
out := new(MsgDepositResponse) out := new(MsgDepositResponse)
err := c.cc.Invoke(ctx, "/cosmos.gov.v1beta1.Msg/Deposit", in, out, opts...) err := c.cc.Invoke(ctx, "/cosmos.gov.v1beta1.Msg/Deposit", in, out, opts...)
@ -382,6 +475,8 @@ type MsgServer interface {
SubmitProposal(context.Context, *MsgSubmitProposal) (*MsgSubmitProposalResponse, error) SubmitProposal(context.Context, *MsgSubmitProposal) (*MsgSubmitProposalResponse, error)
// Vote defines a method to add a vote on a specific proposal. // Vote defines a method to add a vote on a specific proposal.
Vote(context.Context, *MsgVote) (*MsgVoteResponse, error) Vote(context.Context, *MsgVote) (*MsgVoteResponse, error)
// WeightedVote defines a method to add a weighted vote on a specific proposal.
WeightedVote(context.Context, *MsgWeightedVote) (*MsgWeightedVoteResponse, error)
// Deposit defines a method to add deposit on a specific proposal. // Deposit defines a method to add deposit on a specific proposal.
Deposit(context.Context, *MsgDeposit) (*MsgDepositResponse, error) Deposit(context.Context, *MsgDeposit) (*MsgDepositResponse, error)
} }
@ -396,6 +491,9 @@ func (*UnimplementedMsgServer) SubmitProposal(ctx context.Context, req *MsgSubmi
func (*UnimplementedMsgServer) Vote(ctx context.Context, req *MsgVote) (*MsgVoteResponse, error) { func (*UnimplementedMsgServer) Vote(ctx context.Context, req *MsgVote) (*MsgVoteResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method Vote not implemented") return nil, status.Errorf(codes.Unimplemented, "method Vote not implemented")
} }
func (*UnimplementedMsgServer) WeightedVote(ctx context.Context, req *MsgWeightedVote) (*MsgWeightedVoteResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method WeightedVote not implemented")
}
func (*UnimplementedMsgServer) Deposit(ctx context.Context, req *MsgDeposit) (*MsgDepositResponse, error) { func (*UnimplementedMsgServer) Deposit(ctx context.Context, req *MsgDeposit) (*MsgDepositResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method Deposit not implemented") return nil, status.Errorf(codes.Unimplemented, "method Deposit not implemented")
} }
@ -440,6 +538,24 @@ func _Msg_Vote_Handler(srv interface{}, ctx context.Context, dec func(interface{
return interceptor(ctx, in, info, handler) return interceptor(ctx, in, info, handler)
} }
func _Msg_WeightedVote_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(MsgWeightedVote)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(MsgServer).WeightedVote(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/cosmos.gov.v1beta1.Msg/WeightedVote",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(MsgServer).WeightedVote(ctx, req.(*MsgWeightedVote))
}
return interceptor(ctx, in, info, handler)
}
func _Msg_Deposit_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { func _Msg_Deposit_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(MsgDeposit) in := new(MsgDeposit)
if err := dec(in); err != nil { if err := dec(in); err != nil {
@ -470,6 +586,10 @@ var _Msg_serviceDesc = grpc.ServiceDesc{
MethodName: "Vote", MethodName: "Vote",
Handler: _Msg_Vote_Handler, Handler: _Msg_Vote_Handler,
}, },
{
MethodName: "WeightedVote",
Handler: _Msg_WeightedVote_Handler,
},
{ {
MethodName: "Deposit", MethodName: "Deposit",
Handler: _Msg_Deposit_Handler, Handler: _Msg_Deposit_Handler,
@ -603,6 +723,55 @@ func (m *MsgVote) MarshalToSizedBuffer(dAtA []byte) (int, error) {
return len(dAtA) - i, nil return len(dAtA) - i, nil
} }
func (m *MsgWeightedVote) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *MsgWeightedVote) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *MsgWeightedVote) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if len(m.Options) > 0 {
for iNdEx := len(m.Options) - 1; iNdEx >= 0; iNdEx-- {
{
size, err := m.Options[iNdEx].MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintTx(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x1a
}
}
if len(m.Voter) > 0 {
i -= len(m.Voter)
copy(dAtA[i:], m.Voter)
i = encodeVarintTx(dAtA, i, uint64(len(m.Voter)))
i--
dAtA[i] = 0x12
}
if m.ProposalId != 0 {
i = encodeVarintTx(dAtA, i, uint64(m.ProposalId))
i--
dAtA[i] = 0x8
}
return len(dAtA) - i, nil
}
func (m *MsgVoteResponse) Marshal() (dAtA []byte, err error) { func (m *MsgVoteResponse) Marshal() (dAtA []byte, err error) {
size := m.Size() size := m.Size()
dAtA = make([]byte, size) dAtA = make([]byte, size)
@ -626,6 +795,29 @@ func (m *MsgVoteResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
return len(dAtA) - i, nil return len(dAtA) - i, nil
} }
func (m *MsgWeightedVoteResponse) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *MsgWeightedVoteResponse) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *MsgWeightedVoteResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
return len(dAtA) - i, nil
}
func (m *MsgDeposit) Marshal() (dAtA []byte, err error) { func (m *MsgDeposit) Marshal() (dAtA []byte, err error) {
size := m.Size() size := m.Size()
dAtA = make([]byte, size) dAtA = make([]byte, size)
@ -763,6 +955,28 @@ func (m *MsgVote) Size() (n int) {
return n return n
} }
func (m *MsgWeightedVote) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.ProposalId != 0 {
n += 1 + sovTx(uint64(m.ProposalId))
}
l = len(m.Voter)
if l > 0 {
n += 1 + l + sovTx(uint64(l))
}
if len(m.Options) > 0 {
for _, e := range m.Options {
l = e.Size()
n += 1 + l + sovTx(uint64(l))
}
}
return n
}
func (m *MsgVoteResponse) Size() (n int) { func (m *MsgVoteResponse) Size() (n int) {
if m == nil { if m == nil {
return 0 return 0
@ -772,6 +986,15 @@ func (m *MsgVoteResponse) Size() (n int) {
return n return n
} }
func (m *MsgWeightedVoteResponse) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
return n
}
func (m *MsgDeposit) Size() (n int) { func (m *MsgDeposit) Size() (n int) {
if m == nil { if m == nil {
return 0 return 0
@ -1159,6 +1382,144 @@ func (m *MsgVote) Unmarshal(dAtA []byte) error {
} }
return nil return nil
} }
func (m *MsgWeightedVote) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowTx
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: MsgWeightedVote: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: MsgWeightedVote: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field ProposalId", wireType)
}
m.ProposalId = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowTx
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.ProposalId |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Voter", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowTx
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthTx
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthTx
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Voter = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
case 3:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Options", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowTx
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthTx
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthTx
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Options = append(m.Options, WeightedVoteOption{})
if err := m.Options[len(m.Options)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipTx(dAtA[iNdEx:])
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthTx
}
if (iNdEx + skippy) < 0 {
return ErrInvalidLengthTx
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *MsgVoteResponse) Unmarshal(dAtA []byte) error { func (m *MsgVoteResponse) Unmarshal(dAtA []byte) error {
l := len(dAtA) l := len(dAtA)
iNdEx := 0 iNdEx := 0
@ -1212,6 +1573,59 @@ func (m *MsgVoteResponse) Unmarshal(dAtA []byte) error {
} }
return nil return nil
} }
func (m *MsgWeightedVoteResponse) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowTx
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: MsgWeightedVoteResponse: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: MsgWeightedVoteResponse: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
default:
iNdEx = preIndex
skippy, err := skipTx(dAtA[iNdEx:])
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthTx
}
if (iNdEx + skippy) < 0 {
return ErrInvalidLengthTx
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *MsgDeposit) Unmarshal(dAtA []byte) error { func (m *MsgDeposit) Unmarshal(dAtA []byte) error {
l := len(dAtA) l := len(dAtA)
iNdEx := 0 iNdEx := 0

View File

@ -1,7 +1,9 @@
package types package types
import ( import (
"encoding/json"
"fmt" "fmt"
"strings"
yaml "gopkg.in/yaml.v2" yaml "gopkg.in/yaml.v2"
@ -10,8 +12,8 @@ import (
// NewVote creates a new Vote instance // NewVote creates a new Vote instance
//nolint:interfacer //nolint:interfacer
func NewVote(proposalID uint64, voter sdk.AccAddress, option VoteOption) Vote { func NewVote(proposalID uint64, voter sdk.AccAddress, options WeightedVoteOptions) Vote {
return Vote{proposalID, voter.String(), option} return Vote{proposalID, voter.String(), options}
} }
func (v Vote) String() string { func (v Vote) String() string {
@ -43,7 +45,7 @@ func (v Votes) String() string {
} }
out := fmt.Sprintf("Votes for Proposal %d:", v[0].ProposalId) out := fmt.Sprintf("Votes for Proposal %d:", v[0].ProposalId)
for _, vot := range v { for _, vot := range v {
out += fmt.Sprintf("\n %s: %s", vot.Voter, vot.Option) out += fmt.Sprintf("\n %s: %s", vot.Voter, vot.Options)
} }
return out return out
} }
@ -53,6 +55,33 @@ func (v Vote) Empty() bool {
return v.String() == Vote{}.String() return v.String() == Vote{}.String()
} }
// NewNonSplitVoteOption creates a single option vote with weight 1
//nolint:interfacer
func NewNonSplitVoteOption(option VoteOption) WeightedVoteOptions {
return WeightedVoteOptions{{option, sdk.NewDec(1)}}
}
func (v WeightedVoteOption) String() string {
out, _ := json.Marshal(v)
return string(out)
}
// WeightedVoteOptions describes array of WeightedVoteOptions
type WeightedVoteOptions []WeightedVoteOption
func (v WeightedVoteOptions) String() string {
out, _ := json.Marshal(v)
return string(out)
}
// ValidWeightedVoteOption returns true if the sub vote is valid and false otherwise.
func ValidWeightedVoteOption(option WeightedVoteOption) bool {
if !option.Weight.IsPositive() || option.Weight.GT(sdk.NewDec(1)) {
return false
}
return ValidVoteOption(option.Option)
}
// VoteOptionFromString returns a VoteOption from a string. It returns an error // VoteOptionFromString returns a VoteOption from a string. It returns an error
// if the string is invalid. // if the string is invalid.
func VoteOptionFromString(str string) (VoteOption, error) { func VoteOptionFromString(str string) (VoteOption, error) {
@ -63,6 +92,28 @@ func VoteOptionFromString(str string) (VoteOption, error) {
return VoteOption(option), nil return VoteOption(option), nil
} }
// WeightedVoteOptionsFromString returns weighted vote options from string. It returns an error
// if the string is invalid.
func WeightedVoteOptionsFromString(str string) (WeightedVoteOptions, error) {
options := WeightedVoteOptions{}
for _, option := range strings.Split(str, ",") {
fields := strings.Split(option, "=")
option, err := VoteOptionFromString(fields[0])
if err != nil {
return options, err
}
if len(fields) < 2 {
return options, fmt.Errorf("weight field does not exist for %s option", fields[0])
}
weight, err := sdk.NewDecFromStr(fields[1])
if err != nil {
return options, err
}
options = append(options, WeightedVoteOption{option, weight})
}
return options, nil
}
// ValidVoteOption returns true if the vote option is valid and false otherwise. // ValidVoteOption returns true if the vote option is valid and false otherwise.
func ValidVoteOption(option VoteOption) bool { func ValidVoteOption(option VoteOption) bool {
if option == OptionYes || if option == OptionYes ||