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  }