Merge PR #2514: Refactor validator deletion

This commit is contained in:
Sunny Aggarwal 2018-10-18 12:58:18 -07:00 committed by Christopher Goes
parent a7e6969029
commit 505c356f20
7 changed files with 102 additions and 13 deletions

View File

@ -44,6 +44,7 @@ BREAKING CHANGES
* [simulation] \#2162 Added back correct supply invariants * [simulation] \#2162 Added back correct supply invariants
* [x/slashing] \#2430 Simulate more slashes, check if validator is jailed before jailing * [x/slashing] \#2430 Simulate more slashes, check if validator is jailed before jailing
* [x/stake] \#2393 Removed `CompleteUnbonding` and `CompleteRedelegation` Msg types, and instead added unbonding/redelegation queues to endblocker * [x/stake] \#2393 Removed `CompleteUnbonding` and `CompleteRedelegation` Msg types, and instead added unbonding/redelegation queues to endblocker
* [x/stake] \#1673 Validators are no longer deleted until they can no longer possibly be slashed
* SDK * SDK
* [core] \#2219 Update to Tendermint 0.24.0 * [core] \#2219 Update to Tendermint 0.24.0

View File

@ -3,15 +3,19 @@
## Unbonding Validator Queue ## Unbonding Validator Queue
For all unbonding validators that have finished their unbonding period, this switches their validator.Status For all unbonding validators that have finished their unbonding period, this switches their validator.Status
from sdk.Unbonding to sdk.Unbonded from sdk.Unbonding to sdk.Unbonded if they still have any delegation left. Otherwise, it deletes it from state.
```golang ```golang
validatorQueue(currTime time.Time): validatorQueue(currTime time.Time):
// unbonding validators are in ordered queue from oldest to newest // unbonding validators are in ordered queue from oldest to newest
for all unbondingValidators whose CompleteTime < currTime: for all unbondingValidators whose CompleteTime < currTime:
validator = GetValidator(unbondingValidator.ValidatorAddr) validator = GetValidator(unbondingValidator.ValidatorAddr)
validator.Status = sdk.Bonded if validator.DelegatorShares == 0 {
SetValidator(unbondingValidator) RemoveValidator(unbondingValidator)
} else {
validator.Status = sdk.Unbonded
SetValidator(unbondingValidator)
}
return return
``` ```
@ -61,4 +65,4 @@ redelegationQueue(currTime time.Time):
for all redelegations whose CompleteTime < currTime: for all redelegations whose CompleteTime < currTime:
removeRedelegation(redelegation) removeRedelegation(redelegation)
return return
``` ```

View File

