complete staking spec update

This commit is contained in:
rigelrozanski 2018-05-29 14:50:35 -04:00
parent b8cf5f347e
commit e39ba70c08
5 changed files with 164 additions and 111 deletions

View File

@ -0,0 +1,61 @@
# End-Block
Two staking activities are intended to be processed in the application endblock.
- inform tendermint of validator set changes
- process and set atom inflation
# Validator Set Changes
The Tendermint validator set may be updated by state transitions that run at
the end of every block. The Tendermint validator set may be changed by
validators either being revoked due to inactivity/unexpected behaviour (covered
in slashing) or changed in validator power. Determining which validator set
changes must be made occures during staking transactions (and slashing
transactions) - during end-block the already accounted changes are applied and
the changes cleared
```golang
EndBlock() ValidatorSetChanges
vsc = GetTendermintUpdates()
ClearTendermintUpdates()
return vsc
```
# Inflation
The atom inflation rate is changed once per hour based on the current and
historic bond ratio
```golang
processProvisions():
hrsPerYr = 8766 // as defined by a julian year of 365.25 days
time = BFTTime()
if time > pool.InflationLastTime + ProvisionTimeout
pool.InflationLastTime = time
pool.Inflation = nextInflation(hrsPerYr).Round(1000000000)
provisions = pool.Inflation * (pool.TotalSupply / hrsPerYr)
pool.LooseUnbondedTokens += provisions
feePool += LooseUnbondedTokens
setPool(pool)
nextInflation(hrsPerYr rational.Rat):
if pool.TotalSupply > 0
bondedRatio = pool.BondedPool / pool.TotalSupply
else
bondedRation = 0
inflationRateChangePerYear = (1 - bondedRatio / params.GoalBonded) * params.InflationRateChange
inflationRateChange = inflationRateChangePerYear / hrsPerYr
inflation = pool.Inflation + inflationRateChange
if inflation > params.InflationMax then inflation = params.InflationMax
if inflation < params.InflationMin then inflation = params.InflationMin
return inflation
```

View File

