complete staking spec update

This commit is contained in:
rigelrozanski
2018-06-05 09:23:23 -07:00
parent b8cf5f347e
commit e39ba70c08
5 changed files with 164 additions and 111 deletions
+61
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
```
+3 -1
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.
+94 -3
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
```
-99
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)
```