2019-01-11 12:08:01 -08:00
|
|
|
package staking
|
2018-02-23 15:57:31 -08:00
|
|
|
|
|
|
|
import (
|
2018-12-19 19:28:38 -08:00
|
|
|
"time"
|
2018-08-12 00:33:48 -07:00
|
|
|
|
2020-02-06 11:21:02 -08:00
|
|
|
gogotypes "github.com/gogo/protobuf/types"
|
2020-01-16 13:46:51 -08:00
|
|
|
tmstrings "github.com/tendermint/tendermint/libs/strings"
|
2018-11-29 09:21:45 -08:00
|
|
|
tmtypes "github.com/tendermint/tendermint/types"
|
|
|
|
|
2018-02-23 15:57:31 -08:00
|
|
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
2019-12-27 09:57:54 -08:00
|
|
|
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
2019-01-11 12:08:01 -08:00
|
|
|
"github.com/cosmos/cosmos-sdk/x/staking/keeper"
|
|
|
|
"github.com/cosmos/cosmos-sdk/x/staking/types"
|
2018-02-23 15:57:31 -08:00
|
|
|
)
|
|
|
|
|
2018-06-26 19:00:12 -07:00
|
|
|
func NewHandler(k keeper.Keeper) sdk.Handler {
|
2019-12-27 09:57:54 -08:00
|
|
|
return func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) {
|
2019-06-26 09:03:25 -07:00
|
|
|
ctx = ctx.WithEventManager(sdk.NewEventManager())
|
|
|
|
|
2018-03-14 11:42:50 -07:00
|
|
|
switch msg := msg.(type) {
|
2018-06-26 19:00:12 -07:00
|
|
|
case types.MsgCreateValidator:
|
2018-05-31 12:22:46 -07:00
|
|
|
return handleMsgCreateValidator(ctx, msg, k)
|
2019-04-15 09:25:04 -07:00
|
|
|
|
2018-06-26 19:00:12 -07:00
|
|
|
case types.MsgEditValidator:
|
2018-05-31 12:22:46 -07:00
|
|
|
return handleMsgEditValidator(ctx, msg, k)
|
2019-04-15 09:25:04 -07:00
|
|
|
|
2018-06-26 19:00:12 -07:00
|
|
|
case types.MsgDelegate:
|
2018-03-27 17:26:52 -07:00
|
|
|
return handleMsgDelegate(ctx, msg, k)
|
2019-04-15 09:25:04 -07:00
|
|
|
|
2018-06-26 19:00:12 -07:00
|
|
|
case types.MsgBeginRedelegate:
|
|
|
|
return handleMsgBeginRedelegate(ctx, msg, k)
|
2019-04-15 09:25:04 -07:00
|
|
|
|
2019-01-17 09:53:22 -08:00
|
|
|
case types.MsgUndelegate:
|
|
|
|
return handleMsgUndelegate(ctx, msg, k)
|
2019-04-15 09:25:04 -07:00
|
|
|
|
2018-03-13 11:27:52 -07:00
|
|
|
default:
|
2019-12-27 09:57:54 -08:00
|
|
|
return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized %s message type: %T", ModuleName, msg)
|
2018-02-27 18:07:20 -08:00
|
|
|
}
|
2018-02-23 15:57:31 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// These functions assume everything has been authenticated,
|
|
|
|
// now we just perform action and save
|
2018-03-14 11:42:50 -07:00
|
|
|
|
2019-12-27 09:57:54 -08:00
|
|
|
func handleMsgCreateValidator(ctx sdk.Context, msg types.MsgCreateValidator, k keeper.Keeper) (*sdk.Result, error) {
|
2018-02-27 18:07:20 -08:00
|
|
|
// check to see if the pubkey or sender has been registered before
|
2019-02-25 07:16:52 -08:00
|
|
|
if _, found := k.GetValidator(ctx, msg.ValidatorAddress); found {
|
2019-12-27 09:57:54 -08:00
|
|
|
return nil, ErrValidatorOwnerExists
|
2018-07-09 17:42:57 -07:00
|
|
|
}
|
2018-09-24 15:23:58 -07:00
|
|
|
|
2020-02-06 11:21:02 -08:00
|
|
|
pk, err := sdk.GetPubKeyFromBech32(sdk.Bech32PubKeyTypeConsPub, msg.Pubkey)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if _, found := k.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(pk)); found {
|
2019-12-27 09:57:54 -08:00
|
|
|
return nil, ErrValidatorPubKeyExists
|
2018-02-27 18:07:20 -08:00
|
|
|
}
|
2018-09-24 15:23:58 -07:00
|
|
|
|
2019-06-20 06:51:18 -07:00
|
|
|
if msg.Value.Denom != k.BondDenom(ctx) {
|
2019-12-27 09:57:54 -08:00
|
|
|
return nil, ErrBadDenom
|
2018-03-13 11:27:52 -07:00
|
|
|
}
|
2018-02-27 18:07:20 -08:00
|
|
|
|
2019-01-21 16:52:03 -08:00
|
|
|
if _, err := msg.Description.EnsureLength(); err != nil {
|
2019-12-27 09:57:54 -08:00
|
|
|
return nil, err
|
2019-01-21 16:52:03 -08:00
|
|
|
}
|
|
|
|
|
2020-04-16 08:10:39 -07:00
|
|
|
cp := ctx.ConsensusParams()
|
|
|
|
if cp != nil && cp.Validator != nil {
|
2020-02-06 11:21:02 -08:00
|
|
|
tmPubKey := tmtypes.TM2PB.PubKey(pk)
|
2020-04-16 08:10:39 -07:00
|
|
|
|
|
|
|
if !tmstrings.StringInSlice(tmPubKey.Type, cp.Validator.PubKeyTypes) {
|
2019-12-27 09:57:54 -08:00
|
|
|
return nil, sdkerrors.Wrapf(
|
|
|
|
ErrValidatorPubKeyTypeNotSupported,
|
2020-04-16 08:10:39 -07:00
|
|
|
"got: %s, expected: %s", tmPubKey.Type, cp.Validator.PubKeyTypes,
|
2019-12-27 09:57:54 -08:00
|
|
|
)
|
2018-11-29 09:21:45 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-06 11:21:02 -08:00
|
|
|
validator := NewValidator(msg.ValidatorAddress, pk, msg.Description)
|
2018-09-24 15:23:58 -07:00
|
|
|
commission := NewCommissionWithTime(
|
2018-10-10 23:26:54 -07:00
|
|
|
msg.Commission.Rate, msg.Commission.MaxRate,
|
2018-09-24 15:23:58 -07:00
|
|
|
msg.Commission.MaxChangeRate, ctx.BlockHeader().Time,
|
|
|
|
)
|
2020-02-06 11:21:02 -08:00
|
|
|
|
|
|
|
validator, err = validator.SetInitialCommission(commission)
|
2018-09-24 15:23:58 -07:00
|
|
|
if err != nil {
|
2019-12-27 09:57:54 -08:00
|
|
|
return nil, err
|
2018-09-24 15:23:58 -07:00
|
|
|
}
|
|
|
|
|
2019-02-08 12:44:19 -08:00
|
|
|
validator.MinSelfDelegation = msg.MinSelfDelegation
|
|
|
|
|
2018-06-26 19:00:12 -07:00
|
|
|
k.SetValidator(ctx, validator)
|
2018-09-24 21:09:31 -07:00
|
|
|
k.SetValidatorByConsAddr(ctx, validator)
|
2018-10-03 09:37:06 -07:00
|
|
|
k.SetNewValidatorByPowerIndex(ctx, validator)
|
2018-02-23 15:57:31 -08:00
|
|
|
|
2019-02-07 17:41:23 -08:00
|
|
|
// call the after-creation hook
|
2019-02-25 07:16:52 -08:00
|
|
|
k.AfterValidatorCreated(ctx, validator.OperatorAddress)
|
2018-10-22 17:49:53 -07:00
|
|
|
|
2018-06-26 19:00:12 -07:00
|
|
|
// move coins from the msg.Address account to a (self-delegation) delegator account
|
2018-05-09 21:01:58 -07:00
|
|
|
// the validator account and global shares are updated within here
|
2019-06-28 13:11:27 -07:00
|
|
|
// NOTE source will always be from a wallet which are unbonded
|
|
|
|
_, err = k.Delegate(ctx, msg.DelegatorAddress, msg.Value.Amount, sdk.Unbonded, validator, true)
|
2018-04-03 10:03:49 -07:00
|
|
|
if err != nil {
|
2019-12-27 09:57:54 -08:00
|
|
|
return nil, err
|
2018-04-03 10:03:49 -07:00
|
|
|
}
|
2018-06-26 19:00:12 -07:00
|
|
|
|
2019-06-26 09:03:25 -07:00
|
|
|
ctx.EventManager().EmitEvents(sdk.Events{
|
|
|
|
sdk.NewEvent(
|
|
|
|
types.EventTypeCreateValidator,
|
|
|
|
sdk.NewAttribute(types.AttributeKeyValidator, msg.ValidatorAddress.String()),
|
2019-07-24 15:50:34 -07:00
|
|
|
sdk.NewAttribute(sdk.AttributeKeyAmount, msg.Value.Amount.String()),
|
2019-06-26 09:03:25 -07:00
|
|
|
),
|
|
|
|
sdk.NewEvent(
|
|
|
|
sdk.EventTypeMessage,
|
|
|
|
sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory),
|
|
|
|
sdk.NewAttribute(sdk.AttributeKeySender, msg.DelegatorAddress.String()),
|
|
|
|
),
|
|
|
|
})
|
|
|
|
|
2020-03-26 09:46:10 -07:00
|
|
|
return &sdk.Result{Events: ctx.EventManager().ABCIEvents()}, nil
|
2018-02-23 15:57:31 -08:00
|
|
|
}
|
|
|
|
|
2019-12-27 09:57:54 -08:00
|
|
|
func handleMsgEditValidator(ctx sdk.Context, msg types.MsgEditValidator, k keeper.Keeper) (*sdk.Result, error) {
|
2018-05-09 21:01:58 -07:00
|
|
|
// validator must already be registered
|
2019-02-25 07:16:52 -08:00
|
|
|
validator, found := k.GetValidator(ctx, msg.ValidatorAddress)
|
2018-03-20 14:21:18 -07:00
|
|
|
if !found {
|
2019-12-27 09:57:54 -08:00
|
|
|
return nil, ErrNoValidatorFound
|
2018-03-13 11:27:52 -07:00
|
|
|
}
|
2018-02-23 15:57:31 -08:00
|
|
|
|
2018-03-28 08:08:22 -07:00
|
|
|
// replace all editable fields (clients should autofill existing values)
|
2018-06-26 19:00:12 -07:00
|
|
|
description, err := validator.Description.UpdateDescription(msg.Description)
|
|
|
|
if err != nil {
|
2019-12-27 09:57:54 -08:00
|
|
|
return nil, err
|
2018-06-26 19:00:12 -07:00
|
|
|
}
|
2018-09-24 15:23:58 -07:00
|
|
|
|
2018-06-26 19:00:12 -07:00
|
|
|
validator.Description = description
|
2018-05-09 21:01:58 -07:00
|
|
|
|
2018-09-24 15:23:58 -07:00
|
|
|
if msg.CommissionRate != nil {
|
2018-10-03 09:37:06 -07:00
|
|
|
commission, err := k.UpdateValidatorCommission(ctx, validator, *msg.CommissionRate)
|
|
|
|
if err != nil {
|
2019-12-27 09:57:54 -08:00
|
|
|
return nil, err
|
2018-09-24 15:23:58 -07:00
|
|
|
}
|
2019-01-08 21:28:46 -08:00
|
|
|
|
2019-02-07 17:41:23 -08:00
|
|
|
// call the before-modification hook since we're about to update the commission
|
2019-02-25 07:16:52 -08:00
|
|
|
k.BeforeValidatorModified(ctx, msg.ValidatorAddress)
|
2019-02-07 17:41:23 -08:00
|
|
|
|
2018-10-03 09:37:06 -07:00
|
|
|
validator.Commission = commission
|
2018-09-24 15:23:58 -07:00
|
|
|
}
|
|
|
|
|
2019-02-08 12:44:19 -08:00
|
|
|
if msg.MinSelfDelegation != nil {
|
2019-08-19 09:06:27 -07:00
|
|
|
if !msg.MinSelfDelegation.GT(validator.MinSelfDelegation) {
|
2019-12-27 09:57:54 -08:00
|
|
|
return nil, ErrMinSelfDelegationDecreased
|
2019-02-08 12:44:19 -08:00
|
|
|
}
|
2019-08-19 09:06:27 -07:00
|
|
|
if msg.MinSelfDelegation.GT(validator.Tokens) {
|
2019-12-27 09:57:54 -08:00
|
|
|
return nil, ErrSelfDelegationBelowMinimum
|
2019-02-08 12:44:19 -08:00
|
|
|
}
|
2019-12-27 09:57:54 -08:00
|
|
|
|
2019-02-08 12:44:19 -08:00
|
|
|
validator.MinSelfDelegation = (*msg.MinSelfDelegation)
|
|
|
|
}
|
|
|
|
|
2018-07-27 21:42:07 -07:00
|
|
|
k.SetValidator(ctx, validator)
|
2018-09-24 15:23:58 -07:00
|
|
|
|
2019-06-26 09:03:25 -07:00
|
|
|
ctx.EventManager().EmitEvents(sdk.Events{
|
|
|
|
sdk.NewEvent(
|
|
|
|
types.EventTypeEditValidator,
|
|
|
|
sdk.NewAttribute(types.AttributeKeyCommissionRate, validator.Commission.String()),
|
|
|
|
sdk.NewAttribute(types.AttributeKeyMinSelfDelegation, validator.MinSelfDelegation.String()),
|
|
|
|
),
|
|
|
|
sdk.NewEvent(
|
|
|
|
sdk.EventTypeMessage,
|
|
|
|
sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory),
|
|
|
|
sdk.NewAttribute(sdk.AttributeKeySender, msg.ValidatorAddress.String()),
|
|
|
|
),
|
|
|
|
})
|
|
|
|
|
2020-03-26 09:46:10 -07:00
|
|
|
return &sdk.Result{Events: ctx.EventManager().ABCIEvents()}, nil
|
2018-02-23 15:57:31 -08:00
|
|
|
}
|
|
|
|
|
2019-12-27 09:57:54 -08:00
|
|
|
func handleMsgDelegate(ctx sdk.Context, msg types.MsgDelegate, k keeper.Keeper) (*sdk.Result, error) {
|
2019-02-25 07:16:52 -08:00
|
|
|
validator, found := k.GetValidator(ctx, msg.ValidatorAddress)
|
2018-03-20 14:21:18 -07:00
|
|
|
if !found {
|
2019-12-27 09:57:54 -08:00
|
|
|
return nil, ErrNoValidatorFound
|
2018-02-27 18:07:20 -08:00
|
|
|
}
|
2018-08-31 12:21:12 -07:00
|
|
|
|
2019-06-20 06:51:18 -07:00
|
|
|
if msg.Amount.Denom != k.BondDenom(ctx) {
|
2019-12-27 09:57:54 -08:00
|
|
|
return nil, ErrBadDenom
|
2018-02-27 18:07:20 -08:00
|
|
|
}
|
2018-08-31 12:21:12 -07:00
|
|
|
|
2019-06-28 13:11:27 -07:00
|
|
|
// NOTE: source funds are always unbonded
|
|
|
|
_, err := k.Delegate(ctx, msg.DelegatorAddress, msg.Amount.Amount, sdk.Unbonded, validator, true)
|
2018-04-03 10:03:49 -07:00
|
|
|
if err != nil {
|
2019-12-27 09:57:54 -08:00
|
|
|
return nil, err
|
2018-04-03 10:03:49 -07:00
|
|
|
}
|
2018-06-26 19:00:12 -07:00
|
|
|
|
2019-06-26 09:03:25 -07:00
|
|
|
ctx.EventManager().EmitEvents(sdk.Events{
|
|
|
|
sdk.NewEvent(
|
|
|
|
types.EventTypeDelegate,
|
|
|
|
sdk.NewAttribute(types.AttributeKeyValidator, msg.ValidatorAddress.String()),
|
2019-07-24 15:50:34 -07:00
|
|
|
sdk.NewAttribute(sdk.AttributeKeyAmount, msg.Amount.Amount.String()),
|
2019-06-26 09:03:25 -07:00
|
|
|
),
|
|
|
|
sdk.NewEvent(
|
|
|
|
sdk.EventTypeMessage,
|
|
|
|
sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory),
|
|
|
|
sdk.NewAttribute(sdk.AttributeKeySender, msg.DelegatorAddress.String()),
|
|
|
|
),
|
|
|
|
})
|
|
|
|
|
2020-03-26 09:46:10 -07:00
|
|
|
return &sdk.Result{Events: ctx.EventManager().ABCIEvents()}, nil
|
2018-02-23 15:57:31 -08:00
|
|
|
}
|
|
|
|
|
2019-12-27 09:57:54 -08:00
|
|
|
func handleMsgUndelegate(ctx sdk.Context, msg types.MsgUndelegate, k keeper.Keeper) (*sdk.Result, error) {
|
2019-03-25 14:13:02 -07:00
|
|
|
shares, err := k.ValidateUnbondAmount(
|
|
|
|
ctx, msg.DelegatorAddress, msg.ValidatorAddress, msg.Amount.Amount,
|
|
|
|
)
|
|
|
|
if err != nil {
|
2019-12-27 09:57:54 -08:00
|
|
|
return nil, err
|
2019-03-25 14:13:02 -07:00
|
|
|
}
|
|
|
|
|
2019-06-20 06:51:18 -07:00
|
|
|
if msg.Amount.Denom != k.BondDenom(ctx) {
|
2019-12-27 09:57:54 -08:00
|
|
|
return nil, ErrBadDenom
|
2019-06-20 06:51:18 -07:00
|
|
|
}
|
|
|
|
|
2019-03-25 14:13:02 -07:00
|
|
|
completionTime, err := k.Undelegate(ctx, msg.DelegatorAddress, msg.ValidatorAddress, shares)
|
2018-03-16 13:36:16 -07:00
|
|
|
if err != nil {
|
2019-12-27 09:57:54 -08:00
|
|
|
return nil, err
|
2018-03-16 13:36:16 -07:00
|
|
|
}
|
2018-04-03 19:26:39 -07:00
|
|
|
|
2020-02-06 11:21:02 -08:00
|
|
|
ts, err := gogotypes.TimestampProto(completionTime)
|
|
|
|
if err != nil {
|
|
|
|
return nil, ErrBadRedelegationAddr
|
|
|
|
}
|
|
|
|
|
|
|
|
completionTimeBz := types.ModuleCdc.MustMarshalBinaryLengthPrefixed(ts)
|
2019-06-26 09:03:25 -07:00
|
|
|
ctx.EventManager().EmitEvents(sdk.Events{
|
|
|
|
sdk.NewEvent(
|
|
|
|
types.EventTypeUnbond,
|
|
|
|
sdk.NewAttribute(types.AttributeKeyValidator, msg.ValidatorAddress.String()),
|
2019-07-24 15:50:34 -07:00
|
|
|
sdk.NewAttribute(sdk.AttributeKeyAmount, msg.Amount.Amount.String()),
|
2019-06-26 09:03:25 -07:00
|
|
|
sdk.NewAttribute(types.AttributeKeyCompletionTime, completionTime.Format(time.RFC3339)),
|
|
|
|
),
|
|
|
|
sdk.NewEvent(
|
|
|
|
sdk.EventTypeMessage,
|
|
|
|
sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory),
|
|
|
|
sdk.NewAttribute(sdk.AttributeKeySender, msg.DelegatorAddress.String()),
|
|
|
|
),
|
|
|
|
})
|
|
|
|
|
2020-03-26 09:46:10 -07:00
|
|
|
return &sdk.Result{Data: completionTimeBz, Events: ctx.EventManager().ABCIEvents()}, nil
|
2018-06-26 19:00:12 -07:00
|
|
|
}
|
2018-02-23 15:57:31 -08:00
|
|
|
|
2019-12-27 09:57:54 -08:00
|
|
|
func handleMsgBeginRedelegate(ctx sdk.Context, msg types.MsgBeginRedelegate, k keeper.Keeper) (*sdk.Result, error) {
|
2019-03-25 14:13:02 -07:00
|
|
|
shares, err := k.ValidateUnbondAmount(
|
|
|
|
ctx, msg.DelegatorAddress, msg.ValidatorSrcAddress, msg.Amount.Amount,
|
|
|
|
)
|
|
|
|
if err != nil {
|
2019-12-27 09:57:54 -08:00
|
|
|
return nil, err
|
2019-03-25 14:13:02 -07:00
|
|
|
}
|
|
|
|
|
2019-06-20 06:51:18 -07:00
|
|
|
if msg.Amount.Denom != k.BondDenom(ctx) {
|
2019-12-27 09:57:54 -08:00
|
|
|
return nil, ErrBadDenom
|
2019-06-20 06:51:18 -07:00
|
|
|
}
|
|
|
|
|
2019-03-25 14:13:02 -07:00
|
|
|
completionTime, err := k.BeginRedelegation(
|
|
|
|
ctx, msg.DelegatorAddress, msg.ValidatorSrcAddress, msg.ValidatorDstAddress, shares,
|
|
|
|
)
|
2018-06-26 19:00:12 -07:00
|
|
|
if err != nil {
|
2019-12-27 09:57:54 -08:00
|
|
|
return nil, err
|
2018-02-23 15:57:31 -08:00
|
|
|
}
|
|
|
|
|
2020-02-06 11:21:02 -08:00
|
|
|
ts, err := gogotypes.TimestampProto(completionTime)
|
|
|
|
if err != nil {
|
|
|
|
return nil, ErrBadRedelegationAddr
|
|
|
|
}
|
|
|
|
|
|
|
|
completionTimeBz := types.ModuleCdc.MustMarshalBinaryLengthPrefixed(ts)
|
2019-06-26 09:03:25 -07:00
|
|
|
ctx.EventManager().EmitEvents(sdk.Events{
|
|
|
|
sdk.NewEvent(
|
|
|
|
types.EventTypeRedelegate,
|
|
|
|
sdk.NewAttribute(types.AttributeKeySrcValidator, msg.ValidatorSrcAddress.String()),
|
|
|
|
sdk.NewAttribute(types.AttributeKeyDstValidator, msg.ValidatorDstAddress.String()),
|
2019-07-24 15:50:34 -07:00
|
|
|
sdk.NewAttribute(sdk.AttributeKeyAmount, msg.Amount.Amount.String()),
|
2019-06-26 09:03:25 -07:00
|
|
|
sdk.NewAttribute(types.AttributeKeyCompletionTime, completionTime.Format(time.RFC3339)),
|
|
|
|
),
|
|
|
|
sdk.NewEvent(
|
|
|
|
sdk.EventTypeMessage,
|
|
|
|
sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory),
|
|
|
|
sdk.NewAttribute(sdk.AttributeKeySender, msg.DelegatorAddress.String()),
|
|
|
|
),
|
|
|
|
})
|
|
|
|
|
2020-03-26 09:46:10 -07:00
|
|
|
return &sdk.Result{Data: completionTimeBz, Events: ctx.EventManager().ABCIEvents()}, nil
|
2018-02-23 15:57:31 -08:00
|
|
|
}
|