@ -52,7 +52,9 @@ type Params struct {
- index 2: validator Tendermint PubKey
- index 3: bonded validators only
- index 4: voting power
- index 5: Tendermint updates
Related Store which holds Validator.ABCIValidator()
- index: validator owner address
The `Validator` holds the current state and some historical actions of the
validator.

View File

@ -2,15 +2,16 @@
### Transaction Overview
In this section we describe the processing of the transactions and the
corresponding updates to the state.
Available Transactions:
corresponding updates to the state. Transactions:
- TxCreateValidator
- TxEditValidator
- TxDelegation
- TxRedelegation
- TxUnbond
Other important state changes:
- Update Validators
Other notes:
- `tx` denotes a reference to the transaction being processed
- `sender` denotes the address of the sender of the transaction
@ -200,3 +201,93 @@ redelegate(tx TxRedelegate):
return
```
### Update Validators
Within many transactions the validator set must be updated based on changes in
power to a single validator. This process also updates the Tendermint-Updates
store for use in end-block when validators are either added or kicked from the
Tendermint.
```golang
updateBondedValidators(newValidator Validator) (updatedVal Validator)
kickCliffValidator := false
oldCliffValidatorAddr := getCliffValidator(ctx)
// add the actual validator power sorted store
maxValidators := GetParams(ctx).MaxValidators
iterator := ReverseSubspaceIterator(ValidatorsByPowerKey) // largest to smallest
bondedValidatorsCount := 0
var validator Validator
for {
if !iterator.Valid() || bondedValidatorsCount > int(maxValidators-1) {
if bondedValidatorsCount == int(maxValidators) { // is cliff validator
setCliffValidator(ctx, validator, GetPool(ctx))
iterator.Close()
break
// either retrieve the original validator from the store,
// or under the situation that this is the "new validator" just
// use the validator provided because it has not yet been updated
// in the main validator store
ownerAddr := iterator.Value()
if bytes.Equal(ownerAddr, newValidator.Owner) {
validator = newValidator
else
validator = getValidator(ownerAddr)
// if not previously a validator (and unrevoked),
// kick the cliff validator / bond this new validator
if validator.Status() != sdk.Bonded && !validator.Revoked {
kickCliffValidator = true
validator = bondValidator(ctx, store, validator)
if bytes.Equal(ownerAddr, newValidator.Owner) {
updatedVal = validator
bondedValidatorsCount++
iterator.Next()
// perform the actual kicks
if oldCliffValidatorAddr != nil && kickCliffValidator {
validator := getValidator(store, oldCliffValidatorAddr)
unbondValidator(ctx, store, validator)
return
// perform all the store operations for when a validator status becomes unbonded
unbondValidator(ctx sdk.Context, store sdk.KVStore, validator Validator)
pool := GetPool(ctx)
// set the status
validator, pool = validator.UpdateStatus(pool, sdk.Unbonded)
setPool(ctx, pool)
// save the now unbonded validator record
setValidator(validator)
// add to accumulated changes for tendermint
setTendermintUpdates(validator.abciValidatorZero)
// also remove from the bonded validators index
removeValidatorsBonded(validator)
}
// perform all the store operations for when a validator status becomes bonded
bondValidator(ctx sdk.Context, store sdk.KVStore, validator Validator) Validator
pool := GetPool(ctx)
// set the status
validator, pool = validator.UpdateStatus(pool, sdk.Bonded)
setPool(ctx, pool)
// save the now bonded validator record to the three referenced stores
setValidator(validator)
setValidatorsBonded(validator)
// add to accumulated changes for tendermint
setTendermintUpdates(validator.abciValidator)
return validator
```

View File

@ -1,99 +0,0 @@
# Validator Set Changes
The Tendermint validator set may be updated by state transitions that run at
the beginning and end of every block. The Tendermint validator set may be
changed by validators either being revoked due to inactivity/unexpected
behaviour (covered in slashing) or changed in validator power.
At the end of every block, we run the following:
(TODO remove inflation from here)
```golang
tick(ctx Context):
hrsPerYr = 8766 // as defined by a julian year of 365.25 days
time = ctx.Time()
if time > gs.InflationLastTime + ProvisionTimeout
gs.InflationLastTime = time
gs.Inflation = nextInflation(hrsPerYr).Round(1000000000)
provisions = gs.Inflation * (gs.TotalSupply / hrsPerYr)
gs.BondedPool += provisions
gs.TotalSupply += provisions
saveGlobalState(store, gs)
if time > unbondDelegationQueue.head().InitTime + UnbondingPeriod
for each element elem in the unbondDelegationQueue where time > elem.InitTime + UnbondingPeriod do
transfer(unbondingQueueAddress, elem.Payout, elem.Tokens)
unbondDelegationQueue.remove(elem)
if time > reDelegationQueue.head().InitTime + UnbondingPeriod
for each element elem in the unbondDelegationQueue where time > elem.InitTime + UnbondingPeriod do
validator = getValidator(store, elem.PubKey)
returnedCoins = removeShares(validator, elem.Shares)
validator.RedelegatingShares -= elem.Shares
delegateWithValidator(TxDelegate(elem.NewValidator, returnedCoins), validator)
reDelegationQueue.remove(elem)
return UpdateValidatorSet()
nextInflation(hrsPerYr rational.Rat):
if gs.TotalSupply > 0
bondedRatio = gs.BondedPool / gs.TotalSupply
else
bondedRation = 0
inflationRateChangePerYear = (1 - bondedRatio / params.GoalBonded) * params.InflationRateChange
inflationRateChange = inflationRateChangePerYear / hrsPerYr
inflation = gs.Inflation + inflationRateChange
if inflation > params.InflationMax then inflation = params.InflationMax
if inflation < params.InflationMin then inflation = params.InflationMin
return inflation
UpdateValidatorSet():
validators = loadValidators(store)
v1 = validators.Validators()
v2 = updateVotingPower(validators).Validators()
change = v1.validatorsUpdated(v2) // determine all updated validators between two validator sets
return change
updateVotingPower(validators Validators):
foreach validator in validators do
validator.VotingPower = (validator.IssuedDelegatorShares - validator.RedelegatingShares) * delegatorShareExRate(validator)
validators.Sort()
foreach validator in validators do
if validator is not in the first params.MaxVals
validator.VotingPower = rational.Zero
if validator.Status == Bonded then bondedToUnbondedPool(validator Validator)
else if validator.Status == UnBonded then unbondedToBondedPool(validator)
saveValidator(store, c)
return validators
unbondedToBondedPool(validator Validator):
removedTokens = exchangeRate(gs.UnbondedShares, gs.UnbondedPool) * validator.GlobalStakeShares
gs.UnbondedShares -= validator.GlobalStakeShares
gs.UnbondedPool -= removedTokens
gs.BondedPool += removedTokens
issuedShares = removedTokens / exchangeRate(gs.BondedShares, gs.BondedPool)
gs.BondedShares += issuedShares
validator.GlobalStakeShares = issuedShares
validator.Status = Bonded
return transfer(address of the unbonded pool, address of the bonded pool, removedTokens)
```

View File

@ -282,16 +282,14 @@ func (k Keeper) updateValidator(ctx sdk.Context, validator Validator) Validator
return validator
}
// 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
// updatedValidatorAddr term.
//
// The correct subset is retrieved by iterating through an index of the
// validators sorted by power, stored using the ValidatorsByPowerKey. Simultaniously
// the current validator records are updated in store with the
// validators sorted by power, stored using the ValidatorsByPowerKey.
// Simultaneously the current validator records are updated in store with the
// 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.
@ -319,10 +317,10 @@ func (k Keeper) updateBondedValidators(ctx sdk.Context, store sdk.KVStore,
break
}
// either retrieve the original validator from the store,
// or under the situation that this is the "new validator" just
// use the validator provided because it has not yet been updated
// in the main validator store
// either retrieve the original validator from the store, or under the
// situation that this is the "new validator" just use the validator
// provided because it has not yet been updated in the main validator
// store
ownerAddr := iterator.Value()
if bytes.Equal(ownerAddr, newValidator.Owner) {
validator = newValidator