unbonding validators, delegtion effects
This commit is contained in:
parent
c5d44bcaf0
commit
86f07d271d
|
@ -2,6 +2,7 @@ package keeper
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"time"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/stake/types"
|
||||
|
@ -311,6 +312,32 @@ func (k Keeper) unbond(ctx sdk.Context, delegatorAddr, validatorAddr sdk.AccAddr
|
|||
|
||||
//______________________________________________________________________________________________________
|
||||
|
||||
// get info for begin functions: MinTime and CreationHeight
|
||||
func (k Keeper) getBeginInfo(ctx sdk.Context, params types.Params, validatorSrcAddr sdk.AccAddress) (
|
||||
minTime time.Time, height int64, completeNow bool) {
|
||||
|
||||
validator, found := k.GetValidator(ctx, validatorSrcAddr)
|
||||
switch {
|
||||
case !found || validator.Status == sdk.Bonded:
|
||||
|
||||
// longest wait - just unbonding period from now
|
||||
minTime = ctx.BlockHeader().Time.Add(params.UnbondingTime)
|
||||
height = ctx.BlockHeader().Height
|
||||
return minTime, height, false
|
||||
|
||||
case validator.Status == sdk.Unbonding:
|
||||
minTime = validator.UnbondingMinTime
|
||||
height = validator.UnbondingHeight
|
||||
return minTime, height, false
|
||||
|
||||
case validator.Status == sdk.Unbonded:
|
||||
return minTime, height, true
|
||||
|
||||
default:
|
||||
panic("unknown validator status")
|
||||
}
|
||||
}
|
||||
|
||||
// complete unbonding an unbonding record
|
||||
func (k Keeper) BeginUnbonding(ctx sdk.Context, delegatorAddr, validatorAddr sdk.AccAddress, sharesAmount sdk.Dec) sdk.Error {
|
||||
|
||||
|
@ -327,12 +354,22 @@ func (k Keeper) BeginUnbonding(ctx sdk.Context, delegatorAddr, validatorAddr sdk
|
|||
|
||||
// create the unbonding delegation
|
||||
params := k.GetParams(ctx)
|
||||
minTime := ctx.BlockHeader().Time.Add(params.UnbondingTime)
|
||||
minTime, height, completeNow := k.getBeginInfo(ctx, params, validatorAddr)
|
||||
balance := sdk.Coin{params.BondDenom, returnAmount.RoundInt()}
|
||||
|
||||
// no need to create the ubd object just complete now
|
||||
if completeNow {
|
||||
_, _, err := k.coinKeeper.AddCoins(ctx, delegatorAddr, sdk.Coins{balance})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
ubd := types.UnbondingDelegation{
|
||||
DelegatorAddr: delegatorAddr,
|
||||
ValidatorAddr: validatorAddr,
|
||||
CreationHeight: height,
|
||||
MinTime: minTime,
|
||||
Balance: balance,
|
||||
InitialBalance: balance,
|
||||
|
@ -390,11 +427,19 @@ func (k Keeper) BeginRedelegation(ctx sdk.Context, delegatorAddr, validatorSrcAd
|
|||
|
||||
// create the unbonding delegation
|
||||
minTime := ctx.BlockHeader().Time.Add(params.UnbondingTime)
|
||||
height := ctx.BlockHeader().Height
|
||||
|
||||
minTime, height, completeNow := k.getBeginInfo(ctx, params, validatorSrcAddr)
|
||||
|
||||
if completeNow { // no need to create the redelegation object
|
||||
return nil
|
||||
}
|
||||
|
||||
red := types.Redelegation{
|
||||
DelegatorAddr: delegatorAddr,
|
||||
ValidatorSrcAddr: validatorSrcAddr,
|
||||
ValidatorDstAddr: validatorDstAddr,
|
||||
CreationHeight: height,
|
||||
MinTime: minTime,
|
||||
SharesDst: sharesCreated,
|
||||
SharesSrc: sharesAmount,
|
||||
|
|
|
@ -43,6 +43,26 @@ func (k Keeper) Slash(ctx sdk.Context, pubkey crypto.PubKey, infractionHeight in
|
|||
pubkey.Address()))
|
||||
return
|
||||
}
|
||||
|
||||
// do not slash if unbonded
|
||||
unbonded := false
|
||||
if validator.Status == sdk.Unbonded {
|
||||
unbonded = true
|
||||
} else if validator.Status == sdk.Unbonding {
|
||||
ctxTime := ctx.BlockHeader().Time
|
||||
if ctxTime.After(validator.UnbondingMinTime) {
|
||||
|
||||
// TODO should we also just update the
|
||||
// validator status to unbonded here?
|
||||
unbonded = true
|
||||
}
|
||||
}
|
||||
if unbonded {
|
||||
logger.Info(fmt.Sprintf(
|
||||
"failed attempt to slash an unbonded validator"))
|
||||
return
|
||||
}
|
||||
|
||||
operatorAddress := validator.GetOperator()
|
||||
|
||||
// Track remaining slash amount for the validator
|
||||
|
@ -91,17 +111,16 @@ func (k Keeper) Slash(ctx sdk.Context, pubkey crypto.PubKey, infractionHeight in
|
|||
// Cannot decrease balance below zero
|
||||
tokensToBurn := sdk.MinDec(remainingSlashAmount, validator.Tokens)
|
||||
|
||||
// Get the current pool
|
||||
// burn validator's tokens
|
||||
pool := k.GetPool(ctx)
|
||||
// remove tokens from the validator
|
||||
validator, pool = validator.RemoveTokens(pool, tokensToBurn)
|
||||
// burn tokens
|
||||
pool.LooseTokens = pool.LooseTokens.Sub(tokensToBurn)
|
||||
// update the pool
|
||||
k.SetPool(ctx, pool)
|
||||
|
||||
// update the validator, possibly kicking it out
|
||||
validator = k.UpdateValidator(ctx, validator)
|
||||
// remove validator if it has been reduced to zero shares
|
||||
|
||||
// remove validator if it has no more tokens
|
||||
if validator.Tokens.IsZero() {
|
||||
k.RemoveValidator(ctx, validator.Operator)
|
||||
}
|
||||
|
@ -134,12 +153,12 @@ func (k Keeper) Unjail(ctx sdk.Context, pubkey crypto.PubKey) {
|
|||
}
|
||||
|
||||
// set the jailed flag on a validator
|
||||
func (k Keeper) setJailed(ctx sdk.Context, pubkey crypto.PubKey, jailed bool) {
|
||||
func (k Keeper) setJailed(ctx sdk.Context, pubkey crypto.PubKey, isJailed bool) {
|
||||
validator, found := k.GetValidatorByPubKey(ctx, pubkey)
|
||||
if !found {
|
||||
panic(fmt.Errorf("Validator with pubkey %s not found, cannot set jailed to %v", pubkey, jailed))
|
||||
panic(fmt.Errorf("Validator with pubkey %s not found, cannot set jailed to %v", pubkey, isJailed))
|
||||
}
|
||||
validator.Jailed = jailed
|
||||
validator.Jailed = isJailed
|
||||
k.UpdateValidator(ctx, validator) // update validator, possibly unbonding or bonding it
|
||||
return
|
||||
}
|
||||
|
@ -179,6 +198,7 @@ func (k Keeper) slashUnbondingDelegation(ctx sdk.Context, unbondingDelegation ty
|
|||
unbondingDelegation.Balance.Amount = unbondingDelegation.Balance.Amount.Sub(unbondingSlashAmount)
|
||||
k.SetUnbondingDelegation(ctx, unbondingDelegation)
|
||||
pool := k.GetPool(ctx)
|
||||
|
||||
// Burn loose tokens
|
||||
// Ref https://github.com/cosmos/cosmos-sdk/pull/1278#discussion_r198657760
|
||||
pool.LooseTokens = pool.LooseTokens.Sub(slashAmount)
|
||||
|
@ -239,6 +259,7 @@ func (k Keeper) slashRedelegation(ctx sdk.Context, validator types.Validator, re
|
|||
if err != nil {
|
||||
panic(fmt.Errorf("error unbonding delegator: %v", err))
|
||||
}
|
||||
|
||||
// Burn loose tokens
|
||||
pool := k.GetPool(ctx)
|
||||
pool.LooseTokens = pool.LooseTokens.Sub(tokensToBurn)
|
||||
|
|
|
@ -212,6 +212,7 @@ func (k Keeper) UpdateValidator(ctx sdk.Context, validator types.Validator) type
|
|||
cliffPower := k.GetCliffValidatorPower(ctx)
|
||||
|
||||
switch {
|
||||
|
||||
// if the validator is already bonded and the power is increasing, we need
|
||||
// perform the following:
|
||||
// a) update Tendermint
|
||||
|
@ -240,10 +241,11 @@ func (k Keeper) UpdateValidator(ctx sdk.Context, validator types.Validator) type
|
|||
bytes.Compare(valPower, cliffPower) == -1: //(valPower < cliffPower
|
||||
// skip to completion
|
||||
|
||||
default:
|
||||
// default case - validator was either:
|
||||
// a) not-bonded and now has power-rank greater than cliff validator
|
||||
// b) bonded and now has decreased in power
|
||||
default:
|
||||
|
||||
// update the validator set for this validator
|
||||
updatedVal, updated := k.UpdateBondedValidators(ctx, validator)
|
||||
if updated {
|
||||
|
@ -307,10 +309,13 @@ func (k Keeper) updateCliffValidator(ctx sdk.Context, affectedVal types.Validato
|
|||
newCliffValRank := GetValidatorsByPowerIndexKey(newCliffVal, pool)
|
||||
|
||||
if bytes.Equal(affectedVal.Operator, newCliffVal.Operator) {
|
||||
|
||||
// The affected validator remains the cliff validator, however, since
|
||||
// the store does not contain the new power, update the new power rank.
|
||||
store.Set(ValidatorPowerCliffKey, affectedValRank)
|
||||
|
||||
} else if bytes.Compare(affectedValRank, newCliffValRank) > 0 {
|
||||
|
||||
// The affected validator no longer remains the cliff validator as it's
|
||||
// power is greater than the new cliff validator.
|
||||
k.setCliffValidator(ctx, newCliffVal, pool)
|
||||
|
@ -321,7 +326,7 @@ func (k Keeper) updateCliffValidator(ctx sdk.Context, affectedVal types.Validato
|
|||
|
||||
func (k Keeper) updateForJailing(ctx sdk.Context, oldFound bool, oldValidator, newValidator types.Validator) types.Validator {
|
||||
if newValidator.Jailed && oldFound && oldValidator.Status == sdk.Bonded {
|
||||
newValidator = k.unbondValidator(ctx, newValidator)
|
||||
newValidator = k.beginUnbondingValidator(ctx, newValidator)
|
||||
|
||||
// need to also clear the cliff validator spot because the jail has
|
||||
// opened up a new spot which will be filled when
|
||||
|
@ -416,8 +421,12 @@ func (k Keeper) UpdateBondedValidators(
|
|||
}
|
||||
}
|
||||
|
||||
// if we've reached jailed validators no further bonded validators exist
|
||||
if validator.Jailed {
|
||||
break
|
||||
}
|
||||
|
||||
// increment bondedValidatorsCount / get the validator to bond
|
||||
if !validator.Jailed {
|
||||
if validator.Status != sdk.Bonded {
|
||||
validatorToBond = validator
|
||||
if newValidatorBonded {
|
||||
|
@ -425,10 +434,6 @@ func (k Keeper) UpdateBondedValidators(
|
|||
}
|
||||
newValidatorBonded = true
|
||||
}
|
||||
} else {
|
||||
// TODO: document why we must break here.
|
||||
break
|
||||
}
|
||||
|
||||
// increment the total number of bonded validators and potentially mark
|
||||
// the validator to bond
|
||||
|
@ -464,13 +469,15 @@ func (k Keeper) UpdateBondedValidators(
|
|||
}
|
||||
|
||||
if bytes.Equal(validatorToBond.Operator, affectedValidator.Operator) {
|
||||
// unbond the old cliff validator iff the affected validator was
|
||||
// newly bonded and has greater power
|
||||
k.unbondValidator(ctx, oldCliffVal)
|
||||
|
||||
// begin unbonding the old cliff validator iff the affected
|
||||
// validator was newly bonded and has greater power
|
||||
k.beginUnbondingValidator(ctx, oldCliffVal)
|
||||
} else {
|
||||
// otherwise unbond the affected validator, which must have been
|
||||
// kicked out
|
||||
affectedValidator = k.unbondValidator(ctx, affectedValidator)
|
||||
|
||||
// otherwise begin unbonding the affected validator, which must
|
||||
// have been kicked out
|
||||
affectedValidator = k.beginUnbondingValidator(ctx, affectedValidator)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -563,25 +570,30 @@ func kickOutValidators(k Keeper, ctx sdk.Context, toKickOut map[string]byte) {
|
|||
if !found {
|
||||
panic(fmt.Sprintf("validator record not found for address: %v\n", ownerAddr))
|
||||
}
|
||||
k.unbondValidator(ctx, validator)
|
||||
k.beginUnbondingValidator(ctx, validator)
|
||||
}
|
||||
}
|
||||
|
||||
// perform all the store operations for when a validator status becomes unbonded
|
||||
func (k Keeper) unbondValidator(ctx sdk.Context, validator types.Validator) types.Validator {
|
||||
func (k Keeper) beginUnbondingValidator(ctx sdk.Context, validator types.Validator) types.Validator {
|
||||
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
pool := k.GetPool(ctx)
|
||||
params := k.GetParams(ctx)
|
||||
|
||||
// sanity check
|
||||
if validator.Status == sdk.Unbonded {
|
||||
panic(fmt.Sprintf("should not already be unbonded, validator: %v\n", validator))
|
||||
if validator.Status == sdk.Unbonded ||
|
||||
validator.Status == sdk.Unbonding {
|
||||
panic(fmt.Sprintf("should not already be unbonded or unbonding, validator: %v\n", validator))
|
||||
}
|
||||
|
||||
// set the status
|
||||
validator, pool = validator.UpdateStatus(pool, sdk.Unbonded)
|
||||
validator, pool = validator.UpdateStatus(pool, sdk.Unbonding)
|
||||
k.SetPool(ctx, pool)
|
||||
|
||||
validator.UnbondingMinTime = ctx.BlockHeader().Time.Add(params.UnbondingTime)
|
||||
validator.UnbondingHeight = ctx.BlockHeader().Height
|
||||
|
||||
// save the now unbonded validator record
|
||||
k.SetValidator(ctx, validator)
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ package types
|
|||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
|
@ -31,15 +32,14 @@ type Validator struct {
|
|||
Description Description `json:"description"` // description terms for the validator
|
||||
BondHeight int64 `json:"bond_height"` // earliest height as a bonded validator
|
||||
BondIntraTxCounter int16 `json:"bond_intra_tx_counter"` // block-local tx index of validator change
|
||||
ProposerRewardPool sdk.Coins `json:"proposer_reward_pool"` // XXX reward pool collected from being the proposer
|
||||
|
||||
UnbondingHeight int64 `json:"unbonding_height"` // if unbonding, height at which this validator has begun unbonding
|
||||
UnbondingMinTime time.Time `json:"unbonding_time"` // if unbonding, min time for the validator to complete unbonding
|
||||
|
||||
Commission sdk.Dec `json:"commission"` // XXX the commission rate of fees charged to any delegators
|
||||
CommissionMax sdk.Dec `json:"commission_max"` // XXX maximum commission rate which this validator can ever charge
|
||||
CommissionChangeRate sdk.Dec `json:"commission_change_rate"` // XXX maximum daily increase of the validator commission
|
||||
CommissionChangeToday sdk.Dec `json:"commission_change_today"` // XXX commission rate change today, reset each day (UTC time)
|
||||
|
||||
// fee related
|
||||
LastBondedTokens sdk.Dec `json:"prev_bonded_tokens"` // Previous bonded tokens held
|
||||
}
|
||||
|
||||
// NewValidator - initialize a new validator
|
||||
|
@ -54,12 +54,12 @@ func NewValidator(operator sdk.AccAddress, pubKey crypto.PubKey, description Des
|
|||
Description: description,
|
||||
BondHeight: int64(0),
|
||||
BondIntraTxCounter: int16(0),
|
||||
ProposerRewardPool: sdk.Coins{},
|
||||
UnbondingHeight: int64(0),
|
||||
UnbondingMinTime: time.Unix(0, 0),
|
||||
Commission: sdk.ZeroDec(),
|
||||
CommissionMax: sdk.ZeroDec(),
|
||||
CommissionChangeRate: sdk.ZeroDec(),
|
||||
CommissionChangeToday: sdk.ZeroDec(),
|
||||
LastBondedTokens: sdk.ZeroDec(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -73,12 +73,12 @@ type validatorValue struct {
|
|||
Description Description
|
||||
BondHeight int64
|
||||
BondIntraTxCounter int16
|
||||
ProposerRewardPool sdk.Coins
|
||||
UnbondingHeight int64
|
||||
UnbondingMinTime time.Time
|
||||
Commission sdk.Dec
|
||||
CommissionMax sdk.Dec
|
||||
CommissionChangeRate sdk.Dec
|
||||
CommissionChangeToday sdk.Dec
|
||||
LastBondedTokens sdk.Dec
|
||||
}
|
||||
|
||||
// return the redelegation without fields contained within the key for the store
|
||||
|
@ -92,12 +92,12 @@ func MustMarshalValidator(cdc *wire.Codec, validator Validator) []byte {
|
|||
Description: validator.Description,
|
||||
BondHeight: validator.BondHeight,
|
||||
BondIntraTxCounter: validator.BondIntraTxCounter,
|
||||
ProposerRewardPool: validator.ProposerRewardPool,
|
||||
UnbondingHeight: validator.UnbondingHeight,
|
||||
UnbondingMinTime: validator.UnbondingMinTime,
|
||||
Commission: validator.Commission,
|
||||
CommissionMax: validator.CommissionMax,
|
||||
CommissionChangeRate: validator.CommissionChangeRate,
|
||||
CommissionChangeToday: validator.CommissionChangeToday,
|
||||
LastBondedTokens: validator.LastBondedTokens,
|
||||
}
|
||||
return cdc.MustMarshalBinary(val)
|
||||
}
|
||||
|
@ -108,7 +108,6 @@ func MustUnmarshalValidator(cdc *wire.Codec, operatorAddr, value []byte) Validat
|
|||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return validator
|
||||
}
|
||||
|
||||
|
@ -134,12 +133,12 @@ func UnmarshalValidator(cdc *wire.Codec, operatorAddr, value []byte) (validator
|
|||
Description: storeValue.Description,
|
||||
BondHeight: storeValue.BondHeight,
|
||||
BondIntraTxCounter: storeValue.BondIntraTxCounter,
|
||||
ProposerRewardPool: storeValue.ProposerRewardPool,
|
||||
UnbondingHeight: storeValue.UnbondingHeight,
|
||||
UnbondingMinTime: storeValue.UnbondingMinTime,
|
||||
Commission: storeValue.Commission,
|
||||
CommissionMax: storeValue.CommissionMax,
|
||||
CommissionChangeRate: storeValue.CommissionChangeRate,
|
||||
CommissionChangeToday: storeValue.CommissionChangeToday,
|
||||
LastBondedTokens: storeValue.LastBondedTokens,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -161,12 +160,12 @@ func (v Validator) HumanReadableString() (string, error) {
|
|||
resp += fmt.Sprintf("Delegator Shares: %s\n", v.DelegatorShares.String())
|
||||
resp += fmt.Sprintf("Description: %s\n", v.Description)
|
||||
resp += fmt.Sprintf("Bond Height: %d\n", v.BondHeight)
|
||||
resp += fmt.Sprintf("Proposer Reward Pool: %s\n", v.ProposerRewardPool.String())
|
||||
resp += fmt.Sprintf("Unbonding Height: %d\n", v.UnbondingHeight)
|
||||
resp += fmt.Sprintf("Minimum Unbonding Time: %d\n", v.UnbondingMinTime)
|
||||
resp += fmt.Sprintf("Commission: %s\n", v.Commission.String())
|
||||
resp += fmt.Sprintf("Max Commission Rate: %s\n", v.CommissionMax.String())
|
||||
resp += fmt.Sprintf("Commission Change Rate: %s\n", v.CommissionChangeRate.String())
|
||||
resp += fmt.Sprintf("Commission Change Today: %s\n", v.CommissionChangeToday.String())
|
||||
resp += fmt.Sprintf("Previous Bonded Tokens: %s\n", v.LastBondedTokens.String())
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
@ -186,15 +185,14 @@ type BechValidator struct {
|
|||
Description Description `json:"description"` // description terms for the validator
|
||||
BondHeight int64 `json:"bond_height"` // earliest height as a bonded validator
|
||||
BondIntraTxCounter int16 `json:"bond_intra_tx_counter"` // block-local tx index of validator change
|
||||
ProposerRewardPool sdk.Coins `json:"proposer_reward_pool"` // XXX reward pool collected from being the proposer
|
||||
|
||||
UnbondingHeight int64 `json:"unbonding_height"` // if unbonding, height at which this validator has begun unbonding
|
||||
UnbondingMinTime time.Time `json:"unbonding_time"` // if unbonding, min time for the validator to complete unbonding
|
||||
|
||||
Commission sdk.Dec `json:"commission"` // XXX the commission rate of fees charged to any delegators
|
||||
CommissionMax sdk.Dec `json:"commission_max"` // XXX maximum commission rate which this validator can ever charge
|
||||
CommissionChangeRate sdk.Dec `json:"commission_change_rate"` // XXX maximum daily increase of the validator commission
|
||||
CommissionChangeToday sdk.Dec `json:"commission_change_today"` // XXX commission rate change today, reset each day (UTC time)
|
||||
|
||||
// fee related
|
||||
LastBondedTokens sdk.Dec `json:"prev_bonded_shares"` // last bonded token amount
|
||||
}
|
||||
|
||||
// get the bech validator from the the regular validator
|
||||
|
@ -216,14 +214,13 @@ func (v Validator) Bech32Validator() (BechValidator, error) {
|
|||
Description: v.Description,
|
||||
BondHeight: v.BondHeight,
|
||||
BondIntraTxCounter: v.BondIntraTxCounter,
|
||||
ProposerRewardPool: v.ProposerRewardPool,
|
||||
UnbondingHeight: v.UnbondingHeight,
|
||||
UnbondingMinTime: v.UnbondingMinTime,
|
||||
|
||||
Commission: v.Commission,
|
||||
CommissionMax: v.CommissionMax,
|
||||
CommissionChangeRate: v.CommissionChangeRate,
|
||||
CommissionChangeToday: v.CommissionChangeToday,
|
||||
|
||||
LastBondedTokens: v.LastBondedTokens,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -238,12 +235,10 @@ func (v Validator) Equal(c2 Validator) bool {
|
|||
v.Tokens.Equal(c2.Tokens) &&
|
||||
v.DelegatorShares.Equal(c2.DelegatorShares) &&
|
||||
v.Description == c2.Description &&
|
||||
v.ProposerRewardPool.IsEqual(c2.ProposerRewardPool) &&
|
||||
v.Commission.Equal(c2.Commission) &&
|
||||
v.CommissionMax.Equal(c2.CommissionMax) &&
|
||||
v.CommissionChangeRate.Equal(c2.CommissionChangeRate) &&
|
||||
v.CommissionChangeToday.Equal(c2.CommissionChangeToday) &&
|
||||
v.LastBondedTokens.Equal(c2.LastBondedTokens)
|
||||
v.CommissionChangeToday.Equal(c2.CommissionChangeToday)
|
||||
}
|
||||
|
||||
// constant used in flags to indicate that description field should not be updated
|
||||
|
|
Loading…
Reference in New Issue