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 }