From e660adc6e5182ef9ed40121a29767bb520305263 Mon Sep 17 00:00:00 2001 From: Hans Schoenburg Date: Thu, 12 Sep 2019 08:24:31 -0700 Subject: [PATCH] Merge PR #5020: /spec/staking/ add copy on Slashing and Delegator Shares --- docs/spec/staking/01_state.md | 38 ++++++++++++++++++-- docs/spec/staking/02_state_transitions.md | 42 ++++++++++++++++++----- 2 files changed, 69 insertions(+), 11 deletions(-) diff --git a/docs/spec/staking/01_state.md b/docs/spec/staking/01_state.md index 8c0fb0cab..dbf9410ce 100644 --- a/docs/spec/staking/01_state.md +++ b/docs/spec/staking/01_state.md @@ -24,6 +24,21 @@ type Params struct { ## Validator +Validators can have one of three statuses + +- `Unbonded`: The validator is not in the active set. They cannot sign blocks and do not earn + rewards. They can receive delegations. +- `Bonded`": Once the validator receives sufficient bonded tokens they automtically join the + active set during [`EndBlock`](./04_end_block.md#validator-set-changes) and their status is updated to `Bonded`. + They are signing blocks and receiving rewards. They can receive further delegations. + They can be slashed for misbehavior. Delegators to this validator who unbond their delegation + must wait the duration of the UnbondingTime, a chain-specific param. during which time + they are still slashable for offences of the source validator if those offences were committed + during the period of time that the tokens were bonded. +- `Unbonding`: When a validator leaves the active set, either by choice or due to slashing or + tombstoning, an unbonding of all their delegations begins. All delegations must then wait the UnbondingTime + before moving receiving their tokens to their accounts from the `BondedPool`. + Validators objects should be primarily stored and accessed by the `OperatorAddr`, an SDK validator address for the operator of the validator. Two additional indices are maintained per validator object in order to fulfill @@ -113,6 +128,25 @@ type Delegation struct { } ``` +### Delegator Shares + +When one Delegates tokens to a Validator they are issued a number of delegator shares based on a +dynamic exchange rate, calculated as follows from the total number of tokens delegated to the +validator and the number of shares issued so far: + +`Shares per Token = validator.TotalShares() / validator.Tokens()` + +Only the number of shares received is stored on the DelegationEntry. When a delegator then +Undelegates, the token amount they receive is calculated from the number of shares they currently +hold and the inverse exchange rate: + +`Tokens per Share = validator.Tokens() / validatorShares()` + +These `Shares` are simply an accounting mechanism. They are not a fungible asset. The reason for +this mechanism is to simplify the accounting around slashing. Rather than iteratively slashing the +tokens of every delegation entry, instead the Validators total bonded tokens can be slashed, +effectively reducing the value of each issued delegator share. + ## UnbondingDelegation Shares in a `Delegation` can be unbonded, but they must for some time exist as @@ -167,9 +201,9 @@ delegator. The second map is used for slashing based on the `ValidatorSrcAddr`, while the third map is for slashing based on the `ValidatorDstAddr`. A redelegation object is created every time a redelegation occurs. To prevent -"redelegation hopping" redelegations may not occure under the situation that: +"redelegation hopping" redelegations may not occur under the situation that: -- the (re)delegator already has another unmature redelegation in progress +- the (re)delegator already has another immature 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`. diff --git a/docs/spec/staking/02_state_transitions.md b/docs/spec/staking/02_state_transitions.md index 5676e99bb..76f086f5e 100644 --- a/docs/spec/staking/02_state_transitions.md +++ b/docs/spec/staking/02_state_transitions.md @@ -7,12 +7,13 @@ This document describes the state transition operations pertaining to: 3. [Slashing](./02_state_transitions.md#slashing) ## Validators +State transitions in validators are performed on every [`EndBlock`](./04_end_block.md#validator-set-changes) +in order to check for changes in the active `ValidatorSet`. -State transitions in validators are performed on every [`EndBlock`](./04_end_block.md#validator-set-changes) in order to check for changes in the active `ValidatorSet`. +### Unbonded to Bonded -### Non-Bonded to Bonded - -When a validator is bonded from any other state the following operations occur: +The following transition occurs when a validator's ranking in the `ValidatorPowerIndex` surpasses +that of the `LastValidator`. - set `validator.Status` to `Bonded` - send the `validator.Tokens` from the `NotBondedTokens` to the `BondedPool` `ModuleAccount` @@ -53,13 +54,13 @@ this process may be also be reversed. the following operations occur: ### Delegate -When a delegation occurs both the validator and the delegation objects are affected +When a delegation occurs both the validator and the delegation objects are affected - determine the delegators shares based on tokens delegated and the validator's exchange rate - remove tokens from the sending account - add shares the delegation object or add them to a created validator object - add new delegator shares and update the `Validator` object -- transfer the `delegation.Amount` from the delegator's account to the `BondedPool` or the `NotBondedPool` `ModuleAccount` depending if the `validator.Status` is `Bonded` or not +- transfer the `delegation.Amount` from the delegator's account to the `BondedPool` or the `NotBondedPool` `ModuleAccount` depending if the `validator.Status` is `Bonded` or not - delete the existing record from `ValidatorByPowerIndex` - add an new updated record to the `ValidatorByPowerIndex` @@ -76,7 +77,7 @@ Delegation may be called. - if the delegation is the operator of the validator and no more shares exist then trigger a jail validator - update the validator with removed the delegator shares and associated coins - if the validator state is `Bonded`, transfer the `Coins` worth of the unbonded -shares from the `BondedPool` to the `NotBondedPool` `ModuleAccount` + shares from the `BondedPool` to the `NotBondedPool` `ModuleAccount` - remove the validator if it is unbonded and there are no more delegation shares. ### Complete Unbonding @@ -93,8 +94,10 @@ Redelegations affect the delegation, source and destination validators. - perform an `unbond` delegation from the source validator to retrieve the tokens worth of the unbonded shares - using the unbonded tokens, `Delegate` them to the destination validator -- if the `sourceValidator.Status` is `Bonded`, and the `destinationValidator` is not, transfer the newly delegated tokens from the `BondedPool` to the `NotBondedPool` `ModuleAccount` -- otherwise, if the `sourceValidator.Status` is not `Bonded`, and the `destinationValidator` is `Bonded`, transfer the newly delegated tokens from the `NotBondedPool` to the `BondedPool` `ModuleAccount` +- if the `sourceValidator.Status` is `Bonded`, and the `destinationValidator` is not, + transfer the newly delegated tokens from the `BondedPool` to the `NotBondedPool` `ModuleAccount` +- otherwise, if the `sourceValidator.Status` is not `Bonded`, and the `destinationValidator` + is `Bonded`, transfer the newly delegated tokens from the `NotBondedPool` to the `BondedPool` `ModuleAccount` - record the token amount in an new entry in the relevant `Redelegation` ### Complete Redelegation @@ -107,6 +110,27 @@ When a redelegations complete the following occurs: ### Slash Validator +When a Validator is slashed, the following occurs: + +- The total `slashAmount` is calculated as the `slashFactor` (a chain parameter) * `TokensFromConsensusPower`, +the total number of tokens bonded to the validator at the time of the infraction. +- Every unbonding delegation and redelegation from the validator are slashed by the `slashFactor` +percentage of the initialBalance. +- Each amount slashed from redelegations and unbonding delegations is subtracted from the +total slash amount. +- The `remaingSlashAmount` is then slashed from the validator's tokens in the `BondedPool` or +`NonBondedPool` depending on the validator's status. This reduces the total supply of tokens. + ### Slash Unbonding Delegation +When a validator is slashed, so are those unbonding delegations from the validator that began unbonding +after the time of the infraction. Every entry in every unbonding delegation from the validator +is slashed by `slashFactor`. The amount slashed is calculated from the `InitialBalance` of the +delegation and is capped to prevent a resulting negative balance. Completed (or mature) unbondings are not slashed. + ### Slash Redelegation + +When a validator is slashed, so are all redelegations from the validator that began after the +infraction. Redelegations are slashed by `slashFactor`. +The amount slashed is calculated from the `InitialBalance` of the delegation and is capped to +prevent a resulting negative balance. Mature redelegations are not slashed.