diff --git a/x/committee/handler.go b/x/committee/handler.go index 158faa1f..bbc0530c 100644 --- a/x/committee/handler.go +++ b/x/committee/handler.go @@ -1,25 +1,24 @@ package committee -// committee, subcommittee, council, caucus, commission, synod, board -/* import ( "fmt" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/kava-labs/kava/x/committee/keeper" - "github.com/kava-labs/kava/x/committee/types" ) // NewHandler creates an sdk.Handler for committee messages func NewHandler(k keeper.Keeper) sdk.Handler { return func(ctx sdk.Context, msg sdk.Msg) sdk.Result { + ctx = ctx.WithEventManager(sdk.NewEventManager()) + switch msg := msg.(type) { case types.MsgSubmitProposal: - handleMsgSubmitProposal(ctx, k, msg) + return handleMsgSubmitProposal(ctx, k, msg) case types.MsgVote: - handleMsgVote(ctx, k, msg) + return handleMsgVote(ctx, k, msg) default: errMsg := fmt.Sprintf("unrecognized %s msg type: %T", types.ModuleName, msg) return sdk.ErrUnknownRequest(errMsg).Result() @@ -28,21 +27,33 @@ func NewHandler(k keeper.Keeper) sdk.Handler { } func handleMsgSubmitProposal(ctx sdk.Context, k keeper.Keeper, msg types.MsgSubmitProposal) sdk.Result { - err := keeper.SubmitProposal(ctx, msg) - + proposalID, err := k.SubmitProposal(ctx, msg.Proposer, msg.CommitteeID, msg.PubProposal) if err != nil { return err.Result() } - return sdk.Result{} + ctx.EventManager().EmitEvent( + sdk.NewEvent( + sdk.EventTypeMessage, + // TODO sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), + sdk.NewAttribute(sdk.AttributeKeySender, msg.Proposer.String()), + ), + ) + + return sdk.Result{ + Data: GetKeyFromID(proposalID), + Events: ctx.EventManager().Events(), + } } func handleMsgVote(ctx sdk.Context, k keeper.Keeper, msg types.MsgVote) sdk.Result { - err := keeper.AddVote(ctx, msg) - - // Try closing proposal - _ = k.CloseOutProposal(ctx, proposalID) + err := k.AddVote(ctx, msg.ProposalID, msg.Voter) + if err != nil { + return err.Result() + } + // Try closing proposal in case enough votes have been cast + _ = k.CloseOutProposal(ctx, msg.ProposalID) // if err.Error() == "note enough votes to close proposal" { // TODO // return nil // This is not a reason to error // } @@ -50,10 +61,13 @@ func handleMsgVote(ctx sdk.Context, k keeper.Keeper, msg types.MsgVote) sdk.Resu // return err // } - if err != nil { - return err.Result() - } + ctx.EventManager().EmitEvent( + sdk.NewEvent( + sdk.EventTypeMessage, + // TODO sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), + sdk.NewAttribute(sdk.AttributeKeySender, msg.Voter.String()), + ), + ) - return sdk.Result{} + return sdk.Result{Events: ctx.EventManager().Events()} } -*/ diff --git a/x/committee/types/keys.go b/x/committee/types/keys.go index 6b0a8c4d..43455f39 100644 --- a/x/committee/types/keys.go +++ b/x/committee/types/keys.go @@ -13,7 +13,6 @@ const ( // StoreKey Top level store key where all module items will be stored StoreKey = ModuleName -/* // RouterKey Top level router key RouterKey = ModuleName @@ -22,7 +21,6 @@ const ( // DefaultParamspace default name for parameter store DefaultParamspace = ModuleName -*/ ) // Key prefixes diff --git a/x/committee/types/msg.go b/x/committee/types/msg.go index 74b21fa4..e262260b 100644 --- a/x/committee/types/msg.go +++ b/x/committee/types/msg.go @@ -1,11 +1,97 @@ package types +import ( + sdk "github.com/cosmos/cosmos-sdk/types" +) + +const ( + TypeMsgSubmitProposal = "submit_proposal" + TypeMsgVote = "vote" +) + +var _, _ sdk.Msg = MsgSubmitProposal{}, MsgVote{} + // MsgSubmitProposal is used by committee members to create a new proposal that they can vote on. type MsgSubmitProposal struct { + PubProposal PubProposal `json:"pub_proposal" yaml:"pub_proposal"` + Proposer sdk.AccAddress `json:"proposer" yaml:"proposer"` + CommitteeID uint64 +} + +// NewMsgSubmitProposal creates a new MsgSubmitProposal instance +func NewMsgSubmitProposal(pubProposal PubProposal, proposer sdk.AccAddress, committeeID uint64) MsgSubmitProposal { + return MsgSubmitProposal{ + PubProposal: pubProposal, + Proposer: proposer, + CommitteeID: committeeID, + } +} + +// Route return the message type used for routing the message. +func (msg MsgSubmitProposal) Route() string { return RouterKey } + +// Type returns a human-readable string for the message, intended for utilization within events. +func (msg MsgSubmitProposal) Type() string { return TypeMsgSubmitProposal } + +// ValidateBasic does a simple validation check that doesn't require access to any other information. +func (msg MsgSubmitProposal) ValidateBasic() sdk.Error { + if msg.PubProposal == nil { + return sdk.ErrInternal("no proposal") + } + if msg.Proposer.Empty() { + return sdk.ErrInvalidAddress(msg.Proposer.String()) + } // TODO + // if !IsValidProposalType(msg.Content.ProposalType()) { + // return ErrInvalidProposalType(DefaultCodespace, msg.Content.ProposalType()) + // } + + return msg.PubProposal.ValidateBasic() +} + +// GetSignBytes gets the canonical byte representation of the Msg. +func (msg MsgSubmitProposal) GetSignBytes() []byte { + bz := ModuleCdc.MustMarshalJSON(msg) + return sdk.MustSortJSON(bz) +} + +// GetSigners returns the addresses of signers that must sign. +func (msg MsgSubmitProposal) GetSigners() []sdk.AccAddress { + return []sdk.AccAddress{msg.Proposer} } // MsgVote is submitted by committee members to vote on proposals. type MsgVote struct { - // TODO + ProposalID uint64 `json:"proposal_id" yaml:"proposal_id"` + Voter sdk.AccAddress `json:"voter" yaml:"voter"` +} + +// NewMsgVote creates a message to cast a vote on an active proposal +func NewMsgVote(voter sdk.AccAddress, proposalID uint64) MsgVote { + return MsgVote{proposalID, voter} +} + +// Route return the message type used for routing the message. +func (msg MsgVote) Route() string { return RouterKey } + +// Type returns a human-readable string for the message, intended for utilization within events. +func (msg MsgVote) Type() string { return TypeMsgVote } + +// ValidateBasic does a simple validation check that doesn't require access to any other information. +func (msg MsgVote) ValidateBasic() sdk.Error { + if msg.Voter.Empty() { + return sdk.ErrInvalidAddress(msg.Voter.String()) + } + return nil +} + +// GetSignBytes gets the canonical byte representation of the Msg. +func (msg MsgVote) GetSignBytes() []byte { + bz := ModuleCdc.MustMarshalJSON(msg) + return sdk.MustSortJSON(bz) +} + +// GetSigners returns the addresses of signers that must sign. +func (msg MsgVote) GetSigners() []sdk.AccAddress { + return []sdk.AccAddress{msg.Voter} } diff --git a/x/committee/types/msg_test.go b/x/committee/types/msg_test.go new file mode 100644 index 00000000..1a7b15f7 --- /dev/null +++ b/x/committee/types/msg_test.go @@ -0,0 +1,81 @@ +package types + +import ( + "testing" + + "github.com/stretchr/testify/require" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/gov" +) + +func TestMsgSubmitProposal_ValidateBasic(t *testing.T) { + addr := sdk.AccAddress([]byte("someName")) + tests := []struct { + name string + msg MsgSubmitProposal + expectPass bool + }{ + { + name: "normal", + msg: MsgSubmitProposal{gov.NewTextProposal("A Title", "A proposal description."), addr, 3}, + expectPass: true, + }, + { + name: "empty address", + msg: MsgSubmitProposal{gov.NewTextProposal("A Title", "A proposal description."), nil, 3}, + expectPass: false, + }, + { + name: "invalid proposal", + msg: MsgSubmitProposal{gov.TextProposal{}, addr, 3}, + expectPass: false, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + + err := tc.msg.ValidateBasic() + + if tc.expectPass { + require.NoError(t, err) + } else { + require.Error(t, err) + } + }) + } +} + +func TestMsgVote_ValidateBasic(t *testing.T) { + addr := sdk.AccAddress([]byte("someName")) + tests := []struct { + name string + msg MsgVote + expectPass bool + }{ + { + name: "normal", + msg: MsgVote{5, addr}, + expectPass: true, + }, + { + name: "empty address", + msg: MsgVote{5, nil}, + expectPass: false, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + + err := tc.msg.ValidateBasic() + + if tc.expectPass { + require.NoError(t, err) + } else { + require.Error(t, err) + } + }) + } +}