2018-06-26 19:00:12 -07:00
|
|
|
package keeper
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
2019-05-31 08:14:40 -07:00
|
|
|
"fmt"
|
2018-08-24 18:47:31 -07:00
|
|
|
"time"
|
2018-06-26 19:00:12 -07:00
|
|
|
|
|
|
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
2019-01-11 12:08:01 -08:00
|
|
|
"github.com/cosmos/cosmos-sdk/x/staking/types"
|
2018-06-26 19:00:12 -07:00
|
|
|
)
|
|
|
|
|
2018-09-13 14:23:44 -07:00
|
|
|
// return a specific delegation
|
2018-06-26 19:00:12 -07:00
|
|
|
func (k Keeper) GetDelegation(ctx sdk.Context,
|
2018-08-30 21:06:44 -07:00
|
|
|
delAddr sdk.AccAddress, valAddr sdk.ValAddress) (
|
|
|
|
delegation types.Delegation, found bool) {
|
2018-06-26 19:00:12 -07:00
|
|
|
|
|
|
|
store := ctx.KVStore(k.storeKey)
|
2019-05-28 01:44:04 -07:00
|
|
|
key := types.GetDelegationKey(delAddr, valAddr)
|
2018-07-03 20:44:54 -07:00
|
|
|
value := store.Get(key)
|
|
|
|
if value == nil {
|
2018-06-26 19:00:12 -07:00
|
|
|
return delegation, false
|
|
|
|
}
|
|
|
|
|
2019-01-07 12:42:52 -08:00
|
|
|
delegation = types.MustUnmarshalDelegation(k.cdc, value)
|
2018-06-26 19:00:12 -07:00
|
|
|
return delegation, true
|
|
|
|
}
|
|
|
|
|
2018-10-23 13:30:14 -07:00
|
|
|
// return all delegations used during genesis dump
|
2018-06-26 19:00:12 -07:00
|
|
|
func (k Keeper) GetAllDelegations(ctx sdk.Context) (delegations []types.Delegation) {
|
|
|
|
store := ctx.KVStore(k.storeKey)
|
2019-05-28 01:44:04 -07:00
|
|
|
iterator := sdk.KVStorePrefixIterator(store, types.DelegationKey)
|
2018-09-13 14:23:44 -07:00
|
|
|
defer iterator.Close()
|
2018-06-26 19:00:12 -07:00
|
|
|
|
2018-09-13 14:23:44 -07:00
|
|
|
for ; iterator.Valid(); iterator.Next() {
|
2019-01-07 12:42:52 -08:00
|
|
|
delegation := types.MustUnmarshalDelegation(k.cdc, iterator.Value())
|
2018-06-26 19:00:12 -07:00
|
|
|
delegations = append(delegations, delegation)
|
|
|
|
}
|
|
|
|
return delegations
|
|
|
|
}
|
|
|
|
|
2018-10-23 13:30:14 -07:00
|
|
|
// return all delegations to a specific validator. Useful for querier.
|
|
|
|
func (k Keeper) GetValidatorDelegations(ctx sdk.Context, valAddr sdk.ValAddress) (delegations []types.Delegation) {
|
2018-10-23 00:42:06 -07:00
|
|
|
store := ctx.KVStore(k.storeKey)
|
2019-05-28 01:44:04 -07:00
|
|
|
iterator := sdk.KVStorePrefixIterator(store, types.DelegationKey)
|
2018-10-23 00:42:06 -07:00
|
|
|
defer iterator.Close()
|
|
|
|
|
|
|
|
for ; iterator.Valid(); iterator.Next() {
|
2019-01-07 12:42:52 -08:00
|
|
|
delegation := types.MustUnmarshalDelegation(k.cdc, iterator.Value())
|
2018-10-23 13:42:13 -07:00
|
|
|
if delegation.GetValidatorAddr().Equals(valAddr) {
|
2018-10-23 00:42:06 -07:00
|
|
|
delegations = append(delegations, delegation)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return delegations
|
|
|
|
}
|
|
|
|
|
2018-09-13 14:23:44 -07:00
|
|
|
// return a given amount of all the delegations from a delegator
|
|
|
|
func (k Keeper) GetDelegatorDelegations(ctx sdk.Context, delegator sdk.AccAddress,
|
|
|
|
maxRetrieve uint16) (delegations []types.Delegation) {
|
|
|
|
|
|
|
|
delegations = make([]types.Delegation, maxRetrieve)
|
2018-06-26 19:00:12 -07:00
|
|
|
|
|
|
|
store := ctx.KVStore(k.storeKey)
|
2019-05-28 01:44:04 -07:00
|
|
|
delegatorPrefixKey := types.GetDelegationsKey(delegator)
|
2018-09-13 14:23:44 -07:00
|
|
|
iterator := sdk.KVStorePrefixIterator(store, delegatorPrefixKey)
|
|
|
|
defer iterator.Close()
|
2018-06-26 19:00:12 -07:00
|
|
|
|
|
|
|
i := 0
|
2018-09-13 14:23:44 -07:00
|
|
|
for ; iterator.Valid() && i < int(maxRetrieve); iterator.Next() {
|
2019-01-07 12:42:52 -08:00
|
|
|
delegation := types.MustUnmarshalDelegation(k.cdc, iterator.Value())
|
2018-06-26 19:00:12 -07:00
|
|
|
delegations[i] = delegation
|
2018-09-13 14:23:44 -07:00
|
|
|
i++
|
2018-06-26 19:00:12 -07:00
|
|
|
}
|
2018-09-13 14:23:44 -07:00
|
|
|
return delegations[:i] // trim if the array length < maxRetrieve
|
2018-06-26 19:00:12 -07:00
|
|
|
}
|
|
|
|
|
2019-02-07 17:41:23 -08:00
|
|
|
// set a delegation
|
2018-06-26 19:00:12 -07:00
|
|
|
func (k Keeper) SetDelegation(ctx sdk.Context, delegation types.Delegation) {
|
|
|
|
store := ctx.KVStore(k.storeKey)
|
2018-07-04 14:07:06 -07:00
|
|
|
b := types.MustMarshalDelegation(k.cdc, delegation)
|
2019-05-28 01:44:04 -07:00
|
|
|
store.Set(types.GetDelegationKey(delegation.DelegatorAddress, delegation.ValidatorAddress), b)
|
2018-06-26 19:00:12 -07:00
|
|
|
}
|
|
|
|
|
2019-02-07 17:41:23 -08:00
|
|
|
// remove a delegation
|
2018-06-26 19:00:12 -07:00
|
|
|
func (k Keeper) RemoveDelegation(ctx sdk.Context, delegation types.Delegation) {
|
2019-02-07 17:41:23 -08:00
|
|
|
// TODO: Consider calling hooks outside of the store wrapper functions, it's unobvious.
|
2019-02-25 07:16:52 -08:00
|
|
|
k.BeforeDelegationRemoved(ctx, delegation.DelegatorAddress, delegation.ValidatorAddress)
|
2018-06-26 19:00:12 -07:00
|
|
|
store := ctx.KVStore(k.storeKey)
|
2019-05-28 01:44:04 -07:00
|
|
|
store.Delete(types.GetDelegationKey(delegation.DelegatorAddress, delegation.ValidatorAddress))
|
2018-06-26 19:00:12 -07:00
|
|
|
}
|
|
|
|
|
2018-09-13 14:23:44 -07:00
|
|
|
// return a given amount of all the delegator unbonding-delegations
|
|
|
|
func (k Keeper) GetUnbondingDelegations(ctx sdk.Context, delegator sdk.AccAddress,
|
|
|
|
maxRetrieve uint16) (unbondingDelegations []types.UnbondingDelegation) {
|
|
|
|
|
|
|
|
unbondingDelegations = make([]types.UnbondingDelegation, maxRetrieve)
|
|
|
|
|
|
|
|
store := ctx.KVStore(k.storeKey)
|
2019-05-28 01:44:04 -07:00
|
|
|
delegatorPrefixKey := types.GetUBDsKey(delegator)
|
2018-09-13 14:23:44 -07:00
|
|
|
iterator := sdk.KVStorePrefixIterator(store, delegatorPrefixKey)
|
|
|
|
defer iterator.Close()
|
|
|
|
|
|
|
|
i := 0
|
|
|
|
for ; iterator.Valid() && i < int(maxRetrieve); iterator.Next() {
|
2019-01-07 12:42:52 -08:00
|
|
|
unbondingDelegation := types.MustUnmarshalUBD(k.cdc, iterator.Value())
|
2018-09-13 14:23:44 -07:00
|
|
|
unbondingDelegations[i] = unbondingDelegation
|
|
|
|
i++
|
|
|
|
}
|
|
|
|
return unbondingDelegations[:i] // trim if the array length < maxRetrieve
|
|
|
|
}
|
|
|
|
|
|
|
|
// return a unbonding delegation
|
2018-06-26 19:00:12 -07:00
|
|
|
func (k Keeper) GetUnbondingDelegation(ctx sdk.Context,
|
2018-08-30 21:06:44 -07:00
|
|
|
delAddr sdk.AccAddress, valAddr sdk.ValAddress) (ubd types.UnbondingDelegation, found bool) {
|
2018-06-26 19:00:12 -07:00
|
|
|
|
|
|
|
store := ctx.KVStore(k.storeKey)
|
2019-05-28 01:44:04 -07:00
|
|
|
key := types.GetUBDKey(delAddr, valAddr)
|
2018-07-03 21:29:02 -07:00
|
|
|
value := store.Get(key)
|
|
|
|
if value == nil {
|
2018-06-26 19:00:12 -07:00
|
|
|
return ubd, false
|
|
|
|
}
|
|
|
|
|
2019-01-07 12:42:52 -08:00
|
|
|
ubd = types.MustUnmarshalUBD(k.cdc, value)
|
2018-06-26 19:00:12 -07:00
|
|
|
return ubd, true
|
|
|
|
}
|
|
|
|
|
2018-09-13 14:23:44 -07:00
|
|
|
// return all unbonding delegations from a particular validator
|
2018-08-30 21:06:44 -07:00
|
|
|
func (k Keeper) GetUnbondingDelegationsFromValidator(ctx sdk.Context, valAddr sdk.ValAddress) (ubds []types.UnbondingDelegation) {
|
2018-06-29 20:34:55 -07:00
|
|
|
store := ctx.KVStore(k.storeKey)
|
2019-05-28 01:44:04 -07:00
|
|
|
iterator := sdk.KVStorePrefixIterator(store, types.GetUBDsByValIndexKey(valAddr))
|
2018-09-13 14:23:44 -07:00
|
|
|
defer iterator.Close()
|
|
|
|
|
|
|
|
for ; iterator.Valid(); iterator.Next() {
|
2019-05-28 01:44:04 -07:00
|
|
|
key := types.GetUBDKeyFromValIndexKey(iterator.Key())
|
2018-07-03 21:29:02 -07:00
|
|
|
value := store.Get(key)
|
2019-01-07 12:42:52 -08:00
|
|
|
ubd := types.MustUnmarshalUBD(k.cdc, value)
|
2018-07-03 21:29:02 -07:00
|
|
|
ubds = append(ubds, ubd)
|
2018-06-29 20:34:55 -07:00
|
|
|
}
|
2018-07-03 21:29:02 -07:00
|
|
|
return ubds
|
2018-06-29 20:34:55 -07:00
|
|
|
}
|
|
|
|
|
2018-07-17 22:37:38 -07:00
|
|
|
// iterate through all of the unbonding delegations
|
|
|
|
func (k Keeper) IterateUnbondingDelegations(ctx sdk.Context, fn func(index int64, ubd types.UnbondingDelegation) (stop bool)) {
|
|
|
|
store := ctx.KVStore(k.storeKey)
|
2019-05-28 01:44:04 -07:00
|
|
|
iterator := sdk.KVStorePrefixIterator(store, types.UnbondingDelegationKey)
|
2018-09-13 14:23:44 -07:00
|
|
|
defer iterator.Close()
|
|
|
|
|
|
|
|
for i := int64(0); iterator.Valid(); iterator.Next() {
|
2019-01-07 12:42:52 -08:00
|
|
|
ubd := types.MustUnmarshalUBD(k.cdc, iterator.Value())
|
2018-09-13 14:23:44 -07:00
|
|
|
if stop := fn(i, ubd); stop {
|
2018-07-17 22:37:38 -07:00
|
|
|
break
|
|
|
|
}
|
|
|
|
i++
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-07 17:41:23 -08:00
|
|
|
// HasMaxUnbondingDelegationEntries - check if unbonding delegation has maximum number of entries
|
2019-01-24 14:44:31 -08:00
|
|
|
func (k Keeper) HasMaxUnbondingDelegationEntries(ctx sdk.Context,
|
|
|
|
delegatorAddr sdk.AccAddress, validatorAddr sdk.ValAddress) bool {
|
|
|
|
|
|
|
|
ubd, found := k.GetUnbondingDelegation(ctx, delegatorAddr, validatorAddr)
|
|
|
|
if !found {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
return len(ubd.Entries) >= int(k.MaxEntries(ctx))
|
|
|
|
}
|
|
|
|
|
2018-06-26 19:00:12 -07:00
|
|
|
// set the unbonding delegation and associated index
|
|
|
|
func (k Keeper) SetUnbondingDelegation(ctx sdk.Context, ubd types.UnbondingDelegation) {
|
|
|
|
store := ctx.KVStore(k.storeKey)
|
2018-07-04 14:07:06 -07:00
|
|
|
bz := types.MustMarshalUBD(k.cdc, ubd)
|
2019-05-28 01:44:04 -07:00
|
|
|
key := types.GetUBDKey(ubd.DelegatorAddress, ubd.ValidatorAddress)
|
2018-07-03 21:29:02 -07:00
|
|
|
store.Set(key, bz)
|
2019-05-28 01:44:04 -07:00
|
|
|
store.Set(types.GetUBDByValIndexKey(ubd.DelegatorAddress, ubd.ValidatorAddress), []byte{}) // index, store empty bytes
|
2018-06-26 19:00:12 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// remove the unbonding delegation object and associated index
|
|
|
|
func (k Keeper) RemoveUnbondingDelegation(ctx sdk.Context, ubd types.UnbondingDelegation) {
|
|
|
|
store := ctx.KVStore(k.storeKey)
|
2019-05-28 01:44:04 -07:00
|
|
|
key := types.GetUBDKey(ubd.DelegatorAddress, ubd.ValidatorAddress)
|
2018-07-03 21:29:02 -07:00
|
|
|
store.Delete(key)
|
2019-05-28 01:44:04 -07:00
|
|
|
store.Delete(types.GetUBDByValIndexKey(ubd.DelegatorAddress, ubd.ValidatorAddress))
|
2018-06-26 19:00:12 -07:00
|
|
|
}
|
|
|
|
|
2019-01-16 02:35:18 -08:00
|
|
|
// SetUnbondingDelegationEntry adds an entry to the unbonding delegation at
|
|
|
|
// the given addresses. It creates the unbonding delegation if it does not exist
|
|
|
|
func (k Keeper) SetUnbondingDelegationEntry(ctx sdk.Context,
|
|
|
|
delegatorAddr sdk.AccAddress, validatorAddr sdk.ValAddress,
|
2019-02-08 13:28:53 -08:00
|
|
|
creationHeight int64, minTime time.Time, balance sdk.Int) types.UnbondingDelegation {
|
2019-01-16 02:35:18 -08:00
|
|
|
|
|
|
|
ubd, found := k.GetUnbondingDelegation(ctx, delegatorAddr, validatorAddr)
|
|
|
|
if found {
|
|
|
|
ubd.AddEntry(creationHeight, minTime, balance)
|
|
|
|
} else {
|
|
|
|
ubd = types.NewUnbondingDelegation(delegatorAddr, validatorAddr, creationHeight, minTime, balance)
|
|
|
|
}
|
|
|
|
k.SetUnbondingDelegation(ctx, ubd)
|
|
|
|
return ubd
|
|
|
|
}
|
|
|
|
|
|
|
|
// unbonding delegation queue timeslice operations
|
|
|
|
|
|
|
|
// gets a specific unbonding queue timeslice. A timeslice is a slice of DVPairs
|
|
|
|
// corresponding to unbonding delegations that expire at a certain time.
|
|
|
|
func (k Keeper) GetUBDQueueTimeSlice(ctx sdk.Context, timestamp time.Time) (dvPairs []types.DVPair) {
|
2018-10-07 21:43:47 -07:00
|
|
|
store := ctx.KVStore(k.storeKey)
|
2019-05-28 01:44:04 -07:00
|
|
|
bz := store.Get(types.GetUnbondingDelegationTimeKey(timestamp))
|
2018-10-07 21:43:47 -07:00
|
|
|
if bz == nil {
|
|
|
|
return []types.DVPair{}
|
|
|
|
}
|
2018-11-04 18:28:38 -08:00
|
|
|
k.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &dvPairs)
|
2018-10-07 21:43:47 -07:00
|
|
|
return dvPairs
|
|
|
|
}
|
|
|
|
|
|
|
|
// Sets a specific unbonding queue timeslice.
|
2019-01-16 02:35:18 -08:00
|
|
|
func (k Keeper) SetUBDQueueTimeSlice(ctx sdk.Context, timestamp time.Time, keys []types.DVPair) {
|
2018-10-07 21:43:47 -07:00
|
|
|
store := ctx.KVStore(k.storeKey)
|
2018-11-04 18:28:38 -08:00
|
|
|
bz := k.cdc.MustMarshalBinaryLengthPrefixed(keys)
|
2019-05-28 01:44:04 -07:00
|
|
|
store.Set(types.GetUnbondingDelegationTimeKey(timestamp), bz)
|
2018-10-07 21:43:47 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// Insert an unbonding delegation to the appropriate timeslice in the unbonding queue
|
2019-01-16 02:35:18 -08:00
|
|
|
func (k Keeper) InsertUBDQueue(ctx sdk.Context, ubd types.UnbondingDelegation,
|
|
|
|
completionTime time.Time) {
|
|
|
|
|
|
|
|
timeSlice := k.GetUBDQueueTimeSlice(ctx, completionTime)
|
2019-02-25 07:16:52 -08:00
|
|
|
dvPair := types.DVPair{DelegatorAddress: ubd.DelegatorAddress, ValidatorAddress: ubd.ValidatorAddress}
|
2018-10-07 21:43:47 -07:00
|
|
|
if len(timeSlice) == 0 {
|
2019-01-16 02:35:18 -08:00
|
|
|
k.SetUBDQueueTimeSlice(ctx, completionTime, []types.DVPair{dvPair})
|
2018-10-07 21:43:47 -07:00
|
|
|
} else {
|
|
|
|
timeSlice = append(timeSlice, dvPair)
|
2019-01-16 02:35:18 -08:00
|
|
|
k.SetUBDQueueTimeSlice(ctx, completionTime, timeSlice)
|
2018-10-07 21:43:47 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns all the unbonding queue timeslices from time 0 until endTime
|
2019-01-16 02:35:18 -08:00
|
|
|
func (k Keeper) UBDQueueIterator(ctx sdk.Context, endTime time.Time) sdk.Iterator {
|
2018-10-07 21:43:47 -07:00
|
|
|
store := ctx.KVStore(k.storeKey)
|
2019-05-28 01:44:04 -07:00
|
|
|
return store.Iterator(types.UnbondingQueueKey,
|
|
|
|
sdk.InclusiveEndBytes(types.GetUnbondingDelegationTimeKey(endTime)))
|
2018-10-07 21:43:47 -07:00
|
|
|
}
|
|
|
|
|
2019-01-16 02:35:18 -08:00
|
|
|
// Returns a concatenated list of all the timeslices inclusively previous to
|
|
|
|
// currTime, and deletes the timeslices from the queue
|
|
|
|
func (k Keeper) DequeueAllMatureUBDQueue(ctx sdk.Context,
|
|
|
|
currTime time.Time) (matureUnbonds []types.DVPair) {
|
|
|
|
|
2018-10-07 21:43:47 -07:00
|
|
|
store := ctx.KVStore(k.storeKey)
|
|
|
|
// gets an iterator for all timeslices from time 0 until the current Blockheader time
|
2019-01-16 02:35:18 -08:00
|
|
|
unbondingTimesliceIterator := k.UBDQueueIterator(ctx, ctx.BlockHeader().Time)
|
2018-10-07 21:43:47 -07:00
|
|
|
for ; unbondingTimesliceIterator.Valid(); unbondingTimesliceIterator.Next() {
|
|
|
|
timeslice := []types.DVPair{}
|
2019-01-16 02:35:18 -08:00
|
|
|
value := unbondingTimesliceIterator.Value()
|
|
|
|
k.cdc.MustUnmarshalBinaryLengthPrefixed(value, ×lice)
|
2018-10-07 21:43:47 -07:00
|
|
|
matureUnbonds = append(matureUnbonds, timeslice...)
|
|
|
|
store.Delete(unbondingTimesliceIterator.Key())
|
|
|
|
}
|
|
|
|
return matureUnbonds
|
|
|
|
}
|
|
|
|
|
2018-09-13 14:23:44 -07:00
|
|
|
// return a given amount of all the delegator redelegations
|
|
|
|
func (k Keeper) GetRedelegations(ctx sdk.Context, delegator sdk.AccAddress,
|
|
|
|
maxRetrieve uint16) (redelegations []types.Redelegation) {
|
|
|
|
redelegations = make([]types.Redelegation, maxRetrieve)
|
|
|
|
|
|
|
|
store := ctx.KVStore(k.storeKey)
|
2019-05-28 01:44:04 -07:00
|
|
|
delegatorPrefixKey := types.GetREDsKey(delegator)
|
2018-09-13 14:23:44 -07:00
|
|
|
iterator := sdk.KVStorePrefixIterator(store, delegatorPrefixKey)
|
|
|
|
defer iterator.Close()
|
|
|
|
|
|
|
|
i := 0
|
|
|
|
for ; iterator.Valid() && i < int(maxRetrieve); iterator.Next() {
|
2019-01-07 12:42:52 -08:00
|
|
|
redelegation := types.MustUnmarshalRED(k.cdc, iterator.Value())
|
2018-09-13 14:23:44 -07:00
|
|
|
redelegations[i] = redelegation
|
|
|
|
i++
|
|
|
|
}
|
|
|
|
return redelegations[:i] // trim if the array length < maxRetrieve
|
|
|
|
}
|
|
|
|
|
|
|
|
// return a redelegation
|
2018-06-26 19:00:12 -07:00
|
|
|
func (k Keeper) GetRedelegation(ctx sdk.Context,
|
2018-08-30 21:06:44 -07:00
|
|
|
delAddr sdk.AccAddress, valSrcAddr, valDstAddr sdk.ValAddress) (red types.Redelegation, found bool) {
|
2018-06-26 19:00:12 -07:00
|
|
|
|
|
|
|
store := ctx.KVStore(k.storeKey)
|
2019-05-28 01:44:04 -07:00
|
|
|
key := types.GetREDKey(delAddr, valSrcAddr, valDstAddr)
|
2018-07-03 21:29:02 -07:00
|
|
|
value := store.Get(key)
|
|
|
|
if value == nil {
|
2018-06-26 19:00:12 -07:00
|
|
|
return red, false
|
|
|
|
}
|
|
|
|
|
2019-01-07 12:42:52 -08:00
|
|
|
red = types.MustUnmarshalRED(k.cdc, value)
|
2018-06-26 19:00:12 -07:00
|
|
|
return red, true
|
|
|
|
}
|
|
|
|
|
2018-09-13 14:23:44 -07:00
|
|
|
// return all redelegations from a particular validator
|
2018-08-30 21:06:44 -07:00
|
|
|
func (k Keeper) GetRedelegationsFromValidator(ctx sdk.Context, valAddr sdk.ValAddress) (reds []types.Redelegation) {
|
2018-06-29 20:34:55 -07:00
|
|
|
store := ctx.KVStore(k.storeKey)
|
2019-05-28 01:44:04 -07:00
|
|
|
iterator := sdk.KVStorePrefixIterator(store, types.GetREDsFromValSrcIndexKey(valAddr))
|
2018-09-13 14:23:44 -07:00
|
|
|
defer iterator.Close()
|
|
|
|
|
|
|
|
for ; iterator.Valid(); iterator.Next() {
|
2019-05-28 01:44:04 -07:00
|
|
|
key := types.GetREDKeyFromValSrcIndexKey(iterator.Key())
|
2018-07-03 21:29:02 -07:00
|
|
|
value := store.Get(key)
|
2019-01-07 12:42:52 -08:00
|
|
|
red := types.MustUnmarshalRED(k.cdc, value)
|
2018-07-03 21:29:02 -07:00
|
|
|
reds = append(reds, red)
|
2018-06-29 20:34:55 -07:00
|
|
|
}
|
2018-07-03 21:29:02 -07:00
|
|
|
return reds
|
2018-06-29 20:34:55 -07:00
|
|
|
}
|
|
|
|
|
2018-09-13 14:23:44 -07:00
|
|
|
// check if validator is receiving a redelegation
|
2018-06-26 19:00:12 -07:00
|
|
|
func (k Keeper) HasReceivingRedelegation(ctx sdk.Context,
|
2018-08-30 21:06:44 -07:00
|
|
|
delAddr sdk.AccAddress, valDstAddr sdk.ValAddress) bool {
|
2018-06-26 19:00:12 -07:00
|
|
|
|
|
|
|
store := ctx.KVStore(k.storeKey)
|
2019-05-28 01:44:04 -07:00
|
|
|
prefix := types.GetREDsByDelToValDstIndexKey(delAddr, valDstAddr)
|
2018-09-13 14:23:44 -07:00
|
|
|
iterator := sdk.KVStorePrefixIterator(store, prefix)
|
|
|
|
defer iterator.Close()
|
2018-06-26 19:00:12 -07:00
|
|
|
|
2019-02-08 12:45:23 -08:00
|
|
|
return iterator.Valid()
|
2018-06-26 19:00:12 -07:00
|
|
|
}
|
|
|
|
|
2019-01-24 14:44:31 -08:00
|
|
|
// HasMaxRedelegationEntries - redelegation has maximum number of entries
|
|
|
|
func (k Keeper) HasMaxRedelegationEntries(ctx sdk.Context,
|
|
|
|
delegatorAddr sdk.AccAddress, validatorSrcAddr,
|
|
|
|
validatorDstAddr sdk.ValAddress) bool {
|
|
|
|
|
|
|
|
red, found := k.GetRedelegation(ctx, delegatorAddr, validatorSrcAddr, validatorDstAddr)
|
|
|
|
if !found {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
return len(red.Entries) >= int(k.MaxEntries(ctx))
|
|
|
|
}
|
|
|
|
|
2018-06-26 19:00:12 -07:00
|
|
|
// set a redelegation and associated index
|
|
|
|
func (k Keeper) SetRedelegation(ctx sdk.Context, red types.Redelegation) {
|
|
|
|
store := ctx.KVStore(k.storeKey)
|
2018-07-04 14:07:06 -07:00
|
|
|
bz := types.MustMarshalRED(k.cdc, red)
|
2019-05-28 01:44:04 -07:00
|
|
|
key := types.GetREDKey(red.DelegatorAddress, red.ValidatorSrcAddress, red.ValidatorDstAddress)
|
2018-07-03 21:29:02 -07:00
|
|
|
store.Set(key, bz)
|
2019-05-28 01:44:04 -07:00
|
|
|
store.Set(types.GetREDByValSrcIndexKey(red.DelegatorAddress, red.ValidatorSrcAddress, red.ValidatorDstAddress), []byte{})
|
|
|
|
store.Set(types.GetREDByValDstIndexKey(red.DelegatorAddress, red.ValidatorSrcAddress, red.ValidatorDstAddress), []byte{})
|
2018-06-26 19:00:12 -07:00
|
|
|
}
|
|
|
|
|
2019-01-16 02:35:18 -08:00
|
|
|
// SetUnbondingDelegationEntry adds an entry to the unbonding delegation at
|
|
|
|
// the given addresses. It creates the unbonding delegation if it does not exist
|
|
|
|
func (k Keeper) SetRedelegationEntry(ctx sdk.Context,
|
|
|
|
delegatorAddr sdk.AccAddress, validatorSrcAddr,
|
|
|
|
validatorDstAddr sdk.ValAddress, creationHeight int64,
|
2019-02-08 13:28:53 -08:00
|
|
|
minTime time.Time, balance sdk.Int,
|
2019-01-16 02:35:18 -08:00
|
|
|
sharesSrc, sharesDst sdk.Dec) types.Redelegation {
|
|
|
|
|
|
|
|
red, found := k.GetRedelegation(ctx, delegatorAddr, validatorSrcAddr, validatorDstAddr)
|
|
|
|
if found {
|
2019-02-07 17:41:23 -08:00
|
|
|
red.AddEntry(creationHeight, minTime, balance, sharesDst)
|
2019-01-16 02:35:18 -08:00
|
|
|
} else {
|
|
|
|
red = types.NewRedelegation(delegatorAddr, validatorSrcAddr,
|
2019-02-07 17:41:23 -08:00
|
|
|
validatorDstAddr, creationHeight, minTime, balance, sharesDst)
|
2019-01-16 02:35:18 -08:00
|
|
|
}
|
|
|
|
k.SetRedelegation(ctx, red)
|
|
|
|
return red
|
|
|
|
}
|
|
|
|
|
2018-11-08 16:28:28 -08:00
|
|
|
// iterate through all redelegations
|
|
|
|
func (k Keeper) IterateRedelegations(ctx sdk.Context, fn func(index int64, red types.Redelegation) (stop bool)) {
|
|
|
|
store := ctx.KVStore(k.storeKey)
|
2019-05-28 01:44:04 -07:00
|
|
|
iterator := sdk.KVStorePrefixIterator(store, types.RedelegationKey)
|
2018-11-08 16:28:28 -08:00
|
|
|
defer iterator.Close()
|
|
|
|
|
|
|
|
for i := int64(0); iterator.Valid(); iterator.Next() {
|
2019-01-07 12:42:52 -08:00
|
|
|
red := types.MustUnmarshalRED(k.cdc, iterator.Value())
|
2018-11-08 16:28:28 -08:00
|
|
|
if stop := fn(i, red); stop {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
i++
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-26 19:00:12 -07:00
|
|
|
// remove a redelegation object and associated index
|
|
|
|
func (k Keeper) RemoveRedelegation(ctx sdk.Context, red types.Redelegation) {
|
|
|
|
store := ctx.KVStore(k.storeKey)
|
2019-05-28 01:44:04 -07:00
|
|
|
redKey := types.GetREDKey(red.DelegatorAddress, red.ValidatorSrcAddress, red.ValidatorDstAddress)
|
2018-06-26 19:00:12 -07:00
|
|
|
store.Delete(redKey)
|
2019-05-28 01:44:04 -07:00
|
|
|
store.Delete(types.GetREDByValSrcIndexKey(red.DelegatorAddress, red.ValidatorSrcAddress, red.ValidatorDstAddress))
|
|
|
|
store.Delete(types.GetREDByValDstIndexKey(red.DelegatorAddress, red.ValidatorSrcAddress, red.ValidatorDstAddress))
|
2018-06-26 19:00:12 -07:00
|
|
|
}
|
|
|
|
|
2019-01-16 02:35:18 -08:00
|
|
|
// redelegation queue timeslice operations
|
|
|
|
|
2018-10-07 21:43:47 -07:00
|
|
|
// Gets a specific redelegation queue timeslice. A timeslice is a slice of DVVTriplets corresponding to redelegations
|
|
|
|
// that expire at a certain time.
|
|
|
|
func (k Keeper) GetRedelegationQueueTimeSlice(ctx sdk.Context, timestamp time.Time) (dvvTriplets []types.DVVTriplet) {
|
|
|
|
store := ctx.KVStore(k.storeKey)
|
2019-05-28 01:44:04 -07:00
|
|
|
bz := store.Get(types.GetRedelegationTimeKey(timestamp))
|
2018-10-07 21:43:47 -07:00
|
|
|
if bz == nil {
|
|
|
|
return []types.DVVTriplet{}
|
|
|
|
}
|
2018-11-04 18:28:38 -08:00
|
|
|
k.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &dvvTriplets)
|
2018-10-07 21:43:47 -07:00
|
|
|
return dvvTriplets
|
|
|
|
}
|
|
|
|
|
|
|
|
// Sets a specific redelegation queue timeslice.
|
|
|
|
func (k Keeper) SetRedelegationQueueTimeSlice(ctx sdk.Context, timestamp time.Time, keys []types.DVVTriplet) {
|
|
|
|
store := ctx.KVStore(k.storeKey)
|
2018-11-04 18:28:38 -08:00
|
|
|
bz := k.cdc.MustMarshalBinaryLengthPrefixed(keys)
|
2019-05-28 01:44:04 -07:00
|
|
|
store.Set(types.GetRedelegationTimeKey(timestamp), bz)
|
2018-10-07 21:43:47 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// Insert an redelegation delegation to the appropriate timeslice in the redelegation queue
|
2019-01-16 02:35:18 -08:00
|
|
|
func (k Keeper) InsertRedelegationQueue(ctx sdk.Context, red types.Redelegation,
|
|
|
|
completionTime time.Time) {
|
|
|
|
|
|
|
|
timeSlice := k.GetRedelegationQueueTimeSlice(ctx, completionTime)
|
2019-02-08 12:45:23 -08:00
|
|
|
dvvTriplet := types.DVVTriplet{
|
2019-02-25 07:16:52 -08:00
|
|
|
DelegatorAddress: red.DelegatorAddress,
|
|
|
|
ValidatorSrcAddress: red.ValidatorSrcAddress,
|
|
|
|
ValidatorDstAddress: red.ValidatorDstAddress}
|
2019-02-08 12:45:23 -08:00
|
|
|
|
2018-10-07 21:43:47 -07:00
|
|
|
if len(timeSlice) == 0 {
|
2019-01-16 02:35:18 -08:00
|
|
|
k.SetRedelegationQueueTimeSlice(ctx, completionTime, []types.DVVTriplet{dvvTriplet})
|
2018-10-07 21:43:47 -07:00
|
|
|
} else {
|
|
|
|
timeSlice = append(timeSlice, dvvTriplet)
|
2019-01-16 02:35:18 -08:00
|
|
|
k.SetRedelegationQueueTimeSlice(ctx, completionTime, timeSlice)
|
2018-10-07 21:43:47 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns all the redelegation queue timeslices from time 0 until endTime
|
|
|
|
func (k Keeper) RedelegationQueueIterator(ctx sdk.Context, endTime time.Time) sdk.Iterator {
|
|
|
|
store := ctx.KVStore(k.storeKey)
|
2019-05-28 01:44:04 -07:00
|
|
|
return store.Iterator(types.RedelegationQueueKey, sdk.InclusiveEndBytes(types.GetRedelegationTimeKey(endTime)))
|
2018-10-07 21:43:47 -07:00
|
|
|
}
|
|
|
|
|
2019-01-16 02:35:18 -08:00
|
|
|
// Returns a concatenated list of all the timeslices inclusively previous to
|
|
|
|
// currTime, and deletes the timeslices from the queue
|
2018-10-07 21:43:47 -07:00
|
|
|
func (k Keeper) DequeueAllMatureRedelegationQueue(ctx sdk.Context, currTime time.Time) (matureRedelegations []types.DVVTriplet) {
|
|
|
|
store := ctx.KVStore(k.storeKey)
|
|
|
|
// gets an iterator for all timeslices from time 0 until the current Blockheader time
|
|
|
|
redelegationTimesliceIterator := k.RedelegationQueueIterator(ctx, ctx.BlockHeader().Time)
|
|
|
|
for ; redelegationTimesliceIterator.Valid(); redelegationTimesliceIterator.Next() {
|
|
|
|
timeslice := []types.DVVTriplet{}
|
2019-01-16 02:35:18 -08:00
|
|
|
value := redelegationTimesliceIterator.Value()
|
|
|
|
k.cdc.MustUnmarshalBinaryLengthPrefixed(value, ×lice)
|
2018-10-07 21:43:47 -07:00
|
|
|
matureRedelegations = append(matureRedelegations, timeslice...)
|
|
|
|
store.Delete(redelegationTimesliceIterator.Key())
|
|
|
|
}
|
|
|
|
return matureRedelegations
|
|
|
|
}
|
|
|
|
|
2018-07-12 16:38:35 -07:00
|
|
|
// Perform a delegation, set/update everything necessary within the store.
|
2019-02-08 13:28:53 -08:00
|
|
|
func (k Keeper) Delegate(ctx sdk.Context, delAddr sdk.AccAddress, bondAmt sdk.Int,
|
2018-08-14 17:15:02 -07:00
|
|
|
validator types.Validator, subtractAccount bool) (newShares sdk.Dec, err sdk.Error) {
|
2018-06-26 19:00:12 -07:00
|
|
|
|
2018-11-04 22:11:03 -08:00
|
|
|
// In some situations, the exchange rate becomes invalid, e.g. if
|
2019-02-07 17:41:23 -08:00
|
|
|
// Validator loses all tokens due to slashing. In this case,
|
2018-11-04 22:11:03 -08:00
|
|
|
// make all future delegations invalid.
|
2019-03-06 10:54:12 -08:00
|
|
|
if validator.InvalidExRate() {
|
2018-11-04 22:11:03 -08:00
|
|
|
return sdk.ZeroDec(), types.ErrDelegatorShareExRateInvalid(k.Codespace())
|
|
|
|
}
|
|
|
|
|
2019-02-07 17:41:23 -08:00
|
|
|
// Get or create the delegation object
|
2019-02-25 07:16:52 -08:00
|
|
|
delegation, found := k.GetDelegation(ctx, delAddr, validator.OperatorAddress)
|
2018-06-26 19:00:12 -07:00
|
|
|
if !found {
|
2019-02-25 07:16:52 -08:00
|
|
|
delegation = types.NewDelegation(delAddr, validator.OperatorAddress, sdk.ZeroDec())
|
2018-06-26 19:00:12 -07:00
|
|
|
}
|
|
|
|
|
2018-10-19 13:26:28 -07:00
|
|
|
// call the appropriate hook if present
|
|
|
|
if found {
|
2019-02-25 07:16:52 -08:00
|
|
|
k.BeforeDelegationSharesModified(ctx, delAddr, validator.OperatorAddress)
|
2018-10-19 13:26:28 -07:00
|
|
|
} else {
|
2019-02-25 07:16:52 -08:00
|
|
|
k.BeforeDelegationCreated(ctx, delAddr, validator.OperatorAddress)
|
2018-10-19 13:26:28 -07:00
|
|
|
}
|
|
|
|
|
2018-07-12 16:38:35 -07:00
|
|
|
if subtractAccount {
|
2019-02-25 07:16:52 -08:00
|
|
|
_, err := k.bankKeeper.DelegateCoins(ctx, delegation.DelegatorAddress, sdk.Coins{sdk.NewCoin(k.GetParams(ctx).BondDenom, bondAmt)})
|
2018-07-12 16:38:35 -07:00
|
|
|
if err != nil {
|
2019-01-14 08:11:24 -08:00
|
|
|
return sdk.Dec{}, err
|
2018-07-12 16:38:35 -07:00
|
|
|
}
|
2018-06-26 19:00:12 -07:00
|
|
|
}
|
2018-07-12 16:38:35 -07:00
|
|
|
|
2019-02-08 13:28:53 -08:00
|
|
|
validator, newShares = k.AddValidatorTokensAndShares(ctx, validator, bondAmt)
|
2018-06-26 19:00:12 -07:00
|
|
|
|
2018-10-03 09:37:06 -07:00
|
|
|
// Update delegation
|
|
|
|
delegation.Shares = delegation.Shares.Add(newShares)
|
2018-06-26 19:00:12 -07:00
|
|
|
k.SetDelegation(ctx, delegation)
|
2019-02-07 17:41:23 -08:00
|
|
|
|
|
|
|
// Call the after-modification hook
|
2019-02-25 07:16:52 -08:00
|
|
|
k.AfterDelegationModified(ctx, delegation.DelegatorAddress, delegation.ValidatorAddress)
|
2018-10-19 13:26:28 -07:00
|
|
|
|
2018-10-03 09:37:06 -07:00
|
|
|
return newShares, nil
|
2018-06-26 19:00:12 -07:00
|
|
|
}
|
|
|
|
|
2019-02-07 17:41:23 -08:00
|
|
|
// unbond a particular delegation and perform associated store operations
|
2018-08-30 21:06:44 -07:00
|
|
|
func (k Keeper) unbond(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress,
|
2019-01-02 12:29:47 -08:00
|
|
|
shares sdk.Dec) (amount sdk.Int, err sdk.Error) {
|
2018-06-26 19:00:12 -07:00
|
|
|
|
2019-02-07 17:41:23 -08:00
|
|
|
// check if a delegation object exists in the store
|
2018-08-30 21:06:44 -07:00
|
|
|
delegation, found := k.GetDelegation(ctx, delAddr, valAddr)
|
2018-06-26 19:00:12 -07:00
|
|
|
if !found {
|
2019-01-02 12:29:47 -08:00
|
|
|
return amount, types.ErrNoDelegatorForAddress(k.Codespace())
|
2018-06-26 19:00:12 -07:00
|
|
|
}
|
|
|
|
|
2019-02-07 17:41:23 -08:00
|
|
|
// call the before-delegation-modified hook
|
2019-01-08 21:28:46 -08:00
|
|
|
k.BeforeDelegationSharesModified(ctx, delAddr, valAddr)
|
2018-10-05 17:32:06 -07:00
|
|
|
|
2019-02-07 17:41:23 -08:00
|
|
|
// ensure that we have enough shares to remove
|
2018-06-26 19:00:12 -07:00
|
|
|
if delegation.Shares.LT(shares) {
|
2019-01-02 12:29:47 -08:00
|
|
|
return amount, types.ErrNotEnoughDelegationShares(k.Codespace(), delegation.Shares.String())
|
2018-06-26 19:00:12 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// get validator
|
2018-08-30 21:06:44 -07:00
|
|
|
validator, found := k.GetValidator(ctx, valAddr)
|
2018-06-26 19:00:12 -07:00
|
|
|
if !found {
|
2019-01-02 12:29:47 -08:00
|
|
|
return amount, types.ErrNoValidatorFound(k.Codespace())
|
2018-06-26 19:00:12 -07:00
|
|
|
}
|
|
|
|
|
2019-02-07 17:41:23 -08:00
|
|
|
// subtract shares from delegation
|
2018-06-26 19:00:12 -07:00
|
|
|
delegation.Shares = delegation.Shares.Sub(shares)
|
|
|
|
|
2019-02-25 07:16:52 -08:00
|
|
|
isValidatorOperator := bytes.Equal(delegation.DelegatorAddress, validator.OperatorAddress)
|
2019-02-08 12:44:19 -08:00
|
|
|
|
|
|
|
// if the delegation is the operator of the validator and undelegating will decrease the validator's self delegation below their minimum
|
|
|
|
// trigger a jail validator
|
2019-03-06 10:54:12 -08:00
|
|
|
if isValidatorOperator && !validator.Jailed &&
|
2019-03-25 14:13:02 -07:00
|
|
|
validator.TokensFromShares(delegation.Shares).TruncateInt().LT(validator.MinSelfDelegation) {
|
2019-03-06 10:54:12 -08:00
|
|
|
|
2019-02-08 12:44:19 -08:00
|
|
|
k.jailValidator(ctx, validator)
|
2019-02-25 07:16:52 -08:00
|
|
|
validator = k.mustGetValidator(ctx, validator.OperatorAddress)
|
2019-02-08 12:44:19 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
// remove the delegation
|
2018-06-26 19:00:12 -07:00
|
|
|
if delegation.Shares.IsZero() {
|
|
|
|
k.RemoveDelegation(ctx, delegation)
|
|
|
|
} else {
|
|
|
|
k.SetDelegation(ctx, delegation)
|
2019-02-07 17:41:23 -08:00
|
|
|
// call the after delegation modification hook
|
2019-02-25 07:16:52 -08:00
|
|
|
k.AfterDelegationModified(ctx, delegation.DelegatorAddress, delegation.ValidatorAddress)
|
2018-06-26 19:00:12 -07:00
|
|
|
}
|
|
|
|
|
2019-02-08 15:57:33 -08:00
|
|
|
// remove the shares and coins from the validator
|
2018-10-03 09:37:06 -07:00
|
|
|
validator, amount = k.RemoveValidatorTokensAndShares(ctx, validator, shares)
|
2018-06-26 19:00:12 -07:00
|
|
|
|
2019-06-04 15:06:58 -07:00
|
|
|
if validator.DelegatorShares.IsZero() && validator.IsUnbonded() {
|
2018-10-18 12:58:18 -07:00
|
|
|
// if not unbonded, we must instead remove validator in EndBlocker once it finishes its unbonding period
|
2019-02-25 07:16:52 -08:00
|
|
|
k.RemoveValidator(ctx, validator.OperatorAddress)
|
2018-06-26 19:00:12 -07:00
|
|
|
}
|
|
|
|
|
2018-08-31 12:21:12 -07:00
|
|
|
return amount, nil
|
2018-06-26 19:00:12 -07:00
|
|
|
}
|
|
|
|
|
2019-05-31 08:14:40 -07:00
|
|
|
// getBeginInfo returns the completion time and height of a redelegation, along
|
|
|
|
// with a boolean signaling if the redelegation is complete based on the source
|
|
|
|
// validator.
|
|
|
|
func (k Keeper) getBeginInfo(
|
|
|
|
ctx sdk.Context, valSrcAddr sdk.ValAddress,
|
|
|
|
) (completionTime time.Time, height int64, completeNow bool) {
|
2018-08-24 18:47:31 -07:00
|
|
|
|
2018-08-31 18:38:50 -07:00
|
|
|
validator, found := k.GetValidator(ctx, valSrcAddr)
|
2018-10-07 21:43:47 -07:00
|
|
|
|
2019-05-31 08:14:40 -07:00
|
|
|
// TODO: When would the validator not be found?
|
2018-08-24 18:47:31 -07:00
|
|
|
switch {
|
2019-06-04 15:06:58 -07:00
|
|
|
case !found || validator.IsBonded():
|
|
|
|
|
2018-08-29 15:44:40 -07:00
|
|
|
// the longest wait - just unbonding period from now
|
2019-01-16 02:35:18 -08:00
|
|
|
completionTime = ctx.BlockHeader().Time.Add(k.UnbondingTime(ctx))
|
2018-10-14 17:37:06 -07:00
|
|
|
height = ctx.BlockHeight()
|
2019-01-16 02:35:18 -08:00
|
|
|
return completionTime, height, false
|
2018-08-24 18:47:31 -07:00
|
|
|
|
2019-06-04 15:06:58 -07:00
|
|
|
case validator.IsUnbonded():
|
2019-01-16 02:35:18 -08:00
|
|
|
return completionTime, height, true
|
2018-08-29 15:44:40 -07:00
|
|
|
|
2019-06-04 15:06:58 -07:00
|
|
|
case validator.IsUnbonding():
|
2019-05-31 08:14:40 -07:00
|
|
|
return validator.UnbondingCompletionTime, validator.UnbondingHeight, false
|
2018-08-24 18:47:31 -07:00
|
|
|
|
|
|
|
default:
|
2019-05-31 08:14:40 -07:00
|
|
|
panic(fmt.Sprintf("unknown validator status: %s", validator.Status))
|
2018-08-24 18:47:31 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-31 08:14:40 -07:00
|
|
|
// Undelegate unbonds an amount of delegator shares from a given validator. It
|
|
|
|
// will verify that the unbonding entries between the delegator and validator
|
|
|
|
// are not exceeded and unbond the staked tokens (based on shares) by creating
|
|
|
|
// an unbonding object and inserting it into the unbonding queue which will be
|
|
|
|
// processed during the staking EndBlocker.
|
|
|
|
func (k Keeper) Undelegate(
|
|
|
|
ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress, sharesAmount sdk.Dec,
|
|
|
|
) (time.Time, sdk.Error) {
|
2018-10-07 21:43:47 -07:00
|
|
|
|
2018-08-30 21:06:44 -07:00
|
|
|
returnAmount, err := k.unbond(ctx, delAddr, valAddr, sharesAmount)
|
2018-06-26 19:00:12 -07:00
|
|
|
if err != nil {
|
2019-05-31 08:14:40 -07:00
|
|
|
return time.Time{}, err
|
2018-08-24 18:47:31 -07:00
|
|
|
}
|
|
|
|
|
2019-01-24 14:44:31 -08:00
|
|
|
if k.HasMaxUnbondingDelegationEntries(ctx, delAddr, valAddr) {
|
|
|
|
return time.Time{}, types.ErrMaxUnbondingDelegationEntries(k.Codespace())
|
|
|
|
}
|
|
|
|
|
2019-05-31 08:14:40 -07:00
|
|
|
completionTime := ctx.BlockHeader().Time.Add(k.UnbondingTime(ctx))
|
|
|
|
ubd := k.SetUnbondingDelegationEntry(ctx, delAddr, valAddr, ctx.BlockHeight(), completionTime, returnAmount)
|
2019-01-16 02:35:18 -08:00
|
|
|
k.InsertUBDQueue(ctx, ubd, completionTime)
|
2019-05-31 08:14:40 -07:00
|
|
|
|
2019-01-16 02:35:18 -08:00
|
|
|
return completionTime, nil
|
2018-06-26 19:00:12 -07:00
|
|
|
}
|
|
|
|
|
2019-01-16 02:35:18 -08:00
|
|
|
// CompleteUnbonding completes the unbonding of all mature entries in the
|
|
|
|
// retrieved unbonding delegation object.
|
|
|
|
func (k Keeper) CompleteUnbonding(ctx sdk.Context, delAddr sdk.AccAddress,
|
|
|
|
valAddr sdk.ValAddress) sdk.Error {
|
2018-06-26 19:00:12 -07:00
|
|
|
|
2018-08-30 21:06:44 -07:00
|
|
|
ubd, found := k.GetUnbondingDelegation(ctx, delAddr, valAddr)
|
2018-06-26 19:00:12 -07:00
|
|
|
if !found {
|
|
|
|
return types.ErrNoUnbondingDelegation(k.Codespace())
|
|
|
|
}
|
|
|
|
|
2019-01-16 02:35:18 -08:00
|
|
|
ctxTime := ctx.BlockHeader().Time
|
|
|
|
|
|
|
|
// loop through all the entries and complete unbonding mature entries
|
|
|
|
for i := 0; i < len(ubd.Entries); i++ {
|
|
|
|
entry := ubd.Entries[i]
|
|
|
|
if entry.IsMature(ctxTime) {
|
|
|
|
ubd.RemoveEntry(int64(i))
|
|
|
|
i--
|
|
|
|
|
2019-02-07 17:41:23 -08:00
|
|
|
// track undelegation only when remaining or truncated shares are non-zero
|
|
|
|
if !entry.Balance.IsZero() {
|
2019-02-25 07:16:52 -08:00
|
|
|
_, err := k.bankKeeper.UndelegateCoins(ctx, ubd.DelegatorAddress, sdk.Coins{sdk.NewCoin(k.GetParams(ctx).BondDenom, entry.Balance)})
|
2019-02-07 17:41:23 -08:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2019-01-16 02:35:18 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// set the unbonding delegation or remove it if there are no more entries
|
|
|
|
if len(ubd.Entries) == 0 {
|
|
|
|
k.RemoveUnbondingDelegation(ctx, ubd)
|
|
|
|
} else {
|
|
|
|
k.SetUnbondingDelegation(ctx, ubd)
|
2018-06-28 15:52:10 -07:00
|
|
|
}
|
2019-01-16 02:35:18 -08:00
|
|
|
|
2018-06-26 19:00:12 -07:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2018-12-10 02:49:37 -08:00
|
|
|
// begin unbonding / redelegation; create a redelegation record
|
2018-08-30 21:06:44 -07:00
|
|
|
func (k Keeper) BeginRedelegation(ctx sdk.Context, delAddr sdk.AccAddress,
|
2019-01-16 02:35:18 -08:00
|
|
|
valSrcAddr, valDstAddr sdk.ValAddress, sharesAmount sdk.Dec) (
|
|
|
|
completionTime time.Time, errSdk sdk.Error) {
|
2018-06-26 19:00:12 -07:00
|
|
|
|
2018-10-29 16:05:55 -07:00
|
|
|
if bytes.Equal(valSrcAddr, valDstAddr) {
|
2019-01-16 02:35:18 -08:00
|
|
|
return time.Time{}, types.ErrSelfRedelegation(k.Codespace())
|
2018-10-15 18:06:31 -07:00
|
|
|
}
|
|
|
|
|
2018-06-26 19:00:12 -07:00
|
|
|
// check if this is a transitive redelegation
|
2018-08-30 21:06:44 -07:00
|
|
|
if k.HasReceivingRedelegation(ctx, delAddr, valSrcAddr) {
|
2019-01-16 02:35:18 -08:00
|
|
|
return time.Time{}, types.ErrTransitiveRedelegation(k.Codespace())
|
2018-06-26 19:00:12 -07:00
|
|
|
}
|
|
|
|
|
2019-01-24 14:44:31 -08:00
|
|
|
if k.HasMaxRedelegationEntries(ctx, delAddr, valSrcAddr, valDstAddr) {
|
|
|
|
return time.Time{}, types.ErrMaxRedelegationEntries(k.Codespace())
|
|
|
|
}
|
|
|
|
|
2018-08-30 21:06:44 -07:00
|
|
|
returnAmount, err := k.unbond(ctx, delAddr, valSrcAddr, sharesAmount)
|
2018-06-26 19:00:12 -07:00
|
|
|
if err != nil {
|
2019-01-16 02:35:18 -08:00
|
|
|
return time.Time{}, err
|
2018-06-26 19:00:12 -07:00
|
|
|
}
|
|
|
|
|
2019-01-02 12:29:47 -08:00
|
|
|
if returnAmount.IsZero() {
|
2019-01-16 02:35:18 -08:00
|
|
|
return time.Time{}, types.ErrVerySmallRedelegation(k.Codespace())
|
2018-11-26 04:13:47 -08:00
|
|
|
}
|
2018-08-30 21:06:44 -07:00
|
|
|
dstValidator, found := k.GetValidator(ctx, valDstAddr)
|
2018-06-26 19:00:12 -07:00
|
|
|
if !found {
|
2019-01-16 02:35:18 -08:00
|
|
|
return time.Time{}, types.ErrBadRedelegationDst(k.Codespace())
|
2018-06-26 19:00:12 -07:00
|
|
|
}
|
2018-11-26 04:13:47 -08:00
|
|
|
|
2019-02-08 13:28:53 -08:00
|
|
|
sharesCreated, err := k.Delegate(ctx, delAddr, returnAmount, dstValidator, false)
|
2018-06-28 10:12:02 -07:00
|
|
|
if err != nil {
|
2019-01-16 02:35:18 -08:00
|
|
|
return time.Time{}, err
|
2018-06-28 10:12:02 -07:00
|
|
|
}
|
2018-06-26 19:00:12 -07:00
|
|
|
|
|
|
|
// create the unbonding delegation
|
2019-01-16 02:35:18 -08:00
|
|
|
completionTime, height, completeNow := k.getBeginInfo(ctx, valSrcAddr)
|
2018-08-24 18:47:31 -07:00
|
|
|
|
|
|
|
if completeNow { // no need to create the redelegation object
|
2019-01-16 02:35:18 -08:00
|
|
|
return completionTime, nil
|
2018-08-24 18:47:31 -07:00
|
|
|
}
|
2018-06-26 19:00:12 -07:00
|
|
|
|
2019-01-16 02:35:18 -08:00
|
|
|
red := k.SetRedelegationEntry(ctx, delAddr, valSrcAddr, valDstAddr,
|
2019-02-08 13:28:53 -08:00
|
|
|
height, completionTime, returnAmount, sharesAmount, sharesCreated)
|
2019-01-16 02:35:18 -08:00
|
|
|
k.InsertRedelegationQueue(ctx, red, completionTime)
|
|
|
|
return completionTime, nil
|
2018-06-26 19:00:12 -07:00
|
|
|
}
|
|
|
|
|
2019-01-16 02:35:18 -08:00
|
|
|
// CompleteRedelegation completes the unbonding of all mature entries in the
|
|
|
|
// retrieved unbonding delegation object.
|
2018-08-30 21:06:44 -07:00
|
|
|
func (k Keeper) CompleteRedelegation(ctx sdk.Context, delAddr sdk.AccAddress,
|
|
|
|
valSrcAddr, valDstAddr sdk.ValAddress) sdk.Error {
|
2018-06-26 19:00:12 -07:00
|
|
|
|
2018-08-30 21:06:44 -07:00
|
|
|
red, found := k.GetRedelegation(ctx, delAddr, valSrcAddr, valDstAddr)
|
2018-06-26 19:00:12 -07:00
|
|
|
if !found {
|
|
|
|
return types.ErrNoRedelegation(k.Codespace())
|
|
|
|
}
|
|
|
|
|
|
|
|
ctxTime := ctx.BlockHeader().Time
|
2019-01-16 02:35:18 -08:00
|
|
|
|
|
|
|
// loop through all the entries and complete mature redelegation entries
|
|
|
|
for i := 0; i < len(red.Entries); i++ {
|
|
|
|
entry := red.Entries[i]
|
|
|
|
if entry.IsMature(ctxTime) {
|
|
|
|
red.RemoveEntry(int64(i))
|
|
|
|
i--
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// set the redelegation or remove it if there are no more entries
|
|
|
|
if len(red.Entries) == 0 {
|
|
|
|
k.RemoveRedelegation(ctx, red)
|
|
|
|
} else {
|
|
|
|
k.SetRedelegation(ctx, red)
|
2018-06-26 19:00:12 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
2019-03-25 14:13:02 -07:00
|
|
|
|
|
|
|
// ValidateUnbondAmount validates that a given unbond or redelegation amount is
|
|
|
|
// valied based on upon the converted shares. If the amount is valid, the total
|
|
|
|
// amount of respective shares is returned, otherwise an error is returned.
|
|
|
|
func (k Keeper) ValidateUnbondAmount(
|
|
|
|
ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress, amt sdk.Int,
|
|
|
|
) (shares sdk.Dec, err sdk.Error) {
|
|
|
|
|
|
|
|
validator, found := k.GetValidator(ctx, valAddr)
|
|
|
|
if !found {
|
|
|
|
return shares, types.ErrNoValidatorFound(k.Codespace())
|
|
|
|
}
|
|
|
|
|
|
|
|
del, found := k.GetDelegation(ctx, delAddr, valAddr)
|
|
|
|
if !found {
|
|
|
|
return shares, types.ErrNoDelegation(k.Codespace())
|
|
|
|
}
|
|
|
|
|
|
|
|
shares, err = validator.SharesFromTokens(amt)
|
|
|
|
if err != nil {
|
|
|
|
return shares, err
|
|
|
|
}
|
|
|
|
|
|
|
|
sharesTruncated, err := validator.SharesFromTokensTruncated(amt)
|
|
|
|
if err != nil {
|
|
|
|
return shares, err
|
|
|
|
}
|
|
|
|
|
|
|
|
delShares := del.GetShares()
|
|
|
|
if sharesTruncated.GT(delShares) {
|
|
|
|
return shares, types.ErrBadSharesAmount(k.Codespace())
|
|
|
|
}
|
|
|
|
|
|
|
|
// Cap the shares at the delegation's shares. Shares being greater could occur
|
|
|
|
// due to rounding, however we don't want to truncate the shares or take the
|
|
|
|
// minimum because we want to allow for the full withdraw of shares from a
|
|
|
|
// delegation.
|
|
|
|
if shares.GT(delShares) {
|
|
|
|
shares = delShares
|
|
|
|
}
|
|
|
|
|
|
|
|
return shares, nil
|
|
|
|
}
|