cosmos-sdk/x/stake/validator.go

245 lines
8.9 KiB
Go
Raw Normal View History

2018-01-25 19:31:07 -08:00
package stake
import (
"bytes"
2018-05-12 12:45:40 -07:00
"testing"
2018-02-20 07:55:53 -08:00
sdk "github.com/cosmos/cosmos-sdk/types"
2018-04-25 07:12:59 -07:00
"github.com/cosmos/cosmos-sdk/wire"
2018-05-06 07:18:45 -07:00
abci "github.com/tendermint/abci/types"
crypto "github.com/tendermint/go-crypto"
2018-01-25 19:31:07 -08:00
)
2018-05-09 21:01:58 -07:00
// Validator defines the total amount of bond shares and their exchange rate to
2018-01-25 19:31:07 -08:00
// coins. Accumulation of interest is modelled as an in increase in the
// exchange rate, and slashing as a decrease. When coins are delegated to this
2018-05-09 21:01:58 -07:00
// validator, the validator is credited with a Delegation whose number of
2018-01-25 19:31:07 -08:00
// bond shares is based on the amount of coins delegated divided by the current
// exchange rate. Voting power can be calculated as total bonds multiplied by
// exchange rate.
2018-05-09 21:01:58 -07:00
type Validator struct {
Status sdk.BondStatus `json:"status"` // bonded status
2018-05-12 12:45:40 -07:00
Address sdk.Address `json:"address"` // sender of BondTx - UnbondTx returns here
PubKey crypto.PubKey `json:"pub_key"` // pubkey of validator
PShares PoolShares `json:"pool_shares"` // total shares for tokens held in the pool
DelegatorShares sdk.Rat `json:"delegator_shares"` // total shares issued to a validator's delegators
Description Description `json:"description"` // description terms for the validator
BondHeight int64 `json:"validator_bond_height"` // earliest height as a bonded validator
BondIntraTxCounter int16 `json:"validator_bond_counter"` // block-local tx index of validator change
2018-05-10 16:02:35 -07:00
ProposerRewardPool sdk.Coins `json:"proposer_reward_pool"` // XXX reward pool collected from being the proposer
2018-05-04 12:38:25 -07:00
Commission sdk.Rat `json:"commission"` // XXX the commission rate of fees charged to any delegators
2018-05-09 21:01:58 -07:00
CommissionMax sdk.Rat `json:"commission_max"` // XXX maximum commission rate which this validator can ever charge
CommissionChangeRate sdk.Rat `json:"commission_change_rate"` // XXX maximum daily increase of the validator commission
2018-05-04 12:38:25 -07:00
CommissionChangeToday sdk.Rat `json:"commission_change_today"` // XXX commission rate change today, reset each day (UTC time)
// fee related
2018-05-09 18:39:14 -07:00
PrevBondedShares sdk.Rat `json:"prev_bonded_shares"` // total shares of a global hold pools
2018-01-25 19:31:07 -08:00
}
2018-05-09 21:01:58 -07:00
// Validators - list of Validators
type Validators []Validator
2018-04-06 22:50:46 -07:00
2018-05-09 21:01:58 -07:00
// NewValidator - initialize a new validator
func NewValidator(address sdk.Address, pubKey crypto.PubKey, description Description) Validator {
return Validator{
2018-05-10 16:02:35 -07:00
Status: sdk.Unbonded,
Address: address,
PubKey: pubKey,
PShares: NewUnbondedShares(sdk.ZeroRat()),
2018-05-10 16:02:35 -07:00
DelegatorShares: sdk.ZeroRat(),
Description: description,
BondHeight: int64(0),
BondIntraTxCounter: int16(0),
ProposerRewardPool: sdk.Coins{},
Commission: sdk.ZeroRat(),
CommissionMax: sdk.ZeroRat(),
CommissionChangeRate: sdk.ZeroRat(),
CommissionChangeToday: sdk.ZeroRat(),
PrevBondedShares: sdk.ZeroRat(),
2018-01-25 19:31:07 -08:00
}
}
// only the vitals - does not check bond height of IntraTxCounter
2018-05-09 21:01:58 -07:00
func (v Validator) equal(c2 Validator) bool {
return v.Status == c2.Status &&
v.PubKey.Equals(c2.PubKey) &&
bytes.Equal(v.Address, c2.Address) &&
v.PShares.Equal(c2.PShares) &&
2018-05-09 21:01:58 -07:00
v.DelegatorShares.Equal(c2.DelegatorShares) &&
v.Description == c2.Description &&
//v.BondHeight == c2.BondHeight &&
2018-05-10 16:02:35 -07:00
//v.BondIntraTxCounter == c2.BondIntraTxCounter && // counter is always changing
2018-05-09 21:01:58 -07:00
v.ProposerRewardPool.IsEqual(c2.ProposerRewardPool) &&
v.Commission.Equal(c2.Commission) &&
v.CommissionMax.Equal(c2.CommissionMax) &&
v.CommissionChangeRate.Equal(c2.CommissionChangeRate) &&
v.CommissionChangeToday.Equal(c2.CommissionChangeToday) &&
v.PrevBondedShares.Equal(c2.PrevBondedShares)
}
2018-05-12 12:45:40 -07:00
// intended to be used with require/assert: require.True(ValEq(...))
func ValEq(t *testing.T, exp, got Validator) (*testing.T, bool, string, Validator, Validator) {
return t, exp.equal(got), "expected:\t%v\ngot:\t\t%v", exp, got
}
2018-05-09 21:01:58 -07:00
// Description - description fields for a validator
2018-03-26 07:48:15 -07:00
type Description struct {
Moniker string `json:"moniker"`
Identity string `json:"identity"`
Website string `json:"website"`
Details string `json:"details"`
}
func NewDescription(moniker, identity, website, details string) Description {
return Description{
Moniker: moniker,
Identity: identity,
Website: website,
Details: details,
}
}
//XXX updateDescription function
//XXX enforce limit to number of description characters
// get the exchange rate of tokens over delegator shares
// UNITS: eq-val-bonded-shares/delegator-shares
func (v Validator) DelegatorShareExRate(p Pool) sdk.Rat {
2018-05-09 21:01:58 -07:00
if v.DelegatorShares.IsZero() {
2018-04-30 14:21:14 -07:00
return sdk.OneRat()
}
eqBondedShares := v.PShares.ToBonded(p).Amount
return eqBondedShares.Quo(v.DelegatorShares)
}
// abci validator from stake validator type
2018-05-06 07:18:45 -07:00
func (v Validator) abciValidator(cdc *wire.Codec) abci.Validator {
return abci.Validator{
2018-04-16 13:47:28 -07:00
PubKey: v.PubKey.Bytes(),
Power: v.PShares.Bonded().Evaluate(),
}
}
// abci validator from stake validator type
// with zero power used for validator updates
2018-05-06 07:18:45 -07:00
func (v Validator) abciValidatorZero(cdc *wire.Codec) abci.Validator {
return abci.Validator{
2018-04-16 13:47:28 -07:00
PubKey: v.PubKey.Bytes(),
Power: 0,
}
}
// update the location of the shares within a validator if its bond status has changed
func (v Validator) UpdateSharesLocation(p Pool) (Validator, Pool) {
var tokens int64
switch {
case v.PShares.Kind == ShareUnbonded:
if v.Status == sdk.Unbonded {
return v, p
}
p, tokens = p.removeSharesUnbonded(v.PShares.Amount)
case v.PShares.Kind == ShareUnbonding:
if v.Status == sdk.Unbonding {
return v, p
}
p, tokens = p.removeSharesUnbonding(v.PShares.Amount)
case v.PShares.Kind == ShareBonded:
if v.Status == sdk.Bonded { // return if nothing needs switching
return v, p
}
p, tokens = p.removeSharesBonded(v.PShares.Amount)
}
var shares sdk.Rat
switch v.Status {
case sdk.Unbonded, sdk.Revoked:
p, shares = p.addTokensUnbonded(tokens)
v.PShares = NewUnbondedShares(shares)
case sdk.Unbonding:
p, shares = p.addTokensUnbonding(tokens)
v.PShares = NewUnbondingShares(shares)
case sdk.Bonded:
p, shares = p.addTokensBonded(tokens)
v.PShares = NewBondedShares(shares)
}
return v, p
}
// XXX TEST
// get the power or potential power for a validator
// if bonded, the power is the BondedShares
// if not bonded, the power is the amount of bonded shares which the
// the validator would have it was bonded
func (v Validator) EquivalentBondedShares(p Pool) (eqBondedShares sdk.Rat) {
return v.PShares.ToBonded(p).Amount
}
//_________________________________________________________________________________________________________
// XXX Audit this function further to make sure it's correct
// add tokens to a validator
func (v Validator) addTokensFromDel(p Pool,
amount int64) (validator2 Validator, p2 Pool, issuedDelegatorShares sdk.Rat) {
var equivalentBondedShares, poolShares sdk.Rat
switch v.Status {
case sdk.Unbonded, sdk.Revoked:
p, poolShares = p.addTokensUnbonded(amount)
case sdk.Unbonding:
p, poolShares = p.addTokensUnbonding(amount)
case sdk.Bonded:
p, poolShares = p.addTokensBonded(amount)
}
v.PShares.Amount = v.PShares.Amount.Add(poolShares)
equivalentBondedShares = v.PShares.ToBonded(p).Amount
exRate := v.DelegatorShareExRate(p) // bshr/delshr
issuedDelegatorShares = equivalentBondedShares.Quo(exRate) // bshr/(bshr/delshr) = delshr
v.DelegatorShares = v.DelegatorShares.Add(issuedDelegatorShares)
return v, p, issuedDelegatorShares
}
// remove delegator shares from a validator
// NOTE this function assumes the shares have already been updated for the validator status
func (v Validator) removeDelShares(p Pool,
delShares sdk.Rat) (validator2 Validator, p2 Pool, createdCoins int64) {
amount := v.DelegatorShareExRate(p).Mul(delShares)
eqBondedSharesToRemove := NewBondedShares(amount)
v.DelegatorShares = v.DelegatorShares.Sub(delShares)
switch v.Status {
case sdk.Unbonded, sdk.Revoked:
unbondedShares := eqBondedSharesToRemove.ToUnbonded(p).Amount
p, createdCoins = p.removeSharesUnbonded(unbondedShares)
v.PShares.Amount = v.PShares.Amount.Sub(unbondedShares)
case sdk.Unbonding:
unbondingShares := eqBondedSharesToRemove.ToUnbonding(p).Amount
p, createdCoins = p.removeSharesUnbonding(unbondingShares)
v.PShares.Amount = v.PShares.Amount.Sub(unbondingShares)
case sdk.Bonded:
p, createdCoins = p.removeSharesBonded(eqBondedSharesToRemove.Amount)
v.PShares.Amount = v.PShares.Amount.Sub(eqBondedSharesToRemove.Amount)
}
return v, p, createdCoins
}
2018-05-06 07:18:45 -07:00
2018-05-09 18:39:14 -07:00
//______________________________________________________________________
2018-05-06 07:18:45 -07:00
2018-05-09 18:39:14 -07:00
// ensure fulfills the sdk validator types
var _ sdk.Validator = Validator{}
2018-05-06 07:18:45 -07:00
2018-05-09 18:39:14 -07:00
// nolint - for sdk.Validator
func (v Validator) GetStatus() sdk.BondStatus { return v.Status }
2018-05-12 12:45:40 -07:00
func (v Validator) GetAddress() sdk.Address { return v.Address }
func (v Validator) GetPubKey() crypto.PubKey { return v.PubKey }
func (v Validator) GetPower() sdk.Rat { return v.PShares.Bonded() }
2018-05-12 12:45:40 -07:00
func (v Validator) GetBondHeight() int64 { return v.BondHeight }