cosmos-sdk/x/stake/handler.go

275 lines
7.4 KiB
Go
Raw Normal View History

2018-02-23 15:57:31 -08:00
package stake
import (
2018-03-16 12:47:17 -07:00
"bytes"
2018-02-23 15:57:31 -08:00
sdk "github.com/cosmos/cosmos-sdk/types"
abci "github.com/tendermint/abci/types"
2018-02-23 15:57:31 -08:00
)
2018-04-06 11:35:54 -07:00
func NewHandler(k Keeper) sdk.Handler {
2018-02-27 18:07:20 -08:00
return func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
2018-03-20 06:56:07 -07:00
// NOTE msg already has validate basic run
switch msg := msg.(type) {
case MsgDeclareCandidacy:
return handleMsgDeclareCandidacy(ctx, msg, k)
case MsgEditCandidacy:
return handleMsgEditCandidacy(ctx, msg, k)
case MsgDelegate:
return handleMsgDelegate(ctx, msg, k)
case MsgUnbond:
return handleMsgUnbond(ctx, msg, k)
2018-05-25 15:13:29 -07:00
case MsgUnrevoke:
return handleMsgUnrevoke(ctx, msg, k)
default:
2018-03-23 09:15:41 -07:00
return sdk.ErrTxDecode("invalid message parse in staking module").Result()
2018-02-27 18:07:20 -08:00
}
2018-02-23 15:57:31 -08:00
}
}
// NewEndBlocker generates sdk.EndBlocker
// Performs tick functionality
func NewEndBlocker(k Keeper) sdk.EndBlocker {
return func(ctx sdk.Context, req abci.RequestEndBlock) (res abci.ResponseEndBlock) {
res.ValidatorUpdates = k.Tick(ctx)
return
}
}
2018-02-23 15:57:31 -08:00
//_____________________________________________________________________
// These functions assume everything has been authenticated,
// now we just perform action and save
func handleMsgDeclareCandidacy(ctx sdk.Context, msg MsgDeclareCandidacy, k Keeper) sdk.Result {
2018-02-23 15:57:31 -08:00
2018-02-27 18:07:20 -08:00
// check to see if the pubkey or sender has been registered before
2018-05-09 21:01:58 -07:00
_, found := k.GetValidator(ctx, msg.ValidatorAddr)
2018-03-20 14:21:18 -07:00
if found {
2018-05-09 21:01:58 -07:00
return ErrValidatorExistsAddr(k.codespace).Result()
2018-02-27 18:07:20 -08:00
}
if msg.Bond.Denom != k.GetParams(ctx).BondDenom {
return ErrBadBondingDenom(k.codespace).Result()
2018-02-27 18:07:20 -08:00
}
2018-03-20 06:56:07 -07:00
if ctx.IsCheckTx() {
2018-05-17 13:17:19 -07:00
return sdk.Result{}
}
2018-02-27 18:07:20 -08:00
2018-05-09 21:01:58 -07:00
validator := NewValidator(msg.ValidatorAddr, msg.PubKey, msg.Description)
2018-05-22 12:13:03 -07:00
k.setValidator(ctx, validator)
2018-05-09 21:01:58 -07:00
tags := sdk.NewTags(
"action", []byte("declareCandidacy"),
2018-05-10 17:51:48 -07:00
"validator", msg.ValidatorAddr.Bytes(),
2018-05-09 21:01:58 -07:00
"moniker", []byte(msg.Description.Moniker),
"identity", []byte(msg.Description.Identity),
)
2018-02-23 15:57:31 -08:00
2018-03-20 06:56:07 -07:00
// move coins from the msg.Address account to a (self-bond) delegator account
2018-05-09 21:01:58 -07:00
// the validator account and global shares are updated within here
2018-05-10 17:51:48 -07:00
delegateTags, err := delegate(ctx, k, msg.ValidatorAddr, msg.Bond, validator)
2018-04-03 10:03:49 -07:00
if err != nil {
return err.Result()
}
tags = tags.AppendTags(delegateTags)
2018-05-10 08:19:06 -07:00
return sdk.Result{
Tags: tags,
}
2018-02-23 15:57:31 -08:00
}
func handleMsgEditCandidacy(ctx sdk.Context, msg MsgEditCandidacy, k Keeper) sdk.Result {
2018-02-23 15:57:31 -08:00
2018-05-09 21:01:58 -07:00
// validator must already be registered
validator, found := k.GetValidator(ctx, msg.ValidatorAddr)
2018-03-20 14:21:18 -07:00
if !found {
2018-05-09 21:01:58 -07:00
return ErrBadValidatorAddr(k.codespace).Result()
2018-02-27 18:07:20 -08:00
}
2018-03-20 06:56:07 -07:00
if ctx.IsCheckTx() {
2018-05-17 13:17:19 -07:00
return sdk.Result{}
}
2018-02-23 15:57:31 -08:00
2018-03-20 14:21:18 -07:00
// XXX move to types
// replace all editable fields (clients should autofill existing values)
2018-05-09 21:01:58 -07:00
validator.Description.Moniker = msg.Description.Moniker
validator.Description.Identity = msg.Description.Identity
validator.Description.Website = msg.Description.Website
validator.Description.Details = msg.Description.Details
2018-05-22 12:13:03 -07:00
k.updateValidator(ctx, validator)
2018-05-09 21:01:58 -07:00
tags := sdk.NewTags(
"action", []byte("editCandidacy"),
2018-05-10 17:51:48 -07:00
"validator", msg.ValidatorAddr.Bytes(),
2018-05-09 21:01:58 -07:00
"moniker", []byte(msg.Description.Moniker),
"identity", []byte(msg.Description.Identity),
)
return sdk.Result{
Tags: tags,
}
2018-02-23 15:57:31 -08:00
}
func handleMsgDelegate(ctx sdk.Context, msg MsgDelegate, k Keeper) sdk.Result {
2018-02-27 18:07:20 -08:00
2018-05-09 21:01:58 -07:00
validator, found := k.GetValidator(ctx, msg.ValidatorAddr)
2018-03-20 14:21:18 -07:00
if !found {
2018-05-09 21:01:58 -07:00
return ErrBadValidatorAddr(k.codespace).Result()
2018-02-27 18:07:20 -08:00
}
if msg.Bond.Denom != k.GetParams(ctx).BondDenom {
return ErrBadBondingDenom(k.codespace).Result()
2018-02-27 18:07:20 -08:00
}
if validator.Revoked == true {
2018-05-09 21:01:58 -07:00
return ErrValidatorRevoked(k.codespace).Result()
2018-04-03 19:26:39 -07:00
}
2018-03-20 06:56:07 -07:00
if ctx.IsCheckTx() {
2018-05-17 13:17:19 -07:00
return sdk.Result{}
}
2018-05-09 21:01:58 -07:00
tags, err := delegate(ctx, k, msg.DelegatorAddr, msg.Bond, validator)
2018-04-03 10:03:49 -07:00
if err != nil {
return err.Result()
}
2018-05-10 08:19:06 -07:00
return sdk.Result{
Tags: tags,
}
2018-02-23 15:57:31 -08:00
}
2018-04-03 19:26:39 -07:00
// common functionality between handlers
2018-04-01 09:05:58 -07:00
func delegate(ctx sdk.Context, k Keeper, delegatorAddr sdk.Address,
2018-05-09 21:01:58 -07:00
bondAmt sdk.Coin, validator Validator) (sdk.Tags, sdk.Error) {
2018-02-23 15:57:31 -08:00
// Get or create the delegator bond
2018-05-18 15:57:47 -07:00
bond, found := k.GetDelegation(ctx, delegatorAddr, validator.Owner)
2018-03-20 14:21:18 -07:00
if !found {
2018-05-09 18:39:14 -07:00
bond = Delegation{
2018-03-20 14:21:18 -07:00
DelegatorAddr: delegatorAddr,
2018-05-18 15:57:47 -07:00
ValidatorAddr: validator.Owner,
2018-04-30 14:21:14 -07:00
Shares: sdk.ZeroRat(),
2018-02-23 15:57:31 -08:00
}
}
// Account new shares, save
2018-04-03 19:26:39 -07:00
pool := k.GetPool(ctx)
_, _, err := k.coinKeeper.SubtractCoins(ctx, bond.DelegatorAddr, sdk.Coins{bondAmt})
if err != nil {
2018-05-10 08:19:06 -07:00
return nil, err
}
validator, pool, newShares := validator.addTokensFromDel(pool, bondAmt.Amount)
bond.Shares = bond.Shares.Add(newShares)
2018-04-03 19:26:39 -07:00
// Update bond height
bond.Height = ctx.BlockHeight()
2018-05-18 15:57:47 -07:00
k.setPool(ctx, pool)
2018-05-09 18:39:14 -07:00
k.setDelegation(ctx, bond)
2018-05-22 12:13:03 -07:00
k.updateValidator(ctx, validator)
2018-05-18 15:57:47 -07:00
tags := sdk.NewTags("action", []byte("delegate"), "delegator", delegatorAddr.Bytes(), "validator", validator.Owner.Bytes())
2018-05-10 08:19:06 -07:00
return tags, nil
}
func handleMsgUnbond(ctx sdk.Context, msg MsgUnbond, k Keeper) sdk.Result {
2018-02-23 15:57:31 -08:00
2018-02-27 18:07:20 -08:00
// check if bond has any shares in it unbond
2018-05-09 21:01:58 -07:00
bond, found := k.GetDelegation(ctx, msg.DelegatorAddr, msg.ValidatorAddr)
2018-03-20 14:21:18 -07:00
if !found {
return ErrNoDelegatorForAddress(k.codespace).Result()
2018-03-16 12:47:17 -07:00
}
2018-02-27 18:07:20 -08:00
var delShares sdk.Rat
2018-02-27 18:07:20 -08:00
2018-03-27 17:54:54 -07:00
// test that there are enough shares to unbond
if msg.Shares == "MAX" {
2018-04-30 14:21:14 -07:00
if !bond.Shares.GT(sdk.ZeroRat()) {
return ErrNotEnoughBondShares(k.codespace, msg.Shares).Result()
2018-03-27 17:54:54 -07:00
}
} else {
2018-04-10 20:51:09 -07:00
var err sdk.Error
delShares, err = sdk.NewRatFromDecimal(msg.Shares)
2018-04-10 20:51:09 -07:00
if err != nil {
return err.Result()
}
if bond.Shares.LT(delShares) {
return ErrNotEnoughBondShares(k.codespace, msg.Shares).Result()
2018-02-27 18:07:20 -08:00
}
}
2018-03-27 17:54:54 -07:00
2018-05-09 21:01:58 -07:00
// get validator
validator, found := k.GetValidator(ctx, msg.ValidatorAddr)
2018-03-27 17:54:54 -07:00
if !found {
2018-05-09 21:01:58 -07:00
return ErrNoValidatorForAddress(k.codespace).Result()
2018-03-27 17:54:54 -07:00
}
2018-03-20 06:56:07 -07:00
if ctx.IsCheckTx() {
2018-05-17 13:17:19 -07:00
return sdk.Result{}
}
2018-02-27 18:07:20 -08:00
2018-02-23 15:57:31 -08:00
// retrieve the amount of bonds to remove (TODO remove redundancy already serialized)
2018-03-20 06:56:07 -07:00
if msg.Shares == "MAX" {
delShares = bond.Shares
2018-02-23 15:57:31 -08:00
}
// subtract bond tokens from delegator bond
bond.Shares = bond.Shares.Sub(delShares)
2018-02-23 15:57:31 -08:00
2018-03-27 17:54:54 -07:00
// remove the bond
2018-02-23 15:57:31 -08:00
revokeCandidacy := false
if bond.Shares.IsZero() {
2018-05-09 21:01:58 -07:00
// if the bond is the owner of the validator then
2018-02-23 15:57:31 -08:00
// trigger a revoke candidacy
2018-05-18 15:57:47 -07:00
if bytes.Equal(bond.DelegatorAddr, validator.Owner) &&
validator.Revoked == false {
2018-02-23 15:57:31 -08:00
revokeCandidacy = true
}
2018-05-09 18:39:14 -07:00
k.removeDelegation(ctx, bond)
2018-02-23 15:57:31 -08:00
} else {
// Update bond height
bond.Height = ctx.BlockHeight()
2018-05-09 18:39:14 -07:00
k.setDelegation(ctx, bond)
2018-02-23 15:57:31 -08:00
}
2018-03-16 12:47:17 -07:00
// Add the coins
2018-05-18 15:57:47 -07:00
pool := k.GetPool(ctx)
validator, pool, returnAmount := validator.removeDelShares(pool, delShares)
k.setPool(ctx, pool)
returnCoins := sdk.Coins{{k.GetParams(ctx).BondDenom, returnAmount}}
2018-03-20 14:21:18 -07:00
k.coinKeeper.AddCoins(ctx, bond.DelegatorAddr, returnCoins)
2018-02-23 15:57:31 -08:00
2018-04-03 19:26:39 -07:00
/////////////////////////////////////
2018-05-09 21:01:58 -07:00
// revoke validator if necessary
2018-02-23 15:57:31 -08:00
if revokeCandidacy {
validator.Revoked = true
2018-02-23 15:57:31 -08:00
}
validator = k.updateValidator(ctx, validator)
2018-05-09 21:01:58 -07:00
if validator.DelegatorShares.IsZero() {
2018-05-18 15:57:47 -07:00
k.removeValidator(ctx, validator.Owner)
2018-02-23 15:57:31 -08:00
}
2018-05-10 17:51:48 -07:00
tags := sdk.NewTags("action", []byte("unbond"), "delegator", msg.DelegatorAddr.Bytes(), "validator", msg.ValidatorAddr.Bytes())
2018-05-10 08:23:46 -07:00
return sdk.Result{
Tags: tags,
}
2018-02-23 15:57:31 -08:00
}
2018-05-25 15:13:29 -07:00
func handleMsgUnrevoke(ctx sdk.Context, msg MsgUnrevoke, k Keeper) sdk.Result {
validator, found := k.GetValidator(ctx, msg.ValidatorAddr)
if !found {
return ErrNoValidatorForAddress(k.codespace).Result()
}
if ctx.BlockHeader().Time < validator.RevokedUntilTime {
return ErrValidatorJailed(k.codespace).Result()
}
if ctx.IsCheckTx() {
return sdk.Result{}
}
validator.Revoked = false
k.updateValidator(ctx, validator)
tags := sdk.NewTags("action", []byte("unrevoke"), "validator", msg.ValidatorAddr.Bytes())
return sdk.Result{
Tags: tags,
}
}