cosmos-sdk/x/gov/keeper/vote.go
2024-09-05 17:15:01 +00:00

108 lines
3.3 KiB
Go

package keeper
import (
"context"
stderrors "errors"
"fmt"
"cosmossdk.io/collections"
"cosmossdk.io/core/event"
"cosmossdk.io/errors"
"cosmossdk.io/x/gov/types"
v1 "cosmossdk.io/x/gov/types/v1"
sdk "github.com/cosmos/cosmos-sdk/types"
)
// AddVote adds a vote on a specific proposal
func (k Keeper) AddVote(ctx context.Context, proposalID uint64, voterAddr sdk.AccAddress, options v1.WeightedVoteOptions, metadata string) error {
// get proposal
proposal, err := k.Proposals.Get(ctx, proposalID)
if err != nil {
if stderrors.Is(err, collections.ErrNotFound) {
return errors.Wrapf(types.ErrInactiveProposal, "%d", proposalID)
}
return err
}
// check if proposal is in voting period.
if proposal.Status != v1.StatusVotingPeriod {
return errors.Wrapf(types.ErrInactiveProposal, "%d", proposalID)
}
if err := k.assertMetadataLength(metadata); err != nil {
return err
}
err = k.assertVoteOptionsLen(options)
if err != nil {
return err
}
for _, option := range options {
switch proposal.ProposalType {
case v1.ProposalType_PROPOSAL_TYPE_OPTIMISTIC:
if option.Option != v1.OptionNo {
return errors.Wrap(types.ErrInvalidVote, "optimistic proposals can only be rejected")
}
case v1.ProposalType_PROPOSAL_TYPE_MULTIPLE_CHOICE:
proposalOptionsStr, err := k.ProposalVoteOptions.Get(ctx, proposalID)
if err != nil {
if stderrors.Is(err, collections.ErrNotFound) {
return errors.Wrap(types.ErrInvalidProposal, "invalid multiple choice proposal, no options set")
}
return err
}
// verify votes only on existing votes
if proposalOptionsStr.OptionOne == "" && option.Option == v1.OptionOne {
return errors.Wrap(types.ErrInvalidVote, "invalid vote option")
} else if proposalOptionsStr.OptionTwo == "" && option.Option == v1.OptionTwo {
return errors.Wrap(types.ErrInvalidVote, "invalid vote option")
} else if proposalOptionsStr.OptionThree == "" && option.Option == v1.OptionThree {
return errors.Wrap(types.ErrInvalidVote, "invalid vote option")
} else if proposalOptionsStr.OptionFour == "" && option.Option == v1.OptionFour {
return errors.Wrap(types.ErrInvalidVote, "invalid vote option")
}
}
if !v1.ValidWeightedVoteOption(*option) {
return errors.Wrap(types.ErrInvalidVote, option.String())
}
}
voterStrAddr, err := k.authKeeper.AddressCodec().BytesToString(voterAddr)
if err != nil {
return err
}
vote := v1.NewVote(proposalID, voterStrAddr, options, metadata)
err = k.Votes.Set(ctx, collections.Join(proposalID, voterAddr), vote)
if err != nil {
return err
}
// called after a vote on a proposal is cast
if err = k.Hooks().AfterProposalVote(ctx, proposalID, voterAddr); err != nil {
return err
}
return k.EventService.EventManager(ctx).EmitKV(types.EventTypeProposalVote,
event.NewAttribute(types.AttributeKeyVoter, voterStrAddr),
event.NewAttribute(types.AttributeKeyOption, options.String()),
event.NewAttribute(types.AttributeKeyProposalID, fmt.Sprintf("%d", proposalID)),
)
}
// deleteVotes deletes all the votes from a given proposalID.
func (k Keeper) deleteVotes(ctx context.Context, proposalID uint64) error {
rng := collections.NewPrefixedPairRange[uint64, sdk.AccAddress](proposalID)
err := k.Votes.Clear(ctx, rng)
if err != nil {
return err
}
return nil
}