diff --git a/proto/cosmos/gov/v1beta1/tx.proto b/proto/cosmos/gov/v1beta1/tx.proto index 9d38203b9..3fb754d14 100644 --- a/proto/cosmos/gov/v1beta1/tx.proto +++ b/proto/cosmos/gov/v1beta1/tx.proto @@ -17,6 +17,9 @@ service Msg { // Vote defines a method to add a vote on a specific proposal. rpc Vote(MsgVote) returns (MsgVoteResponse); + // WeightedVote defines a method to add a weighted vote on a specific proposal. + rpc WeightedVote(MsgWeightedVote) returns (MsgVoteResponse); + // Deposit defines a method to add deposit on a specific proposal. rpc Deposit(MsgDeposit) returns (MsgDepositResponse); } @@ -50,6 +53,18 @@ message MsgVote { option (gogoproto.stringer) = false; option (gogoproto.goproto_getters) = false; + uint64 proposal_id = 1 [(gogoproto.moretags) = "yaml:\"proposal_id\""]; + string voter = 2; + 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]; diff --git a/simapp/params/weights.go b/simapp/params/weights.go index 0ba377b00..1f4a25eba 100644 --- a/simapp/params/weights.go +++ b/simapp/params/weights.go @@ -10,6 +10,7 @@ const ( DefaultWeightMsgFundCommunityPool int = 50 DefaultWeightMsgDeposit int = 100 DefaultWeightMsgVote int = 67 + DefaultWeightMsgWeightedVote int = 33 DefaultWeightMsgUnjail int = 100 DefaultWeightMsgCreateValidator int = 100 DefaultWeightMsgEditValidator int = 5 diff --git a/x/gov/client/cli/cli_test.go b/x/gov/client/cli/cli_test.go index 0ccac645a..4a35e2776 100644 --- a/x/gov/client/cli/cli_test.go +++ b/x/gov/client/cli/cli_test.go @@ -779,6 +779,66 @@ func (s *IntegrationTestSuite) TestCmdQueryVote() { func (s *IntegrationTestSuite) TestNewCmdVote() { 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, + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + cmd := cli.NewCmdVote() + 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 (s *IntegrationTestSuite) TestNewCmdWeightedVote() { + val := s.network.Validators[0] + testCases := []struct { name string args []string @@ -843,7 +903,7 @@ func (s *IntegrationTestSuite) TestNewCmdVote() { for _, tc := range testCases { tc := tc s.Run(tc.name, func() { - cmd := cli.NewCmdVote() + cmd := cli.NewCmdWeightedVote() clientCtx := val.ClientCtx var txResp sdk.TxResponse diff --git a/x/gov/client/cli/tx.go b/x/gov/client/cli/tx.go index 374bf24c3..46a2c2058 100644 --- a/x/gov/client/cli/tx.go +++ b/x/gov/client/cli/tx.go @@ -68,6 +68,7 @@ func NewTxCmd(propCmds []*cobra.Command) *cobra.Command { govTxCmd.AddCommand( NewCmdDeposit(), NewCmdVote(), + NewCmdWeightedVote(), cmdSubmitProp, ) @@ -207,6 +208,60 @@ func NewCmdVote() *cobra.Command { 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 --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]) + } + + // Find out which vote option user chose + byteVoteOption, err := types.VoteOptionFromString(govutils.NormalizeVoteOption(args[1])) + if err != nil { + return err + } + + // Build vote message and run basic validation + msg := types.NewMsgVote(from, proposalID, byteVoteOption) + err = msg.ValidateBasic() + if err != nil { + return err + } + + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + }, + } + + flags.AddTxFlagsToCmd(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 `, @@ -236,7 +291,7 @@ $ %s tx gov vote 1 yes=0.6,no=0.3,abstain=0.05,no_with_veto=0.05 --from mykey } // Build vote message and run basic validation - msg := types.NewMsgVote(from, proposalID, options) + msg := types.NewMsgWeightedVote(from, proposalID, options) err = msg.ValidateBasic() if err != nil { return err diff --git a/x/gov/client/rest/rest.go b/x/gov/client/rest/rest.go index 6a5217925..e7282d8d9 100644 --- a/x/gov/client/rest/rest.go +++ b/x/gov/client/rest/rest.go @@ -53,6 +53,13 @@ type DepositReq struct { // VoteReq defines the properties of a vote request's body. type VoteReq 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"` // option from OptionSet chosen by 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 +} + +// 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 } diff --git a/x/gov/client/rest/tx.go b/x/gov/client/rest/tx.go index 00c82dd8c..41738269e 100644 --- a/x/gov/client/rest/tx.go +++ b/x/gov/client/rest/tx.go @@ -22,6 +22,7 @@ func registerTxHandlers(clientCtx client.Context, r *mux.Router, phs []ProposalR 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}/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 { @@ -111,14 +112,54 @@ func newVoteHandlerFn(clientCtx client.Context) http.HandlerFunc { return } - // Figure out which vote options user chose - options, err := types.WeightedVoteOptionsFromString(gcutils.NormalizeWeightedVoteOptions(req.Options)) - if err != nil { + voteOption, err := types.VoteOptionFromString(gcutils.NormalizeVoteOption(req.Option)) + if rest.CheckBadRequestError(w, err) { return } // create the message - msg := types.NewMsgVote(req.Voter, proposalID, options) + msg := types.NewMsgVote(req.Voter, proposalID, voteOption) + if rest.CheckBadRequestError(w, msg.ValidateBasic()) { + return + } + + 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 } diff --git a/x/gov/client/testutil/helpers.go b/x/gov/client/testutil/helpers.go index 535cb8d09..ce9d5de72 100644 --- a/x/gov/client/testutil/helpers.go +++ b/x/gov/client/testutil/helpers.go @@ -41,5 +41,5 @@ func MsgVote(clientCtx client.Context, from, id, vote string, extraArgs ...strin args = append(args, extraArgs...) - return clitestutil.ExecTestCLICmd(clientCtx, govcli.NewCmdVote(), args) + return clitestutil.ExecTestCLICmd(clientCtx, govcli.NewCmdWeightedVote(), args) } diff --git a/x/gov/client/utils/query.go b/x/gov/client/utils/query.go index 122d7d255..3f92d6230 100644 --- a/x/gov/client/utils/query.go +++ b/x/gov/client/utils/query.go @@ -80,6 +80,7 @@ func QueryVotesByTxQuery(clientCtx client.Context, params types.QueryProposalVot var ( events = []string{ 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))), } votes []types.Vote @@ -98,6 +99,14 @@ func QueryVotesByTxQuery(clientCtx client.Context, params types.QueryProposalVot if msg.Type() == types.TypeMsgVote { voteMsg := msg.(*types.MsgVote) + votes = append(votes, types.Vote{ + Voter: voteMsg.Voter, + ProposalId: params.ProposalID, + 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, @@ -141,23 +150,33 @@ func QueryVoteByTxQuery(clientCtx client.Context, params types.QueryVoteParams) } for _, info := range searchResult.Txs { for _, msg := range info.GetTx().GetMsgs() { + vote := types.Vote{} + // there should only be a single vote under the given conditions if msg.Type() == types.TypeMsgVote { voteMsg := msg.(*types.MsgVote) - vote := types.Vote{ + vote = types.Vote{ + Voter: voteMsg.Voter, + ProposalId: params.ProposalID, + Options: types.NewNonSplitVoteOption(voteMsg.Option), + } + } 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) - if err != nil { - return nil, err - } - - return bz, nil } + + bz, err := clientCtx.JSONMarshaler.MarshalJSON(&vote) + if err != nil { + return nil, err + } + + return bz, nil } } diff --git a/x/gov/client/utils/query_test.go b/x/gov/client/utils/query_test.go index 90d3e96c7..8ad99b2c1 100644 --- a/x/gov/client/utils/query_test.go +++ b/x/gov/client/utils/query_test.go @@ -70,12 +70,12 @@ func TestGetPaginatedVotes(t *testing.T) { acc2 := make(sdk.AccAddress, 20) acc2[0] = 2 acc1Msgs := []sdk.Msg{ - types.NewMsgVote(acc1, 0, types.NewNonSplitVoteOption(types.OptionYes)), - types.NewMsgVote(acc1, 0, types.NewNonSplitVoteOption(types.OptionYes)), + types.NewMsgVote(acc1, 0, types.OptionYes), + types.NewMsgVote(acc1, 0, types.OptionYes), } acc2Msgs := []sdk.Msg{ - types.NewMsgVote(acc2, 0, types.NewNonSplitVoteOption(types.OptionYes)), - types.NewMsgVote(acc2, 0, types.NewNonSplitVoteOption(types.OptionYes)), + types.NewMsgVote(acc2, 0, types.OptionYes), + types.NewMsgWeightedVote(acc2, 0, types.NewNonSplitVoteOption(types.OptionYes)), } for _, tc := range []testCase{ { diff --git a/x/gov/handler.go b/x/gov/handler.go index 25d97bfab..61258e813 100644 --- a/x/gov/handler.go +++ b/x/gov/handler.go @@ -27,6 +27,10 @@ func NewHandler(k keeper.Keeper) sdk.Handler { res, err := msgServer.Vote(sdk.WrapSDKContext(ctx), msg) return sdk.WrapServiceResult(ctx, res, err) + case *types.MsgWeightedVote: + res, err := msgServer.WeightedVote(sdk.WrapSDKContext(ctx), msg) + return sdk.WrapServiceResult(ctx, res, err) + default: return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized %s message type: %T", types.ModuleName, msg) } diff --git a/x/gov/keeper/msg_server.go b/x/gov/keeper/msg_server.go index aa92591ab..3d02ae95e 100644 --- a/x/gov/keeper/msg_server.go +++ b/x/gov/keeper/msg_server.go @@ -59,6 +59,36 @@ func (k msgServer) SubmitProposal(goCtx context.Context, msg *types.MsgSubmitPro } func (k msgServer) Vote(goCtx context.Context, msg *types.MsgVote) (*types.MsgVoteResponse, 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, types.NewNonSplitVoteOption(msg.Option)) + 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.MsgVoteResponse{}, nil +} + +func (k msgServer) WeightedVote(goCtx context.Context, msg *types.MsgWeightedVote) (*types.MsgVoteResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) accAddr, accErr := sdk.AccAddressFromBech32(msg.Voter) if accErr != nil { diff --git a/x/gov/simulation/operations.go b/x/gov/simulation/operations.go index 29196a20c..88b82bf3f 100644 --- a/x/gov/simulation/operations.go +++ b/x/gov/simulation/operations.go @@ -20,8 +20,9 @@ var initialProposalID = uint64(100000000000000) // Simulation operation weights constants const ( - OpWeightMsgDeposit = "op_weight_msg_deposit" - OpWeightMsgVote = "op_weight_msg_vote" + OpWeightMsgDeposit = "op_weight_msg_deposit" + OpWeightMsgVote = "op_weight_msg_vote" + OpWeightMsgWeightedVote = "op_weight_msg_weighted_vote" ) // WeightedOperations returns all the operations from the module with their respective weights @@ -31,8 +32,9 @@ func WeightedOperations( ) simulation.WeightedOperations { var ( - weightMsgDeposit int - weightMsgVote int + weightMsgDeposit int + weightMsgVote int + weightMsgWeightedVote int ) 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 var wProposalOps simulation.WeightedOperations @@ -74,12 +82,16 @@ func WeightedOperations( weightMsgVote, SimulateMsgVote(ak, bk, k), ), + simulation.NewWeightedOperation( + weightMsgWeightedVote, + SimulateMsgWeightedVote(ak, bk, k), + ), } 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 // future operations. func SimulateMsgSubmitProposal( @@ -281,7 +293,70 @@ func operationSimulateMsgVote(ak types.AccountKeeper, bk types.BankKeeper, k kee } option := randomVotingOption(r) - msg := types.NewMsgVote(simAccount.Address, proposalID, types.NewNonSplitVoteOption(option)) + msg := types.NewMsgVote(simAccount.Address, proposalID, option) + + 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.MakeEncodingConfig().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 + } +} + +// 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()) @@ -393,3 +468,37 @@ func randomVotingOption(r *rand.Rand) types.VoteOption { 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 +} diff --git a/x/gov/simulation/operations_test.go b/x/gov/simulation/operations_test.go index 83197a585..52217ad74 100644 --- a/x/gov/simulation/operations_test.go +++ b/x/gov/simulation/operations_test.go @@ -80,6 +80,7 @@ func TestWeightedOperations(t *testing.T) { {2, types.ModuleName, "submit_proposal"}, {simappparams.DefaultWeightMsgDeposit, types.ModuleName, types.TypeMsgDeposit}, {simappparams.DefaultWeightMsgVote, types.ModuleName, types.TypeMsgVote}, + {simappparams.DefaultWeightMsgWeightedVote, types.ModuleName, types.TypeMsgWeightedVote}, } for i, w := range weightesOps { @@ -202,11 +203,51 @@ func TestSimulateMsgVote(t *testing.T) { 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, types.OptionYes, msg.Options[0].Option) + require.Equal(t, types.OptionYes, msg.Option) require.Equal(t, "gov", msg.Route()) 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 diff --git a/x/gov/spec/04_events.md b/x/gov/spec/04_events.md index dc3b1bf3a..1ea2a36b7 100644 --- a/x/gov/spec/04_events.md +++ b/x/gov/spec/04_events.md @@ -41,6 +41,16 @@ The governance module emits the following events: | message | action | vote | | 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 | Type | Attribute Key | Attribute Value | diff --git a/x/gov/types/codec.go b/x/gov/types/codec.go index 85bac043a..735733e35 100644 --- a/x/gov/types/codec.go +++ b/x/gov/types/codec.go @@ -14,6 +14,7 @@ func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { cdc.RegisterConcrete(&MsgSubmitProposal{}, "cosmos-sdk/MsgSubmitProposal", nil) cdc.RegisterConcrete(&MsgDeposit{}, "cosmos-sdk/MsgDeposit", nil) cdc.RegisterConcrete(&MsgVote{}, "cosmos-sdk/MsgVote", nil) + cdc.RegisterConcrete(&MsgWeightedVote{}, "cosmos-sdk/MsgWeightedVote", nil) cdc.RegisterConcrete(&TextProposal{}, "cosmos-sdk/TextProposal", nil) } @@ -21,6 +22,7 @@ func RegisterInterfaces(registry types.InterfaceRegistry) { registry.RegisterImplementations((*sdk.Msg)(nil), &MsgSubmitProposal{}, &MsgVote{}, + &MsgWeightedVote{}, &MsgDeposit{}, ) registry.RegisterInterface( diff --git a/x/gov/types/msgs.go b/x/gov/types/msgs.go index 2cb3db539..4c8d47d7c 100644 --- a/x/gov/types/msgs.go +++ b/x/gov/types/msgs.go @@ -16,12 +16,13 @@ import ( const ( TypeMsgDeposit = "deposit" TypeMsgVote = "vote" + TypeMsgWeightedVote = "weighted_vote" TypeMsgSubmitProposal = "submit_proposal" ) var ( - _, _, _ sdk.Msg = &MsgSubmitProposal{}, &MsgDeposit{}, &MsgVote{} - _ types.UnpackInterfacesMessage = &MsgSubmitProposal{} + _, _, _, _ sdk.Msg = &MsgSubmitProposal{}, &MsgDeposit{}, &MsgVote{}, &MsgWeightedVote{} + _ types.UnpackInterfacesMessage = &MsgSubmitProposal{} ) // NewMsgSubmitProposal creates a new MsgSubmitProposal. @@ -177,8 +178,8 @@ func (msg MsgDeposit) GetSigners() []sdk.AccAddress { // NewMsgVote creates a message to cast a vote on an active proposal //nolint:interfacer -func NewMsgVote(voter sdk.AccAddress, proposalID uint64, options WeightedVoteOptions) *MsgVote { - return &MsgVote{proposalID, voter.String(), options} +func NewMsgVote(voter sdk.AccAddress, proposalID uint64, option VoteOption) *MsgVote { + return &MsgVote{proposalID, voter.String(), option} } // Route implements Msg @@ -193,6 +194,49 @@ func (msg MsgVote) ValidateBasic() error { return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, msg.Voter) } + if !ValidVoteOption(msg.Option) { + return sdkerrors.Wrap(ErrInvalidVote, msg.Option.String()) + } + + return nil +} + +// String implements the Stringer interface +func (msg MsgVote) String() string { + out, _ := yaml.Marshal(msg) + return string(out) +} + +// GetSignBytes implements Msg +func (msg MsgVote) GetSignBytes() []byte { + bz := ModuleCdc.MustMarshalJSON(&msg) + return sdk.MustSortJSON(bz) +} + +// GetSigners implements Msg +func (msg MsgVote) GetSigners() []sdk.AccAddress { + voter, _ := sdk.AccAddressFromBech32(msg.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()) } @@ -218,19 +262,19 @@ func (msg MsgVote) ValidateBasic() error { } // String implements the Stringer interface -func (msg MsgVote) String() string { +func (msg MsgWeightedVote) String() string { out, _ := yaml.Marshal(msg) return string(out) } // GetSignBytes implements Msg -func (msg MsgVote) GetSignBytes() []byte { +func (msg MsgWeightedVote) GetSignBytes() []byte { bz := ModuleCdc.MustMarshalJSON(&msg) return sdk.MustSortJSON(bz) } // GetSigners implements Msg -func (msg MsgVote) GetSigners() []sdk.AccAddress { +func (msg MsgWeightedVote) GetSigners() []sdk.AccAddress { voter, _ := sdk.AccAddressFromBech32(msg.Voter) return []sdk.AccAddress{voter} } diff --git a/x/gov/types/msgs_test.go b/x/gov/types/msgs_test.go index c457c564f..a1e1accba 100644 --- a/x/gov/types/msgs_test.go +++ b/x/gov/types/msgs_test.go @@ -92,8 +92,34 @@ func TestMsgDeposit(t *testing.T) { } } -// test ValidateBasic for MsgDeposit +// test ValidateBasic for MsgVote func TestMsgVote(t *testing.T) { + tests := []struct { + proposalID uint64 + voterAddr sdk.AccAddress + option VoteOption + expectPass bool + }{ + {0, addrs[0], OptionYes, true}, + {0, sdk.AccAddress{}, OptionYes, false}, + {0, addrs[0], OptionNo, true}, + {0, addrs[0], OptionNoWithVeto, true}, + {0, addrs[0], OptionAbstain, true}, + {0, addrs[0], VoteOption(0x13), false}, + } + + for i, tc := range tests { + msg := NewMsgVote(tc.voterAddr, tc.proposalID, tc.option) + if tc.expectPass { + require.Nil(t, msg.ValidateBasic(), "test: %v", i) + } else { + require.NotNil(t, msg.ValidateBasic(), "test: %v", i) + } + } +} + +// test ValidateBasic for MsgWeightedVote +func TestMsgWeightedVote(t *testing.T) { tests := []struct { proposalID uint64 voterAddr sdk.AccAddress @@ -124,7 +150,7 @@ func TestMsgVote(t *testing.T) { } for i, tc := range tests { - msg := NewMsgVote(tc.voterAddr, tc.proposalID, tc.options) + msg := NewMsgWeightedVote(tc.voterAddr, tc.proposalID, tc.options) if tc.expectPass { require.Nil(t, msg.ValidateBasic(), "test: %v", i) } else { diff --git a/x/gov/types/tx.pb.go b/x/gov/types/tx.pb.go index d77104ae7..71222492c 100644 --- a/x/gov/types/tx.pb.go +++ b/x/gov/types/tx.pb.go @@ -119,9 +119,9 @@ func (m *MsgSubmitProposalResponse) GetProposalId() uint64 { // MsgVote defines a message to cast a vote. type MsgVote 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"` + 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"` + Option VoteOption `protobuf:"varint,3,opt,name=option,proto3,enum=cosmos.gov.v1beta1.VoteOption" json:"option,omitempty"` } func (m *MsgVote) Reset() { *m = MsgVote{} } @@ -156,6 +156,45 @@ func (m *MsgVote) XXX_DiscardUnknown() { 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. type MsgVoteResponse struct { } @@ -164,7 +203,7 @@ func (m *MsgVoteResponse) Reset() { *m = MsgVoteResponse{} } func (m *MsgVoteResponse) String() string { return proto.CompactTextString(m) } func (*MsgVoteResponse) ProtoMessage() {} func (*MsgVoteResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_3c053992595e3dce, []int{3} + return fileDescriptor_3c053992595e3dce, []int{4} } func (m *MsgVoteResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -203,7 +242,7 @@ type MsgDeposit struct { func (m *MsgDeposit) Reset() { *m = MsgDeposit{} } func (*MsgDeposit) ProtoMessage() {} func (*MsgDeposit) Descriptor() ([]byte, []int) { - return fileDescriptor_3c053992595e3dce, []int{4} + return fileDescriptor_3c053992595e3dce, []int{5} } func (m *MsgDeposit) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -240,7 +279,7 @@ func (m *MsgDepositResponse) Reset() { *m = MsgDepositResponse{} } func (m *MsgDepositResponse) String() string { return proto.CompactTextString(m) } func (*MsgDepositResponse) ProtoMessage() {} func (*MsgDepositResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_3c053992595e3dce, []int{5} + return fileDescriptor_3c053992595e3dce, []int{6} } func (m *MsgDepositResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -273,6 +312,7 @@ func init() { proto.RegisterType((*MsgSubmitProposal)(nil), "cosmos.gov.v1beta1.MsgSubmitProposal") proto.RegisterType((*MsgSubmitProposalResponse)(nil), "cosmos.gov.v1beta1.MsgSubmitProposalResponse") 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((*MsgDeposit)(nil), "cosmos.gov.v1beta1.MsgDeposit") proto.RegisterType((*MsgDepositResponse)(nil), "cosmos.gov.v1beta1.MsgDepositResponse") @@ -281,45 +321,48 @@ func init() { func init() { proto.RegisterFile("cosmos/gov/v1beta1/tx.proto", fileDescriptor_3c053992595e3dce) } var fileDescriptor_3c053992595e3dce = []byte{ - // 596 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x54, 0x3d, 0x6f, 0xd3, 0x50, - 0x14, 0xb5, 0x93, 0xd2, 0xd0, 0x17, 0xa9, 0xa5, 0x4f, 0x11, 0x4a, 0xd3, 0xca, 0x8e, 0x8c, 0x5a, - 0x65, 0xa9, 0x4d, 0xcb, 0x56, 0x26, 0x5c, 0x54, 0x01, 0x52, 0x04, 0x18, 0x09, 0x24, 0x96, 0xca, - 0x76, 0x5e, 0x5f, 0x2d, 0x12, 0x5f, 0x2b, 0xef, 0x25, 0x6a, 0x36, 0x46, 0xc4, 0x00, 0x8c, 0x8c, - 0x9d, 0x99, 0xf9, 0x03, 0x6c, 0x15, 0x53, 0x47, 0x06, 0x14, 0x50, 0xbb, 0x00, 0x63, 0x7f, 0x01, - 0xf2, 0xfb, 0x48, 0xab, 0x36, 0x8d, 0x40, 0xea, 0x94, 0xdc, 0x7b, 0xee, 0x39, 0xf2, 0x39, 0xf7, - 0xda, 0x68, 0x31, 0x06, 0xd6, 0x01, 0xe6, 0x51, 0xe8, 0x7b, 0xfd, 0xb5, 0x88, 0xf0, 0x70, 0xcd, - 0xe3, 0x7b, 0x6e, 0xd6, 0x05, 0x0e, 0x18, 0x4b, 0xd0, 0xa5, 0xd0, 0x77, 0x15, 0x58, 0xb3, 0x14, - 0x21, 0x0a, 0x19, 0x19, 0x31, 0x62, 0x48, 0x52, 0xc9, 0xa9, 0x2d, 0x8d, 0x11, 0xcc, 0xf9, 0x12, - 0x5d, 0x90, 0xe8, 0xb6, 0xa8, 0x3c, 0x25, 0x2f, 0xa1, 0x0a, 0x05, 0x0a, 0xb2, 0x9f, 0xff, 0xd3, - 0x04, 0x0a, 0x40, 0xdb, 0xc4, 0x13, 0x55, 0xd4, 0xdb, 0xf1, 0xc2, 0x74, 0x20, 0x21, 0xe7, 0x7d, - 0x01, 0xcd, 0x37, 0x19, 0x7d, 0xd6, 0x8b, 0x3a, 0x09, 0x7f, 0xd2, 0x85, 0x0c, 0x58, 0xd8, 0xc6, - 0x77, 0x51, 0x29, 0x86, 0x94, 0x93, 0x94, 0x57, 0xcd, 0xba, 0xd9, 0x28, 0xaf, 0x57, 0x5c, 0x29, - 0xe1, 0x6a, 0x09, 0xf7, 0x5e, 0x3a, 0xf0, 0xcb, 0x5f, 0x3f, 0xaf, 0x96, 0x36, 0xe5, 0x60, 0xa0, - 0x19, 0xf8, 0x9d, 0x89, 0xe6, 0x92, 0x34, 0xe1, 0x49, 0xd8, 0xde, 0x6e, 0x91, 0x0c, 0x58, 0xc2, - 0xab, 0x85, 0x7a, 0xb1, 0x51, 0x5e, 0x5f, 0x70, 0xd5, 0xc3, 0xe6, 0xbe, 0x75, 0x18, 0xee, 0x26, - 0x24, 0xa9, 0xff, 0xe8, 0x60, 0x68, 0x1b, 0x27, 0x43, 0xfb, 0xe6, 0x20, 0xec, 0xb4, 0x37, 0x9c, - 0x73, 0x7c, 0xe7, 0xd3, 0x0f, 0xbb, 0x41, 0x13, 0xbe, 0xdb, 0x8b, 0xdc, 0x18, 0x3a, 0xca, 0xb3, - 0xfa, 0x59, 0x65, 0xad, 0x57, 0x1e, 0x1f, 0x64, 0x84, 0x09, 0x29, 0x16, 0xcc, 0x2a, 0xf6, 0x7d, - 0x49, 0xc6, 0x35, 0x74, 0x3d, 0x13, 0xce, 0x48, 0xb7, 0x5a, 0xac, 0x9b, 0x8d, 0x99, 0x60, 0x54, - 0x6f, 0xdc, 0x78, 0xb3, 0x6f, 0x1b, 0x1f, 0xf7, 0x6d, 0xe3, 0xd7, 0xbe, 0x6d, 0xbc, 0xfe, 0x5e, - 0x37, 0x9c, 0x18, 0x2d, 0x5c, 0x08, 0x24, 0x20, 0x2c, 0x83, 0x94, 0x11, 0xbc, 0x85, 0xca, 0x99, - 0xea, 0x6d, 0x27, 0x2d, 0x11, 0xce, 0x94, 0xbf, 0xfc, 0x67, 0x68, 0x9f, 0x6d, 0x9f, 0x0c, 0x6d, - 0x2c, 0x6d, 0x9c, 0x69, 0x3a, 0x01, 0xd2, 0xd5, 0xc3, 0x96, 0xf3, 0xc5, 0x44, 0xa5, 0x26, 0xa3, - 0xcf, 0x81, 0x5f, 0x99, 0x26, 0xae, 0xa0, 0x6b, 0x7d, 0xe0, 0xa4, 0x5b, 0x2d, 0x08, 0x8f, 0xb2, - 0xc0, 0x5b, 0xa8, 0x04, 0x19, 0x4f, 0x20, 0x65, 0xd5, 0xa2, 0x58, 0xc2, 0x8a, 0x7b, 0xf1, 0x20, - 0xdd, 0x17, 0x24, 0xa1, 0xbb, 0x9c, 0xb4, 0xf2, 0x07, 0x7a, 0x2c, 0xc6, 0xfd, 0xa9, 0x7c, 0x23, - 0x81, 0x26, 0x8f, 0x09, 0x6a, 0x1e, 0xcd, 0x29, 0x0b, 0x3a, 0x1e, 0xe7, 0xb7, 0x89, 0x50, 0x93, - 0x51, 0x1d, 0xfc, 0x55, 0x39, 0x5b, 0x42, 0x33, 0xea, 0x10, 0x40, 0xbb, 0x3b, 0x6d, 0xe0, 0x18, - 0x4d, 0x87, 0x1d, 0xe8, 0xa5, 0x5c, 0x19, 0x9c, 0x70, 0x65, 0xb7, 0x73, 0x4f, 0xff, 0x75, 0x4b, - 0x4a, 0x7a, 0x8c, 0xfd, 0x0a, 0xc2, 0xa7, 0x56, 0x75, 0x02, 0xeb, 0x6f, 0x0b, 0xa8, 0xd8, 0x64, - 0x14, 0xef, 0xa0, 0xd9, 0x73, 0xef, 0xd4, 0xf2, 0xb8, 0xdc, 0x2f, 0x5c, 0x5a, 0x6d, 0xf5, 0x9f, - 0xc6, 0x46, 0x07, 0xf9, 0x00, 0x4d, 0x89, 0x23, 0x5a, 0xbc, 0x84, 0x96, 0x83, 0xb5, 0x5b, 0x13, - 0xc0, 0x91, 0xd2, 0x53, 0x54, 0xd2, 0x7b, 0xb3, 0x2e, 0x99, 0x57, 0x78, 0x6d, 0x65, 0x32, 0xae, - 0x25, 0x7d, 0xff, 0xe0, 0xc8, 0x32, 0x0f, 0x8f, 0x2c, 0xf3, 0xe7, 0x91, 0x65, 0x7e, 0x38, 0xb6, - 0x8c, 0xc3, 0x63, 0xcb, 0xf8, 0x76, 0x6c, 0x19, 0x2f, 0x27, 0x2f, 0x60, 0x4f, 0x7c, 0xf8, 0xc4, - 0x1a, 0xa2, 0x69, 0xf1, 0xc5, 0xb9, 0xf3, 0x37, 0x00, 0x00, 0xff, 0xff, 0xb6, 0x98, 0x0c, 0x31, - 0x64, 0x05, 0x00, 0x00, + // 652 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x54, 0xb1, 0x6f, 0xd3, 0x4e, + 0x14, 0xb6, 0x93, 0xfe, 0x9a, 0x5f, 0x2f, 0xa8, 0xa5, 0xa7, 0xa8, 0x4a, 0xd3, 0xca, 0x8e, 0x8c, + 0x5a, 0x65, 0xa9, 0x4d, 0x83, 0x04, 0x52, 0x99, 0x48, 0x51, 0x05, 0x48, 0x11, 0x60, 0x24, 0x40, + 0x2c, 0x95, 0x93, 0x5c, 0xaf, 0x16, 0x89, 0x9f, 0x95, 0xbb, 0x44, 0xcd, 0xc6, 0xc8, 0x04, 0x8c, + 0x8c, 0x15, 0x23, 0x33, 0x7f, 0x44, 0x61, 0xea, 0xc8, 0x80, 0x02, 0x6a, 0x19, 0x80, 0xb1, 0x7f, + 0x01, 0xf2, 0xdd, 0x39, 0x0d, 0x8d, 0x13, 0x8a, 0xd4, 0x29, 0x79, 0xef, 0x7b, 0xdf, 0xe7, 0xf7, + 0xbd, 0x7b, 0x77, 0x68, 0xa9, 0x0e, 0xac, 0x05, 0xcc, 0xa1, 0xd0, 0x75, 0xba, 0xeb, 0x35, 0xc2, + 0xbd, 0x75, 0x87, 0xef, 0xd9, 0x61, 0x1b, 0x38, 0x60, 0x2c, 0x41, 0x9b, 0x42, 0xd7, 0x56, 0x60, + 0xc1, 0x50, 0x84, 0x9a, 0xc7, 0xc8, 0x80, 0x51, 0x07, 0x3f, 0x90, 0x9c, 0xc2, 0x72, 0x82, 0x60, + 0xc4, 0x97, 0xe8, 0xa2, 0x44, 0xb7, 0x45, 0xe4, 0x28, 0x79, 0x09, 0xe5, 0x28, 0x50, 0x90, 0xf9, + 0xe8, 0x5f, 0x4c, 0xa0, 0x00, 0xb4, 0x49, 0x1c, 0x11, 0xd5, 0x3a, 0x3b, 0x8e, 0x17, 0xf4, 0x24, + 0x64, 0xbd, 0x4e, 0xa1, 0xf9, 0x2a, 0xa3, 0x8f, 0x3a, 0xb5, 0x96, 0xcf, 0x1f, 0xb4, 0x21, 0x04, + 0xe6, 0x35, 0xf1, 0x4d, 0x94, 0xa9, 0x43, 0xc0, 0x49, 0xc0, 0xf3, 0x7a, 0x51, 0x2f, 0x65, 0xcb, + 0x39, 0x5b, 0x4a, 0xd8, 0xb1, 0x84, 0x7d, 0x2b, 0xe8, 0x55, 0xb2, 0x9f, 0x3e, 0xac, 0x65, 0x36, + 0x65, 0xa1, 0x1b, 0x33, 0xf0, 0x2b, 0x1d, 0xcd, 0xf9, 0x81, 0xcf, 0x7d, 0xaf, 0xb9, 0xdd, 0x20, + 0x21, 0x30, 0x9f, 0xe7, 0x53, 0xc5, 0x74, 0x29, 0x5b, 0x5e, 0xb4, 0x55, 0xb3, 0x91, 0xef, 0x78, + 0x18, 0xf6, 0x26, 0xf8, 0x41, 0xe5, 0xde, 0x41, 0xdf, 0xd4, 0x4e, 0xfa, 0xe6, 0x42, 0xcf, 0x6b, + 0x35, 0x37, 0xac, 0x33, 0x7c, 0xeb, 0xfd, 0x57, 0xb3, 0x44, 0x7d, 0xbe, 0xdb, 0xa9, 0xd9, 0x75, + 0x68, 0x29, 0xcf, 0xea, 0x67, 0x8d, 0x35, 0x9e, 0x3b, 0xbc, 0x17, 0x12, 0x26, 0xa4, 0x98, 0x3b, + 0xab, 0xd8, 0xb7, 0x25, 0x19, 0x17, 0xd0, 0xff, 0xa1, 0x70, 0x46, 0xda, 0xf9, 0x74, 0x51, 0x2f, + 0xcd, 0xb8, 0x83, 0x78, 0xe3, 0xf2, 0xcb, 0x7d, 0x53, 0x7b, 0xbb, 0x6f, 0x6a, 0x3f, 0xf6, 0x4d, + 0xed, 0xc5, 0x97, 0xa2, 0x66, 0xd5, 0xd1, 0xe2, 0xc8, 0x40, 0x5c, 0xc2, 0x42, 0x08, 0x18, 0xc1, + 0x5b, 0x28, 0x1b, 0xaa, 0xdc, 0xb6, 0xdf, 0x10, 0xc3, 0x99, 0xaa, 0xac, 0xfc, 0xea, 0x9b, 0xc3, + 0xe9, 0x93, 0xbe, 0x89, 0xa5, 0x8d, 0xa1, 0xa4, 0xe5, 0xa2, 0x38, 0xba, 0xdb, 0xb0, 0xde, 0xe9, + 0x28, 0x53, 0x65, 0xf4, 0x31, 0x70, 0x82, 0x6f, 0x24, 0x69, 0x2e, 0xfc, 0x5d, 0x04, 0xe7, 0xd0, + 0x7f, 0x5d, 0xe0, 0xa4, 0x9d, 0x4f, 0x09, 0x53, 0x32, 0xc0, 0xd7, 0xd1, 0x34, 0x84, 0xdc, 0x87, + 0x40, 0x78, 0x9d, 0x2d, 0x1b, 0xf6, 0xe8, 0x02, 0xda, 0xd1, 0x87, 0xef, 0x8b, 0x2a, 0x57, 0x55, + 0x27, 0x4c, 0xe2, 0xa3, 0x8e, 0xe6, 0xaa, 0x8c, 0x3e, 0x21, 0x3e, 0xdd, 0xe5, 0xa4, 0x21, 0x9a, + 0xbd, 0xa0, 0x01, 0x8c, 0xe9, 0x7d, 0x0b, 0x65, 0x64, 0x37, 0x2c, 0x9f, 0x16, 0x1b, 0xb3, 0x9a, + 0xd4, 0xfc, 0x70, 0x43, 0xd2, 0x44, 0x65, 0x2a, 0x5a, 0x1f, 0x37, 0x26, 0x27, 0x78, 0x99, 0x17, + 0x56, 0x22, 0x46, 0x7c, 0x96, 0xd6, 0x4f, 0x1d, 0xa1, 0x2a, 0xa3, 0xf1, 0x96, 0x5c, 0x94, 0xb3, + 0x65, 0x34, 0xa3, 0xb6, 0x16, 0x62, 0x77, 0xa7, 0x09, 0x5c, 0x47, 0xd3, 0x5e, 0x0b, 0x3a, 0x01, + 0x57, 0x06, 0x27, 0x5c, 0x89, 0xab, 0x91, 0xa7, 0x7f, 0x5a, 0x7c, 0x25, 0x9d, 0x60, 0x3f, 0x87, + 0xf0, 0xa9, 0xd5, 0x78, 0x02, 0xe5, 0xef, 0x29, 0x94, 0xae, 0x32, 0x8a, 0x77, 0xd0, 0xec, 0x99, + 0x07, 0x60, 0x25, 0x69, 0xee, 0x23, 0xd7, 0xa2, 0xb0, 0x76, 0xae, 0xb2, 0xc1, 0xed, 0xb9, 0x83, + 0xa6, 0xc4, 0x12, 0x2d, 0x8d, 0xa1, 0x45, 0x60, 0xe1, 0xca, 0x04, 0x70, 0xa0, 0xf4, 0x14, 0x5d, + 0xfa, 0x63, 0x2d, 0xc7, 0x91, 0x86, 0x8b, 0xce, 0xa7, 0xfc, 0x10, 0x65, 0xe2, 0x8d, 0x30, 0xc6, + 0xd4, 0x2b, 0xbc, 0xb0, 0x3a, 0x19, 0x8f, 0x25, 0x2b, 0x95, 0x83, 0x23, 0x43, 0x3f, 0x3c, 0x32, + 0xf4, 0x6f, 0x47, 0x86, 0xfe, 0xe6, 0xd8, 0xd0, 0x0e, 0x8f, 0x0d, 0xed, 0xf3, 0xb1, 0xa1, 0x3d, + 0x9b, 0x7c, 0xb4, 0x7b, 0xe2, 0xfd, 0x17, 0x07, 0x5c, 0x9b, 0x16, 0x0f, 0xef, 0xb5, 0xdf, 0x01, + 0x00, 0x00, 0xff, 0xff, 0x77, 0xea, 0x20, 0x5e, 0x6b, 0x06, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -338,6 +381,8 @@ type MsgClient interface { SubmitProposal(ctx context.Context, in *MsgSubmitProposal, opts ...grpc.CallOption) (*MsgSubmitProposalResponse, error) // Vote defines a method to add a vote on a specific proposal. 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) (*MsgVoteResponse, error) // Deposit defines a method to add deposit on a specific proposal. Deposit(ctx context.Context, in *MsgDeposit, opts ...grpc.CallOption) (*MsgDepositResponse, error) } @@ -368,6 +413,15 @@ func (c *msgClient) Vote(ctx context.Context, in *MsgVote, opts ...grpc.CallOpti return out, nil } +func (c *msgClient) WeightedVote(ctx context.Context, in *MsgWeightedVote, opts ...grpc.CallOption) (*MsgVoteResponse, error) { + out := new(MsgVoteResponse) + 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) { out := new(MsgDepositResponse) err := c.cc.Invoke(ctx, "/cosmos.gov.v1beta1.Msg/Deposit", in, out, opts...) @@ -383,6 +437,8 @@ type MsgServer interface { SubmitProposal(context.Context, *MsgSubmitProposal) (*MsgSubmitProposalResponse, error) // Vote defines a method to add a vote on a specific proposal. Vote(context.Context, *MsgVote) (*MsgVoteResponse, error) + // WeightedVote defines a method to add a weighted vote on a specific proposal. + WeightedVote(context.Context, *MsgWeightedVote) (*MsgVoteResponse, error) // Deposit defines a method to add deposit on a specific proposal. Deposit(context.Context, *MsgDeposit) (*MsgDepositResponse, error) } @@ -397,6 +453,9 @@ func (*UnimplementedMsgServer) SubmitProposal(ctx context.Context, req *MsgSubmi func (*UnimplementedMsgServer) Vote(ctx context.Context, req *MsgVote) (*MsgVoteResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method Vote not implemented") } +func (*UnimplementedMsgServer) WeightedVote(ctx context.Context, req *MsgWeightedVote) (*MsgVoteResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method WeightedVote not implemented") +} func (*UnimplementedMsgServer) Deposit(ctx context.Context, req *MsgDeposit) (*MsgDepositResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method Deposit not implemented") } @@ -441,6 +500,24 @@ func _Msg_Vote_Handler(srv interface{}, ctx context.Context, dec func(interface{ 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) { in := new(MsgDeposit) if err := dec(in); err != nil { @@ -471,6 +548,10 @@ var _Msg_serviceDesc = grpc.ServiceDesc{ MethodName: "Vote", Handler: _Msg_Vote_Handler, }, + { + MethodName: "WeightedVote", + Handler: _Msg_WeightedVote_Handler, + }, { MethodName: "Deposit", Handler: _Msg_Deposit_Handler, @@ -580,6 +661,46 @@ func (m *MsgVote) MarshalTo(dAtA []byte) (int, error) { } func (m *MsgVote) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Option != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.Option)) + i-- + dAtA[i] = 0x18 + } + 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 *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 @@ -755,6 +876,25 @@ func (m *MsgSubmitProposalResponse) Size() (n int) { } func (m *MsgVote) 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 m.Option != 0 { + n += 1 + sovTx(uint64(m.Option)) + } + return n +} + +func (m *MsgWeightedVote) Size() (n int) { if m == nil { return 0 } @@ -1078,6 +1218,129 @@ func (m *MsgVote) Unmarshal(dAtA []byte) error { return fmt.Errorf("proto: MsgVote: 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 != 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 ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Option |= VoteOption(b&0x7F) << shift + if b < 0x80 { + break + } + } + 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 *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)