Merge PR #3281: Staking Spec Upgrade
* remove kv seperation for marshalling * pending * cleanup * cleanup x2 * pending * working * minor refactors * entry structs defined * uncompiled mechanism written * add many compile fixes * code compiles * fix test compile errors * test cover passes * ... * multiple entries fix * ... * more design fix * working * fix test cover bug * Update PENDING.md * update comment around queue completion for redelegations/ubds * basic spec updates * spec folder cleanup * cleanup docs folder cont. * ... * find-replace and folder rename * supplimentary find/replace * pending * supplimentary * pending * few undos, stakingd -> staked * to staking -> to stake * undos * most staking -> most stake * ... * undos * simplestake->simplestaking * ... * pending update * capital letter replacements * ... * working * staking doc updates from rigel/delegation-index branch * spec-spec * spec-spec * LooseTokens -> NotBondedTokens * staking state.md updates * updates to hook and endblock spec * Update docs/gaia/gaiacli.md Co-Authored-By: rigelrozanski <rigel.rozanski@gmail.com> * Update docs/gaia/validators/validator-setup.md Co-Authored-By: rigelrozanski <rigel.rozanski@gmail.com> * Update docs/gaia/validators/validator-setup.md Co-Authored-By: rigelrozanski <rigel.rozanski@gmail.com> * comment undo * remove ErrConflictingRedelegation * @cwgoes comments are resolved * further updates to endblock and state * msg json update * working transaction updates * working * complete transaction rewrite * PENDING.md * typo * add todo * address @jackzampolin @cwgoes comments * couple leftover comments, rename * Update x/staking/types/pool.go Co-Authored-By: rigelrozanski <rigel.rozanski@gmail.com> * cwgoes additions * cwgoes suggestions x2
This commit is contained in:
@@ -499,9 +499,9 @@ gaiacli query staking pool
|
||||
|
||||
With the `pool` command you will get the values for:
|
||||
|
||||
- Loose and bonded tokens
|
||||
- Not-bonded and bonded tokens
|
||||
- Token supply
|
||||
- Current anual inflation and the block in which the last inflation was processed
|
||||
- Current annual inflation and the block in which the last inflation was processed
|
||||
- Last recorded bonded shares
|
||||
|
||||
##### Query Delegations To Validator
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
# Specification of Specifications
|
||||
|
||||
This file intends to outline the common structure for specifications within
|
||||
this directory.
|
||||
|
||||
## Tense
|
||||
|
||||
For consistency, specs should be written in passive present tense.
|
||||
|
||||
## Common Layout
|
||||
|
||||
The following generalized structure should be used to breakdown specifications
|
||||
for modules. Note that not all files may be required depending on the modules
|
||||
function
|
||||
|
||||
- `overview.md` - describe module
|
||||
- `state.md` - specify and describe structures expected to marshalled into the store, and their keys
|
||||
- `state_transitions.md` - standard state transition operations triggered by by hooks, messages, etc.
|
||||
- `end_block.md` - specify any end-block operations
|
||||
- `begin_block.md` - specify any begin-block operations
|
||||
- `messages.md` - specify message structure and expected state machine behaviour
|
||||
- `hooks.md` - describe available hooks to be called by/from this module
|
||||
- `tags.md` - list and describe event tags used
|
||||
|
||||
### Notation for key-value mapping
|
||||
|
||||
Within `state.md` the following notation `->` should be used to describe key to
|
||||
value mapping:
|
||||
|
||||
```
|
||||
key -> value
|
||||
```
|
||||
|
||||
to represent byte concatenation the `|` may be used. In addition, encoding
|
||||
type may be specified, for example:
|
||||
|
||||
```
|
||||
0x00 | addressBytes | address2Bytes -> amino(value_object)
|
||||
```
|
||||
|
||||
Additionally, index mappings may be specified by mapping to the `nil` value, for example:
|
||||
|
||||
```
|
||||
0x01 | address2Bytes | addressBytes -> nil
|
||||
```
|
||||
@@ -1,4 +1,4 @@
|
||||
## Transactions
|
||||
## Messages
|
||||
|
||||
### MsgSend
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Transactions
|
||||
# Messages
|
||||
|
||||
## MsgWithdrawDelegationRewardsAll
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Implementation (2/2)
|
||||
|
||||
## Transactions
|
||||
## Messages
|
||||
|
||||
### Proposal Submission
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
# End Block
|
||||
|
||||
Validator provisions are minted on an hourly basis (the first block of a new
|
||||
hour). The annual target of between 7% and 20%. The long-term target ratio of
|
||||
bonded tokens to unbonded tokens is 67%.
|
||||
|
||||
The target annual inflation rate is recalculated for each provisions cycle. The
|
||||
inflation is also subject to a rate change (positive or negative) depending on
|
||||
the distance from the target ratio (67%). The maximum rate change possible is
|
||||
defined to be 13% per year, however the annual inflation is capped as between
|
||||
7% and 20%.
|
||||
|
||||
Within the inflation module the tokens are created, and fed to the distribution
|
||||
module to be further processed and distributed similarly to fee distribution (with
|
||||
the exception that there are no special rewards for the block proposer)
|
||||
|
||||
Note that params are global params (TODO: link to the global params spec)
|
||||
|
||||
```
|
||||
EndBlock():
|
||||
|
||||
//process provisions
|
||||
hrsPerYr = 8766 // as defined by a julian year of 365.25 days
|
||||
precision = 10000
|
||||
|
||||
time = BFTTime() // time is in seconds
|
||||
if time > GetInflationLastTime() + 3600
|
||||
SetInflationLastTime(InflationLastTime + 3600)
|
||||
inflation = nextInflation(hrsPerYr).Round(precision)
|
||||
SetInflation(inflation)
|
||||
|
||||
provisions = inflation * (pool.TotalSupply() / hrsPerYr)
|
||||
pool.LooseTokens += provisions
|
||||
|
||||
distribution.AddInflation(provisions)
|
||||
|
||||
nextInflation(hrsPerYr rational.Rat):
|
||||
|
||||
bondedRatio = pool.BondedPool / pool.TotalSupply()
|
||||
|
||||
inflationRateChangePerYear = (1 - bondedRatio / params.GoalBonded) * params.InflationRateChange
|
||||
inflationRateChange = inflationRateChangePerYear / hrsPerYr
|
||||
|
||||
inflation = GetInflation() + inflationRateChange
|
||||
switch inflation
|
||||
case > params.InflationMax
|
||||
return params.InflationMax
|
||||
case < params.InflationMin
|
||||
return params.InflationMin
|
||||
default
|
||||
return inflation
|
||||
```
|
||||
@@ -1,21 +0,0 @@
|
||||
## State
|
||||
|
||||
### Inflation
|
||||
- key: `0x00`
|
||||
- value: `amino(Inflation)`
|
||||
|
||||
The current annual inflation rate.
|
||||
|
||||
```golang
|
||||
type Inflation sdk.Dec
|
||||
```
|
||||
|
||||
### InflationLastTime
|
||||
- key: `0x01`
|
||||
- value: `amino(InflationLastTime)`
|
||||
|
||||
The last unix time which the inflation was processed for.
|
||||
|
||||
```golang
|
||||
type InflationLastTime int64
|
||||
```
|
||||
@@ -1,2 +0,0 @@
|
||||
- reserve pool
|
||||
- AiB vesting
|
||||
@@ -0,0 +1,5 @@
|
||||
TODO
|
||||
|
||||
The reserve pool is the pool of collected funds for use by governance taken via the `CommunityTax`.
|
||||
Currently with the SDK, tokens collected by the CommunityTax are accounted for but unspendable.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
## Transactions
|
||||
## Messages
|
||||
|
||||
In this section we describe the processing of transactions for the `slashing` module.
|
||||
In this section we describe the processing of messages for the `slashing` module.
|
||||
|
||||
### Unjail
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
|
||||
- `state.md` needs updates to include
|
||||
- LastValidatorPower
|
||||
- LastTotalPower
|
||||
- state for the queues: UnbondingDelegation, UnbondingValidator, Redelegation
|
||||
- introduce `state_transitions.md` to describe validator/delegator state
|
||||
transitions which are called from transactions
|
||||
@@ -1,68 +1,62 @@
|
||||
# End-Block
|
||||
|
||||
## Unbonding Validator Queue
|
||||
|
||||
For all unbonding validators that have finished their unbonding period, this switches their validator.Status
|
||||
from sdk.Unbonding to sdk.Unbonded if they still have any delegation left. Otherwise, it deletes it from state.
|
||||
|
||||
```golang
|
||||
validatorQueue(currTime time.Time):
|
||||
// unbonding validators are in ordered queue from oldest to newest
|
||||
for all unbondingValidators whose CompleteTime < currTime:
|
||||
validator = GetValidator(unbondingValidator.ValidatorAddr)
|
||||
if validator.DelegatorShares == 0 {
|
||||
RemoveValidator(unbondingValidator)
|
||||
} else {
|
||||
validator.Status = sdk.Unbonded
|
||||
SetValidator(unbondingValidator)
|
||||
}
|
||||
return
|
||||
```
|
||||
Each abci end block call, the operations to update queues and validator set
|
||||
changes are specified to execute.
|
||||
|
||||
## 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 jailed due to inactivity/unexpected behaviour (covered
|
||||
in slashing) or changed in validator power. Determining which validator set
|
||||
changes must be made occurs during staking transactions (and slashing
|
||||
transactions) - during end-block the already accounted changes are applied and
|
||||
the changes cleared
|
||||
The staking validator set is updated during this process by state transitions
|
||||
that run at the end of every block. As a part of this process any updated
|
||||
validators are also returned back to Tendermint for inclusion in the Tendermint
|
||||
validator set which is responsible for validating Tendermint messages at the
|
||||
consensus layer. Operations are as following:
|
||||
|
||||
```golang
|
||||
EndBlock() ValidatorSetChanges
|
||||
vsc = GetValidTendermintUpdates()
|
||||
ClearTendermintUpdates()
|
||||
return vsc
|
||||
```
|
||||
- the new validator set is taken as the top `params.MaxValidators` number of
|
||||
validators retrieved from the ValidatorsByPower index
|
||||
- the previous validator set is compared with the new validator set
|
||||
- missing validators begin unbonding
|
||||
- new validator are instantly bonded
|
||||
|
||||
## CompleteUnbonding
|
||||
In all cases, any validators leaving or entering the bonded validator set or
|
||||
changing balances and staying within the bonded validator set incur an update
|
||||
message which is passed back to Tendermint.
|
||||
|
||||
Complete the unbonding and transfer the coins to the delegate. Realize any
|
||||
slashing that occurred during the unbonding period.
|
||||
## Queues
|
||||
|
||||
```golang
|
||||
unbondingQueue(currTime time.Time):
|
||||
// unbondings are in ordered queue from oldest to newest
|
||||
for all unbondings whose CompleteTime < currTime:
|
||||
validator = GetValidator(unbonding.ValidatorAddr)
|
||||
AddCoins(unbonding.DelegatorAddr, unbonding.Balance)
|
||||
removeUnbondingDelegation(unbonding)
|
||||
return
|
||||
```
|
||||
Within staking, certain state-transitions are not instantaneous but take place
|
||||
over a duration of time (typically the unbonding period). When these
|
||||
transitions are mature certain operations must take place in order to complete
|
||||
the state operation. This is achieved through the use of queues which are
|
||||
checked/processed at the end of each block.
|
||||
|
||||
## CompleteRedelegation
|
||||
### Unbonding Validators
|
||||
|
||||
Note that unlike CompleteUnbonding slashing of redelegating shares does not
|
||||
take place during completion. Slashing on redelegated shares takes place
|
||||
actively as a slashing occurs. The redelegation completion queue serves simply to
|
||||
clean up state, as redelegations older than an unbonding period need not be kept,
|
||||
as that is the max time that their old validator's evidence can be used to slash them.
|
||||
When a validator is kicked out of the bonded validator set (either through
|
||||
being jailed, or not having sufficient bonded tokens) it begins the unbonding
|
||||
process along with all its delegations begin unbonding (while still being
|
||||
delegated to this validator). At this point the validator is said to be an
|
||||
unbonding validator, whereby it will mature to become an "unbonded validator"
|
||||
after the unbonding period has passed.
|
||||
|
||||
```golang
|
||||
redelegationQueue(currTime time.Time):
|
||||
// redelegations are in ordered queue from oldest to newest
|
||||
for all redelegations whose CompleteTime < currTime:
|
||||
removeRedelegation(redelegation)
|
||||
return
|
||||
```
|
||||
Each block the validator queue is to be checked for mature unbonding
|
||||
validators. For all unbonding validators that have finished their unbonding
|
||||
period, the validator.Status is switched from sdk.Unbonding to sdk.Unbonded.
|
||||
If at this switch they do not have any delegation left the validator object
|
||||
instead just deleted from state.
|
||||
|
||||
### Unbonding Delegations
|
||||
|
||||
Complete the unbonding of all mature `UnbondingDelegations.Entries` within the
|
||||
`UnbondingDelegations` queue with the following procedure:
|
||||
- transfer the balance coins to the delegator's wallet address
|
||||
- remove the mature entry from `UnbondingDelegation.Entries`
|
||||
- remove the `UnbondingDelegation` object from the store if there are no
|
||||
remaining entries.
|
||||
|
||||
### Redelegations
|
||||
|
||||
Complete the unbonding of all mature `Redelegation.Entries` within the
|
||||
`Redelegations` queue with the following procedure:
|
||||
- remove the mature entry from `Redelegation.Entries`
|
||||
- remove the `Redelegation` object from the store if there are no
|
||||
remaining entries.
|
||||
|
||||
+23
-17
@@ -1,19 +1,25 @@
|
||||
## Receiver Hooks
|
||||
# Hooks
|
||||
|
||||
The staking module allow for the following hooks to be registered with staking events:
|
||||
Other modules may register operations to execute when a certain event has
|
||||
occurred within staking. These events can be registered to execute either
|
||||
right `Before` or `After` the staking event (as per the hook name). The
|
||||
following hooks can registered with staking:
|
||||
|
||||
``` golang
|
||||
// event hooks for staking validator object
|
||||
type StakingHooks interface {
|
||||
OnValidatorCreated(ctx Context, address ValAddress) // Must be called when a validator is created
|
||||
OnValidatorModified(ctx Context, address ValAddress) // Must be called when a validator's state changes
|
||||
OnValidatorRemoved(ctx Context, address ConsAddress, operator ValAddress) // Must be called when a validator is deleted
|
||||
|
||||
OnValidatorBonded(ctx Context, address ConsAddress) // called when a validator is bonded
|
||||
OnValidatorBeginUnbonding(ctx Context, address ConsAddress, operator ValAddress) // called when a validator begins unbonding
|
||||
|
||||
OnDelegationCreated(ctx Context, delAddr AccAddress, valAddr ValAddress) // called when a delegation is created
|
||||
OnDelegationSharesModified(ctx Context, delAddr AccAddress, valAddr ValAddress) // called when a delegation's shares are modified
|
||||
OnDelegationRemoved(ctx Context, delAddr AccAddress, valAddr ValAddress) // called when a delegation is removed
|
||||
}
|
||||
```
|
||||
- `AfterValidatorCreated(Context, ValAddress)`
|
||||
- called when a validator is created
|
||||
- `BeforeValidatorModified(Context, ValAddress)`
|
||||
- called when a validator's state is changed
|
||||
- `AfterValidatorRemoved(Context, ConsAddress, ValAddress)`
|
||||
- called when a validator is deleted
|
||||
- `AfterValidatorBonded(Context, ConsAddress, ValAddress)`
|
||||
- called when a validator is bonded
|
||||
- `AfterValidatorBeginUnbonding(Context, ConsAddress, ValAddress)`
|
||||
- called when a validator begins unbonding
|
||||
- `AfterValidatorPowerDidChange(Context, ConsAddress, ValAddress)`
|
||||
- called at EndBlock when a validator's power is changed
|
||||
- `BeforeDelegationCreated(Context, AccAddress, ValAddress)`
|
||||
- called when a delegation is created
|
||||
- `BeforeDelegationSharesModified(Context, AccAddress, ValAddress)`
|
||||
- called when a delegation's shares are modified
|
||||
- `BeforeDelegationRemoved(Context, AccAddress, ValAddress)`
|
||||
- called when a delegation is removed
|
||||
|
||||
@@ -0,0 +1,167 @@
|
||||
# Messages
|
||||
|
||||
In this section we describe the processing of the staking messages and the
|
||||
corresponding updates to the state. All created/modified state objects
|
||||
specified by each message are defined within [state.md](state.md).
|
||||
|
||||
## MsgCreateValidator
|
||||
|
||||
A validator is created using the `MsgCreateValidator` message.
|
||||
|
||||
```golang
|
||||
type MsgCreateValidator struct {
|
||||
Description Description
|
||||
Commission Commission
|
||||
|
||||
DelegatorAddr sdk.AccAddress
|
||||
ValidatorAddr sdk.ValAddress
|
||||
PubKey crypto.PubKey
|
||||
Delegation sdk.Coin
|
||||
}
|
||||
```
|
||||
|
||||
This message is expected to fail if:
|
||||
|
||||
- another validator with this operator address is already registered
|
||||
- another validator with this pubkey is already registered
|
||||
- the initial self-delegation tokens are of a denom not specified as the
|
||||
bonding denom
|
||||
- the commission parameters are faulty, namely:
|
||||
- `MaxRate` is either > 1 or < 0
|
||||
- the initial `Rate` is either negative or > `MaxRate`
|
||||
- the initial `MaxChangeRate` is either negative or > `MaxRate`
|
||||
- the description fields are too large
|
||||
|
||||
This message creates and stores the `Validator` object at appropriate indexes.
|
||||
Additionally a self-delegation is made with the initial tokens delegation
|
||||
tokens `Delegation`. The validator always starts as unbonded but may be bonded
|
||||
in the first end-block.
|
||||
|
||||
|
||||
## MsgEditValidator
|
||||
|
||||
The `Description`, `CommissionRate` of a validator can be updated using the
|
||||
`MsgEditCandidacy`.
|
||||
|
||||
```golang
|
||||
type MsgEditCandidacy struct {
|
||||
Description Description
|
||||
ValidatorAddr sdk.ValAddress
|
||||
CommissionRate sdk.Dec
|
||||
}
|
||||
```
|
||||
|
||||
This message is expected to fail if:
|
||||
|
||||
- the initial `CommissionRate` is either negative or > `MaxRate`
|
||||
- the `CommissionRate` has already been updated within the previous 24 hours
|
||||
- the `CommissionRate` is > `MaxChangeRate`
|
||||
- the description fields are too large
|
||||
|
||||
This message stores the updated `Validator` object.
|
||||
|
||||
## MsgDelegate
|
||||
|
||||
Within this message the delegator provides coins, and in return receives
|
||||
some amount of their validator's (newly created) delegator-shares that are
|
||||
assigned to `Delegation.Shares`.
|
||||
|
||||
```golang
|
||||
type MsgDelegate struct {
|
||||
DelegatorAddr sdk.AccAddress
|
||||
ValidatorAddr sdk.ValAddress
|
||||
Delegation sdk.Coin
|
||||
}
|
||||
```
|
||||
|
||||
This message is expected to fail if:
|
||||
|
||||
- the validator is does not exist
|
||||
- the validator is jailed
|
||||
|
||||
If an existing `Delegation` object for provided addresses does not already
|
||||
exist than it is created as part of this message otherwise the existing
|
||||
`Delegation` is updated to include the newly received shares.
|
||||
|
||||
## MsgBeginUnbonding
|
||||
|
||||
The begin unbonding message allows delegators to undelegate their tokens from
|
||||
validator.
|
||||
|
||||
```golang
|
||||
type MsgBeginUnbonding struct {
|
||||
DelegatorAddr sdk.AccAddress
|
||||
ValidatorAddr sdk.ValAddress
|
||||
SharesAmount sdk.Dec
|
||||
}
|
||||
```
|
||||
|
||||
This message is expected to fail if:
|
||||
|
||||
- the delegation doesn't exist
|
||||
- the validator doesn't exist
|
||||
- the delegation has less shares than `SharesAmount`
|
||||
|
||||
When this message is processed the following actions occur:
|
||||
- validator's `DelegatorShares` and the delegation's `Shares` are both reduced
|
||||
by the message `SharesAmount`
|
||||
- calculate the token worth of the shares remove that amount tokens held
|
||||
within the validator
|
||||
- with those removed tokens, if the validator is:
|
||||
- bonded - add them to an entry in `UnbondingDelegation` (create
|
||||
`UnbondingDelegation` if it doesn't exist) with a completion time a full
|
||||
unbonding period from the current time. Update pool shares to reduce
|
||||
BondedTokens and increase NotBondedTokens by token worth of the shares.
|
||||
- unbonding - add them to an entry in `UnbondingDelegation` (create
|
||||
`UnbondingDelegation` if it doesn't exist) with the same completion time
|
||||
as the validator (`UnbondingMinTime`).
|
||||
- unbonded - then send the coins the message `DelegatorAddr`
|
||||
- if there are no more `Shares` in the delegation, then the delegation object
|
||||
is removed from the store
|
||||
- under this situation if the delegation is the validator's self-delegation
|
||||
then also jail the validator.
|
||||
|
||||
## MsgBeginRedelegate
|
||||
|
||||
The redelegation command allows delegators to instantly switch validators. Once
|
||||
the unbonding period has passed, the redelegation is automatically completed in
|
||||
the EndBlocker.
|
||||
|
||||
```golang
|
||||
type MsgBeginRedelegate struct {
|
||||
DelegatorAddr sdk.AccAddress
|
||||
ValidatorSrcAddr sdk.ValAddress
|
||||
ValidatorDstAddr sdk.ValAddress
|
||||
SharesAmount sdk.Dec
|
||||
}
|
||||
```
|
||||
|
||||
This message is expected to fail if:
|
||||
|
||||
- the delegation doesn't exist
|
||||
- the source or destination validators don't exist
|
||||
- the delegation has less shares than `SharesAmount`
|
||||
- the source validator has a receiving redelegation which
|
||||
is not matured (aka. the redelegation may be transitive)
|
||||
|
||||
When this message is processed the following actions occur:
|
||||
- the source validator's `DelegatorShares` and the delegations `Shares` are
|
||||
both reduced by the message `SharesAmount`
|
||||
- calculate the token worth of the shares remove that amount tokens held
|
||||
within the source validator.
|
||||
- if the source validator is:
|
||||
- bonded - add an entry to the `Redelegation` (create
|
||||
`Redelegation` if it doesn't exist) with a completion time a full
|
||||
unbonding period from the current time. Update pool shares to reduce
|
||||
BondedTokens and increase NotBondedTokens by token worth of the shares
|
||||
(this may be effectively reversed in the next step however).
|
||||
- unbonding - add an entry to the `Redelegation` (create `Redelegation` if
|
||||
it doesn't exist) with the same completion time as the validator
|
||||
(`UnbondingMinTime`).
|
||||
- unbonded - no action required in this step
|
||||
- Delegate the token worth to the destination validator, possibly moving
|
||||
tokens back to the bonded state.
|
||||
- if there are no more `Shares` in the source delegation, then the source
|
||||
delegation object is removed from the store
|
||||
- under this situation if the delegation is the validator's self-delegation
|
||||
then also jail the validator.
|
||||
@@ -20,19 +20,25 @@ The following specification uses *Atom* as the native staking token. The module
|
||||
can be adapted to any Proof-Of-Stake blockchain by replacing *Atom* with the
|
||||
native staking token of the chain.
|
||||
|
||||
1. **[State](state.md)**
|
||||
1. Params
|
||||
1. Pool
|
||||
2. Validators
|
||||
3. Delegations
|
||||
2. **[Transactions](transactions.md)**
|
||||
1. Create-Validator
|
||||
2. Edit-Validator
|
||||
3. Repeal-Revocation
|
||||
4. Delegate
|
||||
5. Unbond
|
||||
6. Redelegate
|
||||
3. **[Validator Set Changes](valset-changes.md)**
|
||||
1. Validator set updates
|
||||
2. Slashing
|
||||
3. Automatic Unbonding
|
||||
1. **[State](state.md)**
|
||||
- Pool
|
||||
- Params
|
||||
- Validator
|
||||
- Delegation
|
||||
- UnbondingDelegation
|
||||
- Redelegation
|
||||
2. **[Messages](messages.md)**
|
||||
- MsgCreateValidator
|
||||
- MsgEditValidator
|
||||
- MsgDelegate
|
||||
- MsgBeginUnbonding
|
||||
- MsgBeginRedelegate
|
||||
3. **[End-Block](end_block.md)**
|
||||
- Validator Set Changes
|
||||
- Queues
|
||||
- Unbonding Validators
|
||||
- Unbonding Delegations
|
||||
- Redelegations
|
||||
4. **[Hooks](hooks.md)**
|
||||
5. **[Tags](tags.md)**
|
||||
|
||||
+61
-64
@@ -1,66 +1,67 @@
|
||||
## State
|
||||
# State
|
||||
|
||||
### Pool
|
||||
## Pool
|
||||
|
||||
The pool is a space for all dynamic global state of the Cosmos Hub. It tracks
|
||||
information about the total amounts of Atoms in all states, moving Atom
|
||||
inflation information, etc.
|
||||
The pool tracks the total amounts of tokens (each staking denom is tracked
|
||||
separately) and their state (bonded or loose).
|
||||
|
||||
Note: `NotBondedTokens` _includes_ both tokens in an `unbonding` state as well
|
||||
as fully `unbonded` state.
|
||||
|
||||
- Pool: `0x01 -> amino(pool)`
|
||||
|
||||
```golang
|
||||
type Pool struct {
|
||||
LooseTokens sdk.Int // tokens not associated with any bonded validator
|
||||
BondedTokens sdk.Int // reserve of bonded tokens
|
||||
NotBondedTokens sdk.Int // tokens not associated with any bonded validator
|
||||
BondedTokens sdk.Int // reserve of bonded tokens
|
||||
}
|
||||
```
|
||||
|
||||
### Params
|
||||
## Params
|
||||
|
||||
Params is global data structure that stores system parameters and defines
|
||||
overall functioning of the staking module.
|
||||
Params is a module-wide configuration structure that stores system parameters
|
||||
and defines overall functioning of the staking module.
|
||||
|
||||
- Params: `0x00 -> amino(params)`
|
||||
- Params: `Paramsspace("staking") -> amino(params)`
|
||||
|
||||
```golang
|
||||
type Params struct {
|
||||
MaxValidators uint16 // maximum number of validators
|
||||
BondDenom string // bondable coin denomination
|
||||
UnbondingTime time.Duration // time duration of unbonding
|
||||
MaxValidators uint16 // maximum number of validators
|
||||
BondDenom string // bondable coin denomination
|
||||
}
|
||||
```
|
||||
|
||||
### Validator
|
||||
## Validator
|
||||
|
||||
Validators are identified according to the `OperatorAddr`, an SDK validator
|
||||
address for the operator of the validator.
|
||||
Validators objects should be primarily stored and accessed by the
|
||||
`OperatorAddr`, an SDK validator address for the operator of the validator. Two
|
||||
additional indexes are maintained in order to fulfill required lookups for
|
||||
slashing and validator-set updates.
|
||||
|
||||
Validators also have a `ConsPubKey`, the public key of the validator used in
|
||||
Tendermint consensus. The validator can be retrieved from it's `ConsPubKey`
|
||||
once it can be converted into the corresponding `ConsAddr`. Validators are
|
||||
indexed in the store using the following maps:
|
||||
|
||||
- Validators: `0x02 | OperatorAddr -> amino(validator)`
|
||||
- ValidatorsByConsAddr: `0x03 | ConsAddr -> OperatorAddr`
|
||||
- ValidatorsByPower: `0x05 | power | blockHeight | blockTx -> OperatorAddr`
|
||||
- Validators: `0x21 | OperatorAddr -> amino(validator)`
|
||||
- ValidatorsByConsAddr: `0x22 | ConsAddr -> OperatorAddr`
|
||||
- ValidatorsByPower: `0x23 | BigEndian(Tokens) | OperatorAddr -> OperatorAddr`
|
||||
|
||||
`Validators` is the primary index - it ensures that each operator can have only one
|
||||
associated validator, where the public key of that validator can change in the
|
||||
future. Delegators can refer to the immutable operator of the validator, without
|
||||
concern for the changing public key.
|
||||
|
||||
`ValidatorsByPubKey` is a secondary index that enables lookups for slashing.
|
||||
`ValidatorByConsAddr` is a secondary index that enables lookups for slashing.
|
||||
When Tendermint reports evidence, it provides the validator address, so this
|
||||
map is needed to find the operator.
|
||||
map is needed to find the operator. Note that the `ConsAddr` corresponds to the
|
||||
address which can be derived from the validator's `ConsPubKey`.
|
||||
|
||||
`ValidatorsByPower` is a secondary index that provides a sorted list of
|
||||
potential validators to quickly determine the current active set. For instance,
|
||||
the first 100 validators in this list can be returned with every EndBlock.
|
||||
potential validators to quickly determine the current active set. Note
|
||||
that all validators where `Jailed` is true are not stored within this index.
|
||||
|
||||
The `Validator` holds the current state and some historical actions of the
|
||||
validator.
|
||||
Each validator's state is stored in a `Validator` struct:
|
||||
|
||||
```golang
|
||||
type Validator struct {
|
||||
OperatorAddr sdk.ValAddress // address of the validator's operator; bech encoded in JSON
|
||||
ConsPubKey crypto.PubKey // Tendermint consensus pubkey of validator
|
||||
Jailed bool // has the validator been jailed?
|
||||
|
||||
@@ -68,21 +69,20 @@ type Validator struct {
|
||||
Tokens sdk.Int // delegated tokens (incl. self-delegation)
|
||||
DelegatorShares sdk.Dec // total shares issued to a validator's delegators
|
||||
|
||||
Description Description // description terms for the validator
|
||||
Description Description // description terms for the validator
|
||||
|
||||
// Needed for ordering vals in the by-power key
|
||||
BondHeight int64 // earliest height as a bonded validator
|
||||
BondIntraTxCounter int16 // block-local tx index of validator change
|
||||
UnbondingHeight int64 // if unbonding, height at which this validator has begun unbonding
|
||||
UnbondingMinTime time.Time // if unbonding, min time for the validator to complete unbonding
|
||||
|
||||
CommissionInfo CommissionInfo // info about the validator's commission
|
||||
Commission Commission // info about the validator's commission
|
||||
}
|
||||
|
||||
type CommissionInfo struct {
|
||||
Rate sdk.Dec // the commission rate of fees charged to any delegators
|
||||
Max sdk.Dec // maximum commission rate which this validator can ever charge
|
||||
ChangeRate sdk.Dec // maximum daily increase of the validator commission
|
||||
ChangeToday sdk.Dec // commission rate change today, reset each day (UTC time)
|
||||
LastChange int64 // unix timestamp of last commission change
|
||||
type Commission struct {
|
||||
Rate sdk.Dec // the commission rate charged to delegators
|
||||
MaxRate sdk.Dec // maximum commission rate which this validator can ever charge
|
||||
MaxChangeRate sdk.Dec // maximum daily increase of the validator commission
|
||||
UpdateTime time.Time // the last time the commission rate was changed
|
||||
}
|
||||
|
||||
type Description struct {
|
||||
@@ -93,12 +93,12 @@ type Description struct {
|
||||
}
|
||||
```
|
||||
|
||||
### Delegation
|
||||
## Delegation
|
||||
|
||||
Delegations are identified by combining `DelegatorAddr` (the address of the delegator)
|
||||
with the `ValidatorAddr` Delegators are indexed in the store as follows:
|
||||
|
||||
- Delegation: ` 0x0A | DelegatorAddr | ValidatorAddr -> amino(delegation)`
|
||||
- Delegation: ` 0x31 | DelegatorAddr | ValidatorAddr -> amino(delegation)`
|
||||
|
||||
Atom holders may delegate coins to validators; under this circumstance their
|
||||
funds are held in a `Delegation` data structure. It is owned by one
|
||||
@@ -113,7 +113,7 @@ type Delegation struct {
|
||||
}
|
||||
```
|
||||
|
||||
### UnbondingDelegation
|
||||
## UnbondingDelegation
|
||||
|
||||
Shares in a `Delegation` can be unbonded, but they must for some time exist as
|
||||
an `UnbondingDelegation`, where shares can be reduced if Byzantine behavior is
|
||||
@@ -121,9 +121,9 @@ detected.
|
||||
|
||||
`UnbondingDelegation` are indexed in the store as:
|
||||
|
||||
- UnbondingDelegationByDelegator: ` 0x0B | DelegatorAddr | ValidatorAddr ->
|
||||
- UnbondingDelegation: ` 0x32 | DelegatorAddr | ValidatorAddr ->
|
||||
amino(unbondingDelegation)`
|
||||
- UnbondingDelegationByValOwner: ` 0x0C | ValidatorAddr | DelegatorAddr | ValidatorAddr ->
|
||||
- UnbondingDelegationsFromValidator: ` 0x33 | ValidatorAddr | DelegatorAddr ->
|
||||
nil`
|
||||
|
||||
The first map here is used in queries, to lookup all unbonding delegations for
|
||||
@@ -132,8 +132,6 @@ unbonding delegations associated with a given validator that need to be
|
||||
slashed.
|
||||
|
||||
A UnbondingDelegation object is created every time an unbonding is initiated.
|
||||
The unbond must be completed with a second transaction provided by the
|
||||
delegation owner after the unbonding period has passed.
|
||||
|
||||
```golang
|
||||
type UnbondingDelegation struct {
|
||||
@@ -150,31 +148,30 @@ type UnbondingDelegationEntry struct {
|
||||
}
|
||||
```
|
||||
|
||||
### Redelegation
|
||||
## Redelegation
|
||||
|
||||
Shares in a `Delegation` can be rebonded to a different validator, but they must
|
||||
for some time exist as a `Redelegation`, where shares can be reduced if Byzantine
|
||||
behavior is detected. This is tracked as moving a delegation from a `ValidatorSrcAddr`
|
||||
to a `ValidatorDstAddr`.
|
||||
The bonded tokens worth of a `Delegation` may be instantly redelegated from a
|
||||
source validator to a different validator (destination validator). However when
|
||||
this occurs they must be tracked in a `Redelegation` object, whereby their
|
||||
shares can be slashed if their tokens have contributed to a Byzantine fault
|
||||
committed by the source validator.
|
||||
|
||||
`Redelegation` are indexed in the store as:
|
||||
|
||||
- Redelegations: `0x0D | DelegatorAddr | ValidatorSrcAddr | ValidatorDstAddr ->
|
||||
amino(redelegation)`
|
||||
- RedelegationsBySrc: `0x0E | ValidatorSrcAddr | ValidatorDstAddr |
|
||||
DelegatorAddr -> nil`
|
||||
- RedelegationsByDst: `0x0F | ValidatorDstAddr | ValidatorSrcAddr | DelegatorAddr
|
||||
-> nil`
|
||||
- Redelegations: `0x34 | DelegatorAddr | ValidatorSrcAddr | ValidatorDstAddr -> amino(redelegation)`
|
||||
- RedelegationsBySrc: `0x35 | ValidatorSrcAddr | ValidatorDstAddr | DelegatorAddr -> nil`
|
||||
- RedelegationsByDst: `0x36 | ValidatorDstAddr | ValidatorSrcAddr | DelegatorAddr -> nil`
|
||||
|
||||
The first map here is used for queries, to lookup all redelegations for a given
|
||||
delegator. The second map is used for slashing based on the `ValidatorSrcAddr`,
|
||||
while the third map is for slashing based on the ToValOwnerAddr.
|
||||
while the third map is for slashing based on the `ValidatorDstAddr`.
|
||||
|
||||
A redelegation object is created every time a redelegation occurs. The
|
||||
redelegation must be completed with a second transaction provided by the
|
||||
delegation owner after the unbonding period has passed. The destination
|
||||
delegation of a redelegation may not itself undergo a new redelegation until
|
||||
the original redelegation has been completed.
|
||||
A redelegation object is created every time a redelegation occurs. To prevent
|
||||
"redelegation hopping" redelegations may not occure under the situation that:
|
||||
- the (re)delegator already has another unmature redelegation in progress
|
||||
with a destination to a validator (let's call it `Validator X`)
|
||||
- and, the (re)delegator is attempting to create a _new_ redelegation
|
||||
where the source validator for this new redelegation is `Validator-X`.
|
||||
|
||||
```golang
|
||||
type Redelegation struct {
|
||||
|
||||
@@ -1,312 +0,0 @@
|
||||
# Transaction Overview
|
||||
|
||||
In this section we describe the processing of the transactions and the
|
||||
corresponding updates to the state. Transactions:
|
||||
|
||||
* TxCreateValidator
|
||||
* TxEditValidator
|
||||
* TxDelegation
|
||||
* TxStartUnbonding
|
||||
* TxRedelegate
|
||||
|
||||
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
|
||||
* `getXxx`, `setXxx`, and `removeXxx` functions are used to retrieve and
|
||||
modify objects from the store
|
||||
* `sdk.Dec` refers to a decimal type specified by the SDK.
|
||||
* `sdk.Int` refers to an integer type specified by the SDK.
|
||||
|
||||
## TxCreateValidator
|
||||
|
||||
* triggers: `distribution.CreateValidatorDistribution`
|
||||
|
||||
A validator is created using the `TxCreateValidator` transaction.
|
||||
|
||||
```golang
|
||||
type TxCreateValidator struct {
|
||||
Description Description
|
||||
Commission Commission
|
||||
|
||||
DelegatorAddr sdk.AccAddress
|
||||
ValidatorAddr sdk.ValAddress
|
||||
PubKey crypto.PubKey
|
||||
Delegation sdk.Coin
|
||||
}
|
||||
|
||||
createValidator(tx TxCreateValidator):
|
||||
ok := validatorExists(tx.ValidatorAddr)
|
||||
if ok return err // only one validator per address
|
||||
|
||||
ok := validatorByPubKeyExists(tx.PubKey)
|
||||
if ok return err // only one validator per public key
|
||||
|
||||
err := validateDenom(tx.Delegation.Denom)
|
||||
if err != nil return err // denomination must be valid
|
||||
|
||||
validator := NewValidator(tx.ValidatorAddr, tx.PubKey, tx.Description)
|
||||
|
||||
err := setInitialCommission(validator, tx.Commission, blockTime)
|
||||
if err != nil return err // must be able to set initial commission correctly
|
||||
|
||||
// set the validator and public key
|
||||
setValidator(validator)
|
||||
setValidatorByPubKeyIndex(validator)
|
||||
|
||||
// delegate coins from tx.DelegatorAddr to the validator
|
||||
err := delegate(tx.DelegatorAddr, tx.Delegation, validator)
|
||||
if err != nil return err // must be able to set delegation correctly
|
||||
|
||||
tags := createTags(tx)
|
||||
return tags
|
||||
```
|
||||
|
||||
## TxEditValidator
|
||||
|
||||
If either the `Description`, `Commission`, or the `ValidatorAddr` need to be
|
||||
updated, the `TxEditCandidacy` transaction should be sent from the operator
|
||||
account:
|
||||
|
||||
```golang
|
||||
type TxEditCandidacy struct {
|
||||
Description Description
|
||||
ValidatorAddr sdk.ValAddress
|
||||
CommissionRate sdk.Dec
|
||||
}
|
||||
|
||||
editCandidacy(tx TxEditCandidacy):
|
||||
validator, ok := getValidator(tx.ValidatorAddr)
|
||||
if !ok return err // validator must exist
|
||||
|
||||
// Attempt to update the validator's description. The description provided
|
||||
// must be valid.
|
||||
description, err := updateDescription(validator, tx.Description)
|
||||
if err != nil return err
|
||||
|
||||
// a validator is not required to update it's commission rate
|
||||
if tx.CommissionRate != nil {
|
||||
// Attempt to update a validator's commission rate. The rate provided
|
||||
// must be valid. It's rate can only be updated once a day.
|
||||
err := updateValidatorCommission(validator, tx.CommissionRate)
|
||||
if err != nil return err
|
||||
}
|
||||
|
||||
// set the validator and public key
|
||||
setValidator(validator)
|
||||
|
||||
tags := createTags(tx)
|
||||
return tags
|
||||
```
|
||||
|
||||
### TxDelegate
|
||||
|
||||
- triggers: `distribution.CreateOrModDelegationDistribution`
|
||||
|
||||
Within this transaction the delegator provides coins, and in return receives
|
||||
some amount of their validator's delegator-shares that are assigned to
|
||||
`Delegation.Shares`.
|
||||
|
||||
```golang
|
||||
type TxDelegate struct {
|
||||
DelegatorAddr sdk.Address
|
||||
ValidatorAddr sdk.Address
|
||||
Amount sdk.Coin
|
||||
}
|
||||
|
||||
delegate(tx TxDelegate):
|
||||
pool = getPool()
|
||||
if validator.Status == Jailed return
|
||||
|
||||
delegation = getDelegatorBond(DelegatorAddr, ValidatorAddr)
|
||||
if delegation == nil then delegation = NewDelegation(DelegatorAddr, ValidatorAddr)
|
||||
|
||||
validator, pool, issuedDelegatorShares = validator.addTokensFromDel(tx.Amount, pool)
|
||||
delegation.Shares += issuedDelegatorShares
|
||||
|
||||
setDelegation(delegation)
|
||||
updateValidator(validator)
|
||||
setPool(pool)
|
||||
return
|
||||
```
|
||||
|
||||
### TxStartUnbonding
|
||||
|
||||
Delegator unbonding is defined with the following transaction:
|
||||
|
||||
```golang
|
||||
type TxStartUnbonding struct {
|
||||
DelegatorAddr sdk.Address
|
||||
ValidatorAddr sdk.Address
|
||||
Shares string
|
||||
}
|
||||
|
||||
startUnbonding(tx TxStartUnbonding):
|
||||
delegation, found = getDelegatorBond(store, sender, tx.PubKey)
|
||||
if !found == nil return
|
||||
|
||||
if bond.Shares < tx.Shares
|
||||
return ErrNotEnoughBondShares
|
||||
|
||||
validator, found = GetValidator(tx.ValidatorAddr)
|
||||
if !found {
|
||||
return err
|
||||
|
||||
bond.Shares -= tx.Shares
|
||||
|
||||
revokeCandidacy = false
|
||||
if bond.Shares.IsZero() {
|
||||
|
||||
if bond.DelegatorAddr == validator.Operator && validator.Jailed == false
|
||||
revokeCandidacy = true
|
||||
|
||||
removeDelegation( bond)
|
||||
else
|
||||
bond.Height = currentBlockHeight
|
||||
setDelegation(bond)
|
||||
|
||||
pool = GetPool()
|
||||
validator, pool, returnAmount = validator.removeDelShares(pool, tx.Shares)
|
||||
setPool( pool)
|
||||
|
||||
unbondingDelegation = NewUnbondingDelegation(sender, returnAmount, currentHeight/Time, startSlashRatio)
|
||||
setUnbondingDelegation(unbondingDelegation)
|
||||
|
||||
if revokeCandidacy
|
||||
validator.Jailed = true
|
||||
|
||||
validator = updateValidator(validator)
|
||||
|
||||
if validator.Status == Unbonded && validator.DelegatorShares == 0 {
|
||||
removeValidator(validator.Operator)
|
||||
|
||||
return
|
||||
```
|
||||
|
||||
### TxRedelegation
|
||||
|
||||
The redelegation command allows delegators to instantly switch validators. Once
|
||||
the unbonding period has passed, the redelegation is automatically completed in the EndBlocker.
|
||||
|
||||
```golang
|
||||
type TxRedelegate struct {
|
||||
DelegatorAddr Address
|
||||
ValidatorFrom Validator
|
||||
ValidatorTo Validator
|
||||
Shares sdk.Dec
|
||||
CompletedTime int64
|
||||
}
|
||||
|
||||
redelegate(tx TxRedelegate):
|
||||
|
||||
pool = getPool()
|
||||
delegation = getDelegatorBond(tx.DelegatorAddr, tx.ValidatorFrom.Operator)
|
||||
if delegation == nil
|
||||
return
|
||||
|
||||
if delegation.Shares < tx.Shares
|
||||
return
|
||||
delegation.shares -= Tx.Shares
|
||||
validator, pool, createdCoins = validator.RemoveShares(pool, tx.Shares)
|
||||
setPool(pool)
|
||||
|
||||
redelegation = newRedelegation(tx.DelegatorAddr, tx.validatorFrom,
|
||||
tx.validatorTo, tx.Shares, createdCoins, tx.CompletedTime)
|
||||
setRedelegation(redelegation)
|
||||
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
|
||||
|
||||
operatorAddr = iterator.Value()
|
||||
if bytes.Equal(operatorAddr, newValidator.Operator) {
|
||||
validator = newValidator
|
||||
else
|
||||
validator = getValidator(operatorAddr)
|
||||
|
||||
// if not previously a validator (and unjailed),
|
||||
// kick the cliff validator / bond this new validator
|
||||
if validator.Status() != Bonded && !validator.Jailed {
|
||||
kickCliffValidator = true
|
||||
|
||||
validator = bondValidator(ctx, store, validator)
|
||||
if bytes.Equal(operatorAddr, newValidator.Operator) {
|
||||
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 Context, store KVStore, validator Validator)
|
||||
pool = GetPool(ctx)
|
||||
|
||||
// set the status
|
||||
validator, pool = validator.UpdateStatus(pool, 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 Context, store KVStore, validator Validator) Validator
|
||||
pool = GetPool(ctx)
|
||||
|
||||
// set the status
|
||||
validator, pool = validator.UpdateStatus(pool, 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
|
||||
```
|
||||
Reference in New Issue
Block a user