github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/x/gov/handler.go (about) 1 package gov 2 3 import ( 4 "fmt" 5 6 sdk "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types" 7 8 "github.com/fibonacci-chain/fbc/x/common" 9 "github.com/fibonacci-chain/fbc/x/gov/keeper" 10 "github.com/fibonacci-chain/fbc/x/gov/types" 11 ) 12 13 // NewHandler handle all "gov" type messages. 14 func NewHandler(keeper Keeper) sdk.Handler { 15 return func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { 16 ctx.SetEventManager(sdk.NewEventManager()) 17 18 switch msg := msg.(type) { 19 case MsgDeposit: 20 return handleMsgDeposit(ctx, keeper, msg) 21 22 case MsgSubmitProposal: 23 return handleMsgSubmitProposal(ctx, keeper, msg) 24 25 case MsgVote: 26 return handleMsgVote(ctx, keeper, msg) 27 default: 28 errMsg := fmt.Sprintf("unrecognized gov message type: %T", msg) 29 return sdk.ErrUnknownRequest(errMsg).Result() 30 } 31 } 32 } 33 34 func handleMsgSubmitProposal(ctx sdk.Context, keeper keeper.Keeper, msg MsgSubmitProposal) (*sdk.Result, error) { 35 err := hasOnlyDefaultBondDenom(msg.InitialDeposit) 36 if err != nil { 37 return sdk.EnvelopedErr{err}.Result() 38 } 39 40 // use ctx directly 41 if !keeper.ProposalHandlerRouter().HasRoute(msg.Content.ProposalRoute()) { 42 err = keeper.CheckMsgSubmitProposal(ctx, msg) 43 } else { 44 proposalHandler := keeper.ProposalHandlerRouter().GetRoute(msg.Content.ProposalRoute()) 45 err = proposalHandler.CheckMsgSubmitProposal(ctx, msg) 46 } 47 if err != nil { 48 return sdk.EnvelopedErr{err}.Result() 49 } 50 51 proposal, err := keeper.SubmitProposal(ctx, msg.Content) 52 if err != nil { 53 return sdk.EnvelopedErr{err}.Result() 54 } 55 56 err = keeper.AddDeposit(ctx, proposal.ProposalID, msg.Proposer, 57 msg.InitialDeposit, types.EventTypeSubmitProposal) 58 if err != nil { 59 return sdk.EnvelopedErr{err}.Result() 60 } 61 62 ctx.EventManager().EmitEvent( 63 sdk.NewEvent( 64 sdk.EventTypeMessage, 65 sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), 66 sdk.NewAttribute(sdk.AttributeKeySender, msg.Proposer.String()), 67 ), 68 ) 69 70 return &sdk.Result{ 71 Data: keeper.Cdc().MustMarshalBinaryLengthPrefixed(proposal.ProposalID), 72 Events: ctx.EventManager().Events(), 73 }, nil 74 } 75 76 func handleMsgDeposit(ctx sdk.Context, keeper keeper.Keeper, msg MsgDeposit) (*sdk.Result, error) { 77 if err := hasOnlyDefaultBondDenom(msg.Amount); err != nil { 78 return sdk.EnvelopedErr{err}.Result() 79 } 80 // check depositor has sufficient coins 81 err := common.HasSufficientCoins(msg.Depositor, keeper.BankKeeper().GetCoins(ctx, msg.Depositor), 82 msg.Amount) 83 if err != nil { 84 return common.ErrInsufficientCoins(DefaultParamspace, err.Error()).Result() 85 } 86 87 sdkErr := keeper.AddDeposit(ctx, msg.ProposalID, msg.Depositor, 88 msg.Amount, types.EventTypeProposalDeposit) 89 if sdkErr != nil { 90 return sdk.EnvelopedErr{sdkErr}.Result() 91 } 92 93 ctx.EventManager().EmitEvent( 94 sdk.NewEvent( 95 sdk.EventTypeMessage, 96 sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), 97 sdk.NewAttribute(sdk.AttributeKeySender, msg.Depositor.String()), 98 ), 99 ) 100 101 return &sdk.Result{Events: ctx.EventManager().Events()}, nil 102 } 103 104 func handleMsgVote(ctx sdk.Context, k keeper.Keeper, msg MsgVote) (*sdk.Result, error) { 105 proposal, ok := k.GetProposal(ctx, msg.ProposalID) 106 if !ok { 107 return sdk.EnvelopedErr{types.ErrUnknownProposal(msg.ProposalID)}.Result() 108 } 109 110 err, _ := k.AddVote(ctx, msg.ProposalID, msg.Voter, msg.Option) 111 if err != nil { 112 return sdk.EnvelopedErr{err}.Result() 113 } 114 115 status, distribute, tallyResults := keeper.Tally(ctx, k, proposal, false) 116 // update tally results after vote every time 117 proposal.FinalTallyResult = tallyResults 118 119 // this vote makes the votingPeriod end 120 if status != StatusVotingPeriod { 121 tagValue, logMsg := handleProposalAfterTally(ctx, k, &proposal, distribute, status) 122 k.RemoveFromActiveProposalQueue(ctx, proposal.ProposalID, proposal.VotingEndTime) 123 proposal.VotingEndTime = ctx.BlockHeader().Time 124 k.DeleteVotes(ctx, proposal.ProposalID) 125 ctx.EventManager().EmitEvent( 126 sdk.NewEvent( 127 types.EventTypeProposalVoteTally, 128 sdk.NewAttribute(types.AttributeKeyProposalID, fmt.Sprintf("%d", proposal.ProposalID)), 129 sdk.NewAttribute(types.AttributeKeyProposalResult, tagValue), 130 sdk.NewAttribute(types.AttributeKeyProposalLog, logMsg), 131 ), 132 ) 133 } 134 k.SetProposal(ctx, proposal) 135 136 ctx.EventManager().EmitEvent( 137 sdk.NewEvent( 138 sdk.EventTypeMessage, 139 sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), 140 sdk.NewAttribute(sdk.AttributeKeySender, msg.Voter.String()), 141 sdk.NewAttribute(types.AttributeKeyProposalStatus, proposal.Status.String()), 142 ), 143 ) 144 145 return &sdk.Result{Events: ctx.EventManager().Events()}, nil 146 } 147 148 func handleProposalAfterTally( 149 ctx sdk.Context, k keeper.Keeper, proposal *types.Proposal, distribute bool, status ProposalStatus, 150 ) (string, string) { 151 if distribute { 152 k.DistributeDeposits(ctx, proposal.ProposalID) 153 } else { 154 k.RefundDeposits(ctx, proposal.ProposalID) 155 } 156 157 if status == StatusPassed { 158 handler := k.Router().GetRoute(proposal.ProposalRoute()) 159 cacheCtx, writeCache := ctx.CacheContext() 160 161 // The proposal handler may execute state mutating logic depending 162 // on the proposal content. If the handler fails, no state mutation 163 // is written and the error message is logged. 164 err := handler(cacheCtx, proposal) 165 if err == nil { 166 proposal.Status = StatusPassed 167 // write state to the underlying multi-store 168 writeCache() 169 return types.AttributeValueProposalPassed, "passed" 170 } 171 172 proposal.Status = StatusFailed 173 return types.AttributeValueProposalFailed, fmt.Sprintf("passed, but failed on execution: %s", 174 err.Error()) 175 } else if status == StatusRejected { 176 if k.ProposalHandlerRouter().HasRoute(proposal.ProposalRoute()) { 177 k.ProposalHandlerRouter().GetRoute(proposal.ProposalRoute()).RejectedHandler(ctx, proposal.Content) 178 } 179 proposal.Status = StatusRejected 180 return types.AttributeValueProposalRejected, "rejected" 181 } 182 return "", "" 183 } 184 185 func hasOnlyDefaultBondDenom(decCoins sdk.SysCoins) sdk.Error { 186 if len(decCoins) != 1 || decCoins[0].Denom != sdk.DefaultBondDenom || !decCoins.IsValid() { 187 return types.ErrInvalidCoins() 188 } 189 return nil 190 }