250 lines
7.8 KiB
Go
250 lines
7.8 KiB
Go
package stake
|
|
|
|
import (
|
|
"bytes"
|
|
"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/stake/keeper"
|
|
"github.com/cosmos/cosmos-sdk/x/stake/tags"
|
|
"github.com/cosmos/cosmos-sdk/x/stake/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.MsgBeginUnbonding:
|
|
return handleMsgBeginUnbonding(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.DequeueAllMatureUnbondingQueue(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, []byte(dvPair.DelegatorAddr.String()),
|
|
tags.SrcValidator, []byte(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, []byte(dvvTriplet.DelegatorAddr.String()),
|
|
tags.SrcValidator, []byte(dvvTriplet.ValidatorSrcAddr.String()),
|
|
tags.DstValidator, []byte(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
|
|
_, found := k.GetValidator(ctx, msg.ValidatorAddr)
|
|
if found {
|
|
return ErrValidatorOwnerExists(k.Codespace()).Result()
|
|
}
|
|
|
|
_, found = k.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(msg.PubKey))
|
|
if found {
|
|
return ErrValidatorPubKeyExists(k.Codespace()).Result()
|
|
}
|
|
|
|
if msg.Delegation.Denom != k.GetParams(ctx).BondDenom {
|
|
return ErrBadDenom(k.Codespace()).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)
|
|
|
|
k.OnValidatorCreated(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.Delegation, validator, true)
|
|
if err != nil {
|
|
return err.Result()
|
|
}
|
|
|
|
tags := sdk.NewTags(
|
|
tags.DstValidator, []byte(msg.ValidatorAddr.String()),
|
|
tags.Moniker, []byte(msg.Description.Moniker),
|
|
tags.Identity, []byte(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()
|
|
}
|
|
validator.Commission = commission
|
|
k.OnValidatorModified(ctx, msg.ValidatorAddr)
|
|
}
|
|
|
|
k.SetValidator(ctx, validator)
|
|
|
|
tags := sdk.NewTags(
|
|
tags.DstValidator, []byte(msg.ValidatorAddr.String()),
|
|
tags.Moniker, []byte(description.Moniker),
|
|
tags.Identity, []byte(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.Delegation.Denom != k.GetParams(ctx).BondDenom {
|
|
return ErrBadDenom(k.Codespace()).Result()
|
|
}
|
|
|
|
if validator.Jailed && !bytes.Equal(validator.OperatorAddr, msg.DelegatorAddr) {
|
|
return ErrValidatorJailed(k.Codespace()).Result()
|
|
}
|
|
|
|
_, err := k.Delegate(ctx, msg.DelegatorAddr, msg.Delegation, validator, true)
|
|
if err != nil {
|
|
return err.Result()
|
|
}
|
|
|
|
tags := sdk.NewTags(
|
|
tags.Delegator, []byte(msg.DelegatorAddr.String()),
|
|
tags.DstValidator, []byte(msg.ValidatorAddr.String()),
|
|
)
|
|
|
|
return sdk.Result{
|
|
Tags: tags,
|
|
}
|
|
}
|
|
|
|
func handleMsgBeginUnbonding(ctx sdk.Context, msg types.MsgBeginUnbonding, k keeper.Keeper) sdk.Result {
|
|
ubd, err := k.BeginUnbonding(ctx, msg.DelegatorAddr, msg.ValidatorAddr, msg.SharesAmount)
|
|
if err != nil {
|
|
return err.Result()
|
|
}
|
|
|
|
finishTime := types.MsgCdc.MustMarshalBinaryLengthPrefixed(ubd.MinTime)
|
|
tags := sdk.NewTags(
|
|
tags.Delegator, []byte(msg.DelegatorAddr.String()),
|
|
tags.SrcValidator, []byte(msg.ValidatorAddr.String()),
|
|
tags.EndTime, []byte(ubd.MinTime.Format(time.RFC3339)),
|
|
)
|
|
|
|
return sdk.Result{Data: finishTime, Tags: tags}
|
|
}
|
|
|
|
func handleMsgBeginRedelegate(ctx sdk.Context, msg types.MsgBeginRedelegate, k keeper.Keeper) sdk.Result {
|
|
red, err := k.BeginRedelegation(ctx, msg.DelegatorAddr, msg.ValidatorSrcAddr,
|
|
msg.ValidatorDstAddr, msg.SharesAmount)
|
|
if err != nil {
|
|
return err.Result()
|
|
}
|
|
|
|
finishTime := types.MsgCdc.MustMarshalBinaryLengthPrefixed(red.MinTime)
|
|
resTags := sdk.NewTags(
|
|
tags.Delegator, []byte(msg.DelegatorAddr.String()),
|
|
tags.SrcValidator, []byte(msg.ValidatorSrcAddr.String()),
|
|
tags.DstValidator, []byte(msg.ValidatorDstAddr.String()),
|
|
tags.EndTime, []byte(red.MinTime.Format(time.RFC3339)),
|
|
)
|
|
|
|
return sdk.Result{Data: finishTime, Tags: resTags}
|
|
}
|