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

     1  package keeper
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"sort"
     7  
     8  	sdk "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types"
     9  	abci "github.com/fibonacci-chain/fbc/libs/tendermint/abci/types"
    10  	"github.com/fibonacci-chain/fbc/x/staking/types"
    11  )
    12  
    13  // ApplyAndReturnValidatorSetUpdates applies and returns accumulated updates to the bonded validator set. Also,
    14  // * Updates the active valset as keyed by LastValidatorPowerKey.
    15  // * Updates the total power as keyed by LastTotalPowerKey.
    16  // * Updates validator status' according to updated powers.
    17  // * Updates the fee pool bonded vs not-bonded tokens.
    18  // * Updates relevant indices.
    19  // It gets called once after genesis, another time maybe after genesis transactions,
    20  // then once at every EndBlock.
    21  //
    22  // CONTRACT: Only validators with non-zero power or zero-power that were bonded
    23  // at the previous block height or were removed from the validator set entirely
    24  // are returned to Tendermint.
    25  func (k Keeper) ApplyAndReturnValidatorSetUpdates(ctx sdk.Context) (updates []abci.ValidatorUpdate) {
    26  
    27  	store := ctx.KVStore(k.storeKey)
    28  	maxValidators := k.GetParams(ctx).MaxValidators
    29  	totalPower := sdk.ZeroInt()
    30  
    31  	// Retrieve the last validator set. The persistent set is updated later in this function (see LastValidatorPowerKey)
    32  	last := k.getLastValidatorsByAddr(ctx)
    33  
    34  	// Iterate over validators, highest power to lowest.
    35  	iterator := sdk.KVStoreReversePrefixIterator(store, types.ValidatorsByPowerIndexKey)
    36  	defer iterator.Close()
    37  	for count := 0; iterator.Valid() && count < int(maxValidators); iterator.Next() {
    38  		valAddr := sdk.ValAddress(iterator.Value())
    39  		validator := k.mustGetValidator(ctx, valAddr)
    40  
    41  		if validator.Jailed {
    42  			panic("should never retrieve a jailed validator from the power store")
    43  		}
    44  
    45  		// if we get to a zero-power validator (which we don't add shares to), there are no more possible elected validators
    46  		if validator.PotentialConsensusPowerByShares() == 0 {
    47  			break
    48  		}
    49  
    50  		// apply the appropriate state change if necessary
    51  		switch {
    52  		case validator.IsUnbonded():
    53  			validator = k.unbondedToBonded(ctx, validator)
    54  		case validator.IsUnbonding():
    55  			validator = k.unbondingToBonded(ctx, validator)
    56  		case validator.IsBonded():
    57  			// no state change
    58  		default:
    59  			panic("unexpected validator status")
    60  		}
    61  
    62  		// fetch the old power bytes
    63  		var valAddrBytes [sdk.AddrLen]byte
    64  		copy(valAddrBytes[:], valAddr[:])
    65  		oldPowerBytes, found := last[valAddrBytes]
    66  
    67  		// calculate the new power bytes
    68  		newPower := validator.ConsensusPowerByShares()
    69  		newPowerBytes := k.cdcMarshl.GetCdc().MustMarshalBinaryLengthPrefixed(newPower)
    70  
    71  		// update the validator set if power has changed
    72  		if !found || !bytes.Equal(oldPowerBytes, newPowerBytes) {
    73  			updates = append(updates, validator.ABCIValidatorUpdateByShares())
    74  
    75  			// set validator power on lookup index
    76  			k.SetLastValidatorPower(ctx, valAddr, newPower)
    77  		}
    78  
    79  		// validator still in the validator set, so delete from the copy
    80  		delete(last, valAddrBytes)
    81  
    82  		// keep count
    83  		count++
    84  		totalPower = totalPower.Add(sdk.NewInt(newPower))
    85  	}
    86  
    87  	// sort the no-longer-bonded validators
    88  	noLongerBonded := sortNoLongerBonded(last)
    89  
    90  	// iterate through the sorted no-longer-bonded validators
    91  	for _, valAddrBytes := range noLongerBonded {
    92  
    93  		// fetch the validator
    94  		validator := k.mustGetValidator(ctx, sdk.ValAddress(valAddrBytes))
    95  
    96  		// bonded to unbonding
    97  		validator = k.bondedToUnbonding(ctx, validator)
    98  
    99  		// delete from the bonded validator index
   100  		k.DeleteLastValidatorPower(ctx, validator.GetOperator())
   101  
   102  		// update the validator set
   103  		updates = append(updates, validator.ABCIValidatorUpdateZero())
   104  	}
   105  
   106  	// set total power on lookup index if there are any updates
   107  	if len(updates) > 0 {
   108  		k.SetLastTotalPower(ctx, totalPower)
   109  	}
   110  
   111  	return updates
   112  }
   113  
   114  // Validator state transitions
   115  // bondedToUnbonding switches a validator from bonded state to unbonding state
   116  func (k Keeper) bondedToUnbonding(ctx sdk.Context, validator types.Validator) types.Validator {
   117  	if !validator.IsBonded() {
   118  		panic(fmt.Sprintf("bad state transition bondedToUnbonding, validator: %v\n", validator))
   119  	}
   120  	return k.beginUnbondingValidator(ctx, validator)
   121  }
   122  
   123  // unbondingToBonded switches a validator from unbonding state to bonded state
   124  func (k Keeper) unbondingToBonded(ctx sdk.Context, validator types.Validator) types.Validator {
   125  	if !validator.IsUnbonding() {
   126  		panic(fmt.Sprintf("bad state transition unbondingToBonded, validator: %v\n", validator))
   127  	}
   128  	return k.bondValidator(ctx, validator)
   129  }
   130  
   131  // unbondedToBonded switches a validator from unbonded state to bonded state
   132  func (k Keeper) unbondedToBonded(ctx sdk.Context, validator types.Validator) types.Validator {
   133  	if !validator.IsUnbonded() {
   134  		panic(fmt.Sprintf("bad state transition unbondedToBonded, validator: %v\n", validator))
   135  	}
   136  	return k.bondValidator(ctx, validator)
   137  }
   138  
   139  // unbondingToUnbonded switches a validator from unbonding state to unbonded state
   140  func (k Keeper) unbondingToUnbonded(ctx sdk.Context, validator types.Validator) types.Validator {
   141  	if !validator.IsUnbonding() {
   142  		panic(fmt.Sprintf("bad state transition unbondingToBonded, validator: %v\n", validator))
   143  	}
   144  	return k.completeUnbondingValidator(ctx, validator)
   145  }
   146  
   147  // jailValidator sends a validator to jail
   148  func (k Keeper) jailValidator(ctx sdk.Context, validator types.Validator) {
   149  	if validator.Jailed {
   150  		panic(fmt.Sprintf("cannot jail already jailed validator, validator: %v\n", validator))
   151  	}
   152  
   153  	validator.Jailed = true
   154  	k.SetValidator(ctx, validator)
   155  	k.DeleteValidatorByPowerIndex(ctx, validator)
   156  }
   157  
   158  // unjailValidator removes a validator from jail
   159  func (k Keeper) unjailValidator(ctx sdk.Context, validator types.Validator) {
   160  	if !validator.Jailed {
   161  		panic(fmt.Sprintf("cannot unjail already unjailed validator, validator: %v\n", validator))
   162  	}
   163  
   164  	validator.Jailed = false
   165  	k.SetValidator(ctx, validator)
   166  	k.SetValidatorByPowerIndex(ctx, validator)
   167  }
   168  
   169  // bondValidator performs all the store operations for when a validator status becomes bonded
   170  func (k Keeper) bondValidator(ctx sdk.Context, validator types.Validator) types.Validator {
   171  
   172  	// delete the validator by power index, as the key will change
   173  	k.DeleteValidatorByPowerIndex(ctx, validator)
   174  
   175  	// set the status
   176  	validator = validator.UpdateStatus(sdk.Bonded)
   177  
   178  	// save the now bonded validator record to the two referenced stores
   179  	k.SetValidator(ctx, validator)
   180  	k.SetValidatorByPowerIndex(ctx, validator)
   181  
   182  	// delete from queue if present
   183  	k.DeleteValidatorQueue(ctx, validator)
   184  
   185  	// trigger hook
   186  	k.AfterValidatorBonded(ctx, validator.ConsAddress(), validator.OperatorAddress)
   187  
   188  	return validator
   189  }
   190  
   191  // beginUnbondingValidator performs all the store operations for when a validator begins unbonding
   192  func (k Keeper) beginUnbondingValidator(ctx sdk.Context, validator types.Validator) types.Validator {
   193  
   194  	params := k.GetParams(ctx)
   195  
   196  	// delete the validator by power index, as the key will change
   197  	k.DeleteValidatorByPowerIndex(ctx, validator)
   198  
   199  	// sanity check
   200  	if validator.Status != sdk.Bonded {
   201  		panic(fmt.Sprintf("should not already be unbonded or unbonding, validator: %v\n", validator))
   202  	}
   203  
   204  	// set the status
   205  	validator = validator.UpdateStatus(sdk.Unbonding)
   206  
   207  	// set the unbonding completion time and completion height appropriately
   208  	validator.UnbondingCompletionTime = ctx.BlockHeader().Time.Add(params.UnbondingTime)
   209  	validator.UnbondingHeight = ctx.BlockHeader().Height
   210  
   211  	// save the now unbonded validator record and power index
   212  	k.SetValidator(ctx, validator)
   213  	k.SetValidatorByPowerIndex(ctx, validator)
   214  
   215  	// adds to unbonding validator queue
   216  	k.InsertValidatorQueue(ctx, validator)
   217  
   218  	// trigger hook
   219  	k.AfterValidatorBeginUnbonding(ctx, validator.ConsAddress(), validator.OperatorAddress)
   220  
   221  	return validator
   222  }
   223  
   224  // completeUnbondingValidator performs all the store operations for when a validator status becomes unbonded
   225  func (k Keeper) completeUnbondingValidator(ctx sdk.Context, validator types.Validator) types.Validator {
   226  	validator = validator.UpdateStatus(sdk.Unbonded)
   227  	k.SetValidator(ctx, validator)
   228  	return validator
   229  }
   230  
   231  // map of operator addresses to serialized power
   232  type validatorsByAddr map[[sdk.AddrLen]byte][]byte
   233  
   234  // get the last validator set
   235  func (k Keeper) getLastValidatorsByAddr(ctx sdk.Context) validatorsByAddr {
   236  	last := make(validatorsByAddr)
   237  	store := ctx.KVStore(k.storeKey)
   238  	iterator := sdk.KVStorePrefixIterator(store, types.LastValidatorPowerKey)
   239  	defer iterator.Close()
   240  	// iterate over the last validator set index
   241  	for ; iterator.Valid(); iterator.Next() {
   242  		var valAddr [sdk.AddrLen]byte
   243  		// extract the validator address from the key (prefix is 1-byte)
   244  		copy(valAddr[:], iterator.Key()[1:])
   245  		// power bytes is just the value
   246  		powerBytes := iterator.Value()
   247  		last[valAddr] = make([]byte, len(powerBytes))
   248  		copy(last[valAddr][:], powerBytes[:])
   249  	}
   250  	return last
   251  }
   252  
   253  // given a map of remaining validators to previous bonded power
   254  // returns the list of validators to be unbonded, sorted by operator address
   255  func sortNoLongerBonded(last validatorsByAddr) [][]byte {
   256  	// sort the map keys for determinism
   257  	noLongerBonded := make([][]byte, len(last))
   258  	index := 0
   259  	for valAddrBytes := range last {
   260  		valAddr := make([]byte, sdk.AddrLen)
   261  		copy(valAddr[:], valAddrBytes[:])
   262  		noLongerBonded[index] = valAddr
   263  		index++
   264  	}
   265  	// sorted by address - order doesn't matter
   266  	sort.SliceStable(noLongerBonded, func(i, j int) bool {
   267  		// -1 means strictly less than
   268  		return bytes.Compare(noLongerBonded[i], noLongerBonded[j]) == -1
   269  	})
   270  	return noLongerBonded
   271  }