introduce UpdateSharesLocation to deal with different share types

This commit is contained in:
rigelrozanski 2018-05-11 17:58:28 -04:00
parent 9bb01c9504
commit eb87a5dbbf
6 changed files with 106 additions and 45 deletions

View File

@ -6,23 +6,23 @@ import (
)
// status of a validator
type ValidatorStatus byte
type BondStatus byte
// nolint
const (
Bonded ValidatorStatus = 0x00
Unbonding ValidatorStatus = 0x01
Unbonded ValidatorStatus = 0x02
Revoked ValidatorStatus = 0x03
Bonded BondStatus = 0x00
Unbonding BondStatus = 0x01
Unbonded BondStatus = 0x02
Revoked BondStatus = 0x03
)
// validator for a delegated proof of stake system
type Validator interface {
GetStatus() ValidatorStatus // status of the validator
GetAddress() Address // owner address to receive/return validators coins
GetPubKey() crypto.PubKey // validation pubkey
GetPower() Rat // validation power
GetBondHeight() int64 // height in which the validator became active
GetStatus() BondStatus // status of the validator
GetAddress() Address // owner address to receive/return validators coins
GetPubKey() crypto.PubKey // validation pubkey
GetPower() Rat // validation power
GetBondHeight() int64 // height in which the validator became active
}
// validator which fulfills abci validator interface for use in Tendermint

View File

