package staking import ( "time" abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/libs/common" tmtypes "github.com/tendermint/tendermint/types" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/staking/keeper" "github.com/cosmos/cosmos-sdk/x/staking/tags" "github.com/cosmos/cosmos-sdk/x/staking/types" ) func NewHandler(k keeper.Keeper) sdk.Handler { return func(ctx sdk.Context, msg sdk.Msg) sdk.Result { // NOTE msg already has validate basic run switch msg := msg.(type) { case types.MsgCreateValidator: return handleMsgCreateValidator(ctx, msg, k) case types.MsgEditValidator: return handleMsgEditValidator(ctx, msg, k) case types.MsgDelegate: return handleMsgDelegate(ctx, msg, k) case types.MsgBeginRedelegate: return handleMsgBeginRedelegate(ctx, msg, k) case types.MsgUndelegate: return handleMsgUndelegate(ctx, msg, k) default: return sdk.ErrTxDecode("invalid message parse in staking module").Result() } } } // Called every block, update validator set func EndBlocker(ctx sdk.Context, k keeper.Keeper) ([]abci.ValidatorUpdate, sdk.Tags) { resTags := sdk.NewTags() // Calculate validator set changes. // // NOTE: ApplyAndReturnValidatorSetUpdates has to come before // UnbondAllMatureValidatorQueue. // This fixes a bug when the unbonding period is instant (is the case in // some of the tests). The test expected the validator to be completely // unbonded after the Endblocker (go from Bonded -> Unbonding during // ApplyAndReturnValidatorSetUpdates and then Unbonding -> Unbonded during // UnbondAllMatureValidatorQueue). validatorUpdates := k.ApplyAndReturnValidatorSetUpdates(ctx) // Unbond all mature validators from the unbonding queue. k.UnbondAllMatureValidatorQueue(ctx) // Remove all mature unbonding delegations from the ubd queue. matureUnbonds := k.DequeueAllMatureUBDQueue(ctx, ctx.BlockHeader().Time) for _, dvPair := range matureUnbonds { err := k.CompleteUnbonding(ctx, dvPair.DelegatorAddr, dvPair.ValidatorAddr) if err != nil { continue } resTags.AppendTags(sdk.NewTags( tags.Action, ActionCompleteUnbonding, tags.Delegator, dvPair.DelegatorAddr.String(), tags.SrcValidator, dvPair.ValidatorAddr.String(), )) } // Remove all mature redelegations from the red queue. matureRedelegations := k.DequeueAllMatureRedelegationQueue(ctx, ctx.BlockHeader().Time) for _, dvvTriplet := range matureRedelegations { err := k.CompleteRedelegation(ctx, dvvTriplet.DelegatorAddr, dvvTriplet.ValidatorSrcAddr, dvvTriplet.ValidatorDstAddr) if err != nil { continue } resTags.AppendTags(sdk.NewTags( tags.Action, tags.ActionCompleteRedelegation, tags.Delegator, dvvTriplet.DelegatorAddr.String(), tags.SrcValidator, dvvTriplet.ValidatorSrcAddr.String(), tags.DstValidator, dvvTriplet.ValidatorDstAddr.String(), )) } return validatorUpdates, resTags } // These functions assume everything has been authenticated, // now we just perform action and save func handleMsgCreateValidator(ctx sdk.Context, msg types.MsgCreateValidator, k keeper.Keeper) sdk.Result { // check to see if the pubkey or sender has been registered before if _, found := k.GetValidator(ctx, msg.ValidatorAddr); found { return ErrValidatorOwnerExists(k.Codespace()).Result() } if _, found := k.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(msg.PubKey)); found { return ErrValidatorPubKeyExists(k.Codespace()).Result() } if msg.Value.Denom != k.GetParams(ctx).BondDenom { return ErrBadDenom(k.Codespace()).Result() } if _, err := msg.Description.EnsureLength(); err != nil { return err.Result() } if ctx.ConsensusParams() != nil { tmPubKey := tmtypes.TM2PB.PubKey(msg.PubKey) if !common.StringInSlice(tmPubKey.Type, ctx.ConsensusParams().Validator.PubKeyTypes) { return ErrValidatorPubKeyTypeUnsupported(k.Codespace(), tmPubKey.Type, ctx.ConsensusParams().Validator.PubKeyTypes).Result() } } validator := NewValidator(msg.ValidatorAddr, msg.PubKey, msg.Description) commission := NewCommissionWithTime( msg.Commission.Rate, msg.Commission.MaxRate, msg.Commission.MaxChangeRate, ctx.BlockHeader().Time, ) validator, err := validator.SetInitialCommission(commission) if err != nil { return err.Result() } k.SetValidator(ctx, validator) k.SetValidatorByConsAddr(ctx, validator) k.SetNewValidatorByPowerIndex(ctx, validator) // call the after-creation hook k.AfterValidatorCreated(ctx, validator.OperatorAddr) // move coins from the msg.Address account to a (self-delegation) delegator account // the validator account and global shares are updated within here _, err = k.Delegate(ctx, msg.DelegatorAddr, msg.Value, validator, true) if err != nil { return err.Result() } tags := sdk.NewTags( tags.DstValidator, msg.ValidatorAddr.String(), tags.Moniker, msg.Description.Moniker, tags.Identity, msg.Description.Identity, ) return sdk.Result{ Tags: tags, } } func handleMsgEditValidator(ctx sdk.Context, msg types.MsgEditValidator, k keeper.Keeper) sdk.Result { // validator must already be registered validator, found := k.GetValidator(ctx, msg.ValidatorAddr) if !found { return ErrNoValidatorFound(k.Codespace()).Result() } // replace all editable fields (clients should autofill existing values) description, err := validator.Description.UpdateDescription(msg.Description) if err != nil { return err.Result() } validator.Description = description if msg.CommissionRate != nil { commission, err := k.UpdateValidatorCommission(ctx, validator, *msg.CommissionRate) if err != nil { return err.Result() } // call the before-modification hook since we're about to update the commission k.BeforeValidatorModified(ctx, msg.ValidatorAddr) validator.Commission = commission } k.SetValidator(ctx, validator) tags := sdk.NewTags( tags.DstValidator, msg.ValidatorAddr.String(), tags.Moniker, description.Moniker, tags.Identity, description.Identity, ) return sdk.Result{ Tags: tags, } } func handleMsgDelegate(ctx sdk.Context, msg types.MsgDelegate, k keeper.Keeper) sdk.Result { validator, found := k.GetValidator(ctx, msg.ValidatorAddr) if !found { return ErrNoValidatorFound(k.Codespace()).Result() } if msg.Value.Denom != k.GetParams(ctx).BondDenom { return ErrBadDenom(k.Codespace()).Result() } _, err := k.Delegate(ctx, msg.DelegatorAddr, msg.Value, validator, true) if err != nil { return err.Result() } tags := sdk.NewTags( tags.Delegator, msg.DelegatorAddr.String(), tags.DstValidator, msg.ValidatorAddr.String(), ) return sdk.Result{ Tags: tags, } } func handleMsgUndelegate(ctx sdk.Context, msg types.MsgUndelegate, k keeper.Keeper) sdk.Result { completionTime, err := k.Undelegate(ctx, msg.DelegatorAddr, msg.ValidatorAddr, msg.SharesAmount) if err != nil { return err.Result() } finishTime := types.MsgCdc.MustMarshalBinaryLengthPrefixed(completionTime) tags := sdk.NewTags( tags.Delegator, msg.DelegatorAddr.String(), tags.SrcValidator, msg.ValidatorAddr.String(), tags.EndTime, completionTime.Format(time.RFC3339), ) return sdk.Result{Data: finishTime, Tags: tags} } func handleMsgBeginRedelegate(ctx sdk.Context, msg types.MsgBeginRedelegate, k keeper.Keeper) sdk.Result { completionTime, err := k.BeginRedelegation(ctx, msg.DelegatorAddr, msg.ValidatorSrcAddr, msg.ValidatorDstAddr, msg.SharesAmount) if err != nil { return err.Result() } finishTime := types.MsgCdc.MustMarshalBinaryLengthPrefixed(completionTime) resTags := sdk.NewTags( tags.Delegator, msg.DelegatorAddr.String(), tags.SrcValidator, msg.ValidatorSrcAddr.String(), tags.DstValidator, msg.ValidatorDstAddr.String(), tags.EndTime, completionTime.Format(time.RFC3339), ) return sdk.Result{Data: finishTime, Tags: resTags} }