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 }