package keeper import ( "bytes" "fmt" "time" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/staking/types" ) // Cache the amino decoding of validators, as it can be the case that repeated slashing calls // cause many calls to GetValidator, which were shown to throttle the state machine in our // simulation. Note this is quite biased though, as the simulator does more slashes than a // live chain should, however we require the slashing to be fast as noone pays gas for it. type cachedValidator struct { val types.Validator marshalled string // marshalled amino bytes for the validator object (not operator address) } func newCachedValidator(val types.Validator, marshalled string) cachedValidator { return cachedValidator{ val: val, marshalled: marshalled, } } // get a single validator func (k Keeper) GetValidator(ctx sdk.Context, addr sdk.ValAddress) (validator types.Validator, found bool) { store := ctx.KVStore(k.storeKey) value := store.Get(GetValidatorKey(addr)) if value == nil { return validator, false } // If these amino encoded bytes are in the cache, return the cached validator strValue := string(value) if val, ok := k.validatorCache[strValue]; ok { valToReturn := val.val // Doesn't mutate the cache's value valToReturn.OperatorAddress = addr return valToReturn, true } // amino bytes weren't found in cache, so amino unmarshal and add it to the cache validator = types.MustUnmarshalValidator(k.cdc, value) cachedVal := newCachedValidator(validator, strValue) k.validatorCache[strValue] = newCachedValidator(validator, strValue) k.validatorCacheList.PushBack(cachedVal) // if the cache is too big, pop off the last element from it if k.validatorCacheList.Len() > aminoCacheSize { valToRemove := k.validatorCacheList.Remove(k.validatorCacheList.Front()).(cachedValidator) delete(k.validatorCache, valToRemove.marshalled) } validator = types.MustUnmarshalValidator(k.cdc, value) return validator, true } func (k Keeper) mustGetValidator(ctx sdk.Context, addr sdk.ValAddress) types.Validator { validator, found := k.GetValidator(ctx, addr) if !found { panic(fmt.Sprintf("validator record not found for address: %X\n", addr)) } return validator } // get a single validator by consensus address func (k Keeper) GetValidatorByConsAddr(ctx sdk.Context, consAddr sdk.ConsAddress) (validator types.Validator, found bool) { store := ctx.KVStore(k.storeKey) opAddr := store.Get(GetValidatorByConsAddrKey(consAddr)) if opAddr == nil { return validator, false } return k.GetValidator(ctx, opAddr) } func (k Keeper) mustGetValidatorByConsAddr(ctx sdk.Context, consAddr sdk.ConsAddress) types.Validator { validator, found := k.GetValidatorByConsAddr(ctx, consAddr) if !found { panic(fmt.Errorf("validator with consensus-Address %s not found", consAddr)) } return validator } // set the main record holding validator details func (k Keeper) SetValidator(ctx sdk.Context, validator types.Validator) { store := ctx.KVStore(k.storeKey) bz := types.MustMarshalValidator(k.cdc, validator) store.Set(GetValidatorKey(validator.OperatorAddress), bz) } // validator index func (k Keeper) SetValidatorByConsAddr(ctx sdk.Context, validator types.Validator) { store := ctx.KVStore(k.storeKey) consAddr := sdk.ConsAddress(validator.ConsPubKey.Address()) store.Set(GetValidatorByConsAddrKey(consAddr), validator.OperatorAddress) } // validator index func (k Keeper) SetValidatorByPowerIndex(ctx sdk.Context, validator types.Validator) { // jailed validators are not kept in the power index if validator.Jailed { return } store := ctx.KVStore(k.storeKey) store.Set(GetValidatorsByPowerIndexKey(validator), validator.OperatorAddress) } // validator index func (k Keeper) DeleteValidatorByPowerIndex(ctx sdk.Context, validator types.Validator) { store := ctx.KVStore(k.storeKey) store.Delete(GetValidatorsByPowerIndexKey(validator)) } // validator index func (k Keeper) SetNewValidatorByPowerIndex(ctx sdk.Context, validator types.Validator) { store := ctx.KVStore(k.storeKey) store.Set(GetValidatorsByPowerIndexKey(validator), validator.OperatorAddress) } // Update the tokens of an existing validator, update the validators power index key func (k Keeper) AddValidatorTokensAndShares(ctx sdk.Context, validator types.Validator, tokensToAdd sdk.Int) (valOut types.Validator, addedShares sdk.Dec) { k.DeleteValidatorByPowerIndex(ctx, validator) pool := k.GetPool(ctx) validator, pool, addedShares = validator.AddTokensFromDel(pool, tokensToAdd) k.SetValidator(ctx, validator) k.SetPool(ctx, pool) k.SetValidatorByPowerIndex(ctx, validator) return validator, addedShares } // Update the tokens of an existing validator, update the validators power index key func (k Keeper) RemoveValidatorTokensAndShares(ctx sdk.Context, validator types.Validator, sharesToRemove sdk.Dec) (valOut types.Validator, removedTokens sdk.Int) { k.DeleteValidatorByPowerIndex(ctx, validator) pool := k.GetPool(ctx) validator, pool, removedTokens = validator.RemoveDelShares(pool, sharesToRemove) k.SetValidator(ctx, validator) k.SetPool(ctx, pool) k.SetValidatorByPowerIndex(ctx, validator) return validator, removedTokens } // Update the tokens of an existing validator, update the validators power index key func (k Keeper) RemoveValidatorTokens(ctx sdk.Context, validator types.Validator, tokensToRemove sdk.Int) types.Validator { k.DeleteValidatorByPowerIndex(ctx, validator) pool := k.GetPool(ctx) validator, pool = validator.RemoveTokens(pool, tokensToRemove) k.SetValidator(ctx, validator) k.SetPool(ctx, pool) k.SetValidatorByPowerIndex(ctx, validator) return validator } // UpdateValidatorCommission attempts to update a validator's commission rate. // An error is returned if the new commission rate is invalid. func (k Keeper) UpdateValidatorCommission(ctx sdk.Context, validator types.Validator, newRate sdk.Dec) (types.Commission, sdk.Error) { commission := validator.Commission blockTime := ctx.BlockHeader().Time if err := commission.ValidateNewRate(newRate, blockTime); err != nil { return commission, err } commission.Rate = newRate commission.UpdateTime = blockTime return commission, nil } // remove the validator record and associated indexes // except for the bonded validator index which is only handled in ApplyAndReturnTendermintUpdates func (k Keeper) RemoveValidator(ctx sdk.Context, address sdk.ValAddress) { // first retrieve the old validator record validator, found := k.GetValidator(ctx, address) if !found { return } if validator.Status != sdk.Unbonded { 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") } // delete the old validator record store := ctx.KVStore(k.storeKey) store.Delete(GetValidatorKey(address)) store.Delete(GetValidatorByConsAddrKey(sdk.ConsAddress(validator.ConsPubKey.Address()))) store.Delete(GetValidatorsByPowerIndexKey(validator)) // call hooks k.AfterValidatorRemoved(ctx, validator.ConsAddress(), validator.OperatorAddress) } // get groups of validators // get the set of all validators with no limits, used during genesis dump func (k Keeper) GetAllValidators(ctx sdk.Context) (validators []types.Validator) { store := ctx.KVStore(k.storeKey) iterator := sdk.KVStorePrefixIterator(store, ValidatorsKey) defer iterator.Close() for ; iterator.Valid(); iterator.Next() { validator := types.MustUnmarshalValidator(k.cdc, iterator.Value()) validators = append(validators, validator) } return validators } // return a given amount of all the validators func (k Keeper) GetValidators(ctx sdk.Context, maxRetrieve uint16) (validators []types.Validator) { store := ctx.KVStore(k.storeKey) validators = make([]types.Validator, maxRetrieve) iterator := sdk.KVStorePrefixIterator(store, ValidatorsKey) defer iterator.Close() i := 0 for ; iterator.Valid() && i < int(maxRetrieve); iterator.Next() { validator := types.MustUnmarshalValidator(k.cdc, iterator.Value()) validators[i] = validator i++ } return validators[:i] // trim if the array length < maxRetrieve } // 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) maxValidators := k.MaxValidators(ctx) validators := make([]types.Validator, maxValidators) iterator := sdk.KVStoreReversePrefixIterator(store, ValidatorsByPowerIndexKey) defer iterator.Close() i := 0 for ; iterator.Valid() && i < int(maxValidators); iterator.Next() { address := iterator.Value() validator := k.mustGetValidator(ctx, address) if validator.Status == sdk.Bonded { validators[i] = validator i++ } } return validators[:i] // trim } // returns an iterator for the current validator power store func (k Keeper) ValidatorsPowerStoreIterator(ctx sdk.Context) (iterator sdk.Iterator) { store := ctx.KVStore(k.storeKey) iterator = sdk.KVStoreReversePrefixIterator(store, ValidatorsByPowerIndexKey) 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) { store := ctx.KVStore(k.storeKey) bz := store.Get(GetValidatorQueueTimeKey(timestamp)) if bz == nil { return []sdk.ValAddress{} } k.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &valAddrs) return valAddrs } // Sets a specific validator queue timeslice. func (k Keeper) SetValidatorQueueTimeSlice(ctx sdk.Context, timestamp time.Time, keys []sdk.ValAddress) { store := ctx.KVStore(k.storeKey) bz := k.cdc.MustMarshalBinaryLengthPrefixed(keys) store.Set(GetValidatorQueueTimeKey(timestamp), bz) } // Deletes a specific validator queue timeslice. func (k Keeper) DeleteValidatorQueueTimeSlice(ctx sdk.Context, timestamp time.Time) { store := ctx.KVStore(k.storeKey) store.Delete(GetValidatorQueueTimeKey(timestamp)) } // Insert an validator address to the appropriate timeslice in the validator queue func (k Keeper) InsertValidatorQueue(ctx sdk.Context, val types.Validator) { timeSlice := k.GetValidatorQueueTimeSlice(ctx, val.UnbondingCompletionTime) var keys []sdk.ValAddress if len(timeSlice) == 0 { keys = []sdk.ValAddress{val.OperatorAddress} } else { keys = append(timeSlice, val.OperatorAddress) } k.SetValidatorQueueTimeSlice(ctx, val.UnbondingCompletionTime, keys) } // Delete a validator address from the validator queue func (k Keeper) DeleteValidatorQueue(ctx sdk.Context, val types.Validator) { timeSlice := k.GetValidatorQueueTimeSlice(ctx, val.UnbondingCompletionTime) newTimeSlice := []sdk.ValAddress{} for _, addr := range timeSlice { if !bytes.Equal(addr, val.OperatorAddress) { newTimeSlice = append(newTimeSlice, addr) } } if len(newTimeSlice) == 0 { k.DeleteValidatorQueueTimeSlice(ctx, val.UnbondingCompletionTime) } else { k.SetValidatorQueueTimeSlice(ctx, val.UnbondingCompletionTime, newTimeSlice) } } // Returns all the validator queue timeslices from time 0 until endTime func (k Keeper) ValidatorQueueIterator(ctx sdk.Context, endTime time.Time) sdk.Iterator { store := ctx.KVStore(k.storeKey) return store.Iterator(ValidatorQueueKey, sdk.InclusiveEndBytes(GetValidatorQueueTimeKey(endTime))) } // Returns a concatenated list of all the timeslices before currTime, and deletes the timeslices from the queue func (k Keeper) GetAllMatureValidatorQueue(ctx sdk.Context, currTime time.Time) (matureValsAddrs []sdk.ValAddress) { // gets an iterator for all timeslices from time 0 until the current Blockheader time validatorTimesliceIterator := k.ValidatorQueueIterator(ctx, ctx.BlockHeader().Time) for ; validatorTimesliceIterator.Valid(); validatorTimesliceIterator.Next() { timeslice := []sdk.ValAddress{} k.cdc.MustUnmarshalBinaryLengthPrefixed(validatorTimesliceIterator.Value(), ×lice) matureValsAddrs = append(matureValsAddrs, timeslice...) } return matureValsAddrs } // Unbonds all the unbonding validators that have finished their unbonding period func (k Keeper) UnbondAllMatureValidatorQueue(ctx sdk.Context) { store := ctx.KVStore(k.storeKey) validatorTimesliceIterator := k.ValidatorQueueIterator(ctx, ctx.BlockHeader().Time) for ; validatorTimesliceIterator.Valid(); validatorTimesliceIterator.Next() { timeslice := []sdk.ValAddress{} k.cdc.MustUnmarshalBinaryLengthPrefixed(validatorTimesliceIterator.Value(), ×lice) for _, valAddr := range timeslice { val, found := k.GetValidator(ctx, valAddr) if !found { panic("validator in the unbonding queue was not found") } if val.GetStatus() != sdk.Unbonding { panic("unexpected validator in unbonding queue, status was not unbonding") } k.unbondingToUnbonded(ctx, val) if val.GetDelegatorShares().IsZero() { k.RemoveValidator(ctx, val.OperatorAddress) } } store.Delete(validatorTimesliceIterator.Key()) } }