2018-10-03 09:37:06 -07:00
|
|
|
package keeper
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"fmt"
|
|
|
|
"sort"
|
|
|
|
|
2020-02-06 11:21:02 -08:00
|
|
|
gogotypes "github.com/gogo/protobuf/types"
|
2018-10-03 09:37:06 -07:00
|
|
|
abci "github.com/tendermint/tendermint/abci/types"
|
|
|
|
|
|
|
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
2019-01-11 12:08:01 -08:00
|
|
|
"github.com/cosmos/cosmos-sdk/x/staking/types"
|
2018-10-03 09:37:06 -07:00
|
|
|
)
|
|
|
|
|
2019-12-18 05:20:02 -08:00
|
|
|
// Calculate the ValidatorUpdates for the current block
|
|
|
|
// Called in each EndBlock
|
|
|
|
func (k Keeper) BlockValidatorUpdates(ctx sdk.Context) []abci.ValidatorUpdate {
|
|
|
|
// Calculate validator set changes.
|
|
|
|
//
|
|
|
|
// NOTE: ApplyAndReturnValidatorSetUpdates has to come before
|
|
|
|
// UnbondAllMatureValidatorQueue.
|
|
|
|
// This fixes a bug when the unbonding period is instant (is the case in
|
|
|
|
// some of the tests). The test expected the validator to be completely
|
|
|
|
// unbonded after the Endblocker (go from Bonded -> Unbonding during
|
|
|
|
// ApplyAndReturnValidatorSetUpdates and then Unbonding -> Unbonded during
|
|
|
|
// UnbondAllMatureValidatorQueue).
|
|
|
|
validatorUpdates := k.ApplyAndReturnValidatorSetUpdates(ctx)
|
|
|
|
|
|
|
|
// Unbond all mature validators from the unbonding queue.
|
|
|
|
k.UnbondAllMatureValidatorQueue(ctx)
|
|
|
|
|
|
|
|
// Remove all mature unbonding delegations from the ubd queue.
|
|
|
|
matureUnbonds := k.DequeueAllMatureUBDQueue(ctx, ctx.BlockHeader().Time)
|
|
|
|
for _, dvPair := range matureUnbonds {
|
2020-03-06 05:40:50 -08:00
|
|
|
balances, err := k.CompleteUnbonding(ctx, dvPair.DelegatorAddress, dvPair.ValidatorAddress)
|
2019-12-18 05:20:02 -08:00
|
|
|
if err != nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx.EventManager().EmitEvent(
|
|
|
|
sdk.NewEvent(
|
|
|
|
types.EventTypeCompleteUnbonding,
|
2020-01-31 08:12:04 -08:00
|
|
|
sdk.NewAttribute(sdk.AttributeKeyAmount, balances.String()),
|
2019-12-18 05:20:02 -08:00
|
|
|
sdk.NewAttribute(types.AttributeKeyValidator, dvPair.ValidatorAddress.String()),
|
|
|
|
sdk.NewAttribute(types.AttributeKeyDelegator, dvPair.DelegatorAddress.String()),
|
|
|
|
),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Remove all mature redelegations from the red queue.
|
|
|
|
matureRedelegations := k.DequeueAllMatureRedelegationQueue(ctx, ctx.BlockHeader().Time)
|
|
|
|
for _, dvvTriplet := range matureRedelegations {
|
2020-03-06 05:40:50 -08:00
|
|
|
balances, err := k.CompleteRedelegation(
|
2020-01-31 08:12:04 -08:00
|
|
|
ctx,
|
|
|
|
dvvTriplet.DelegatorAddress,
|
|
|
|
dvvTriplet.ValidatorSrcAddress,
|
|
|
|
dvvTriplet.ValidatorDstAddress,
|
|
|
|
)
|
2019-12-18 05:20:02 -08:00
|
|
|
if err != nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx.EventManager().EmitEvent(
|
|
|
|
sdk.NewEvent(
|
|
|
|
types.EventTypeCompleteRedelegation,
|
2020-01-31 08:12:04 -08:00
|
|
|
sdk.NewAttribute(sdk.AttributeKeyAmount, balances.String()),
|
2019-12-18 05:20:02 -08:00
|
|
|
sdk.NewAttribute(types.AttributeKeyDelegator, dvvTriplet.DelegatorAddress.String()),
|
|
|
|
sdk.NewAttribute(types.AttributeKeySrcValidator, dvvTriplet.ValidatorSrcAddress.String()),
|
|
|
|
sdk.NewAttribute(types.AttributeKeyDstValidator, dvvTriplet.ValidatorDstAddress.String()),
|
|
|
|
),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
return validatorUpdates
|
|
|
|
}
|
|
|
|
|
2018-10-21 12:02:45 -07:00
|
|
|
// Apply and return accumulated updates to the bonded validator set. Also,
|
2018-10-22 01:18:10 -07:00
|
|
|
// * Updates the active valset as keyed by LastValidatorPowerKey.
|
|
|
|
// * Updates the total power as keyed by LastTotalPowerKey.
|
2018-10-21 12:02:45 -07:00
|
|
|
// * Updates validator status' according to updated powers.
|
2019-01-21 16:52:03 -08:00
|
|
|
// * Updates the fee pool bonded vs not-bonded tokens.
|
2018-10-21 12:02:45 -07:00
|
|
|
// * Updates relevant indices.
|
2018-10-21 15:26:58 -07:00
|
|
|
// It gets called once after genesis, another time maybe after genesis transactions,
|
|
|
|
// then once at every EndBlock.
|
2018-10-03 09:37:06 -07:00
|
|
|
//
|
|
|
|
// CONTRACT: Only validators with non-zero power or zero-power that were bonded
|
|
|
|
// at the previous block height or were removed from the validator set entirely
|
|
|
|
// are returned to Tendermint.
|
|
|
|
func (k Keeper) ApplyAndReturnValidatorSetUpdates(ctx sdk.Context) (updates []abci.ValidatorUpdate) {
|
|
|
|
maxValidators := k.GetParams(ctx).MaxValidators
|
2018-10-23 11:48:50 -07:00
|
|
|
totalPower := sdk.ZeroInt()
|
2019-06-28 13:11:27 -07:00
|
|
|
amtFromBondedToNotBonded, amtFromNotBondedToBonded := sdk.ZeroInt(), sdk.ZeroInt()
|
2018-10-03 09:37:06 -07:00
|
|
|
|
2018-10-21 12:02:45 -07:00
|
|
|
// Retrieve the last validator set.
|
2018-10-22 01:18:10 -07:00
|
|
|
// The persistent set is updated later in this function.
|
|
|
|
// (see LastValidatorPowerKey).
|
|
|
|
last := k.getLastValidatorsByAddr(ctx)
|
2018-10-03 09:37:06 -07:00
|
|
|
|
2018-10-21 15:26:58 -07:00
|
|
|
// Iterate over validators, highest power to lowest.
|
2020-01-10 06:33:43 -08:00
|
|
|
iterator := k.ValidatorsPowerStoreIterator(ctx)
|
2019-01-25 03:13:17 -08:00
|
|
|
defer iterator.Close()
|
2018-10-03 09:37:06 -07:00
|
|
|
|
2020-05-02 12:26:59 -07:00
|
|
|
for count := 0; iterator.Valid() && count < int(maxValidators); iterator.Next() {
|
2019-06-28 13:11:27 -07:00
|
|
|
// everything that is iterated in this loop is becoming or already a
|
|
|
|
// part of the bonded validator set
|
2018-10-26 04:27:55 -07:00
|
|
|
valAddr := sdk.ValAddress(iterator.Value())
|
|
|
|
validator := k.mustGetValidator(ctx, valAddr)
|
2018-10-03 09:37:06 -07:00
|
|
|
|
|
|
|
if validator.Jailed {
|
|
|
|
panic("should never retrieve a jailed validator from the power store")
|
|
|
|
}
|
|
|
|
|
|
|
|
// if we get to a zero-power validator (which we don't bond),
|
|
|
|
// there are no more possible bonded validators
|
2019-06-12 08:57:47 -07:00
|
|
|
if validator.PotentialConsensusPower() == 0 {
|
2018-10-03 09:37:06 -07:00
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
// apply the appropriate state change if necessary
|
2019-06-28 13:11:27 -07:00
|
|
|
switch {
|
|
|
|
case validator.IsUnbonded():
|
2018-10-03 09:37:06 -07:00
|
|
|
validator = k.unbondedToBonded(ctx, validator)
|
2019-06-28 13:11:27 -07:00
|
|
|
amtFromNotBondedToBonded = amtFromNotBondedToBonded.Add(validator.GetTokens())
|
|
|
|
case validator.IsUnbonding():
|
2018-10-03 09:37:06 -07:00
|
|
|
validator = k.unbondingToBonded(ctx, validator)
|
2019-06-28 13:11:27 -07:00
|
|
|
amtFromNotBondedToBonded = amtFromNotBondedToBonded.Add(validator.GetTokens())
|
|
|
|
case validator.IsBonded():
|
2018-10-03 09:37:06 -07:00
|
|
|
// no state change
|
|
|
|
default:
|
|
|
|
panic("unexpected validator status")
|
|
|
|
}
|
|
|
|
|
|
|
|
// fetch the old power bytes
|
2018-10-26 04:27:55 -07:00
|
|
|
var valAddrBytes [sdk.AddrLen]byte
|
2020-05-02 12:26:59 -07:00
|
|
|
|
2018-10-26 04:27:55 -07:00
|
|
|
copy(valAddrBytes[:], valAddr[:])
|
|
|
|
oldPowerBytes, found := last[valAddrBytes]
|
2019-06-12 08:57:47 -07:00
|
|
|
newPower := validator.ConsensusPower()
|
2020-03-13 12:58:43 -07:00
|
|
|
newPowerBytes := k.cdc.MustMarshalBinaryBare(&gogotypes.Int64Value{Value: newPower})
|
2019-02-07 17:41:23 -08:00
|
|
|
|
2018-10-03 09:37:06 -07:00
|
|
|
// update the validator set if power has changed
|
|
|
|
if !found || !bytes.Equal(oldPowerBytes, newPowerBytes) {
|
|
|
|
updates = append(updates, validator.ABCIValidatorUpdate())
|
2020-05-02 12:26:59 -07:00
|
|
|
|
2019-02-05 21:30:48 -08:00
|
|
|
k.SetLastValidatorPower(ctx, valAddr, newPower)
|
2018-10-03 09:37:06 -07:00
|
|
|
}
|
|
|
|
|
2018-10-26 04:27:55 -07:00
|
|
|
delete(last, valAddrBytes)
|
2018-10-03 09:37:06 -07:00
|
|
|
count++
|
2020-05-02 12:26:59 -07:00
|
|
|
|
2018-10-23 11:48:50 -07:00
|
|
|
totalPower = totalPower.Add(sdk.NewInt(newPower))
|
2018-10-03 09:37:06 -07:00
|
|
|
}
|
|
|
|
|
2019-02-07 17:41:23 -08:00
|
|
|
noLongerBonded := sortNoLongerBonded(last)
|
2018-10-26 04:27:55 -07:00
|
|
|
for _, valAddrBytes := range noLongerBonded {
|
|
|
|
validator := k.mustGetValidator(ctx, sdk.ValAddress(valAddrBytes))
|
2019-06-28 13:11:27 -07:00
|
|
|
validator = k.bondedToUnbonding(ctx, validator)
|
|
|
|
amtFromBondedToNotBonded = amtFromBondedToNotBonded.Add(validator.GetTokens())
|
|
|
|
k.DeleteLastValidatorPower(ctx, validator.GetOperator())
|
2018-10-03 09:37:06 -07:00
|
|
|
updates = append(updates, validator.ABCIValidatorUpdateZero())
|
2018-10-22 14:17:46 -07:00
|
|
|
}
|
2018-10-03 09:37:06 -07:00
|
|
|
|
2019-06-28 13:11:27 -07:00
|
|
|
// Update the pools based on the recent updates in the validator set:
|
|
|
|
// - The tokens from the non-bonded candidates that enter the new validator set need to be transferred
|
|
|
|
// to the Bonded pool.
|
2019-07-22 08:26:42 -07:00
|
|
|
// - The tokens from the bonded validators that are being kicked out from the validator set
|
2019-06-28 13:11:27 -07:00
|
|
|
// need to be transferred to the NotBonded pool.
|
|
|
|
switch {
|
2019-07-22 08:26:42 -07:00
|
|
|
// Compare and subtract the respective amounts to only perform one transfer.
|
|
|
|
// This is done in order to avoid doing multiple updates inside each iterator/loop.
|
2019-06-28 13:11:27 -07:00
|
|
|
case amtFromNotBondedToBonded.GT(amtFromBondedToNotBonded):
|
|
|
|
k.notBondedTokensToBonded(ctx, amtFromNotBondedToBonded.Sub(amtFromBondedToNotBonded))
|
|
|
|
case amtFromNotBondedToBonded.LT(amtFromBondedToNotBonded):
|
|
|
|
k.bondedTokensToNotBonded(ctx, amtFromBondedToNotBonded.Sub(amtFromNotBondedToBonded))
|
2020-05-02 12:26:59 -07:00
|
|
|
default: // equal amounts of tokens; no update required
|
2019-06-28 13:11:27 -07:00
|
|
|
}
|
|
|
|
|
2018-10-22 14:17:46 -07:00
|
|
|
// set total power on lookup index if there are any updates
|
|
|
|
if len(updates) > 0 {
|
2018-10-23 11:48:50 -07:00
|
|
|
k.SetLastTotalPower(ctx, totalPower)
|
2018-10-03 09:37:06 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
return updates
|
|
|
|
}
|
|
|
|
|
|
|
|
// Validator state transitions
|
|
|
|
|
|
|
|
func (k Keeper) bondedToUnbonding(ctx sdk.Context, validator types.Validator) types.Validator {
|
2019-06-04 15:06:58 -07:00
|
|
|
if !validator.IsBonded() {
|
2018-10-03 09:37:06 -07:00
|
|
|
panic(fmt.Sprintf("bad state transition bondedToUnbonding, validator: %v\n", validator))
|
|
|
|
}
|
2020-05-02 12:26:59 -07:00
|
|
|
|
2018-10-03 09:37:06 -07:00
|
|
|
return k.beginUnbondingValidator(ctx, validator)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (k Keeper) unbondingToBonded(ctx sdk.Context, validator types.Validator) types.Validator {
|
2019-06-04 15:06:58 -07:00
|
|
|
if !validator.IsUnbonding() {
|
2018-10-03 09:37:06 -07:00
|
|
|
panic(fmt.Sprintf("bad state transition unbondingToBonded, validator: %v\n", validator))
|
|
|
|
}
|
2020-05-02 12:26:59 -07:00
|
|
|
|
2018-10-03 09:37:06 -07:00
|
|
|
return k.bondValidator(ctx, validator)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (k Keeper) unbondedToBonded(ctx sdk.Context, validator types.Validator) types.Validator {
|
2019-06-04 15:06:58 -07:00
|
|
|
if !validator.IsUnbonded() {
|
2018-10-03 09:37:06 -07:00
|
|
|
panic(fmt.Sprintf("bad state transition unbondedToBonded, validator: %v\n", validator))
|
|
|
|
}
|
2020-05-02 12:26:59 -07:00
|
|
|
|
2018-10-03 09:37:06 -07:00
|
|
|
return k.bondValidator(ctx, validator)
|
|
|
|
}
|
|
|
|
|
2018-10-14 17:37:06 -07:00
|
|
|
// switches a validator from unbonding state to unbonded state
|
2020-02-24 06:37:03 -08:00
|
|
|
func (k Keeper) UnbondingToUnbonded(ctx sdk.Context, validator types.Validator) types.Validator {
|
2019-06-04 15:06:58 -07:00
|
|
|
if !validator.IsUnbonding() {
|
2018-10-03 09:37:06 -07:00
|
|
|
panic(fmt.Sprintf("bad state transition unbondingToBonded, validator: %v\n", validator))
|
|
|
|
}
|
2020-05-02 12:26:59 -07:00
|
|
|
|
2018-10-03 09:37:06 -07:00
|
|
|
return k.completeUnbondingValidator(ctx, validator)
|
|
|
|
}
|
|
|
|
|
|
|
|
// send a validator to jail
|
|
|
|
func (k Keeper) jailValidator(ctx sdk.Context, validator types.Validator) {
|
|
|
|
if validator.Jailed {
|
|
|
|
panic(fmt.Sprintf("cannot jail already jailed validator, validator: %v\n", validator))
|
|
|
|
}
|
|
|
|
|
|
|
|
validator.Jailed = true
|
|
|
|
k.SetValidator(ctx, validator)
|
2018-12-07 16:04:52 -08:00
|
|
|
k.DeleteValidatorByPowerIndex(ctx, validator)
|
2018-10-03 09:37:06 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// remove a validator from jail
|
|
|
|
func (k Keeper) unjailValidator(ctx sdk.Context, validator types.Validator) {
|
|
|
|
if !validator.Jailed {
|
|
|
|
panic(fmt.Sprintf("cannot unjail already unjailed validator, validator: %v\n", validator))
|
|
|
|
}
|
|
|
|
|
|
|
|
validator.Jailed = false
|
|
|
|
k.SetValidator(ctx, validator)
|
2018-12-07 16:04:52 -08:00
|
|
|
k.SetValidatorByPowerIndex(ctx, validator)
|
2018-10-03 09:37:06 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// perform all the store operations for when a validator status becomes bonded
|
|
|
|
func (k Keeper) bondValidator(ctx sdk.Context, validator types.Validator) types.Validator {
|
2019-02-07 17:41:23 -08:00
|
|
|
// delete the validator by power index, as the key will change
|
2018-12-07 16:04:52 -08:00
|
|
|
k.DeleteValidatorByPowerIndex(ctx, validator)
|
2018-10-03 09:37:06 -07:00
|
|
|
|
2019-06-28 13:11:27 -07:00
|
|
|
validator = validator.UpdateStatus(sdk.Bonded)
|
2018-10-03 09:37:06 -07:00
|
|
|
|
2018-11-08 16:28:28 -08:00
|
|
|
// save the now bonded validator record to the two referenced stores
|
2018-10-03 09:37:06 -07:00
|
|
|
k.SetValidator(ctx, validator)
|
2018-12-07 16:04:52 -08:00
|
|
|
k.SetValidatorByPowerIndex(ctx, validator)
|
2018-10-03 09:37:06 -07:00
|
|
|
|
2018-11-08 16:28:28 -08:00
|
|
|
// delete from queue if present
|
|
|
|
k.DeleteValidatorQueue(ctx, validator)
|
|
|
|
|
2019-02-08 15:57:33 -08:00
|
|
|
// trigger hook
|
2020-02-06 11:21:02 -08:00
|
|
|
k.AfterValidatorBonded(ctx, validator.GetConsAddr(), validator.OperatorAddress)
|
2018-10-03 09:37:06 -07:00
|
|
|
|
|
|
|
return validator
|
|
|
|
}
|
|
|
|
|
2019-02-07 17:41:23 -08:00
|
|
|
// perform all the store operations for when a validator begins unbonding
|
2018-10-03 09:37:06 -07:00
|
|
|
func (k Keeper) beginUnbondingValidator(ctx sdk.Context, validator types.Validator) types.Validator {
|
|
|
|
params := k.GetParams(ctx)
|
|
|
|
|
2019-02-07 17:41:23 -08:00
|
|
|
// delete the validator by power index, as the key will change
|
2018-12-07 16:04:52 -08:00
|
|
|
k.DeleteValidatorByPowerIndex(ctx, validator)
|
2018-10-03 09:37:06 -07:00
|
|
|
|
|
|
|
// sanity check
|
|
|
|
if validator.Status != sdk.Bonded {
|
|
|
|
panic(fmt.Sprintf("should not already be unbonded or unbonding, validator: %v\n", validator))
|
|
|
|
}
|
|
|
|
|
2019-06-28 13:11:27 -07:00
|
|
|
validator = validator.UpdateStatus(sdk.Unbonding)
|
2018-10-03 09:37:06 -07:00
|
|
|
|
2019-02-07 17:41:23 -08:00
|
|
|
// set the unbonding completion time and completion height appropriately
|
2020-02-06 11:21:02 -08:00
|
|
|
validator.UnbondingTime = ctx.BlockHeader().Time.Add(params.UnbondingTime)
|
2018-10-03 09:37:06 -07:00
|
|
|
validator.UnbondingHeight = ctx.BlockHeader().Height
|
|
|
|
|
2018-11-08 16:28:28 -08:00
|
|
|
// save the now unbonded validator record and power index
|
2018-10-03 09:37:06 -07:00
|
|
|
k.SetValidator(ctx, validator)
|
2018-12-07 16:04:52 -08:00
|
|
|
k.SetValidatorByPowerIndex(ctx, validator)
|
2018-10-03 09:37:06 -07:00
|
|
|
|
2018-10-14 17:37:06 -07:00
|
|
|
// Adds to unbonding validator queue
|
|
|
|
k.InsertValidatorQueue(ctx, validator)
|
|
|
|
|
2019-02-08 15:57:33 -08:00
|
|
|
// trigger hook
|
2020-02-06 11:21:02 -08:00
|
|
|
k.AfterValidatorBeginUnbonding(ctx, validator.GetConsAddr(), validator.OperatorAddress)
|
2018-10-03 09:37:06 -07:00
|
|
|
|
|
|
|
return validator
|
|
|
|
}
|
|
|
|
|
|
|
|
// perform all the store operations for when a validator status becomes unbonded
|
|
|
|
func (k Keeper) completeUnbondingValidator(ctx sdk.Context, validator types.Validator) types.Validator {
|
2019-06-28 13:11:27 -07:00
|
|
|
validator = validator.UpdateStatus(sdk.Unbonded)
|
2018-10-03 09:37:06 -07:00
|
|
|
k.SetValidator(ctx, validator)
|
2020-05-02 12:26:59 -07:00
|
|
|
|
2018-10-03 09:37:06 -07:00
|
|
|
return validator
|
|
|
|
}
|
|
|
|
|
|
|
|
// map of operator addresses to serialized power
|
|
|
|
type validatorsByAddr map[[sdk.AddrLen]byte][]byte
|
|
|
|
|
2018-10-22 01:18:10 -07:00
|
|
|
// get the last validator set
|
|
|
|
func (k Keeper) getLastValidatorsByAddr(ctx sdk.Context) validatorsByAddr {
|
2018-10-03 09:37:06 -07:00
|
|
|
last := make(validatorsByAddr)
|
2020-05-02 12:26:59 -07:00
|
|
|
|
2020-01-10 06:33:43 -08:00
|
|
|
iterator := k.LastValidatorsIterator(ctx)
|
2019-01-25 03:13:17 -08:00
|
|
|
defer iterator.Close()
|
2020-01-10 06:33:43 -08:00
|
|
|
|
2018-10-03 09:37:06 -07:00
|
|
|
for ; iterator.Valid(); iterator.Next() {
|
2018-10-26 04:27:55 -07:00
|
|
|
var valAddr [sdk.AddrLen]byte
|
2019-02-07 17:41:23 -08:00
|
|
|
// extract the validator address from the key (prefix is 1-byte)
|
2018-10-26 04:27:55 -07:00
|
|
|
copy(valAddr[:], iterator.Key()[1:])
|
2018-10-03 09:37:06 -07:00
|
|
|
powerBytes := iterator.Value()
|
2018-10-26 04:27:55 -07:00
|
|
|
last[valAddr] = make([]byte, len(powerBytes))
|
2019-08-19 09:06:27 -07:00
|
|
|
copy(last[valAddr], powerBytes)
|
2018-10-03 09:37:06 -07:00
|
|
|
}
|
2020-05-02 12:26:59 -07:00
|
|
|
|
2018-10-03 09:37:06 -07:00
|
|
|
return last
|
|
|
|
}
|
|
|
|
|
|
|
|
// given a map of remaining validators to previous bonded power
|
|
|
|
// returns the list of validators to be unbonded, sorted by operator address
|
2019-02-07 17:41:23 -08:00
|
|
|
func sortNoLongerBonded(last validatorsByAddr) [][]byte {
|
2018-10-03 09:37:06 -07:00
|
|
|
// sort the map keys for determinism
|
|
|
|
noLongerBonded := make([][]byte, len(last))
|
|
|
|
index := 0
|
2020-05-02 12:26:59 -07:00
|
|
|
|
2018-10-26 04:27:55 -07:00
|
|
|
for valAddrBytes := range last {
|
|
|
|
valAddr := make([]byte, sdk.AddrLen)
|
2019-08-19 09:06:27 -07:00
|
|
|
copy(valAddr, valAddrBytes[:])
|
2018-10-26 04:27:55 -07:00
|
|
|
noLongerBonded[index] = valAddr
|
2018-10-03 09:37:06 -07:00
|
|
|
index++
|
|
|
|
}
|
|
|
|
// sorted by address - order doesn't matter
|
|
|
|
sort.SliceStable(noLongerBonded, func(i, j int) bool {
|
2019-02-07 17:41:23 -08:00
|
|
|
// -1 means strictly less than
|
2018-10-03 09:37:06 -07:00
|
|
|
return bytes.Compare(noLongerBonded[i], noLongerBonded[j]) == -1
|
|
|
|
})
|
2020-05-02 12:26:59 -07:00
|
|
|
|
2018-10-03 09:37:06 -07:00
|
|
|
return noLongerBonded
|
|
|
|
}
|