Merge PR #3357: Staking spec upgrades / some renaming
This commit is contained in:
parent
da0426a2dd
commit
7f74b33e21
|
@ -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.
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)**
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Reference in New Issue