cosmos-sdk/x/gov/types/msgs.go

293 lines
8.0 KiB
Go

package types
import (
"fmt"
"sigs.k8s.io/yaml"
"github.com/gogo/protobuf/proto"
"github.com/cosmos/cosmos-sdk/codec/types"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
sdktx "github.com/cosmos/cosmos-sdk/types/tx"
)
// Governance message types and routes
const (
TypeMsgDeposit = "deposit"
TypeMsgVote = "vote"
TypeMsgVoteWeighted = "weighted_vote"
TypeMsgSubmitProposal = "submit_proposal"
TypeMsgSignal = "signal"
)
var (
_, _, _, _ sdk.Msg = &MsgSubmitProposal{}, &MsgDeposit{}, &MsgVote{}, &MsgVoteWeighted{}
_ types.UnpackInterfacesMessage = &MsgSubmitProposal{}
)
// NewMsgSubmitProposal creates a new MsgSubmitProposal.
//nolint:interfacer
func NewMsgSubmitProposal(messages []sdk.Msg, initialDeposit sdk.Coins, proposer sdk.AccAddress) (*MsgSubmitProposal, error) {
m := &MsgSubmitProposal{
InitialDeposit: initialDeposit,
Proposer: proposer.String(),
}
if err := m.SetMessages(messages); err != nil {
return &MsgSubmitProposal{}, err
}
return m, nil
}
func (m *MsgSubmitProposal) GetInitialDeposit() sdk.Coins { return m.InitialDeposit }
func (m *MsgSubmitProposal) GetProposer() sdk.AccAddress {
proposer, _ := sdk.AccAddressFromBech32(m.Proposer)
return proposer
}
func (m *MsgSubmitProposal) GetMessages() ([]sdk.Msg, error) {
return sdktx.GetMsgs(m.Messages, "sdk.MsgProposal")
}
func (m *MsgSubmitProposal) SetInitialDeposit(coins sdk.Coins) {
m.InitialDeposit = coins
}
func (m *MsgSubmitProposal) SetProposer(address fmt.Stringer) {
m.Proposer = address.String()
}
func (m *MsgSubmitProposal) SetMessages(messages []sdk.Msg) error {
msgs := make([]*types.Any, len(messages))
for i, msg := range messages {
m, ok := msg.(proto.Message)
if !ok {
return fmt.Errorf("can't proto marshal %T", msg)
}
any, err := types.NewAnyWithValue(m)
if err != nil {
return err
}
msgs[i] = any
}
m.Messages = msgs
return nil
}
// Route implements Msg
func (m MsgSubmitProposal) Route() string { return RouterKey }
// Type implements Msg
func (m MsgSubmitProposal) Type() string { return TypeMsgSubmitProposal }
// ValidateBasic implements Msg
func (m MsgSubmitProposal) ValidateBasic() error {
if _, err := sdk.AccAddressFromBech32(m.Proposer); err != nil {
return sdkerrors.ErrInvalidAddress.Wrapf("invalid proposer address: %s", err)
}
if !m.InitialDeposit.IsValid() {
return sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, m.InitialDeposit.String())
}
if m.InitialDeposit.IsAnyNegative() {
return sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, m.InitialDeposit.String())
}
// Empty messages are not allowed
// TODO: ValidateBasic should check that either metadata or length is non nil
// if m.Messages == nil || len(m.Messages) == 0 {
// return ErrNoProposalMsgs
// }
msgs, err := m.GetMessages()
if err != nil {
return err
}
for idx, msg := range msgs {
if err := msg.ValidateBasic(); err != nil {
return sdkerrors.Wrap(ErrInvalidProposalMsg,
fmt.Sprintf("msg: %d, err: %s", idx, err.Error()))
}
}
return nil
}
// GetSignBytes implements Msg
func (m MsgSubmitProposal) GetSignBytes() []byte {
bz := ModuleCdc.MustMarshalJSON(&m)
return sdk.MustSortJSON(bz)
}
// GetSigners implements Msg
func (m MsgSubmitProposal) GetSigners() []sdk.AccAddress {
proposer, _ := sdk.AccAddressFromBech32(m.Proposer)
return []sdk.AccAddress{proposer}
}
// String implements the Stringer interface
func (m MsgSubmitProposal) String() string {
out, _ := yaml.Marshal(m)
return string(out)
}
// UnpackInterfaces implements UnpackInterfacesMessage.UnpackInterfaces
func (m MsgSubmitProposal) UnpackInterfaces(unpacker types.AnyUnpacker) error {
return sdktx.UnpackInterfaces(unpacker, m.Messages)
}
// NewMsgDeposit creates a new MsgDeposit instance
//nolint:interfacer
func NewMsgDeposit(depositor sdk.AccAddress, proposalID uint64, amount sdk.Coins) *MsgDeposit {
return &MsgDeposit{proposalID, depositor.String(), amount}
}
// Route implements Msg
func (msg MsgDeposit) Route() string { return RouterKey }
// Type implements Msg
func (msg MsgDeposit) Type() string { return TypeMsgDeposit }
// ValidateBasic implements Msg
func (msg MsgDeposit) ValidateBasic() error {
if _, err := sdk.AccAddressFromBech32(msg.Depositor); err != nil {
return sdkerrors.ErrInvalidAddress.Wrapf("invalid depositor address: %s", err)
}
if !msg.Amount.IsValid() {
return sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, msg.Amount.String())
}
if msg.Amount.IsAnyNegative() {
return sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, msg.Amount.String())
}
return nil
}
// String implements the Stringer interface
func (msg MsgDeposit) String() string {
out, _ := yaml.Marshal(msg)
return string(out)
}
// GetSignBytes implements Msg
func (msg MsgDeposit) GetSignBytes() []byte {
bz := ModuleCdc.MustMarshalJSON(&msg)
return sdk.MustSortJSON(bz)
}
// GetSigners implements Msg
func (msg MsgDeposit) GetSigners() []sdk.AccAddress {
depositor, _ := sdk.AccAddressFromBech32(msg.Depositor)
return []sdk.AccAddress{depositor}
}
// NewMsgVote creates a message to cast a vote on an active proposal
//nolint:interfacer
func NewMsgVote(voter sdk.AccAddress, proposalID uint64, option VoteOption) *MsgVote {
return &MsgVote{proposalID, voter.String(), option}
}
// Route implements Msg
func (msg MsgVote) Route() string { return RouterKey }
// Type implements Msg
func (msg MsgVote) Type() string { return TypeMsgVote }
// ValidateBasic implements Msg
func (msg MsgVote) ValidateBasic() error {
if _, err := sdk.AccAddressFromBech32(msg.Voter); err != nil {
return sdkerrors.ErrInvalidAddress.Wrapf("invalid voter address: %s", err)
}
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}
}
// NewMsgVoteWeighted creates a message to cast a vote on an active proposal
//nolint:interfacer
func NewMsgVoteWeighted(voter sdk.AccAddress, proposalID uint64, options WeightedVoteOptions) *MsgVoteWeighted {
return &MsgVoteWeighted{proposalID, voter.String(), options}
}
// Route implements Msg
func (msg MsgVoteWeighted) Route() string { return RouterKey }
// Type implements Msg
func (msg MsgVoteWeighted) Type() string { return TypeMsgVoteWeighted }
// ValidateBasic implements Msg
func (msg MsgVoteWeighted) ValidateBasic() error {
if _, err := sdk.AccAddressFromBech32(msg.Voter); err != nil {
return sdkerrors.ErrInvalidAddress.Wrapf("invalid voter address: %s", err)
}
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")
}
if totalWeight.LT(sdk.NewDec(1)) {
return sdkerrors.Wrap(ErrInvalidVote, "Total weight lower than 1.00")
}
return nil
}
// String implements the Stringer interface
func (msg MsgVoteWeighted) String() string {
out, _ := yaml.Marshal(msg)
return string(out)
}
// GetSignBytes implements Msg
func (msg MsgVoteWeighted) GetSignBytes() []byte {
bz := ModuleCdc.MustMarshalJSON(&msg)
return sdk.MustSortJSON(bz)
}
// GetSigners implements Msg
func (msg MsgVoteWeighted) GetSigners() []sdk.AccAddress {
voter, _ := sdk.AccAddressFromBech32(msg.Voter)
return []sdk.AccAddress{voter}
}