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

     1  package keeper
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"sort"
     7  
     8  	abci "github.com/fibonacci-chain/fbc/libs/tendermint/abci/types"
     9  
    10  	sdk "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types"
    11  	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/x/staking/types"
    12  )
    13  
    14  // Calculate the ValidatorUpdates for the current block
    15  // Called in each EndBlock
    16  func (k Keeper) BlockValidatorUpdates(ctx sdk.Context) []abci.ValidatorUpdate {
    17  	// Calculate validator set changes.
    18  	//
    19  	// NOTE: ApplyAndReturnValidatorSetUpdates has to come before
    20  	// UnbondAllMatureValidatorQueue.
    21  	// This fixes a bug when the unbonding period is instant (is the case in
    22  	// some of the tests). The test expected the validator to be completely
    23  	// unbonded after the Endblocker (go from Bonded -> Unbonding during
    24  	// ApplyAndReturnValidatorSetUpdates and then Unbonding -> Unbonded during
    25  	// UnbondAllMatureValidatorQueue).
    26  	validatorUpdates := k.ApplyAndReturnValidatorSetUpdates(ctx)
    27  
    28  	// Unbond all mature validators from the unbonding queue.
    29  	k.UnbondAllMatureValidatorQueue(ctx)
    30  
    31  	// Remove all mature unbonding delegations from the ubd queue.
    32  	matureUnbonds := k.DequeueAllMatureUBDQueue(ctx, ctx.BlockHeader().Time)
    33  	for _, dvPair := range matureUnbonds {
    34  		balances, err := k.CompleteUnbondingWithAmount(ctx, dvPair.DelegatorAddress, dvPair.ValidatorAddress)
    35  		if err != nil {
    36  			continue
    37  		}
    38  
    39  		ctx.EventManager().EmitEvent(
    40  			sdk.NewEvent(
    41  				types.EventTypeCompleteUnbonding,
    42  				sdk.NewAttribute(sdk.AttributeKeyAmount, balances.String()),
    43  				sdk.NewAttribute(types.AttributeKeyValidator, dvPair.ValidatorAddress.String()),
    44  				sdk.NewAttribute(types.AttributeKeyDelegator, dvPair.DelegatorAddress.String()),
    45  			),
    46  		)
    47  	}
    48  
    49  	// Remove all mature redelegations from the red queue.
    50  	matureRedelegations := k.DequeueAllMatureRedelegationQueue(ctx, ctx.BlockHeader().Time)
    51  	for _, dvvTriplet := range matureRedelegations {
    52  		balances, err := k.CompleteRedelegationWithAmount(
    53  			ctx,
    54  			dvvTriplet.DelegatorAddress,
    55  			dvvTriplet.ValidatorSrcAddress,
    56  			dvvTriplet.ValidatorDstAddress,
    57  		)
    58  		if err != nil {
    59  			continue
    60  		}
    61  
    62  		ctx.EventManager().EmitEvent(
    63  			sdk.NewEvent(
    64  				types.EventTypeCompleteRedelegation,
    65  				sdk.NewAttribute(sdk.AttributeKeyAmount, balances.String()),
    66  				sdk.NewAttribute(types.AttributeKeyDelegator, dvvTriplet.DelegatorAddress.String()),
    67  				sdk.NewAttribute(types.AttributeKeySrcValidator, dvvTriplet.ValidatorSrcAddress.String()),
    68  				sdk.NewAttribute(types.AttributeKeyDstValidator, dvvTriplet.ValidatorDstAddress.String()),
    69  			),
    70  		)
    71  	}
    72  
    73  	return validatorUpdates
    74  }
    75  
    76  // Apply and return accumulated updates to the bonded validator set. Also,
    77  // * Updates the active valset as keyed by LastValidatorPowerKey.
    78  // * Updates the total power as keyed by LastTotalPowerKey.
    79  // * Updates validator status' according to updated powers.
    80  // * Updates the fee pool bonded vs not-bonded tokens.
    81  // * Updates relevant indices.
    82  // It gets called once after genesis, another time maybe after genesis transactions,
    83  // then once at every EndBlock.
    84  //
    85  // CONTRACT: Only validators with non-zero power or zero-power that were bonded
    86  // at the previous block height or were removed from the validator set entirely
    87  // are returned to Tendermint.
    88  func (k Keeper) ApplyAndReturnValidatorSetUpdates(ctx sdk.Context) (updates []abci.ValidatorUpdate) {
    89  
    90  	maxValidators := k.GetParams(ctx).MaxValidators
    91  	totalPower := sdk.ZeroInt()
    92  	amtFromBondedToNotBonded, amtFromNotBondedToBonded := sdk.ZeroInt(), sdk.ZeroInt()
    93  
    94  	// Retrieve the last validator set.
    95  	// The persistent set is updated later in this function.
    96  	// (see LastValidatorPowerKey).
    97  	last := k.getLastValidatorsByAddr(ctx)
    98  
    99  	// Iterate over validators, highest power to lowest.
   100  	iterator := k.ValidatorsPowerStoreIterator(ctx)
   101  	defer iterator.Close()
   102  	for count := 0; iterator.Valid() && count < int(maxValidators); iterator.Next() {
   103  
   104  		// everything that is iterated in this loop is becoming or already a
   105  		// part of the bonded validator set
   106  
   107  		valAddr := sdk.ValAddress(iterator.Value())
   108  		validator := k.mustGetValidator(ctx, valAddr)
   109  
   110  		if validator.Jailed {
   111  			panic("should never retrieve a jailed validator from the power store")
   112  		}
   113  
   114  		// if we get to a zero-power validator (which we don't bond),
   115  		// there are no more possible bonded validators
   116  		if validator.PotentialConsensusPower() == 0 {
   117  			break
   118  		}
   119  
   120  		// apply the appropriate state change if necessary
   121  		switch {
   122  		case validator.IsUnbonded():
   123  			validator = k.unbondedToBonded(ctx, validator)
   124  			amtFromNotBondedToBonded = amtFromNotBondedToBonded.Add(validator.GetTokens())
   125  		case validator.IsUnbonding():
   126  			validator = k.unbondingToBonded(ctx, validator)
   127  			amtFromNotBondedToBonded = amtFromNotBondedToBonded.Add(validator.GetTokens())
   128  		case validator.IsBonded():
   129  			// no state change
   130  		default:
   131  			panic("unexpected validator status")
   132  		}
   133  
   134  		// fetch the old power bytes
   135  		var valAddrBytes [sdk.AddrLen]byte
   136  		copy(valAddrBytes[:], valAddr[:])
   137  		oldPowerBytes, found := last[valAddrBytes]
   138  
   139  		newPower := validator.ConsensusPower()
   140  		newPowerBytes := k.cdc.MustMarshalBinaryLengthPrefixed(newPower)
   141  
   142  		// update the validator set if power has changed
   143  		if !found || !bytes.Equal(oldPowerBytes, newPowerBytes) {
   144  			updates = append(updates, validator.ABCIValidatorUpdate())
   145  			k.SetLastValidatorPower(ctx, valAddr, newPower)
   146  		}
   147  
   148  		delete(last, valAddrBytes)
   149  
   150  		count++
   151  		totalPower = totalPower.Add(sdk.NewInt(newPower))
   152  	}
   153  
   154  	noLongerBonded := sortNoLongerBonded(last)
   155  	for _, valAddrBytes := range noLongerBonded {
   156  
   157  		validator := k.mustGetValidator(ctx, sdk.ValAddress(valAddrBytes))
   158  		validator = k.bondedToUnbonding(ctx, validator)
   159  		amtFromBondedToNotBonded = amtFromBondedToNotBonded.Add(validator.GetTokens())
   160  		k.DeleteLastValidatorPower(ctx, validator.GetOperator())
   161  		updates = append(updates, validator.ABCIValidatorUpdateZero())
   162  	}
   163  
   164  	// Update the pools based on the recent updates in the validator set:
   165  	// - The tokens from the non-bonded candidates that enter the new validator set need to be transferred
   166  	// to the Bonded pool.
   167  	// - The tokens from the bonded validators that are being kicked out from the validator set
   168  	// need to be transferred to the NotBonded pool.
   169  	switch {
   170  	// Compare and subtract the respective amounts to only perform one transfer.
   171  	// This is done in order to avoid doing multiple updates inside each iterator/loop.
   172  	case amtFromNotBondedToBonded.GT(amtFromBondedToNotBonded):
   173  		k.notBondedTokensToBonded(ctx, amtFromNotBondedToBonded.Sub(amtFromBondedToNotBonded))
   174  	case amtFromNotBondedToBonded.LT(amtFromBondedToNotBonded):
   175  		k.bondedTokensToNotBonded(ctx, amtFromBondedToNotBonded.Sub(amtFromNotBondedToBonded))
   176  	default:
   177  		// equal amounts of tokens; no update required
   178  	}
   179  
   180  	// set total power on lookup index if there are any updates
   181  	if len(updates) > 0 {
   182  		k.SetLastTotalPower(ctx, totalPower)
   183  	}
   184  
   185  	return updates
   186  }
   187  
   188  // Validator state transitions
   189  
   190  func (k Keeper) bondedToUnbonding(ctx sdk.Context, validator types.Validator) types.Validator {
   191  	if !validator.IsBonded() {
   192  		panic(fmt.Sprintf("bad state transition bondedToUnbonding, validator: %v\n", validator))
   193  	}
   194  	return k.beginUnbondingValidator(ctx, validator)
   195  }
   196  
   197  func (k Keeper) unbondingToBonded(ctx sdk.Context, validator types.Validator) types.Validator {
   198  	if !validator.IsUnbonding() {
   199  		panic(fmt.Sprintf("bad state transition unbondingToBonded, validator: %v\n", validator))
   200  	}
   201  	return k.bondValidator(ctx, validator)
   202  }
   203  
   204  func (k Keeper) unbondedToBonded(ctx sdk.Context, validator types.Validator) types.Validator {
   205  	if !validator.IsUnbonded() {
   206  		panic(fmt.Sprintf("bad state transition unbondedToBonded, validator: %v\n", validator))
   207  	}
   208  	return k.bondValidator(ctx, validator)
   209  }
   210  
   211  // switches a validator from unbonding state to unbonded state
   212  func (k Keeper) unbondingToUnbonded(ctx sdk.Context, validator types.Validator) types.Validator {
   213  	if !validator.IsUnbonding() {
   214  		panic(fmt.Sprintf("bad state transition unbondingToBonded, validator: %v\n", validator))
   215  	}
   216  	return k.completeUnbondingValidator(ctx, validator)
   217  }
   218  
   219  // send a validator to jail
   220  func (k Keeper) jailValidator(ctx sdk.Context, validator types.Validator) {
   221  	if validator.Jailed {
   222  		panic(fmt.Sprintf("cannot jail already jailed validator, validator: %v\n", validator))
   223  	}
   224  
   225  	validator.Jailed = true
   226  	k.SetValidator(ctx, validator)
   227  	k.DeleteValidatorByPowerIndex(ctx, validator)
   228  }
   229  
   230  // remove a validator from jail
   231  func (k Keeper) unjailValidator(ctx sdk.Context, validator types.Validator) {
   232  	if !validator.Jailed {
   233  		panic(fmt.Sprintf("cannot unjail already unjailed validator, validator: %v\n", validator))
   234  	}
   235  
   236  	validator.Jailed = false
   237  	k.SetValidator(ctx, validator)
   238  	k.SetValidatorByPowerIndex(ctx, validator)
   239  }
   240  
   241  // perform all the store operations for when a validator status becomes bonded
   242  func (k Keeper) bondValidator(ctx sdk.Context, validator types.Validator) types.Validator {
   243  
   244  	// delete the validator by power index, as the key will change
   245  	k.DeleteValidatorByPowerIndex(ctx, validator)
   246  
   247  	validator = validator.UpdateStatus(sdk.Bonded)
   248  
   249  	// save the now bonded validator record to the two referenced stores
   250  	k.SetValidator(ctx, validator)
   251  	k.SetValidatorByPowerIndex(ctx, validator)
   252  
   253  	// delete from queue if present
   254  	k.DeleteValidatorQueue(ctx, validator)
   255  
   256  	// trigger hook
   257  	k.AfterValidatorBonded(ctx, validator.ConsAddress(), validator.OperatorAddress)
   258  
   259  	return validator
   260  }
   261  
   262  // perform all the store operations for when a validator begins unbonding
   263  func (k Keeper) beginUnbondingValidator(ctx sdk.Context, validator types.Validator) types.Validator {
   264  
   265  	params := k.GetParams(ctx)
   266  
   267  	// delete the validator by power index, as the key will change
   268  	k.DeleteValidatorByPowerIndex(ctx, validator)
   269  
   270  	// sanity check
   271  	if validator.Status != sdk.Bonded {
   272  		panic(fmt.Sprintf("should not already be unbonded or unbonding, validator: %v\n", validator))
   273  	}
   274  
   275  	validator = validator.UpdateStatus(sdk.Unbonding)
   276  
   277  	// set the unbonding completion time and completion height appropriately
   278  	validator.UnbondingCompletionTime = ctx.BlockHeader().Time.Add(params.UnbondingTime)
   279  	validator.UnbondingHeight = ctx.BlockHeader().Height
   280  
   281  	// save the now unbonded validator record and power index
   282  	k.SetValidator(ctx, validator)
   283  	k.SetValidatorByPowerIndex(ctx, validator)
   284  
   285  	// Adds to unbonding validator queue
   286  	k.InsertValidatorQueue(ctx, validator)
   287  
   288  	// trigger hook
   289  	k.AfterValidatorBeginUnbonding(ctx, validator.ConsAddress(), validator.OperatorAddress)
   290  
   291  	return validator
   292  }
   293  
   294  // perform all the store operations for when a validator status becomes unbonded
   295  func (k Keeper) completeUnbondingValidator(ctx sdk.Context, validator types.Validator) types.Validator {
   296  	validator = validator.UpdateStatus(sdk.Unbonded)
   297  	k.SetValidator(ctx, validator)
   298  	return validator
   299  }
   300  
   301  // map of operator addresses to serialized power
   302  type validatorsByAddr map[[sdk.AddrLen]byte][]byte
   303  
   304  // get the last validator set
   305  func (k Keeper) getLastValidatorsByAddr(ctx sdk.Context) validatorsByAddr {
   306  	last := make(validatorsByAddr)
   307  	iterator := k.LastValidatorsIterator(ctx)
   308  	defer iterator.Close()
   309  
   310  	for ; iterator.Valid(); iterator.Next() {
   311  		var valAddr [sdk.AddrLen]byte
   312  		// extract the validator address from the key (prefix is 1-byte)
   313  		copy(valAddr[:], iterator.Key()[1:])
   314  		powerBytes := iterator.Value()
   315  		last[valAddr] = make([]byte, len(powerBytes))
   316  		copy(last[valAddr], powerBytes)
   317  	}
   318  	return last
   319  }
   320  
   321  // given a map of remaining validators to previous bonded power
   322  // returns the list of validators to be unbonded, sorted by operator address
   323  func sortNoLongerBonded(last validatorsByAddr) [][]byte {
   324  	// sort the map keys for determinism
   325  	noLongerBonded := make([][]byte, len(last))
   326  	index := 0
   327  	for valAddrBytes := range last {
   328  		valAddr := make([]byte, sdk.AddrLen)
   329  		copy(valAddr, valAddrBytes[:])
   330  		noLongerBonded[index] = valAddr
   331  		index++
   332  	}
   333  	// sorted by address - order doesn't matter
   334  	sort.SliceStable(noLongerBonded, func(i, j int) bool {
   335  		// -1 means strictly less than
   336  		return bytes.Compare(noLongerBonded[i], noLongerBonded[j]) == -1
   337  	})
   338  	return noLongerBonded
   339  }