@ -180,7 +180,7 @@ startUnbonding(tx TxStartUnbonding):
validator = updateValidator(validator) validator = updateValidator(validator)
if validator.DelegatorShares == 0 { if validator.Status == Unbonded && validator.DelegatorShares == 0 {
removeValidator(validator.Operator) removeValidator(validator.Operator)
return return
@ -189,8 +189,7 @@ startUnbonding(tx TxStartUnbonding):
### TxRedelegation ### TxRedelegation
The redelegation command allows delegators to instantly switch validators. Once The redelegation command allows delegators to instantly switch validators. Once
the unbonding period has passed, the redelegation must be completed with the unbonding period has passed, the redelegation is automatically completed in the EndBlocker.
txRedelegationComplete.
```golang ```golang
type TxRedelegate struct { type TxRedelegate struct {

View File

@ -425,8 +425,8 @@ func (k Keeper) unbond(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValA
// remove the coins from the validator // remove the coins from the validator
validator, amount = k.RemoveValidatorTokensAndShares(ctx, validator, shares) validator, amount = k.RemoveValidatorTokensAndShares(ctx, validator, shares)
if validator.DelegatorShares.IsZero() && validator.Status != sdk.Bonded { if validator.DelegatorShares.IsZero() && validator.Status == sdk.Unbonded {
// if bonded, we must remove in EndBlocker instead // if not unbonded, we must instead remove validator in EndBlocker once it finishes its unbonding period
k.RemoveValidator(ctx, validator.OperatorAddr) k.RemoveValidator(ctx, validator.OperatorAddr)
} }
@ -501,6 +501,7 @@ func (k Keeper) BeginUnbonding(ctx sdk.Context,
} }
k.SetUnbondingDelegation(ctx, ubd) k.SetUnbondingDelegation(ctx, ubd)
k.InsertUnbondingQueue(ctx, ubd) k.InsertUnbondingQueue(ctx, ubd)
return ubd, nil return ubd, nil
} }

View File

@ -1,6 +1,7 @@
package keeper package keeper
import ( import (
"fmt"
"testing" "testing"
"time" "time"
@ -392,7 +393,13 @@ func TestUndelegateFromUnbondedValidator(t *testing.T) {
require.True(t, ctx.BlockHeader().Time.Add(params.UnbondingTime).Equal(validator.UnbondingMinTime)) require.True(t, ctx.BlockHeader().Time.Add(params.UnbondingTime).Equal(validator.UnbondingMinTime))
// unbond the validator // unbond the validator
keeper.unbondingToUnbonded(ctx, validator) ctx = ctx.WithBlockTime(validator.UnbondingMinTime)
keeper.UnbondAllMatureValidatorQueue(ctx)
// Make sure validator is still in state because there is still an outstanding delegation
validator, found = keeper.GetValidator(ctx, addrVals[0])
require.True(t, found)
require.Equal(t, validator.Status, sdk.Unbonded)
// unbond some of the other delegation's shares // unbond some of the other delegation's shares
_, err = keeper.BeginUnbonding(ctx, addrDels[0], addrVals[0], sdk.NewDec(6)) _, err = keeper.BeginUnbonding(ctx, addrDels[0], addrVals[0], sdk.NewDec(6))
@ -401,6 +408,79 @@ func TestUndelegateFromUnbondedValidator(t *testing.T) {
// no ubd should have been found, coins should have been returned direcly to account // no ubd should have been found, coins should have been returned direcly to account
ubd, found := keeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0]) ubd, found := keeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0])
require.False(t, found, "%v", ubd) require.False(t, found, "%v", ubd)
// unbond rest of the other delegation's shares
_, err = keeper.BeginUnbonding(ctx, addrDels[0], addrVals[0], sdk.NewDec(4))
require.NoError(t, err)
// now validator should now be deleted from state
validator, found = keeper.GetValidator(ctx, addrVals[0])
fmt.Println(validator)
require.False(t, found)
}
func TestUnbondingAllDelegationFromValidator(t *testing.T) {
ctx, _, keeper := CreateTestInput(t, false, 0)
pool := keeper.GetPool(ctx)
pool.LooseTokens = sdk.NewDec(20)
//create a validator with a self-delegation
validator := types.NewValidator(addrVals[0], PKs[0], types.Description{})
validator, pool, issuedShares := validator.AddTokensFromDel(pool, sdk.NewInt(10))
require.Equal(t, int64(10), issuedShares.RoundInt64())
keeper.SetPool(ctx, pool)
validator = TestingUpdateValidator(keeper, ctx, validator)
pool = keeper.GetPool(ctx)
val0AccAddr := sdk.AccAddress(addrVals[0].Bytes())
selfDelegation := types.Delegation{
DelegatorAddr: val0AccAddr,
ValidatorAddr: addrVals[0],
Shares: issuedShares,
}
keeper.SetDelegation(ctx, selfDelegation)
// create a second delegation to this validator
keeper.DeleteValidatorByPowerIndex(ctx, validator, pool)
validator, pool, issuedShares = validator.AddTokensFromDel(pool, sdk.NewInt(10))
require.Equal(t, int64(10), issuedShares.RoundInt64())
keeper.SetPool(ctx, pool)
validator = TestingUpdateValidator(keeper, ctx, validator)
pool = keeper.GetPool(ctx)
delegation := types.Delegation{
DelegatorAddr: addrDels[0],
ValidatorAddr: addrVals[0],
Shares: issuedShares,
}
keeper.SetDelegation(ctx, delegation)
ctx = ctx.WithBlockHeight(10)
ctx = ctx.WithBlockTime(time.Unix(333, 0))
// unbond the all self-delegation to put validator in unbonding state
_, err := keeper.BeginUnbonding(ctx, val0AccAddr, addrVals[0], sdk.NewDec(10))
require.NoError(t, err)
// end block
updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx)
require.Equal(t, 1, len(updates))
// unbond all the remaining delegation
_, err = keeper.BeginUnbonding(ctx, addrDels[0], addrVals[0], sdk.NewDec(10))
require.NoError(t, err)
// validator should still be in state and still be in unbonding state
validator, found := keeper.GetValidator(ctx, addrVals[0])
require.True(t, found)
require.Equal(t, validator.Status, sdk.Unbonding)
// unbond the validator
ctx = ctx.WithBlockTime(validator.UnbondingMinTime)
keeper.UnbondAllMatureValidatorQueue(ctx)
// validator should now be deleted from state
_, found = keeper.GetValidator(ctx, addrVals[0])
require.False(t, found)
} }
// Make sure that that the retrieving the delegations doesn't affect the state // Make sure that that the retrieving the delegations doesn't affect the state

View File

@ -105,8 +105,8 @@ func (k Keeper) Slash(ctx sdk.Context, consAddr sdk.ConsAddress, infractionHeigh
k.SetPool(ctx, pool) k.SetPool(ctx, pool)
// remove validator if it has no more tokens // remove validator if it has no more tokens
if validator.Tokens.IsZero() && validator.Status != sdk.Bonded { if validator.DelegatorShares.IsZero() && validator.Status == sdk.Unbonded {
// if bonded, we must remove in ApplyAndReturnValidatorSetUpdates instead // if not unbonded, we must instead remove validator in EndBlocker once it finishes its unbonding period
k.RemoveValidator(ctx, validator.OperatorAddr) k.RemoveValidator(ctx, validator.OperatorAddr)
} }

View File

@ -343,7 +343,11 @@ func (k Keeper) UnbondAllMatureValidatorQueue(ctx sdk.Context) {
if !found || val.GetStatus() != sdk.Unbonding { if !found || val.GetStatus() != sdk.Unbonding {
continue continue
} }
k.unbondingToUnbonded(ctx, val) if val.GetDelegatorShares().IsZero() {
k.RemoveValidator(ctx, val.OperatorAddr)
} else {
k.unbondingToUnbonded(ctx, val)
}
} }
store.Delete(validatorTimesliceIterator.Key()) store.Delete(validatorTimesliceIterator.Key())
} }