github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/libs/cosmos-sdk/x/staking/keeper/delegation.go (about)

     1  package keeper
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"time"
     7  
     8  	sdk "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types"
     9  	sdkerrors "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types/errors"
    10  	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/x/staking/types"
    11  )
    12  
    13  // return a specific delegation
    14  func (k Keeper) GetDelegation(ctx sdk.Context,
    15  	delAddr sdk.AccAddress, valAddr sdk.ValAddress) (
    16  	delegation types.Delegation, found bool) {
    17  
    18  	store := ctx.KVStore(k.storeKey)
    19  	key := types.GetDelegationKey(delAddr, valAddr)
    20  	value := store.Get(key)
    21  	if value == nil {
    22  		return delegation, false
    23  	}
    24  
    25  	delegation = types.MustUnmarshalDelegation(k.cdc, value)
    26  	return delegation, true
    27  }
    28  
    29  // IterateAllDelegations iterate through all of the delegations
    30  func (k Keeper) IterateAllDelegations(ctx sdk.Context, cb func(delegation types.Delegation) (stop bool)) {
    31  	store := ctx.KVStore(k.storeKey)
    32  	iterator := sdk.KVStorePrefixIterator(store, types.DelegationKey)
    33  	defer iterator.Close()
    34  
    35  	for ; iterator.Valid(); iterator.Next() {
    36  		delegation := types.MustUnmarshalDelegation(k.cdc, iterator.Value())
    37  		if cb(delegation) {
    38  			break
    39  		}
    40  	}
    41  }
    42  
    43  // GetAllDelegations returns all delegations used during genesis dump
    44  func (k Keeper) GetAllDelegations(ctx sdk.Context) (delegations []types.Delegation) {
    45  	k.IterateAllDelegations(ctx, func(delegation types.Delegation) bool {
    46  		delegations = append(delegations, delegation)
    47  		return false
    48  	})
    49  	return delegations
    50  }
    51  
    52  // return all delegations to a specific validator. Useful for querier.
    53  func (k Keeper) GetValidatorDelegations(ctx sdk.Context, valAddr sdk.ValAddress) (delegations []types.Delegation) { //nolint:interfacer
    54  	store := ctx.KVStore(k.storeKey)
    55  	iterator := sdk.KVStorePrefixIterator(store, types.DelegationKey)
    56  	defer iterator.Close()
    57  
    58  	for ; iterator.Valid(); iterator.Next() {
    59  		delegation := types.MustUnmarshalDelegation(k.cdc, iterator.Value())
    60  		if delegation.GetValidatorAddr().Equals(valAddr) {
    61  			delegations = append(delegations, delegation)
    62  		}
    63  	}
    64  	return delegations
    65  }
    66  
    67  // return a given amount of all the delegations from a delegator
    68  func (k Keeper) GetDelegatorDelegations(ctx sdk.Context, delegator sdk.AccAddress,
    69  	maxRetrieve uint16) (delegations []types.Delegation) {
    70  
    71  	delegations = make([]types.Delegation, maxRetrieve)
    72  
    73  	store := ctx.KVStore(k.storeKey)
    74  	delegatorPrefixKey := types.GetDelegationsKey(delegator)
    75  	iterator := sdk.KVStorePrefixIterator(store, delegatorPrefixKey)
    76  	defer iterator.Close()
    77  
    78  	i := 0
    79  	for ; iterator.Valid() && i < int(maxRetrieve); iterator.Next() {
    80  		delegation := types.MustUnmarshalDelegation(k.cdc, iterator.Value())
    81  		delegations[i] = delegation
    82  		i++
    83  	}
    84  	return delegations[:i] // trim if the array length < maxRetrieve
    85  }
    86  
    87  // set a delegation
    88  func (k Keeper) SetDelegation(ctx sdk.Context, delegation types.Delegation) {
    89  	store := ctx.KVStore(k.storeKey)
    90  	b := types.MustMarshalDelegation(k.cdc, delegation)
    91  	store.Set(types.GetDelegationKey(delegation.DelegatorAddress, delegation.ValidatorAddress), b)
    92  }
    93  
    94  // remove a delegation
    95  func (k Keeper) RemoveDelegation(ctx sdk.Context, delegation types.Delegation) {
    96  	// TODO: Consider calling hooks outside of the store wrapper functions, it's unobvious.
    97  	k.BeforeDelegationRemoved(ctx, delegation.DelegatorAddress, delegation.ValidatorAddress)
    98  	store := ctx.KVStore(k.storeKey)
    99  	store.Delete(types.GetDelegationKey(delegation.DelegatorAddress, delegation.ValidatorAddress))
   100  }
   101  
   102  // return a given amount of all the delegator unbonding-delegations
   103  func (k Keeper) GetUnbondingDelegations(ctx sdk.Context, delegator sdk.AccAddress,
   104  	maxRetrieve uint16) (unbondingDelegations []types.UnbondingDelegation) {
   105  
   106  	unbondingDelegations = make([]types.UnbondingDelegation, maxRetrieve)
   107  
   108  	store := ctx.KVStore(k.storeKey)
   109  	delegatorPrefixKey := types.GetUBDsKey(delegator)
   110  	iterator := sdk.KVStorePrefixIterator(store, delegatorPrefixKey)
   111  	defer iterator.Close()
   112  
   113  	i := 0
   114  	for ; iterator.Valid() && i < int(maxRetrieve); iterator.Next() {
   115  		unbondingDelegation := types.MustUnmarshalUBD(k.cdc, iterator.Value())
   116  		unbondingDelegations[i] = unbondingDelegation
   117  		i++
   118  	}
   119  	return unbondingDelegations[:i] // trim if the array length < maxRetrieve
   120  }
   121  
   122  // return a unbonding delegation
   123  func (k Keeper) GetUnbondingDelegation(ctx sdk.Context,
   124  	delAddr sdk.AccAddress, valAddr sdk.ValAddress) (ubd types.UnbondingDelegation, found bool) {
   125  
   126  	store := ctx.KVStore(k.storeKey)
   127  	key := types.GetUBDKey(delAddr, valAddr)
   128  	value := store.Get(key)
   129  	if value == nil {
   130  		return ubd, false
   131  	}
   132  
   133  	ubd = types.MustUnmarshalUBD(k.cdc, value)
   134  	return ubd, true
   135  }
   136  
   137  // return all unbonding delegations from a particular validator
   138  func (k Keeper) GetUnbondingDelegationsFromValidator(ctx sdk.Context, valAddr sdk.ValAddress) (ubds []types.UnbondingDelegation) {
   139  	store := ctx.KVStore(k.storeKey)
   140  	iterator := sdk.KVStorePrefixIterator(store, types.GetUBDsByValIndexKey(valAddr))
   141  	defer iterator.Close()
   142  
   143  	for ; iterator.Valid(); iterator.Next() {
   144  		key := types.GetUBDKeyFromValIndexKey(iterator.Key())
   145  		value := store.Get(key)
   146  		ubd := types.MustUnmarshalUBD(k.cdc, value)
   147  		ubds = append(ubds, ubd)
   148  	}
   149  	return ubds
   150  }
   151  
   152  // iterate through all of the unbonding delegations
   153  func (k Keeper) IterateUnbondingDelegations(ctx sdk.Context, fn func(index int64, ubd types.UnbondingDelegation) (stop bool)) {
   154  	store := ctx.KVStore(k.storeKey)
   155  	iterator := sdk.KVStorePrefixIterator(store, types.UnbondingDelegationKey)
   156  	defer iterator.Close()
   157  
   158  	for i := int64(0); iterator.Valid(); iterator.Next() {
   159  		ubd := types.MustUnmarshalUBD(k.cdc, iterator.Value())
   160  		if stop := fn(i, ubd); stop {
   161  			break
   162  		}
   163  		i++
   164  	}
   165  }
   166  
   167  // HasMaxUnbondingDelegationEntries - check if unbonding delegation has maximum number of entries
   168  func (k Keeper) HasMaxUnbondingDelegationEntries(ctx sdk.Context,
   169  	delegatorAddr sdk.AccAddress, validatorAddr sdk.ValAddress) bool {
   170  
   171  	ubd, found := k.GetUnbondingDelegation(ctx, delegatorAddr, validatorAddr)
   172  	if !found {
   173  		return false
   174  	}
   175  	return len(ubd.Entries) >= int(k.MaxEntries(ctx))
   176  }
   177  
   178  // set the unbonding delegation and associated index
   179  func (k Keeper) SetUnbondingDelegation(ctx sdk.Context, ubd types.UnbondingDelegation) {
   180  	store := ctx.KVStore(k.storeKey)
   181  	bz := types.MustMarshalUBD(k.cdc, ubd)
   182  	key := types.GetUBDKey(ubd.DelegatorAddress, ubd.ValidatorAddress)
   183  	store.Set(key, bz)
   184  	store.Set(types.GetUBDByValIndexKey(ubd.DelegatorAddress, ubd.ValidatorAddress), []byte{}) // index, store empty bytes
   185  }
   186  
   187  // remove the unbonding delegation object and associated index
   188  func (k Keeper) RemoveUnbondingDelegation(ctx sdk.Context, ubd types.UnbondingDelegation) {
   189  	store := ctx.KVStore(k.storeKey)
   190  	key := types.GetUBDKey(ubd.DelegatorAddress, ubd.ValidatorAddress)
   191  	store.Delete(key)
   192  	store.Delete(types.GetUBDByValIndexKey(ubd.DelegatorAddress, ubd.ValidatorAddress))
   193  }
   194  
   195  // SetUnbondingDelegationEntry adds an entry to the unbonding delegation at
   196  // the given addresses. It creates the unbonding delegation if it does not exist
   197  func (k Keeper) SetUnbondingDelegationEntry(ctx sdk.Context,
   198  	delegatorAddr sdk.AccAddress, validatorAddr sdk.ValAddress,
   199  	creationHeight int64, minTime time.Time, balance sdk.Int) types.UnbondingDelegation {
   200  
   201  	ubd, found := k.GetUnbondingDelegation(ctx, delegatorAddr, validatorAddr)
   202  	if found {
   203  		ubd.AddEntry(creationHeight, minTime, balance)
   204  	} else {
   205  		ubd = types.NewUnbondingDelegation(delegatorAddr, validatorAddr, creationHeight, minTime, balance)
   206  	}
   207  	k.SetUnbondingDelegation(ctx, ubd)
   208  	return ubd
   209  }
   210  
   211  // unbonding delegation queue timeslice operations
   212  
   213  // gets a specific unbonding queue timeslice. A timeslice is a slice of DVPairs
   214  // corresponding to unbonding delegations that expire at a certain time.
   215  func (k Keeper) GetUBDQueueTimeSlice(ctx sdk.Context, timestamp time.Time) (dvPairs []types.DVPair) {
   216  	store := ctx.KVStore(k.storeKey)
   217  	bz := store.Get(types.GetUnbondingDelegationTimeKey(timestamp))
   218  	if bz == nil {
   219  		return []types.DVPair{}
   220  	}
   221  	k.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &dvPairs)
   222  	return dvPairs
   223  }
   224  
   225  // Sets a specific unbonding queue timeslice.
   226  func (k Keeper) SetUBDQueueTimeSlice(ctx sdk.Context, timestamp time.Time, keys []types.DVPair) {
   227  	store := ctx.KVStore(k.storeKey)
   228  	bz := k.cdc.MustMarshalBinaryLengthPrefixed(keys)
   229  	store.Set(types.GetUnbondingDelegationTimeKey(timestamp), bz)
   230  }
   231  
   232  // Insert an unbonding delegation to the appropriate timeslice in the unbonding queue
   233  func (k Keeper) InsertUBDQueue(ctx sdk.Context, ubd types.UnbondingDelegation,
   234  	completionTime time.Time) {
   235  
   236  	timeSlice := k.GetUBDQueueTimeSlice(ctx, completionTime)
   237  	dvPair := types.DVPair{DelegatorAddress: ubd.DelegatorAddress, ValidatorAddress: ubd.ValidatorAddress}
   238  	if len(timeSlice) == 0 {
   239  		k.SetUBDQueueTimeSlice(ctx, completionTime, []types.DVPair{dvPair})
   240  	} else {
   241  		timeSlice = append(timeSlice, dvPair)
   242  		k.SetUBDQueueTimeSlice(ctx, completionTime, timeSlice)
   243  	}
   244  }
   245  
   246  // Returns all the unbonding queue timeslices from time 0 until endTime
   247  func (k Keeper) UBDQueueIterator(ctx sdk.Context, endTime time.Time) sdk.Iterator {
   248  	store := ctx.KVStore(k.storeKey)
   249  	return store.Iterator(types.UnbondingQueueKey,
   250  		sdk.InclusiveEndBytes(types.GetUnbondingDelegationTimeKey(endTime)))
   251  }
   252  
   253  // Returns a concatenated list of all the timeslices inclusively previous to
   254  // currTime, and deletes the timeslices from the queue
   255  func (k Keeper) DequeueAllMatureUBDQueue(ctx sdk.Context,
   256  	currTime time.Time) (matureUnbonds []types.DVPair) {
   257  
   258  	store := ctx.KVStore(k.storeKey)
   259  	// gets an iterator for all timeslices from time 0 until the current Blockheader time
   260  	unbondingTimesliceIterator := k.UBDQueueIterator(ctx, ctx.BlockHeader().Time)
   261  	defer unbondingTimesliceIterator.Close()
   262  
   263  	for ; unbondingTimesliceIterator.Valid(); unbondingTimesliceIterator.Next() {
   264  		timeslice := []types.DVPair{}
   265  		value := unbondingTimesliceIterator.Value()
   266  		k.cdc.MustUnmarshalBinaryLengthPrefixed(value, &timeslice)
   267  		matureUnbonds = append(matureUnbonds, timeslice...)
   268  		store.Delete(unbondingTimesliceIterator.Key())
   269  	}
   270  	return matureUnbonds
   271  }
   272  
   273  // return a given amount of all the delegator redelegations
   274  func (k Keeper) GetRedelegations(ctx sdk.Context, delegator sdk.AccAddress,
   275  	maxRetrieve uint16) (redelegations []types.Redelegation) {
   276  	redelegations = make([]types.Redelegation, maxRetrieve)
   277  
   278  	store := ctx.KVStore(k.storeKey)
   279  	delegatorPrefixKey := types.GetREDsKey(delegator)
   280  	iterator := sdk.KVStorePrefixIterator(store, delegatorPrefixKey)
   281  	defer iterator.Close()
   282  
   283  	i := 0
   284  	for ; iterator.Valid() && i < int(maxRetrieve); iterator.Next() {
   285  		redelegation := types.MustUnmarshalRED(k.cdc, iterator.Value())
   286  		redelegations[i] = redelegation
   287  		i++
   288  	}
   289  	return redelegations[:i] // trim if the array length < maxRetrieve
   290  }
   291  
   292  // return a redelegation
   293  func (k Keeper) GetRedelegation(ctx sdk.Context,
   294  	delAddr sdk.AccAddress, valSrcAddr, valDstAddr sdk.ValAddress) (red types.Redelegation, found bool) {
   295  
   296  	store := ctx.KVStore(k.storeKey)
   297  	key := types.GetREDKey(delAddr, valSrcAddr, valDstAddr)
   298  	value := store.Get(key)
   299  	if value == nil {
   300  		return red, false
   301  	}
   302  
   303  	red = types.MustUnmarshalRED(k.cdc, value)
   304  	return red, true
   305  }
   306  
   307  // return all redelegations from a particular validator
   308  func (k Keeper) GetRedelegationsFromSrcValidator(ctx sdk.Context, valAddr sdk.ValAddress) (reds []types.Redelegation) {
   309  	store := ctx.KVStore(k.storeKey)
   310  	iterator := sdk.KVStorePrefixIterator(store, types.GetREDsFromValSrcIndexKey(valAddr))
   311  	defer iterator.Close()
   312  
   313  	for ; iterator.Valid(); iterator.Next() {
   314  		key := types.GetREDKeyFromValSrcIndexKey(iterator.Key())
   315  		value := store.Get(key)
   316  		red := types.MustUnmarshalRED(k.cdc, value)
   317  		reds = append(reds, red)
   318  	}
   319  	return reds
   320  }
   321  
   322  // check if validator is receiving a redelegation
   323  func (k Keeper) HasReceivingRedelegation(ctx sdk.Context,
   324  	delAddr sdk.AccAddress, valDstAddr sdk.ValAddress) bool {
   325  
   326  	store := ctx.KVStore(k.storeKey)
   327  	prefix := types.GetREDsByDelToValDstIndexKey(delAddr, valDstAddr)
   328  	iterator := sdk.KVStorePrefixIterator(store, prefix)
   329  	defer iterator.Close()
   330  
   331  	return iterator.Valid()
   332  }
   333  
   334  // HasMaxRedelegationEntries - redelegation has maximum number of entries
   335  func (k Keeper) HasMaxRedelegationEntries(ctx sdk.Context,
   336  	delegatorAddr sdk.AccAddress, validatorSrcAddr,
   337  	validatorDstAddr sdk.ValAddress) bool {
   338  
   339  	red, found := k.GetRedelegation(ctx, delegatorAddr, validatorSrcAddr, validatorDstAddr)
   340  	if !found {
   341  		return false
   342  	}
   343  	return len(red.Entries) >= int(k.MaxEntries(ctx))
   344  }
   345  
   346  // set a redelegation and associated index
   347  func (k Keeper) SetRedelegation(ctx sdk.Context, red types.Redelegation) {
   348  	store := ctx.KVStore(k.storeKey)
   349  	bz := types.MustMarshalRED(k.cdc, red)
   350  	key := types.GetREDKey(red.DelegatorAddress, red.ValidatorSrcAddress, red.ValidatorDstAddress)
   351  	store.Set(key, bz)
   352  	store.Set(types.GetREDByValSrcIndexKey(red.DelegatorAddress, red.ValidatorSrcAddress, red.ValidatorDstAddress), []byte{})
   353  	store.Set(types.GetREDByValDstIndexKey(red.DelegatorAddress, red.ValidatorSrcAddress, red.ValidatorDstAddress), []byte{})
   354  }
   355  
   356  // SetUnbondingDelegationEntry adds an entry to the unbonding delegation at
   357  // the given addresses. It creates the unbonding delegation if it does not exist
   358  func (k Keeper) SetRedelegationEntry(ctx sdk.Context,
   359  	delegatorAddr sdk.AccAddress, validatorSrcAddr,
   360  	validatorDstAddr sdk.ValAddress, creationHeight int64,
   361  	minTime time.Time, balance sdk.Int,
   362  	sharesSrc, sharesDst sdk.Dec) types.Redelegation {
   363  
   364  	red, found := k.GetRedelegation(ctx, delegatorAddr, validatorSrcAddr, validatorDstAddr)
   365  	if found {
   366  		red.AddEntry(creationHeight, minTime, balance, sharesDst)
   367  	} else {
   368  		red = types.NewRedelegation(delegatorAddr, validatorSrcAddr,
   369  			validatorDstAddr, creationHeight, minTime, balance, sharesDst)
   370  	}
   371  	k.SetRedelegation(ctx, red)
   372  	return red
   373  }
   374  
   375  // iterate through all redelegations
   376  func (k Keeper) IterateRedelegations(ctx sdk.Context, fn func(index int64, red types.Redelegation) (stop bool)) {
   377  	store := ctx.KVStore(k.storeKey)
   378  	iterator := sdk.KVStorePrefixIterator(store, types.RedelegationKey)
   379  	defer iterator.Close()
   380  
   381  	for i := int64(0); iterator.Valid(); iterator.Next() {
   382  		red := types.MustUnmarshalRED(k.cdc, iterator.Value())
   383  		if stop := fn(i, red); stop {
   384  			break
   385  		}
   386  		i++
   387  	}
   388  }
   389  
   390  // remove a redelegation object and associated index
   391  func (k Keeper) RemoveRedelegation(ctx sdk.Context, red types.Redelegation) {
   392  	store := ctx.KVStore(k.storeKey)
   393  	redKey := types.GetREDKey(red.DelegatorAddress, red.ValidatorSrcAddress, red.ValidatorDstAddress)
   394  	store.Delete(redKey)
   395  	store.Delete(types.GetREDByValSrcIndexKey(red.DelegatorAddress, red.ValidatorSrcAddress, red.ValidatorDstAddress))
   396  	store.Delete(types.GetREDByValDstIndexKey(red.DelegatorAddress, red.ValidatorSrcAddress, red.ValidatorDstAddress))
   397  }
   398  
   399  // redelegation queue timeslice operations
   400  
   401  // Gets a specific redelegation queue timeslice. A timeslice is a slice of DVVTriplets corresponding to redelegations
   402  // that expire at a certain time.
   403  func (k Keeper) GetRedelegationQueueTimeSlice(ctx sdk.Context, timestamp time.Time) (dvvTriplets []types.DVVTriplet) {
   404  	store := ctx.KVStore(k.storeKey)
   405  	bz := store.Get(types.GetRedelegationTimeKey(timestamp))
   406  	if bz == nil {
   407  		return []types.DVVTriplet{}
   408  	}
   409  	k.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &dvvTriplets)
   410  	return dvvTriplets
   411  }
   412  
   413  // Sets a specific redelegation queue timeslice.
   414  func (k Keeper) SetRedelegationQueueTimeSlice(ctx sdk.Context, timestamp time.Time, keys []types.DVVTriplet) {
   415  	store := ctx.KVStore(k.storeKey)
   416  	bz := k.cdc.MustMarshalBinaryLengthPrefixed(keys)
   417  	store.Set(types.GetRedelegationTimeKey(timestamp), bz)
   418  }
   419  
   420  // Insert an redelegation delegation to the appropriate timeslice in the redelegation queue
   421  func (k Keeper) InsertRedelegationQueue(ctx sdk.Context, red types.Redelegation,
   422  	completionTime time.Time) {
   423  
   424  	timeSlice := k.GetRedelegationQueueTimeSlice(ctx, completionTime)
   425  	dvvTriplet := types.DVVTriplet{
   426  		DelegatorAddress:    red.DelegatorAddress,
   427  		ValidatorSrcAddress: red.ValidatorSrcAddress,
   428  		ValidatorDstAddress: red.ValidatorDstAddress}
   429  
   430  	if len(timeSlice) == 0 {
   431  		k.SetRedelegationQueueTimeSlice(ctx, completionTime, []types.DVVTriplet{dvvTriplet})
   432  	} else {
   433  		timeSlice = append(timeSlice, dvvTriplet)
   434  		k.SetRedelegationQueueTimeSlice(ctx, completionTime, timeSlice)
   435  	}
   436  }
   437  
   438  // Returns all the redelegation queue timeslices from time 0 until endTime
   439  func (k Keeper) RedelegationQueueIterator(ctx sdk.Context, endTime time.Time) sdk.Iterator {
   440  	store := ctx.KVStore(k.storeKey)
   441  	return store.Iterator(types.RedelegationQueueKey, sdk.InclusiveEndBytes(types.GetRedelegationTimeKey(endTime)))
   442  }
   443  
   444  // Returns a concatenated list of all the timeslices inclusively previous to
   445  // currTime, and deletes the timeslices from the queue
   446  func (k Keeper) DequeueAllMatureRedelegationQueue(ctx sdk.Context, currTime time.Time) (matureRedelegations []types.DVVTriplet) {
   447  	store := ctx.KVStore(k.storeKey)
   448  	// gets an iterator for all timeslices from time 0 until the current Blockheader time
   449  	redelegationTimesliceIterator := k.RedelegationQueueIterator(ctx, ctx.BlockHeader().Time)
   450  	defer redelegationTimesliceIterator.Close()
   451  
   452  	for ; redelegationTimesliceIterator.Valid(); redelegationTimesliceIterator.Next() {
   453  		timeslice := []types.DVVTriplet{}
   454  		value := redelegationTimesliceIterator.Value()
   455  		k.cdc.MustUnmarshalBinaryLengthPrefixed(value, &timeslice)
   456  		matureRedelegations = append(matureRedelegations, timeslice...)
   457  		store.Delete(redelegationTimesliceIterator.Key())
   458  	}
   459  	return matureRedelegations
   460  }
   461  
   462  // Perform a delegation, set/update everything necessary within the store.
   463  // tokenSrc indicates the bond status of the incoming funds.
   464  func (k Keeper) Delegate(
   465  	ctx sdk.Context, delAddr sdk.AccAddress, bondAmt sdk.Dec, tokenSrc sdk.BondStatus,
   466  	validator types.Validator, subtractAccount bool,
   467  ) (newShares sdk.Dec, err error) {
   468  
   469  	// In some situations, the exchange rate becomes invalid, e.g. if
   470  	// Validator loses all tokens due to slashing. In this case,
   471  	// make all future delegations invalid.
   472  	if validator.InvalidExRate() {
   473  		return sdk.ZeroDec(), types.ErrDelegatorShareExRateInvalid
   474  	}
   475  
   476  	// Get or create the delegation object
   477  	delegation, found := k.GetDelegation(ctx, delAddr, validator.OperatorAddress)
   478  	if !found {
   479  		delegation = types.NewDelegation(delAddr, validator.OperatorAddress, sdk.ZeroDec())
   480  	}
   481  
   482  	// call the appropriate hook if present
   483  	if found {
   484  		k.BeforeDelegationSharesModified(ctx, delAddr, validator.OperatorAddress)
   485  	} else {
   486  		k.BeforeDelegationCreated(ctx, delAddr, validator.OperatorAddress)
   487  	}
   488  
   489  	// if subtractAccount is true then we are
   490  	// performing a delegation and not a redelegation, thus the source tokens are
   491  	// all non bonded
   492  	if subtractAccount {
   493  		if tokenSrc == sdk.Bonded {
   494  			panic("delegation token source cannot be bonded")
   495  		}
   496  
   497  		var sendName string
   498  		switch {
   499  		case validator.IsBonded():
   500  			sendName = types.BondedPoolName
   501  		case validator.IsUnbonding(), validator.IsUnbonded():
   502  			sendName = types.NotBondedPoolName
   503  		default:
   504  			panic("invalid validator status")
   505  		}
   506  
   507  		coins := sdk.NewCoins(sdk.NewCoin(k.BondDenom(ctx), bondAmt))
   508  		err := k.supplyKeeper.DelegateCoinsFromAccountToModule(ctx, delegation.DelegatorAddress, sendName, coins)
   509  		if err != nil {
   510  			return sdk.Dec{}, err
   511  		}
   512  	} else {
   513  
   514  		// potentially transfer tokens between pools, if
   515  		switch {
   516  		case tokenSrc == sdk.Bonded && validator.IsBonded():
   517  			// do nothing
   518  		case (tokenSrc == sdk.Unbonded || tokenSrc == sdk.Unbonding) && !validator.IsBonded():
   519  			// do nothing
   520  		case (tokenSrc == sdk.Unbonded || tokenSrc == sdk.Unbonding) && validator.IsBonded():
   521  			// transfer pools
   522  			k.notBondedTokensToBonded(ctx, bondAmt.RoundInt())
   523  		case tokenSrc == sdk.Bonded && !validator.IsBonded():
   524  			// transfer pools
   525  			k.bondedTokensToNotBonded(ctx, bondAmt.RoundInt())
   526  		default:
   527  			panic("unknown token source bond status")
   528  		}
   529  	}
   530  
   531  	validator, newShares = k.AddValidatorTokensAndShares(ctx, validator, bondAmt.RoundInt())
   532  
   533  	// Update delegation
   534  	delegation.Shares = delegation.Shares.Add(newShares)
   535  	k.SetDelegation(ctx, delegation)
   536  
   537  	// Call the after-modification hook
   538  	k.AfterDelegationModified(ctx, delegation.DelegatorAddress, delegation.ValidatorAddress)
   539  
   540  	return newShares, nil
   541  }
   542  
   543  // unbond a particular delegation and perform associated store operations
   544  func (k Keeper) unbond(
   545  	ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress, shares sdk.Dec,
   546  ) (amount sdk.Int, err error) {
   547  
   548  	// check if a delegation object exists in the store
   549  	delegation, found := k.GetDelegation(ctx, delAddr, valAddr)
   550  	if !found {
   551  		return amount, types.ErrNoDelegatorForAddress
   552  	}
   553  
   554  	// call the before-delegation-modified hook
   555  	k.BeforeDelegationSharesModified(ctx, delAddr, valAddr)
   556  
   557  	// ensure that we have enough shares to remove
   558  	if delegation.Shares.LT(shares) {
   559  		return amount, sdkerrors.Wrap(types.ErrNotEnoughDelegationShares, delegation.Shares.String())
   560  	}
   561  
   562  	// get validator
   563  	validator, found := k.GetValidator(ctx, valAddr)
   564  	if !found {
   565  		return amount, types.ErrNoValidatorFound
   566  	}
   567  
   568  	// subtract shares from delegation
   569  	delegation.Shares = delegation.Shares.Sub(shares)
   570  
   571  	isValidatorOperator := delegation.DelegatorAddress.Equals(validator.OperatorAddress)
   572  
   573  	// if the delegation is the operator of the validator and undelegating will decrease the validator's self delegation below their minimum
   574  	// trigger a jail validator
   575  	if isValidatorOperator && !validator.Jailed &&
   576  		validator.TokensFromShares(delegation.Shares).TruncateInt().LT(validator.MinSelfDelegation) {
   577  
   578  		k.jailValidator(ctx, validator)
   579  		validator = k.mustGetValidator(ctx, validator.OperatorAddress)
   580  	}
   581  
   582  	// remove the delegation
   583  	if delegation.Shares.IsZero() {
   584  		k.RemoveDelegation(ctx, delegation)
   585  	} else {
   586  		k.SetDelegation(ctx, delegation)
   587  		// call the after delegation modification hook
   588  		k.AfterDelegationModified(ctx, delegation.DelegatorAddress, delegation.ValidatorAddress)
   589  	}
   590  
   591  	// remove the shares and coins from the validator
   592  	// NOTE that the amount is later (in keeper.Delegation) moved between staking module pools
   593  	validator, amount = k.RemoveValidatorTokensAndShares(ctx, validator, shares)
   594  
   595  	if validator.DelegatorShares.IsZero() && validator.IsUnbonded() {
   596  		// if not unbonded, we must instead remove validator in EndBlocker once it finishes its unbonding period
   597  		k.RemoveValidator(ctx, validator.OperatorAddress)
   598  	}
   599  
   600  	return amount, nil
   601  }
   602  
   603  // getBeginInfo returns the completion time and height of a redelegation, along
   604  // with a boolean signaling if the redelegation is complete based on the source
   605  // validator.
   606  func (k Keeper) getBeginInfo(
   607  	ctx sdk.Context, valSrcAddr sdk.ValAddress,
   608  ) (completionTime time.Time, height int64, completeNow bool) {
   609  
   610  	validator, found := k.GetValidator(ctx, valSrcAddr)
   611  
   612  	// TODO: When would the validator not be found?
   613  	switch {
   614  	case !found || validator.IsBonded():
   615  
   616  		// the longest wait - just unbonding period from now
   617  		completionTime = ctx.BlockHeader().Time.Add(k.UnbondingTime(ctx))
   618  		height = ctx.BlockHeight()
   619  		return completionTime, height, false
   620  
   621  	case validator.IsUnbonded():
   622  		return completionTime, height, true
   623  
   624  	case validator.IsUnbonding():
   625  		return validator.UnbondingCompletionTime, validator.UnbondingHeight, false
   626  
   627  	default:
   628  		panic(fmt.Sprintf("unknown validator status: %s", validator.Status))
   629  	}
   630  }
   631  
   632  // Undelegate unbonds an amount of delegator shares from a given validator. It
   633  // will verify that the unbonding entries between the delegator and validator
   634  // are not exceeded and unbond the staked tokens (based on shares) by creating
   635  // an unbonding object and inserting it into the unbonding queue which will be
   636  // processed during the staking EndBlocker.
   637  func (k Keeper) Undelegate(
   638  	ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress, sharesAmount sdk.Dec,
   639  ) (time.Time, error) {
   640  
   641  	validator, found := k.GetValidator(ctx, valAddr)
   642  	if !found {
   643  		return time.Time{}, types.ErrNoDelegatorForAddress
   644  	}
   645  
   646  	if k.HasMaxUnbondingDelegationEntries(ctx, delAddr, valAddr) {
   647  		return time.Time{}, types.ErrMaxUnbondingDelegationEntries
   648  	}
   649  
   650  	returnAmount, err := k.unbond(ctx, delAddr, valAddr, sharesAmount)
   651  	if err != nil {
   652  		return time.Time{}, err
   653  	}
   654  
   655  	// transfer the validator tokens to the not bonded pool
   656  	if validator.IsBonded() {
   657  		k.bondedTokensToNotBonded(ctx, returnAmount)
   658  	}
   659  
   660  	completionTime := ctx.BlockHeader().Time.Add(k.UnbondingTime(ctx))
   661  	ubd := k.SetUnbondingDelegationEntry(ctx, delAddr, valAddr, ctx.BlockHeight(), completionTime, returnAmount)
   662  	k.InsertUBDQueue(ctx, ubd, completionTime)
   663  
   664  	return completionTime, nil
   665  }
   666  
   667  // CompleteUnbondingWithAmount completes the unbonding of all mature entries in
   668  // the retrieved unbonding delegation object and returns the total unbonding
   669  // balance or an error upon failure.
   670  func (k Keeper) CompleteUnbondingWithAmount(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) (sdk.Coins, error) {
   671  	ubd, found := k.GetUnbondingDelegation(ctx, delAddr, valAddr)
   672  	if !found {
   673  		return nil, types.ErrNoUnbondingDelegation
   674  	}
   675  
   676  	bondDenom := k.GetParams(ctx).BondDenom
   677  	balances := sdk.NewCoins()
   678  	ctxTime := ctx.BlockHeader().Time
   679  
   680  	// loop through all the entries and complete unbonding mature entries
   681  	for i := 0; i < len(ubd.Entries); i++ {
   682  		entry := ubd.Entries[i]
   683  		if entry.IsMature(ctxTime) {
   684  			ubd.RemoveEntry(int64(i))
   685  			i--
   686  
   687  			// track undelegation only when remaining or truncated shares are non-zero
   688  			if !entry.Balance.IsZero() {
   689  				amt := sdk.NewCoin(bondDenom, entry.Balance)
   690  				err := k.supplyKeeper.UndelegateCoinsFromModuleToAccount(
   691  					ctx, types.NotBondedPoolName, ubd.DelegatorAddress, sdk.NewCoins(amt),
   692  				)
   693  				if err != nil {
   694  					return nil, err
   695  				}
   696  
   697  				balances = balances.Add(amt)
   698  			}
   699  		}
   700  	}
   701  
   702  	// set the unbonding delegation or remove it if there are no more entries
   703  	if len(ubd.Entries) == 0 {
   704  		k.RemoveUnbondingDelegation(ctx, ubd)
   705  	} else {
   706  		k.SetUnbondingDelegation(ctx, ubd)
   707  	}
   708  
   709  	return balances, nil
   710  }
   711  
   712  // CompleteUnbonding performs the same logic as CompleteUnbondingWithAmount except
   713  // it does not return the total unbonding amount.
   714  func (k Keeper) CompleteUnbonding(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) error {
   715  	_, err := k.CompleteUnbondingWithAmount(ctx, delAddr, valAddr)
   716  	return err
   717  }
   718  
   719  // begin unbonding / redelegation; create a redelegation record
   720  func (k Keeper) BeginRedelegation(
   721  	ctx sdk.Context, delAddr sdk.AccAddress, valSrcAddr, valDstAddr sdk.ValAddress, sharesAmount sdk.Dec,
   722  ) (completionTime time.Time, err error) {
   723  
   724  	if bytes.Equal(valSrcAddr, valDstAddr) {
   725  		return time.Time{}, types.ErrSelfRedelegation
   726  	}
   727  
   728  	dstValidator, found := k.GetValidator(ctx, valDstAddr)
   729  	if !found {
   730  		return time.Time{}, types.ErrBadRedelegationDst
   731  	}
   732  
   733  	srcValidator, found := k.GetValidator(ctx, valSrcAddr)
   734  	if !found {
   735  		return time.Time{}, types.ErrBadRedelegationDst
   736  	}
   737  
   738  	// check if this is a transitive redelegation
   739  	if k.HasReceivingRedelegation(ctx, delAddr, valSrcAddr) {
   740  		return time.Time{}, types.ErrTransitiveRedelegation
   741  	}
   742  
   743  	if k.HasMaxRedelegationEntries(ctx, delAddr, valSrcAddr, valDstAddr) {
   744  		return time.Time{}, types.ErrMaxRedelegationEntries
   745  	}
   746  
   747  	returnAmount, err := k.unbond(ctx, delAddr, valSrcAddr, sharesAmount)
   748  	if err != nil {
   749  		return time.Time{}, err
   750  	}
   751  
   752  	if returnAmount.IsZero() {
   753  		return time.Time{}, types.ErrTinyRedelegationAmount
   754  	}
   755  
   756  	sharesCreated, err := k.Delegate(ctx, delAddr, returnAmount.ToDec(), srcValidator.GetStatus(), dstValidator, false)
   757  	if err != nil {
   758  		return time.Time{}, err
   759  	}
   760  
   761  	// create the unbonding delegation
   762  	completionTime, height, completeNow := k.getBeginInfo(ctx, valSrcAddr)
   763  
   764  	if completeNow { // no need to create the redelegation object
   765  		return completionTime, nil
   766  	}
   767  
   768  	red := k.SetRedelegationEntry(
   769  		ctx, delAddr, valSrcAddr, valDstAddr,
   770  		height, completionTime, returnAmount, sharesAmount, sharesCreated,
   771  	)
   772  	k.InsertRedelegationQueue(ctx, red, completionTime)
   773  	return completionTime, nil
   774  }
   775  
   776  // CompleteRedelegationWithAmount completes the redelegations of all mature entries in the
   777  // retrieved redelegation object and returns the total redelegation (initial)
   778  // balance or an error upon failure.
   779  func (k Keeper) CompleteRedelegationWithAmount(
   780  	ctx sdk.Context, delAddr sdk.AccAddress, valSrcAddr, valDstAddr sdk.ValAddress,
   781  ) (sdk.Coins, error) {
   782  
   783  	red, found := k.GetRedelegation(ctx, delAddr, valSrcAddr, valDstAddr)
   784  	if !found {
   785  		return nil, types.ErrNoRedelegation
   786  	}
   787  
   788  	bondDenom := k.GetParams(ctx).BondDenom
   789  	balances := sdk.NewCoins()
   790  	ctxTime := ctx.BlockHeader().Time
   791  
   792  	// loop through all the entries and complete mature redelegation entries
   793  	for i := 0; i < len(red.Entries); i++ {
   794  		entry := red.Entries[i]
   795  		if entry.IsMature(ctxTime) {
   796  			red.RemoveEntry(int64(i))
   797  			i--
   798  
   799  			if !entry.InitialBalance.IsZero() {
   800  				balances = balances.Add(sdk.NewCoin(bondDenom, entry.InitialBalance))
   801  			}
   802  		}
   803  	}
   804  
   805  	// set the redelegation or remove it if there are no more entries
   806  	if len(red.Entries) == 0 {
   807  		k.RemoveRedelegation(ctx, red)
   808  	} else {
   809  		k.SetRedelegation(ctx, red)
   810  	}
   811  
   812  	return balances, nil
   813  }
   814  
   815  // CompleteRedelegation performs the same logic as CompleteRedelegationWithAmount
   816  // except it does not return the total redelegation amount.
   817  func (k Keeper) CompleteRedelegation(
   818  	ctx sdk.Context, delAddr sdk.AccAddress, valSrcAddr, valDstAddr sdk.ValAddress,
   819  ) error {
   820  
   821  	_, err := k.CompleteRedelegationWithAmount(ctx, delAddr, valSrcAddr, valDstAddr)
   822  	return err
   823  }
   824  
   825  // ValidateUnbondAmount validates that a given unbond or redelegation amount is
   826  // valied based on upon the converted shares. If the amount is valid, the total
   827  // amount of respective shares is returned, otherwise an error is returned.
   828  func (k Keeper) ValidateUnbondAmount(
   829  	ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress, amt sdk.Dec,
   830  ) (shares sdk.Dec, err error) {
   831  
   832  	validator, found := k.GetValidator(ctx, valAddr)
   833  	if !found {
   834  		return shares, types.ErrNoValidatorFound
   835  	}
   836  
   837  	del, found := k.GetDelegation(ctx, delAddr, valAddr)
   838  	if !found {
   839  		return shares, types.ErrNoDelegation
   840  	}
   841  
   842  	shares, err = validator.SharesFromTokens(amt.RoundInt())
   843  	if err != nil {
   844  		return shares, err
   845  	}
   846  
   847  	sharesTruncated, err := validator.SharesFromTokensTruncated(amt.RoundInt())
   848  	if err != nil {
   849  		return shares, err
   850  	}
   851  
   852  	delShares := del.GetShares()
   853  	if sharesTruncated.GT(delShares) {
   854  		return shares, types.ErrBadSharesAmount
   855  	}
   856  
   857  	// Cap the shares at the delegation's shares. Shares being greater could occur
   858  	// due to rounding, however we don't want to truncate the shares or take the
   859  	// minimum because we want to allow for the full withdraw of shares from a
   860  	// delegation.
   861  	if shares.GT(delShares) {
   862  		shares = delShares
   863  	}
   864  
   865  	return shares, nil
   866  }