github.com/cosmos/cosmos-sdk@v0.50.10/x/staking/keeper/unbonding.go (about) 1 package keeper 2 3 import ( 4 "context" 5 "encoding/binary" 6 7 errorsmod "cosmossdk.io/errors" 8 9 sdk "github.com/cosmos/cosmos-sdk/types" 10 "github.com/cosmos/cosmos-sdk/x/staking/types" 11 ) 12 13 // IncrementUnbondingID increments and returns a unique ID for an unbonding operation 14 func (k Keeper) IncrementUnbondingID(ctx context.Context) (unbondingID uint64, err error) { 15 store := k.storeService.OpenKVStore(ctx) 16 bz, err := store.Get(types.UnbondingIDKey) 17 if err != nil { 18 return 0, err 19 } 20 21 if bz != nil { 22 unbondingID = binary.BigEndian.Uint64(bz) 23 } 24 25 unbondingID++ 26 27 // Convert back into bytes for storage 28 bz = make([]byte, 8) 29 binary.BigEndian.PutUint64(bz, unbondingID) 30 31 if err = store.Set(types.UnbondingIDKey, bz); err != nil { 32 return 0, err 33 } 34 35 return unbondingID, err 36 } 37 38 // DeleteUnbondingIndex removes a mapping from UnbondingId to unbonding operation 39 func (k Keeper) DeleteUnbondingIndex(ctx context.Context, id uint64) error { 40 store := k.storeService.OpenKVStore(ctx) 41 return store.Delete(types.GetUnbondingIndexKey(id)) 42 } 43 44 // GetUnbondingType returns the enum type of unbonding which is any of 45 // {UnbondingDelegation | Redelegation | ValidatorUnbonding} 46 func (k Keeper) GetUnbondingType(ctx context.Context, id uint64) (unbondingType types.UnbondingType, err error) { 47 store := k.storeService.OpenKVStore(ctx) 48 49 bz, err := store.Get(types.GetUnbondingTypeKey(id)) 50 if err != nil { 51 return unbondingType, err 52 } 53 54 if bz == nil { 55 return unbondingType, types.ErrNoUnbondingType 56 } 57 58 return types.UnbondingType(binary.BigEndian.Uint64(bz)), nil 59 } 60 61 // SetUnbondingType sets the enum type of unbonding which is any of 62 // {UnbondingDelegation | Redelegation | ValidatorUnbonding} 63 func (k Keeper) SetUnbondingType(ctx context.Context, id uint64, unbondingType types.UnbondingType) error { 64 store := k.storeService.OpenKVStore(ctx) 65 66 // Convert into bytes for storage 67 bz := make([]byte, 8) 68 binary.BigEndian.PutUint64(bz, uint64(unbondingType)) 69 70 return store.Set(types.GetUnbondingTypeKey(id), bz) 71 } 72 73 // GetUnbondingDelegationByUnbondingID returns a unbonding delegation that has an unbonding delegation entry with a certain ID 74 func (k Keeper) GetUnbondingDelegationByUnbondingID(ctx context.Context, id uint64) (ubd types.UnbondingDelegation, err error) { 75 store := k.storeService.OpenKVStore(ctx) 76 77 ubdKey, err := store.Get(types.GetUnbondingIndexKey(id)) 78 if err != nil { 79 return types.UnbondingDelegation{}, err 80 } 81 82 if ubdKey == nil { 83 return types.UnbondingDelegation{}, types.ErrNoUnbondingDelegation 84 } 85 86 value, err := store.Get(ubdKey) 87 if err != nil { 88 return types.UnbondingDelegation{}, err 89 } 90 91 if value == nil { 92 return types.UnbondingDelegation{}, types.ErrNoUnbondingDelegation 93 } 94 95 ubd, err = types.UnmarshalUBD(k.cdc, value) 96 // An error here means that what we got wasn't the right type 97 if err != nil { 98 return types.UnbondingDelegation{}, err 99 } 100 101 return ubd, nil 102 } 103 104 // GetRedelegationByUnbondingID returns a unbonding delegation that has an unbonding delegation entry with a certain ID 105 func (k Keeper) GetRedelegationByUnbondingID(ctx context.Context, id uint64) (red types.Redelegation, err error) { 106 store := k.storeService.OpenKVStore(ctx) 107 108 redKey, err := store.Get(types.GetUnbondingIndexKey(id)) 109 if err != nil { 110 return types.Redelegation{}, err 111 } 112 113 if redKey == nil { 114 return types.Redelegation{}, types.ErrNoRedelegation 115 } 116 117 value, err := store.Get(redKey) 118 if err != nil { 119 return types.Redelegation{}, err 120 } 121 122 if value == nil { 123 return types.Redelegation{}, types.ErrNoRedelegation 124 } 125 126 red, err = types.UnmarshalRED(k.cdc, value) 127 // An error here means that what we got wasn't the right type 128 if err != nil { 129 return types.Redelegation{}, err 130 } 131 132 return red, nil 133 } 134 135 // GetValidatorByUnbondingID returns the validator that is unbonding with a certain unbonding op ID 136 func (k Keeper) GetValidatorByUnbondingID(ctx context.Context, id uint64) (val types.Validator, err error) { 137 store := k.storeService.OpenKVStore(ctx) 138 139 valKey, err := store.Get(types.GetUnbondingIndexKey(id)) 140 if err != nil { 141 return types.Validator{}, err 142 } 143 144 if valKey == nil { 145 return types.Validator{}, types.ErrNoValidatorFound 146 } 147 148 value, err := store.Get(valKey) 149 if err != nil { 150 return types.Validator{}, err 151 } 152 153 if value == nil { 154 return types.Validator{}, types.ErrNoValidatorFound 155 } 156 157 val, err = types.UnmarshalValidator(k.cdc, value) 158 // An error here means that what we got wasn't the right type 159 if err != nil { 160 return types.Validator{}, err 161 } 162 163 return val, nil 164 } 165 166 // SetUnbondingDelegationByUnbondingID sets an index to look up an UnbondingDelegation 167 // by the unbondingID of an UnbondingDelegationEntry that it contains Note, it does not 168 // set the unbonding delegation itself, use SetUnbondingDelegation(ctx, ubd) for that 169 func (k Keeper) SetUnbondingDelegationByUnbondingID(ctx context.Context, ubd types.UnbondingDelegation, id uint64) error { 170 store := k.storeService.OpenKVStore(ctx) 171 delAddr, err := k.authKeeper.AddressCodec().StringToBytes(ubd.DelegatorAddress) 172 if err != nil { 173 return err 174 } 175 valAddr, err := k.validatorAddressCodec.StringToBytes(ubd.ValidatorAddress) 176 if err != nil { 177 return err 178 } 179 180 ubdKey := types.GetUBDKey(delAddr, valAddr) 181 if err = store.Set(types.GetUnbondingIndexKey(id), ubdKey); err != nil { 182 return err 183 } 184 185 // Set unbonding type so that we know how to deserialize it later 186 return k.SetUnbondingType(ctx, id, types.UnbondingType_UnbondingDelegation) 187 } 188 189 // SetRedelegationByUnbondingID sets an index to look up an Redelegation by the unbondingID of an RedelegationEntry that it contains 190 // Note, it does not set the redelegation itself, use SetRedelegation(ctx, red) for that 191 func (k Keeper) SetRedelegationByUnbondingID(ctx context.Context, red types.Redelegation, id uint64) error { 192 store := k.storeService.OpenKVStore(ctx) 193 194 delAddr, err := k.authKeeper.AddressCodec().StringToBytes(red.DelegatorAddress) 195 if err != nil { 196 return err 197 } 198 199 valSrcAddr, err := k.validatorAddressCodec.StringToBytes(red.ValidatorSrcAddress) 200 if err != nil { 201 return err 202 } 203 204 valDstAddr, err := k.validatorAddressCodec.StringToBytes(red.ValidatorDstAddress) 205 if err != nil { 206 return err 207 } 208 209 redKey := types.GetREDKey(delAddr, valSrcAddr, valDstAddr) 210 if err = store.Set(types.GetUnbondingIndexKey(id), redKey); err != nil { 211 return err 212 } 213 214 // Set unbonding type so that we know how to deserialize it later 215 return k.SetUnbondingType(ctx, id, types.UnbondingType_Redelegation) 216 } 217 218 // SetValidatorByUnbondingID sets an index to look up a Validator by the unbondingID corresponding to its current unbonding 219 // Note, it does not set the validator itself, use SetValidator(ctx, val) for that 220 func (k Keeper) SetValidatorByUnbondingID(ctx context.Context, val types.Validator, id uint64) error { 221 store := k.storeService.OpenKVStore(ctx) 222 223 valAddr, err := k.validatorAddressCodec.StringToBytes(val.OperatorAddress) 224 if err != nil { 225 return err 226 } 227 228 valKey := types.GetValidatorKey(valAddr) 229 if err = store.Set(types.GetUnbondingIndexKey(id), valKey); err != nil { 230 return err 231 } 232 233 // Set unbonding type so that we know how to deserialize it later 234 return k.SetUnbondingType(ctx, id, types.UnbondingType_ValidatorUnbonding) 235 } 236 237 // unbondingDelegationEntryArrayIndex and redelegationEntryArrayIndex are utilities to find 238 // at which position in the Entries array the entry with a given id is 239 func unbondingDelegationEntryArrayIndex(ubd types.UnbondingDelegation, id uint64) (index int, err error) { 240 for i, entry := range ubd.Entries { 241 // we find the entry with the right ID 242 if entry.UnbondingId == id { 243 return i, nil 244 } 245 } 246 247 return 0, types.ErrNoUnbondingDelegation 248 } 249 250 func redelegationEntryArrayIndex(red types.Redelegation, id uint64) (index int, err error) { 251 for i, entry := range red.Entries { 252 // we find the entry with the right ID 253 if entry.UnbondingId == id { 254 return i, nil 255 } 256 } 257 258 return 0, types.ErrNoRedelegation 259 } 260 261 // UnbondingCanComplete allows a stopped unbonding operation, such as an 262 // unbonding delegation, a redelegation, or a validator unbonding to complete. 263 // In order for the unbonding operation with `id` to eventually complete, every call 264 // to PutUnbondingOnHold(id) must be matched by a call to UnbondingCanComplete(id). 265 func (k Keeper) UnbondingCanComplete(ctx context.Context, id uint64) error { 266 unbondingType, err := k.GetUnbondingType(ctx, id) 267 if err != nil { 268 return err 269 } 270 271 switch unbondingType { 272 case types.UnbondingType_UnbondingDelegation: 273 if err := k.unbondingDelegationEntryCanComplete(ctx, id); err != nil { 274 return err 275 } 276 case types.UnbondingType_Redelegation: 277 if err := k.redelegationEntryCanComplete(ctx, id); err != nil { 278 return err 279 } 280 case types.UnbondingType_ValidatorUnbonding: 281 if err := k.validatorUnbondingCanComplete(ctx, id); err != nil { 282 return err 283 } 284 default: 285 return types.ErrUnbondingNotFound 286 } 287 288 return nil 289 } 290 291 func (k Keeper) unbondingDelegationEntryCanComplete(ctx context.Context, id uint64) error { 292 ubd, err := k.GetUnbondingDelegationByUnbondingID(ctx, id) 293 if err != nil { 294 return err 295 } 296 297 i, err := unbondingDelegationEntryArrayIndex(ubd, id) 298 if err != nil { 299 return err 300 } 301 302 // The entry must be on hold 303 if !ubd.Entries[i].OnHold() { 304 return errorsmod.Wrapf( 305 types.ErrUnbondingOnHoldRefCountNegative, 306 "undelegation unbondingID(%d), expecting UnbondingOnHoldRefCount > 0, got %T", 307 id, ubd.Entries[i].UnbondingOnHoldRefCount, 308 ) 309 } 310 ubd.Entries[i].UnbondingOnHoldRefCount-- 311 312 sdkCtx := sdk.UnwrapSDKContext(ctx) 313 // Check if entry is matured. 314 if !ubd.Entries[i].OnHold() && ubd.Entries[i].IsMature(sdkCtx.BlockHeader().Time) { 315 // If matured, complete it. 316 delegatorAddress, err := k.authKeeper.AddressCodec().StringToBytes(ubd.DelegatorAddress) 317 if err != nil { 318 return err 319 } 320 321 bondDenom, err := k.BondDenom(ctx) 322 if err != nil { 323 return err 324 } 325 326 // track undelegation only when remaining or truncated shares are non-zero 327 if !ubd.Entries[i].Balance.IsZero() { 328 amt := sdk.NewCoin(bondDenom, ubd.Entries[i].Balance) 329 if err := k.bankKeeper.UndelegateCoinsFromModuleToAccount( 330 ctx, types.NotBondedPoolName, delegatorAddress, sdk.NewCoins(amt), 331 ); err != nil { 332 return err 333 } 334 } 335 336 // Remove entry 337 ubd.RemoveEntry(int64(i)) 338 // Remove from the UnbondingIndex 339 err = k.DeleteUnbondingIndex(ctx, id) 340 if err != nil { 341 return err 342 } 343 344 } 345 346 // set the unbonding delegation or remove it if there are no more entries 347 if len(ubd.Entries) == 0 { 348 return k.RemoveUnbondingDelegation(ctx, ubd) 349 } 350 351 return k.SetUnbondingDelegation(ctx, ubd) 352 } 353 354 func (k Keeper) redelegationEntryCanComplete(ctx context.Context, id uint64) error { 355 red, err := k.GetRedelegationByUnbondingID(ctx, id) 356 if err != nil { 357 return err 358 } 359 360 i, err := redelegationEntryArrayIndex(red, id) 361 if err != nil { 362 return err 363 } 364 365 // The entry must be on hold 366 if !red.Entries[i].OnHold() { 367 return errorsmod.Wrapf( 368 types.ErrUnbondingOnHoldRefCountNegative, 369 "redelegation unbondingID(%d), expecting UnbondingOnHoldRefCount > 0, got %T", 370 id, red.Entries[i].UnbondingOnHoldRefCount, 371 ) 372 } 373 red.Entries[i].UnbondingOnHoldRefCount-- 374 375 sdkCtx := sdk.UnwrapSDKContext(ctx) 376 if !red.Entries[i].OnHold() && red.Entries[i].IsMature(sdkCtx.BlockHeader().Time) { 377 // If matured, complete it. 378 // Remove entry 379 red.RemoveEntry(int64(i)) 380 // Remove from the Unbonding index 381 if err = k.DeleteUnbondingIndex(ctx, id); err != nil { 382 return err 383 } 384 } 385 386 // set the redelegation or remove it if there are no more entries 387 if len(red.Entries) == 0 { 388 return k.RemoveRedelegation(ctx, red) 389 } 390 391 return k.SetRedelegation(ctx, red) 392 } 393 394 func (k Keeper) validatorUnbondingCanComplete(ctx context.Context, id uint64) error { 395 val, err := k.GetValidatorByUnbondingID(ctx, id) 396 if err != nil { 397 return err 398 } 399 400 if val.UnbondingOnHoldRefCount <= 0 { 401 return errorsmod.Wrapf( 402 types.ErrUnbondingOnHoldRefCountNegative, 403 "val(%s), expecting UnbondingOnHoldRefCount > 0, got %T", 404 val.OperatorAddress, val.UnbondingOnHoldRefCount, 405 ) 406 } 407 val.UnbondingOnHoldRefCount-- 408 return k.SetValidator(ctx, val) 409 } 410 411 // PutUnbondingOnHold allows an external module to stop an unbonding operation, 412 // such as an unbonding delegation, a redelegation, or a validator unbonding. 413 // In order for the unbonding operation with `id` to eventually complete, every call 414 // to PutUnbondingOnHold(id) must be matched by a call to UnbondingCanComplete(id). 415 func (k Keeper) PutUnbondingOnHold(ctx context.Context, id uint64) error { 416 unbondingType, err := k.GetUnbondingType(ctx, id) 417 if err != nil { 418 return err 419 } 420 switch unbondingType { 421 case types.UnbondingType_UnbondingDelegation: 422 if err := k.putUnbondingDelegationEntryOnHold(ctx, id); err != nil { 423 return err 424 } 425 case types.UnbondingType_Redelegation: 426 if err := k.putRedelegationEntryOnHold(ctx, id); err != nil { 427 return err 428 } 429 case types.UnbondingType_ValidatorUnbonding: 430 if err := k.putValidatorOnHold(ctx, id); err != nil { 431 return err 432 } 433 default: 434 return types.ErrUnbondingNotFound 435 } 436 437 return nil 438 } 439 440 func (k Keeper) putUnbondingDelegationEntryOnHold(ctx context.Context, id uint64) error { 441 ubd, err := k.GetUnbondingDelegationByUnbondingID(ctx, id) 442 if err != nil { 443 return err 444 } 445 446 i, err := unbondingDelegationEntryArrayIndex(ubd, id) 447 if err != nil { 448 return err 449 } 450 451 ubd.Entries[i].UnbondingOnHoldRefCount++ 452 return k.SetUnbondingDelegation(ctx, ubd) 453 } 454 455 func (k Keeper) putRedelegationEntryOnHold(ctx context.Context, id uint64) error { 456 red, err := k.GetRedelegationByUnbondingID(ctx, id) 457 if err != nil { 458 return err 459 } 460 461 i, err := redelegationEntryArrayIndex(red, id) 462 if err != nil { 463 return err 464 } 465 466 red.Entries[i].UnbondingOnHoldRefCount++ 467 return k.SetRedelegation(ctx, red) 468 } 469 470 func (k Keeper) putValidatorOnHold(ctx context.Context, id uint64) error { 471 val, err := k.GetValidatorByUnbondingID(ctx, id) 472 if err != nil { 473 return err 474 } 475 476 val.UnbondingOnHoldRefCount++ 477 return k.SetValidator(ctx, val) 478 }