github.com/cosmos/cosmos-sdk@v0.50.10/x/staking/types/validator.go (about) 1 package types 2 3 import ( 4 "bytes" 5 "fmt" 6 "sort" 7 "strings" 8 "time" 9 10 abci "github.com/cometbft/cometbft/abci/types" 11 cmtprotocrypto "github.com/cometbft/cometbft/proto/tendermint/crypto" 12 13 "cosmossdk.io/core/address" 14 "cosmossdk.io/errors" 15 "cosmossdk.io/math" 16 17 "github.com/cosmos/cosmos-sdk/codec" 18 codectypes "github.com/cosmos/cosmos-sdk/codec/types" 19 cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" 20 cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" 21 sdk "github.com/cosmos/cosmos-sdk/types" 22 sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" 23 ) 24 25 const ( 26 // TODO: Why can't we just have one string description which can be JSON by convention 27 MaxMonikerLength = 70 28 MaxIdentityLength = 3000 29 MaxWebsiteLength = 140 30 MaxSecurityContactLength = 140 31 MaxDetailsLength = 280 32 ) 33 34 var ( 35 BondStatusUnspecified = BondStatus_name[int32(Unspecified)] 36 BondStatusUnbonded = BondStatus_name[int32(Unbonded)] 37 BondStatusUnbonding = BondStatus_name[int32(Unbonding)] 38 BondStatusBonded = BondStatus_name[int32(Bonded)] 39 ) 40 41 var _ ValidatorI = Validator{} 42 43 // NewValidator constructs a new Validator 44 func NewValidator(operator string, pubKey cryptotypes.PubKey, description Description) (Validator, error) { 45 pkAny, err := codectypes.NewAnyWithValue(pubKey) 46 if err != nil { 47 return Validator{}, err 48 } 49 50 return Validator{ 51 OperatorAddress: operator, 52 ConsensusPubkey: pkAny, 53 Jailed: false, 54 Status: Unbonded, 55 Tokens: math.ZeroInt(), 56 DelegatorShares: math.LegacyZeroDec(), 57 Description: description, 58 UnbondingHeight: int64(0), 59 UnbondingTime: time.Unix(0, 0).UTC(), 60 Commission: NewCommission(math.LegacyZeroDec(), math.LegacyZeroDec(), math.LegacyZeroDec()), 61 MinSelfDelegation: math.OneInt(), 62 UnbondingOnHoldRefCount: 0, 63 }, nil 64 } 65 66 // Validators is a collection of Validator 67 type Validators struct { 68 Validators []Validator 69 ValidatorCodec address.Codec 70 } 71 72 func (v Validators) String() (out string) { 73 for _, val := range v.Validators { 74 out += val.String() + "\n" 75 } 76 77 return strings.TrimSpace(out) 78 } 79 80 // ToSDKValidators - convenience function convert []Validator to []sdk.ValidatorI 81 func (v Validators) ToSDKValidators() (validators []ValidatorI) { 82 for _, val := range v.Validators { 83 validators = append(validators, val) 84 } 85 86 return validators 87 } 88 89 // Sort Validators sorts validator array in ascending operator address order 90 func (v Validators) Sort() { 91 sort.Sort(v) 92 } 93 94 // Implements sort interface 95 func (v Validators) Len() int { 96 return len(v.Validators) 97 } 98 99 // Implements sort interface 100 func (v Validators) Less(i, j int) bool { 101 vi, err := v.ValidatorCodec.StringToBytes(v.Validators[i].GetOperator()) 102 if err != nil { 103 panic(err) 104 } 105 vj, err := v.ValidatorCodec.StringToBytes(v.Validators[j].GetOperator()) 106 if err != nil { 107 panic(err) 108 } 109 110 return bytes.Compare(vi, vj) == -1 111 } 112 113 // Implements sort interface 114 func (v Validators) Swap(i, j int) { 115 v.Validators[i], v.Validators[j] = v.Validators[j], v.Validators[i] 116 } 117 118 // ValidatorsByVotingPower implements sort.Interface for []Validator based on 119 // the VotingPower and Address fields. 120 // The validators are sorted first by their voting power (descending). Secondary index - Address (ascending). 121 // Copied from tendermint/types/validator_set.go 122 type ValidatorsByVotingPower []Validator 123 124 func (valz ValidatorsByVotingPower) Len() int { return len(valz) } 125 126 func (valz ValidatorsByVotingPower) Less(i, j int, r math.Int) bool { 127 if valz[i].ConsensusPower(r) == valz[j].ConsensusPower(r) { 128 addrI, errI := valz[i].GetConsAddr() 129 addrJ, errJ := valz[j].GetConsAddr() 130 // If either returns error, then return false 131 if errI != nil || errJ != nil { 132 return false 133 } 134 return bytes.Compare(addrI, addrJ) == -1 135 } 136 return valz[i].ConsensusPower(r) > valz[j].ConsensusPower(r) 137 } 138 139 func (valz ValidatorsByVotingPower) Swap(i, j int) { 140 valz[i], valz[j] = valz[j], valz[i] 141 } 142 143 // UnpackInterfaces implements UnpackInterfacesMessage.UnpackInterfaces 144 func (v Validators) UnpackInterfaces(c codectypes.AnyUnpacker) error { 145 for i := range v.Validators { 146 if err := v.Validators[i].UnpackInterfaces(c); err != nil { 147 return err 148 } 149 } 150 return nil 151 } 152 153 // return the redelegation 154 func MustMarshalValidator(cdc codec.BinaryCodec, validator *Validator) []byte { 155 return cdc.MustMarshal(validator) 156 } 157 158 // unmarshal a redelegation from a store value 159 func MustUnmarshalValidator(cdc codec.BinaryCodec, value []byte) Validator { 160 validator, err := UnmarshalValidator(cdc, value) 161 if err != nil { 162 panic(err) 163 } 164 165 return validator 166 } 167 168 // unmarshal a redelegation from a store value 169 func UnmarshalValidator(cdc codec.BinaryCodec, value []byte) (v Validator, err error) { 170 err = cdc.Unmarshal(value, &v) 171 return v, err 172 } 173 174 // IsBonded checks if the validator status equals Bonded 175 func (v Validator) IsBonded() bool { 176 return v.GetStatus() == Bonded 177 } 178 179 // IsUnbonded checks if the validator status equals Unbonded 180 func (v Validator) IsUnbonded() bool { 181 return v.GetStatus() == Unbonded 182 } 183 184 // IsUnbonding checks if the validator status equals Unbonding 185 func (v Validator) IsUnbonding() bool { 186 return v.GetStatus() == Unbonding 187 } 188 189 // constant used in flags to indicate that description field should not be updated 190 const DoNotModifyDesc = "[do-not-modify]" 191 192 func NewDescription(moniker, identity, website, securityContact, details string) Description { 193 return Description{ 194 Moniker: moniker, 195 Identity: identity, 196 Website: website, 197 SecurityContact: securityContact, 198 Details: details, 199 } 200 } 201 202 // UpdateDescription updates the fields of a given description. An error is 203 // returned if the resulting description contains an invalid length. 204 func (d Description) UpdateDescription(d2 Description) (Description, error) { 205 if d2.Moniker == DoNotModifyDesc { 206 d2.Moniker = d.Moniker 207 } 208 209 if d2.Identity == DoNotModifyDesc { 210 d2.Identity = d.Identity 211 } 212 213 if d2.Website == DoNotModifyDesc { 214 d2.Website = d.Website 215 } 216 217 if d2.SecurityContact == DoNotModifyDesc { 218 d2.SecurityContact = d.SecurityContact 219 } 220 221 if d2.Details == DoNotModifyDesc { 222 d2.Details = d.Details 223 } 224 225 return NewDescription( 226 d2.Moniker, 227 d2.Identity, 228 d2.Website, 229 d2.SecurityContact, 230 d2.Details, 231 ).EnsureLength() 232 } 233 234 // EnsureLength ensures the length of a validator's description. 235 func (d Description) EnsureLength() (Description, error) { 236 if len(d.Moniker) > MaxMonikerLength { 237 return d, errors.Wrapf(sdkerrors.ErrInvalidRequest, "invalid moniker length; got: %d, max: %d", len(d.Moniker), MaxMonikerLength) 238 } 239 240 if len(d.Identity) > MaxIdentityLength { 241 return d, errors.Wrapf(sdkerrors.ErrInvalidRequest, "invalid identity length; got: %d, max: %d", len(d.Identity), MaxIdentityLength) 242 } 243 244 if len(d.Website) > MaxWebsiteLength { 245 return d, errors.Wrapf(sdkerrors.ErrInvalidRequest, "invalid website length; got: %d, max: %d", len(d.Website), MaxWebsiteLength) 246 } 247 248 if len(d.SecurityContact) > MaxSecurityContactLength { 249 return d, errors.Wrapf(sdkerrors.ErrInvalidRequest, "invalid security contact length; got: %d, max: %d", len(d.SecurityContact), MaxSecurityContactLength) 250 } 251 252 if len(d.Details) > MaxDetailsLength { 253 return d, errors.Wrapf(sdkerrors.ErrInvalidRequest, "invalid details length; got: %d, max: %d", len(d.Details), MaxDetailsLength) 254 } 255 256 return d, nil 257 } 258 259 // ABCIValidatorUpdate returns an abci.ValidatorUpdate from a staking validator type 260 // with the full validator power 261 func (v Validator) ABCIValidatorUpdate(r math.Int) abci.ValidatorUpdate { 262 tmProtoPk, err := v.TmConsPublicKey() 263 if err != nil { 264 panic(err) 265 } 266 267 return abci.ValidatorUpdate{ 268 PubKey: tmProtoPk, 269 Power: v.ConsensusPower(r), 270 } 271 } 272 273 // ABCIValidatorUpdateZero returns an abci.ValidatorUpdate from a staking validator type 274 // with zero power used for validator updates. 275 func (v Validator) ABCIValidatorUpdateZero() abci.ValidatorUpdate { 276 tmProtoPk, err := v.TmConsPublicKey() 277 if err != nil { 278 panic(err) 279 } 280 281 return abci.ValidatorUpdate{ 282 PubKey: tmProtoPk, 283 Power: 0, 284 } 285 } 286 287 // SetInitialCommission attempts to set a validator's initial commission. An 288 // error is returned if the commission is invalid. 289 func (v Validator) SetInitialCommission(commission Commission) (Validator, error) { 290 if err := commission.Validate(); err != nil { 291 return v, err 292 } 293 294 v.Commission = commission 295 296 return v, nil 297 } 298 299 // In some situations, the exchange rate becomes invalid, e.g. if 300 // Validator loses all tokens due to slashing. In this case, 301 // make all future delegations invalid. 302 func (v Validator) InvalidExRate() bool { 303 return v.Tokens.IsZero() && v.DelegatorShares.IsPositive() 304 } 305 306 // calculate the token worth of provided shares 307 func (v Validator) TokensFromShares(shares math.LegacyDec) math.LegacyDec { 308 return (shares.MulInt(v.Tokens)).Quo(v.DelegatorShares) 309 } 310 311 // calculate the token worth of provided shares, truncated 312 func (v Validator) TokensFromSharesTruncated(shares math.LegacyDec) math.LegacyDec { 313 return (shares.MulInt(v.Tokens)).QuoTruncate(v.DelegatorShares) 314 } 315 316 // TokensFromSharesRoundUp returns the token worth of provided shares, rounded 317 // up. 318 func (v Validator) TokensFromSharesRoundUp(shares math.LegacyDec) math.LegacyDec { 319 return (shares.MulInt(v.Tokens)).QuoRoundUp(v.DelegatorShares) 320 } 321 322 // SharesFromTokens returns the shares of a delegation given a bond amount. It 323 // returns an error if the validator has no tokens. 324 func (v Validator) SharesFromTokens(amt math.Int) (math.LegacyDec, error) { 325 if v.Tokens.IsZero() { 326 return math.LegacyZeroDec(), ErrInsufficientShares 327 } 328 329 return v.GetDelegatorShares().MulInt(amt).QuoInt(v.GetTokens()), nil 330 } 331 332 // SharesFromTokensTruncated returns the truncated shares of a delegation given 333 // a bond amount. It returns an error if the validator has no tokens. 334 func (v Validator) SharesFromTokensTruncated(amt math.Int) (math.LegacyDec, error) { 335 if v.Tokens.IsZero() { 336 return math.LegacyZeroDec(), ErrInsufficientShares 337 } 338 339 return v.GetDelegatorShares().MulInt(amt).QuoTruncate(math.LegacyNewDecFromInt(v.GetTokens())), nil 340 } 341 342 // get the bonded tokens which the validator holds 343 func (v Validator) BondedTokens() math.Int { 344 if v.IsBonded() { 345 return v.Tokens 346 } 347 348 return math.ZeroInt() 349 } 350 351 // ConsensusPower gets the consensus-engine power. Aa reduction of 10^6 from 352 // validator tokens is applied 353 func (v Validator) ConsensusPower(r math.Int) int64 { 354 if v.IsBonded() { 355 return v.PotentialConsensusPower(r) 356 } 357 358 return 0 359 } 360 361 // PotentialConsensusPower returns the potential consensus-engine power. 362 func (v Validator) PotentialConsensusPower(r math.Int) int64 { 363 return sdk.TokensToConsensusPower(v.Tokens, r) 364 } 365 366 // UpdateStatus updates the location of the shares within a validator 367 // to reflect the new status 368 func (v Validator) UpdateStatus(newStatus BondStatus) Validator { 369 v.Status = newStatus 370 return v 371 } 372 373 // AddTokensFromDel adds tokens to a validator 374 func (v Validator) AddTokensFromDel(amount math.Int) (Validator, math.LegacyDec) { 375 // calculate the shares to issue 376 var issuedShares math.LegacyDec 377 if v.DelegatorShares.IsZero() { 378 // the first delegation to a validator sets the exchange rate to one 379 issuedShares = math.LegacyNewDecFromInt(amount) 380 } else { 381 shares, err := v.SharesFromTokens(amount) 382 if err != nil { 383 panic(err) 384 } 385 386 issuedShares = shares 387 } 388 389 v.Tokens = v.Tokens.Add(amount) 390 v.DelegatorShares = v.DelegatorShares.Add(issuedShares) 391 392 return v, issuedShares 393 } 394 395 // RemoveTokens removes tokens from a validator 396 func (v Validator) RemoveTokens(tokens math.Int) Validator { 397 if tokens.IsNegative() { 398 panic(fmt.Sprintf("should not happen: trying to remove negative tokens %v", tokens)) 399 } 400 401 if v.Tokens.LT(tokens) { 402 panic(fmt.Sprintf("should not happen: only have %v tokens, trying to remove %v", v.Tokens, tokens)) 403 } 404 405 v.Tokens = v.Tokens.Sub(tokens) 406 407 return v 408 } 409 410 // RemoveDelShares removes delegator shares from a validator. 411 // NOTE: because token fractions are left in the valiadator, 412 // 413 // the exchange rate of future shares of this validator can increase. 414 func (v Validator) RemoveDelShares(delShares math.LegacyDec) (Validator, math.Int) { 415 remainingShares := v.DelegatorShares.Sub(delShares) 416 417 var issuedTokens math.Int 418 if remainingShares.IsZero() { 419 // last delegation share gets any trimmings 420 issuedTokens = v.Tokens 421 v.Tokens = math.ZeroInt() 422 } else { 423 // leave excess tokens in the validator 424 // however fully use all the delegator shares 425 issuedTokens = v.TokensFromShares(delShares).TruncateInt() 426 v.Tokens = v.Tokens.Sub(issuedTokens) 427 428 if v.Tokens.IsNegative() { 429 panic("attempting to remove more tokens than available in validator") 430 } 431 } 432 433 v.DelegatorShares = remainingShares 434 435 return v, issuedTokens 436 } 437 438 // MinEqual defines a more minimum set of equality conditions when comparing two 439 // validators. 440 func (v *Validator) MinEqual(other *Validator) bool { 441 return v.OperatorAddress == other.OperatorAddress && 442 v.Status == other.Status && 443 v.Tokens.Equal(other.Tokens) && 444 v.DelegatorShares.Equal(other.DelegatorShares) && 445 v.Description.Equal(other.Description) && 446 v.Commission.Equal(other.Commission) && 447 v.Jailed == other.Jailed && 448 v.MinSelfDelegation.Equal(other.MinSelfDelegation) && 449 v.ConsensusPubkey.Equal(other.ConsensusPubkey) 450 } 451 452 // Equal checks if the receiver equals the parameter 453 func (v *Validator) Equal(v2 *Validator) bool { 454 return v.MinEqual(v2) && 455 v.UnbondingHeight == v2.UnbondingHeight && 456 v.UnbondingTime.Equal(v2.UnbondingTime) 457 } 458 459 func (v Validator) IsJailed() bool { return v.Jailed } 460 func (v Validator) GetMoniker() string { return v.Description.Moniker } 461 func (v Validator) GetStatus() BondStatus { return v.Status } 462 func (v Validator) GetOperator() string { 463 return v.OperatorAddress 464 } 465 466 // ConsPubKey returns the validator PubKey as a cryptotypes.PubKey. 467 func (v Validator) ConsPubKey() (cryptotypes.PubKey, error) { 468 pk, ok := v.ConsensusPubkey.GetCachedValue().(cryptotypes.PubKey) 469 if !ok { 470 return nil, errors.Wrapf(sdkerrors.ErrInvalidType, "expecting cryptotypes.PubKey, got %T", pk) 471 } 472 473 return pk, nil 474 } 475 476 // Deprecated: use CmtConsPublicKey instead 477 func (v Validator) TmConsPublicKey() (cmtprotocrypto.PublicKey, error) { 478 return v.CmtConsPublicKey() 479 } 480 481 // CmtConsPublicKey casts Validator.ConsensusPubkey to cmtprotocrypto.PubKey. 482 func (v Validator) CmtConsPublicKey() (cmtprotocrypto.PublicKey, error) { 483 pk, err := v.ConsPubKey() 484 if err != nil { 485 return cmtprotocrypto.PublicKey{}, err 486 } 487 488 tmPk, err := cryptocodec.ToCmtProtoPublicKey(pk) 489 if err != nil { 490 return cmtprotocrypto.PublicKey{}, err 491 } 492 493 return tmPk, nil 494 } 495 496 // GetConsAddr extracts Consensus key address 497 func (v Validator) GetConsAddr() ([]byte, error) { 498 pk, ok := v.ConsensusPubkey.GetCachedValue().(cryptotypes.PubKey) 499 if !ok { 500 return nil, errors.Wrapf(sdkerrors.ErrInvalidType, "expecting cryptotypes.PubKey, got %T", pk) 501 } 502 503 return pk.Address().Bytes(), nil 504 } 505 506 func (v Validator) GetTokens() math.Int { return v.Tokens } 507 func (v Validator) GetBondedTokens() math.Int { return v.BondedTokens() } 508 func (v Validator) GetConsensusPower(r math.Int) int64 { 509 return v.ConsensusPower(r) 510 } 511 func (v Validator) GetCommission() math.LegacyDec { return v.Commission.Rate } 512 func (v Validator) GetMinSelfDelegation() math.Int { return v.MinSelfDelegation } 513 func (v Validator) GetDelegatorShares() math.LegacyDec { return v.DelegatorShares } 514 515 // UnpackInterfaces implements UnpackInterfacesMessage.UnpackInterfaces 516 func (v Validator) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { 517 var pk cryptotypes.PubKey 518 return unpacker.UnpackAny(v.ConsensusPubkey, &pk) 519 }