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  }