Merge PR #3357: Staking spec upgrades / some renaming

This commit is contained in:
frog power 4000 2019-02-08 15:57:33 -08:00 committed by Christopher Goes
parent da0426a2dd
commit 7f74b33e21
17 changed files with 316 additions and 125 deletions

View File

@ -103,6 +103,7 @@ IMPROVEMENTS
* \#2509 Sanitize all usage of Dec.RoundInt64()
* [\#556](https://github.com/cosmos/cosmos-sdk/issues/556) Increase `BaseApp`
test coverage.
* \#3357 develop state-transitions.md for staking spec, missing states added to `state.md`
* [\#3552](https://github.com/cosmos/cosmos-sdk/pull/3552) Validate bit length when
deserializing `Int` types.

View File

@ -15,7 +15,7 @@ function
- `overview.md` - describe module
- `state.md` - specify and describe structures expected to marshalled into the store, and their keys
- `state_transitions.md` - standard state transition operations triggered by by hooks, messages, etc.
- `state_transitions.md` - standard state transition operations triggered by hooks, messages, etc.
- `end_block.md` - specify any end-block operations
- `begin_block.md` - specify any begin-block operations
- `messages.md` - specify message structure and expected state machine behaviour

View File

@ -1,7 +0,0 @@
- `state.md` needs updates to include
- LastValidatorPower
- LastTotalPower
- state for the queues: UnbondingDelegation, UnbondingValidator, Redelegation
- introduce `state_transitions.md` to describe validator/delegator state
transitions which are called from transactions

View File

@ -38,11 +38,12 @@ delegated to this validator). At this point the validator is said to be an
unbonding validator, whereby it will mature to become an "unbonded validator"
after the unbonding period has passed.
Each block the validator queue is to be checked for mature unbonding
validators. For all unbonding validators that have finished their unbonding
period, the validator.Status is switched from sdk.Unbonding to sdk.Unbonded.
If at this switch they do not have any delegation left the validator object
instead just deleted from state.
Each block the validator queue is to be checked for mature unbonding validators
(namely with a completion time <= current time). At this point any mature
validators which do not have any delegations remaining are deleted from state.
For all other mature unbonding validators that still have remaining
delegations, the validator.Status is switched from sdk.Unbonding to
sdk.Unbonded.
### Unbonding Delegations

View File

@ -27,18 +27,23 @@ native staking token of the chain.
- Delegation
- UnbondingDelegation
- Redelegation
2. **[Messages](messages.md)**
- Queues
2. **[State Transistions](state_transitions.md)**
- Validator
- Delegation
- Slashing
3. **[Messages](messages.md)**
- MsgCreateValidator
- MsgEditValidator
- MsgDelegate
- MsgBeginUnbonding
- MsgBeginRedelegate
3. **[End-Block](end_block.md)**
4. **[End-Block](end_block.md)**
- Validator Set Changes
- Queues
- Unbonding Validators
- Unbonding Delegations
- Redelegations
4. **[Hooks](hooks.md)**
5. **[Tags](tags.md)**
5. **[Hooks](hooks.md)**
6. **[Tags](tags.md)**

View File

@ -17,6 +17,13 @@ type Pool struct {
}
```
## LastTotalPower
LastTotalPower tracks the total amounts of bonded tokens recorded during the previous
end block.
- LastTotalPower: `0x12 -> amino(sdk.Int)`
## Params
Params is a module-wide configuration structure that stores system parameters
@ -37,27 +44,36 @@ type Params struct {
Validators objects should be primarily stored and accessed by the
`OperatorAddr`, an SDK validator address for the operator of the validator. Two
additional indexes are maintained in order to fulfill required lookups for
slashing and validator-set updates.
additional indices are maintained per validator object in order to fulfill
required lookups for slashing and validator-set updates. A third special index
(`LastValidatorPower`) is also maintained which however remains constant
throughout each block, unlike the first two indices which mirror the validator
records within a block.
- Validators: `0x21 | OperatorAddr -> amino(validator)`
- ValidatorsByConsAddr: `0x22 | ConsAddr -> OperatorAddr`
- ValidatorsByPower: `0x23 | BigEndian(Tokens) | OperatorAddr -> OperatorAddr`
- LastValidatorsPower: `0x11 OperatorAddr -> amino(Tokens)
`Validators` is the primary index - it ensures that each operator can have only one
associated validator, where the public key of that validator can change in the
future. Delegators can refer to the immutable operator of the validator, without
concern for the changing public key.
`ValidatorByConsAddr` is a secondary index that enables lookups for slashing.
`ValidatorByConsAddr` is an additional index that enables lookups for slashing.
When Tendermint reports evidence, it provides the validator address, so this
map is needed to find the operator. Note that the `ConsAddr` corresponds to the
address which can be derived from the validator's `ConsPubKey`.
`ValidatorsByPower` is a secondary index that provides a sorted list of
`ValidatorsByPower` is an additional index that provides a sorted list o
potential validators to quickly determine the current active set. Note
that all validators where `Jailed` is true are not stored within this index.
`LastValidatorsPower` is a special index that provides a historical list of the
last-block's bonded validators. This index remains constant during a block but
is updated during the validator set update process which takes place in [end
block](end_block.md).
Each validator's state is stored in a `Validator` struct:
```golang
@ -190,3 +206,57 @@ type RedelegationEntry struct {
SharesDst sdk.Dec // amount of destination-validator shares created by redelegation
}
```
## Queues
All queues objects are sorted by timestamp. The time used within any queue is
first rounded to the nearest nanosecond then sorted. The sortable time format
used is a slight modification of the RFC3339Nano and uses the the format string
`"2006-01-02T15:04:05.000000000"`. Noteably This format:
- right pads all zeros
- drops the time zone info (uses UTC)
In all cases, the stored timestamp represents the maturation time of the queue
element.
### UnbondingDelegationQueue
For the purpose of tracking progress of unbonding delegations the unbonding
delegations queue is kept.
- UnbondingDelegation: `0x41 | format(time) -> []DVPair`
```
type DVPair struct {
DelegatorAddr sdk.AccAddress
ValidatorAddr sdk.ValAddress
}
```
### RedelegationQueue
For the purpose of tracking progress of redelegations the redelegation queue is
kept.
- UnbondingDelegation: `0x42 | format(time) -> []DVVTriplet`
```
type DVVTriplet struct {
DelegatorAddr sdk.AccAddress
ValidatorSrcAddr sdk.ValAddress
ValidatorDstAddr sdk.ValAddress
}
```
### ValidatorQueue
For the purpose of tracking progress of unbonding validators the validator
queue is kept.
- ValidatorQueueTime: `0x43 | format(time) -> []sdk.ValAddress`
The stored object as each key is an array of validator operator addresses from
which the validator object can be accessed. Typically it is expected that only
a single validator record will be associated with a given timestamp however it is possible
that multiple validators exist in the queue at the same location.

View File

@ -0,0 +1,101 @@
# State Transitions
This document describes the state transition operations pertaining to:
- Validators
- Delegations
- Slashing
## Validators
### Non-Bonded to Bonded
When a validator is bonded from any other state the following operations occur:
- set `validator.Status` to `Bonded`
- update the `Pool` object with tokens moved from `NotBondedTokens` to `BondedTokens`
- delete record the existing record from `ValidatorByPowerIndex`
- add an new updated record to the `ValidatorByPowerIndex`
- update the `Validator` object for this validator
- if it exists, delete any `ValidatorQueue` record for this validator
### Bonded to Unbonding
When a validator begins the unbonding process the following operations occur:
- update the `Pool` object with tokens moved from `BondedTokens` to `NotBondedTokens`
- set `validator.Status` to `Unbonding`
- delete record the existing record from `ValidatorByPowerIndex`
- add an new updated record to the `ValidatorByPowerIndex`
- update the `Validator` object for this validator
- insert a new record into the `ValidatorQueue` for this validator
### Unbonding to Unbonded
A validator moves from unbonding to unbonded when the `ValidatorQueue` object
moves from bonded to unbonded
- update the `Validator` object for this validator
- set `validator.Status` to `Unbonded`
### Jail/Unjail
when a validator is jailed it is effectively removed from the Tendermint set.
this process may be also be reversed. the following operations occur:
- set `Validator.Jailed` and update object
- if jailed delete record from `ValidatorByPowerIndex`
- if unjailed add record to `ValidatorByPowerIndex`
## Delegations
### Delegate
When a delegation occurs both the validator and the delegtion objects are affected
- determine the delegators shares based on tokens delegated and the validator's exchange rate
- remove tokens from the sending account
- add shares the delegation object or add them to a created validator object
- add new delegator shares and update the `Validator` object
- update the `Pool` object appropriately if tokens have moved into a bonded validator
- delete record the existing record from `ValidatorByPowerIndex`
- add an new updated record to the `ValidatorByPowerIndex`
#### Unbond Delegation
As a part of the Undelegate and Complete Unbonding state transitions Unbond
Delegation may be called.
- subtract the unbonded shares from delegator
- update the delegation or remove the delegation if there are no more shares
- if the delegation is the operator of the validator and no more shares exist
then trigger a jail validator
- update the validator with removed the delegator shares and associated coins, update
the pool for any shifts between bonded and non-bonded tokens.
- remove the validator if it is unbonded and there are no more delegation shares.
### Undelegate
When an delegation occurs both the validator and the delegtion objects are affected
- perform an unbond delegation
- if the validator is unbonding or bonded add the tokens to an
`UnbondingDelegation` Entry
- if the validator is unbonded send the tokens directly to the withdraw
account
### Complete Unbonding
For undelegations which do not complete immediately, the following operations
occur when the unbonding delegation queue element matures:
- remove the entry from the `UnbondingDelegation` object
- withdraw the tokens to the delegator withdraw address
### Begin Redelegation
Redelegations affect the delegation, source and destination validators.
- perform an unbond delegation from the source validator
- using the generated tokens perform a Delegate to the destination
validator
- record the token amount in an new entry in the relevant `Redelegation`
### Complete Redelegation
When a redelegations complete the following occurs:
- remove the entry from the `Redelegation` object
TODO TODO TOFU TODO
## Slashing
### Slash Validator
### Slash Unbonding Delegation
### Slash Redelegation

View File

@ -11,7 +11,7 @@ type StakingKeeper interface {
ValidatorByConsAddr(ctx sdk.Context, consAddr sdk.ConsAddress) sdk.Validator
TotalPower(ctx sdk.Context) sdk.Int
GetLastTotalPower(ctx sdk.Context) sdk.Int
GetLastValidatorPower(ctx sdk.Context, valAddr sdk.ValAddress) sdk.Int
GetLastValidatorPower(ctx sdk.Context, valAddr sdk.ValAddress) int64
}
// expected coin keeper

View File

@ -457,11 +457,7 @@ func (k Keeper) Delegate(ctx sdk.Context, delAddr sdk.AccAddress, bondAmt sdk.In
// Get or create the delegation object
delegation, found := k.GetDelegation(ctx, delAddr, validator.OperatorAddr)
if !found {
delegation = types.Delegation{
DelegatorAddr: delAddr,
ValidatorAddr: validator.OperatorAddr,
Shares: sdk.ZeroDec(),
}
delegation = types.NewDelegation(delAddr, validator.OperatorAddr, sdk.ZeroDec())
}
// call the appropriate hook if present
@ -535,7 +531,7 @@ func (k Keeper) unbond(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValA
k.AfterDelegationModified(ctx, delegation.DelegatorAddr, delegation.ValidatorAddr)
}
// remove the coins from the validator
// remove the shares and coins from the validator
validator, amount = k.RemoveValidatorTokensAndShares(ctx, validator, shares)
if validator.DelegatorShares.IsZero() && validator.Status == sdk.Unbonded {

View File

@ -1,65 +1,73 @@
//nolint
package keeper
import (
sdk "github.com/cosmos/cosmos-sdk/types"
)
// Expose the hooks if present
// AfterValidatorCreated - call hook if registered
func (k Keeper) AfterValidatorCreated(ctx sdk.Context, valAddr sdk.ValAddress) {
if k.hooks != nil {
k.hooks.AfterValidatorCreated(ctx, valAddr)
}
}
// BeforeValidatorModified - call hook if registered
func (k Keeper) BeforeValidatorModified(ctx sdk.Context, valAddr sdk.ValAddress) {
if k.hooks != nil {
k.hooks.BeforeValidatorModified(ctx, valAddr)
}
}
// AfterValidatorRemoved - call hook if registered
func (k Keeper) AfterValidatorRemoved(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) {
if k.hooks != nil {
k.hooks.AfterValidatorRemoved(ctx, consAddr, valAddr)
}
}
// AfterValidatorBonded - call hook if registered
func (k Keeper) AfterValidatorBonded(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) {
if k.hooks != nil {
k.hooks.AfterValidatorBonded(ctx, consAddr, valAddr)
}
}
// AfterValidatorBeginUnbonding - call hook if registered
func (k Keeper) AfterValidatorBeginUnbonding(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) {
if k.hooks != nil {
k.hooks.AfterValidatorBeginUnbonding(ctx, consAddr, valAddr)
}
}
// BeforeDelegationCreated - call hook if registered
func (k Keeper) BeforeDelegationCreated(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) {
if k.hooks != nil {
k.hooks.BeforeDelegationCreated(ctx, delAddr, valAddr)
}
}
// BeforeDelegationSharesModified - call hook if registered
func (k Keeper) BeforeDelegationSharesModified(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) {
if k.hooks != nil {
k.hooks.BeforeDelegationSharesModified(ctx, delAddr, valAddr)
}
}
// BeforeDelegationRemoved - call hook if registered
func (k Keeper) BeforeDelegationRemoved(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) {
if k.hooks != nil {
k.hooks.BeforeDelegationRemoved(ctx, delAddr, valAddr)
}
}
// AfterDelegationModified - call hook if registered
func (k Keeper) AfterDelegationModified(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) {
if k.hooks != nil {
k.hooks.AfterDelegationModified(ctx, delAddr, valAddr)
}
}
// BeforeValidatorSlashed - call hook if registered
func (k Keeper) BeforeValidatorSlashed(ctx sdk.Context, valAddr sdk.ValAddress, fraction sdk.Dec) {
if k.hooks != nil {
k.hooks.BeforeValidatorSlashed(ctx, valAddr, fraction)

View File

@ -28,7 +28,9 @@ type Keeper struct {
codespace sdk.CodespaceType
}
func NewKeeper(cdc *codec.Codec, key, tkey sdk.StoreKey, ck bank.Keeper, paramstore params.Subspace, codespace sdk.CodespaceType) Keeper {
func NewKeeper(cdc *codec.Codec, key, tkey sdk.StoreKey, ck bank.Keeper,
paramstore params.Subspace, codespace sdk.CodespaceType) Keeper {
keeper := Keeper{
storeKey: key,
storeTKey: tkey,
@ -92,45 +94,3 @@ func (k Keeper) SetLastTotalPower(ctx sdk.Context, power sdk.Int) {
b := k.cdc.MustMarshalBinaryLengthPrefixed(power)
store.Set(LastTotalPowerKey, b)
}
// Get the last validator power.
// Returns zero if the operator was not a validator last block.
func (k Keeper) GetLastValidatorPower(ctx sdk.Context, operator sdk.ValAddress) (power sdk.Int) {
store := ctx.KVStore(k.storeKey)
bz := store.Get(GetLastValidatorPowerKey(operator))
if bz == nil {
return sdk.ZeroInt()
}
k.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &power)
return
}
// Set the last validator power.
func (k Keeper) SetLastValidatorPower(ctx sdk.Context, operator sdk.ValAddress, power int64) {
store := ctx.KVStore(k.storeKey)
bz := k.cdc.MustMarshalBinaryLengthPrefixed(power)
store.Set(GetLastValidatorPowerKey(operator), bz)
}
// Iterate over last validator powers.
func (k Keeper) IterateLastValidatorPowers(ctx sdk.Context,
handler func(operator sdk.ValAddress, power int64) (stop bool)) {
store := ctx.KVStore(k.storeKey)
iter := sdk.KVStorePrefixIterator(store, LastValidatorPowerKey)
defer iter.Close()
for ; iter.Valid(); iter.Next() {
addr := sdk.ValAddress(iter.Key()[len(LastValidatorPowerKey):])
var power int64
k.cdc.MustUnmarshalBinaryLengthPrefixed(iter.Value(), &power)
if handler(addr, power) {
break
}
}
}
// Delete the last validator power.
func (k Keeper) DeleteLastValidatorPower(ctx sdk.Context, operator sdk.ValAddress) {
store := ctx.KVStore(k.storeKey)
store.Delete(GetLastValidatorPowerKey(operator))
}

View File

@ -15,7 +15,7 @@ var (
// Keys for store prefixes
PoolKey = []byte{0x00} // key for the staking pools
// Last* values are const during a block.
// Last* values are constant during a block.
LastValidatorPowerKey = []byte{0x11} // prefix for each key to a validator index, for bonded validators
LastTotalPowerKey = []byte{0x12} // prefix for the total power

View File

@ -84,7 +84,10 @@ func (k Keeper) GetAllUnbondingDelegations(ctx sdk.Context, delegator sdk.AccAdd
}
// return all redelegations for a delegator
func (k Keeper) GetAllRedelegations(ctx sdk.Context, delegator sdk.AccAddress, srcValAddress, dstValAddress sdk.ValAddress) (redelegations []types.Redelegation) {
func (k Keeper) GetAllRedelegations(ctx sdk.Context, delegator sdk.AccAddress,
srcValAddress, dstValAddress sdk.ValAddress) (
redelegations []types.Redelegation) {
store := ctx.KVStore(k.storeKey)
delegatorPrefixKey := GetREDsKey(delegator)
iterator := sdk.KVStorePrefixIterator(store, delegatorPrefixKey) // smallest to largest

View File

@ -188,10 +188,8 @@ func (k Keeper) bondValidator(ctx sdk.Context, validator types.Validator) types.
// delete from queue if present
k.DeleteValidatorQueue(ctx, validator)
// call the bond hook if present
if k.hooks != nil {
k.hooks.AfterValidatorBonded(ctx, validator.ConsAddress(), validator.OperatorAddr)
}
// trigger hook
k.AfterValidatorBonded(ctx, validator.ConsAddress(), validator.OperatorAddr)
return validator
}
@ -225,10 +223,8 @@ func (k Keeper) beginUnbondingValidator(ctx sdk.Context, validator types.Validat
// Adds to unbonding validator queue
k.InsertValidatorQueue(ctx, validator)
// call the unbond hook if present
if k.hooks != nil {
k.hooks.AfterValidatorBeginUnbonding(ctx, validator.ConsAddress(), validator.OperatorAddr)
}
// trigger hook
k.AfterValidatorBeginUnbonding(ctx, validator.ConsAddress(), validator.OperatorAddr)
return validator
}

View File

@ -188,9 +188,11 @@ func (k Keeper) RemoveValidator(ctx sdk.Context, address sdk.ValAddress) {
}
if validator.Status != sdk.Unbonded {
panic("Cannot call RemoveValidator on bonded or unbonding validators")
panic("cannot call RemoveValidator on bonded or unbonding validators")
}
if validator.Tokens.IsPositive() {
panic("attempting to remove a validator which still contains tokens")
}
if validator.Tokens.GT(sdk.ZeroInt()) {
panic("validator being removed should never have positive tokens")
}
@ -201,11 +203,8 @@ func (k Keeper) RemoveValidator(ctx sdk.Context, address sdk.ValAddress) {
store.Delete(GetValidatorByConsAddrKey(sdk.ConsAddress(validator.ConsPubKey.Address())))
store.Delete(GetValidatorsByPowerIndexKey(validator))
// call hook if present
if k.hooks != nil {
k.hooks.AfterValidatorRemoved(ctx, validator.ConsAddress(), validator.OperatorAddr)
}
// call hooks
k.AfterValidatorRemoved(ctx, validator.ConsAddress(), validator.OperatorAddr)
}
// get groups of validators
@ -240,40 +239,6 @@ func (k Keeper) GetValidators(ctx sdk.Context, maxRetrieve uint16) (validators [
return validators[:i] // trim if the array length < maxRetrieve
}
// get the group of the bonded validators
func (k Keeper) GetLastValidators(ctx sdk.Context) (validators []types.Validator) {
store := ctx.KVStore(k.storeKey)
// add the actual validator power sorted store
maxValidators := k.MaxValidators(ctx)
validators = make([]types.Validator, maxValidators)
iterator := sdk.KVStorePrefixIterator(store, LastValidatorPowerKey)
defer iterator.Close()
i := 0
for ; iterator.Valid(); iterator.Next() {
// sanity check
if i >= int(maxValidators) {
panic("more validators than maxValidators found")
}
address := AddressFromLastValidatorPowerKey(iterator.Key())
validator := k.mustGetValidator(ctx, address)
validators[i] = validator
i++
}
return validators[:i] // trim
}
// returns an iterator for the consensus validators in the last block
func (k Keeper) LastValidatorsIterator(ctx sdk.Context) (iterator sdk.Iterator) {
store := ctx.KVStore(k.storeKey)
iterator = sdk.KVStorePrefixIterator(store, LastValidatorPowerKey)
return iterator
}
// get the current group of bonded validators sorted by power-rank
func (k Keeper) GetBondedValidatorsByPower(ctx sdk.Context) []types.Validator {
store := ctx.KVStore(k.storeKey)
@ -303,6 +268,86 @@ func (k Keeper) ValidatorsPowerStoreIterator(ctx sdk.Context) (iterator sdk.Iter
return iterator
}
//_______________________________________________________________________
// Last Validator Index
// Load the last validator power.
// Returns zero if the operator was not a validator last block.
func (k Keeper) GetLastValidatorPower(ctx sdk.Context, operator sdk.ValAddress) (power int64) {
store := ctx.KVStore(k.storeKey)
bz := store.Get(GetLastValidatorPowerKey(operator))
if bz == nil {
return 0
}
k.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &power)
return
}
// Set the last validator power.
func (k Keeper) SetLastValidatorPower(ctx sdk.Context, operator sdk.ValAddress, power int64) {
store := ctx.KVStore(k.storeKey)
bz := k.cdc.MustMarshalBinaryLengthPrefixed(power)
store.Set(GetLastValidatorPowerKey(operator), bz)
}
// Delete the last validator power.
func (k Keeper) DeleteLastValidatorPower(ctx sdk.Context, operator sdk.ValAddress) {
store := ctx.KVStore(k.storeKey)
store.Delete(GetLastValidatorPowerKey(operator))
}
// returns an iterator for the consensus validators in the last block
func (k Keeper) LastValidatorsIterator(ctx sdk.Context) (iterator sdk.Iterator) {
store := ctx.KVStore(k.storeKey)
iterator = sdk.KVStorePrefixIterator(store, LastValidatorPowerKey)
return iterator
}
// Iterate over last validator powers.
func (k Keeper) IterateLastValidatorPowers(ctx sdk.Context, handler func(operator sdk.ValAddress, power int64) (stop bool)) {
store := ctx.KVStore(k.storeKey)
iter := sdk.KVStorePrefixIterator(store, LastValidatorPowerKey)
defer iter.Close()
for ; iter.Valid(); iter.Next() {
addr := sdk.ValAddress(iter.Key()[len(LastValidatorPowerKey):])
var power int64
k.cdc.MustUnmarshalBinaryLengthPrefixed(iter.Value(), &power)
if handler(addr, power) {
break
}
}
}
// get the group of the bonded validators
func (k Keeper) GetLastValidators(ctx sdk.Context) (validators []types.Validator) {
store := ctx.KVStore(k.storeKey)
// add the actual validator power sorted store
maxValidators := k.MaxValidators(ctx)
validators = make([]types.Validator, maxValidators)
iterator := sdk.KVStorePrefixIterator(store, LastValidatorPowerKey)
defer iterator.Close()
i := 0
for ; iterator.Valid(); iterator.Next() {
// sanity check
if i >= int(maxValidators) {
panic("more validators than maxValidators found")
}
address := AddressFromLastValidatorPowerKey(iterator.Key())
validator := k.mustGetValidator(ctx, address)
validators[i] = validator
i++
}
return validators[:i] // trim
}
//_______________________________________________________________________
// Validator Queue
// gets a specific validator queue timeslice. A timeslice is a slice of ValAddresses corresponding to unbonding validators
// that expire at a certain time.
func (k Keeper) GetValidatorQueueTimeSlice(ctx sdk.Context, timestamp time.Time) (valAddrs []sdk.ValAddress) {

View File

@ -280,7 +280,19 @@ func TestValidatorBasics(t *testing.T) {
assert.True(ValEq(t, validators[2], resVals[2]))
// remove a record
validators[1].Status = sdk.Unbonded // First must set to Unbonded.
// shouldn't be able to remove if status is not unbonded
assert.PanicsWithValue(t,
"cannot call RemoveValidator on bonded or unbonding validators",
func() { keeper.RemoveValidator(ctx, validators[1].OperatorAddr) })
// shouldn't be able to remove if there are still tokens left
validators[1].Status = sdk.Unbonded
keeper.SetValidator(ctx, validators[1])
assert.PanicsWithValue(t,
"attempting to remove a validator which still contains tokens",
func() { keeper.RemoveValidator(ctx, validators[1].OperatorAddr) })
validators[1].Tokens = sdk.ZeroInt() // ...remove all tokens
keeper.SetValidator(ctx, validators[1]) // ...set the validator
keeper.RemoveValidator(ctx, validators[1].OperatorAddr) // Now it can be removed.