@ -287,7 +287,8 @@ func handleMsgUnbond(ctx sdk.Context, msg MsgUnbond, k Keeper) sdk.Result {
// change the share types to unbonded if they were not already
if validator.Status == sdk.Bonded {
p, validator = p.bondedToUnbondedPool(validator)
validator.Status = sdk.Unbonded
p, validator = p.UpdateSharesLocation(validator)
}
// lastly update the status

View File

@ -69,19 +69,23 @@ func (k Keeper) GetValidators(ctx sdk.Context, maxRetrieve int16) (validators Va
func (k Keeper) setValidator(ctx sdk.Context, validator Validator) {
store := ctx.KVStore(k.storeKey)
pool := k.getPool(store)
address := validator.Address
// update the main list ordered by address before exiting
defer func() {
bz := k.cdc.MustMarshalBinary(validator)
store.Set(GetValidatorKey(address), bz)
}()
// retreive the old validator record
oldValidator, oldFound := k.GetValidator(ctx, address)
// marshal the validator record and add to the state
bz := k.cdc.MustMarshalBinary(validator)
store.Set(GetValidatorKey(address), bz)
powerIncreasing := false
if oldFound {
// if the voting power is the same no need to update any of the other indexes
if oldValidator.BondedShares.Equal(validator.BondedShares) {
if oldValidator.Status == sdk.Bonded &&
oldValidator.BondedShares.Equal(validator.BondedShares) {
return
} else if oldValidator.BondedShares.LT(validator.BondedShares) {
powerIncreasing = true
@ -116,7 +120,14 @@ func (k Keeper) setValidator(ctx sdk.Context, validator Validator) {
}
// update the validator set for this validator
k.updateValidators(ctx, store, validator.Address)
valIsNowBonded := k.updateValidators(ctx, store, validator.Address)
if oldValidator.Status != sdk.Bonded && valIsNowBonded {
validator.Status = sdk.Bonded
pool, validator = pool.UpdateSharesLocation(validator)
k.setPool(ctx, pool)
}
return
}
@ -187,6 +198,8 @@ func (k Keeper) GetValidatorsBondedByPower(ctx sdk.Context) []Validator {
return validators
}
// XXX TODO build in consideration for revoked
//
// Update the validator group and kick out any old validators. In addition this
// function adds (or doesn't add) a validator which has updated its bonded
// tokens to the validator group. -> this validator is specified through the
@ -198,7 +211,8 @@ func (k Keeper) GetValidatorsBondedByPower(ctx sdk.Context) []Validator {
// ValidatorsBondedKey. This store is used to determine if a validator is a
// validator without needing to iterate over the subspace as we do in
// GetValidators.
func (k Keeper) updateValidators(ctx sdk.Context, store sdk.KVStore, updatedValidatorAddr sdk.Address) {
func (k Keeper) updateValidators(ctx sdk.Context, store sdk.KVStore, updatedValidatorAddr sdk.Address) (updatedIsBonded bool) {
updatedIsBonded = false
// clear the current validators store, add to the ToKickOut temp store
toKickOut := make(map[string][]byte) // map[key]value
@ -240,6 +254,7 @@ func (k Keeper) updateValidators(ctx sdk.Context, store sdk.KVStore, updatedVali
if bytes.Equal(updatedValidatorAddr, validator.Address) {
bz = k.cdc.MustMarshalBinary(validator.abciValidator(k.cdc))
store.Set(GetTendermintUpdatesKey(updatedValidatorAddr), bz)
updatedIsBonded = true // the updatedValidatorAddr is for a bonded validator
}
iterator.Next()
@ -257,6 +272,7 @@ func (k Keeper) updateValidators(ctx sdk.Context, store sdk.KVStore, updatedVali
bz := k.cdc.MustMarshalBinary(validator.abciValidatorZero(k.cdc))
store.Set(GetTendermintUpdatesKey(addr), bz)
}
return
}
//_________________________________________________________________________
@ -392,11 +408,14 @@ func (k Keeper) setParams(ctx sdk.Context, params Params) {
// load/save the pool
func (k Keeper) GetPool(ctx sdk.Context) (pool Pool) {
store := ctx.KVStore(k.storeKey)
return k.getPool(store)
}
func (k Keeper) getPool(store sdk.KVStore) (pool Pool) {
// check if cached before anything
if !k.pool.equal(Pool{}) {
return k.pool
}
store := ctx.KVStore(k.storeKey)
b := store.Get(PoolKey)
if b == nil {
panic("Stored pool should not have been nil")

View File

@ -20,6 +20,14 @@ func (p Pool) bondedShareExRate() sdk.Rat {
return sdk.NewRat(p.BondedPool).Quo(p.BondedShares)
}
// get the exchange rate of unbonding tokens held in validators per issued share
func (p Pool) unbondingShareExRate() sdk.Rat {
if p.UnbondingShares.IsZero() {
return sdk.OneRat()
}
return sdk.NewRat(p.UnbondingPool).Quo(p.UnbondingShares)
}
// get the exchange rate of unbonded tokens held in validators per issued share
func (p Pool) unbondedShareExRate() sdk.Rat {
if p.UnbondedShares.IsZero() {
@ -28,23 +36,38 @@ func (p Pool) unbondedShareExRate() sdk.Rat {
return sdk.NewRat(p.UnbondedPool).Quo(p.UnbondedShares)
}
// move a validators asset pool from bonded to unbonded pool
func (p Pool) bondedToUnbondedPool(validator Validator) (Pool, Validator) {
// XXX write test
// update the location of the shares within a validator if its bond status has changed
func (p Pool) UpdateSharesLocation(validator Validator) (Pool, Validator) {
var tokens int64
// replace bonded shares with unbonded shares
p, tokens := p.removeSharesBonded(validator.BondedShares)
p, validator.BondedShares = p.addTokensUnbonded(tokens)
validator.Status = sdk.Unbonded
return p, validator
}
switch {
case !validator.BondedShares.IsZero():
if validator.Status == sdk.Bonded { // return if nothing needs switching
return p, validator
}
p, tokens = p.removeSharesBonded(validator.BondedShares)
case !validator.UnbondingShares.IsZero():
if validator.Status == sdk.Unbonding {
return p, validator
}
p, tokens = p.removeSharesUnbonding(validator.BondedShares)
case !validator.UnbondedShares.IsZero():
if validator.Status == sdk.Unbonding {
return p, validator
}
p, tokens = p.removeSharesUnbonded(validator.BondedShares)
}
// move a validators asset pool from unbonded to bonded pool
func (p Pool) unbondedToBondedPool(validator Validator) (Pool, Validator) {
switch validator.Status {
case sdk.Bonded:
p, validator.BondedShares = p.addTokensBonded(tokens)
case sdk.Unbonding:
p, validator.UnbondingShares = p.addTokensUnbonding(tokens)
case sdk.Unbonded, sdk.Revoked:
p, validator.UnbondedShares = p.addTokensUnbonded(tokens)
}
// replace unbonded shares with bonded shares
p, tokens := p.removeSharesUnbonded(validator.BondedShares)
p, validator.BondedShares = p.addTokensBonded(tokens)
validator.Status = sdk.Bonded
return p, validator
}
@ -64,6 +87,20 @@ func (p Pool) removeSharesBonded(shares sdk.Rat) (p2 Pool, removedTokens int64)
return p, removedTokens
}
func (p Pool) addTokensUnbonding(amount int64) (p2 Pool, issuedShares sdk.Rat) {
issuedShares = sdk.NewRat(amount).Quo(p.unbondingShareExRate()) // tokens * (shares/tokens)
p.UnbondingShares = p.UnbondingShares.Add(issuedShares)
p.UnbondingPool += amount
return p, issuedShares
}
func (p Pool) removeSharesUnbonding(shares sdk.Rat) (p2 Pool, removedTokens int64) {
removedTokens = p.unbondingShareExRate().Mul(shares).Evaluate() // (tokens/shares) * shares
p.UnbondingShares = p.UnbondingShares.Sub(shares)
p.UnbondingPool -= removedTokens
return p, removedTokens
}
func (p Pool) addTokensUnbonded(amount int64) (p2 Pool, issuedShares sdk.Rat) {
issuedShares = sdk.NewRat(amount).Quo(p.unbondedShareExRate()) // tokens * (shares/tokens)
p.UnbondedShares = p.UnbondedShares.Add(issuedShares)

View File

@ -258,7 +258,7 @@ func TestValidatorRemoveShares(t *testing.T) {
// generate a random validator
func randomValidator(r *rand.Rand) Validator {
var status sdk.ValidatorStatus
var status sdk.BondStatus
if r.Float64() < float64(0.5) {
status = sdk.Bonded
} else {

View File

@ -126,17 +126,21 @@ func initialPool() Pool {
// exchange rate. Voting power can be calculated as total bonds multiplied by
// exchange rate.
type Validator struct {
Status sdk.ValidatorStatus `json:"status"` // Bonded status
Address sdk.Address `json:"address"` // Sender of BondTx - UnbondTx returns here
PubKey crypto.PubKey `json:"pub_key"` // Pubkey of validator
BondedShares sdk.Rat `json:"bonded_shares"` // total shares of bonded global hold pool
UnbondingShares sdk.Rat `json:"unbonding_shares"` // total shares of unbonding global hold pool
UnbondedShares sdk.Rat `json:"unbonded_shares"` // total shares of unbonded global hold pool
DelegatorShares sdk.Rat `json:"liabilities"` // total shares issued to a validator's delegators
Status sdk.BondStatus `json:"status"` // bonded status
Address sdk.Address `json:"address"` // sender of BondTx - UnbondTx returns here
PubKey crypto.PubKey `json:"pub_key"` // pubkey of validator
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
// note: There should only be one of the following 3 shares ever active in a delegator
// multiple terms are only added here for clarity.
BondedShares sdk.Rat `json:"bonded_shares"` // total shares of bonded global hold pool
UnbondingShares sdk.Rat `json:"unbonding_shares"` // total shares of unbonding global hold pool
UnbondedShares sdk.Rat `json:"unbonded_shares"` // total shares of unbonded global hold pool
DelegatorShares sdk.Rat `json:"liabilities"` // 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
ProposerRewardPool sdk.Coins `json:"proposer_reward_pool"` // XXX reward pool collected from being the proposer
Commission sdk.Rat `json:"commission"` // XXX the commission rate of fees charged to any delegators
@ -246,7 +250,7 @@ func (v Validator) abciValidatorZero(cdc *wire.Codec) abci.Validator {
var _ sdk.Validator = Validator{}
// nolint - for sdk.Validator
func (v Validator) GetStatus() sdk.ValidatorStatus { return v.Status }
func (v Validator) GetStatus() sdk.BondStatus { return v.Status }
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.BondedShares }