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:
frog power 4000 2019-01-21 19:52:03 -05:00 committed by GitHub
parent 332a5a34c5
commit 7f789d2ed3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
49 changed files with 496 additions and 648 deletions

View File

@ -6,6 +6,7 @@ BREAKING CHANGES
* [gaia-lite] [\#2182] Renamed and merged all redelegations endpoints into `/staking/redelegations`
* [\#3176](https://github.com/cosmos/cosmos-sdk/issues/3176) `tx/sign` endpoint now expects `BaseReq` fields as nested object.
* [\#2222] all endpoints renamed from `/stake` -> `/staking`
* [\#1268] `LooseTokens` -> `NotBondedTokens`
* [\#3289] misc renames:
* `Validator.UnbondingMinTime` -> `Validator.UnbondingCompletionTime`
* `Delegation` -> `Value` in `MsgCreateValidator` and `MsgDelegate`
@ -31,9 +32,10 @@ BREAKING CHANGES
* [\#3064](https://github.com/cosmos/cosmos-sdk/issues/3064) Sanitize `sdk.Coin` denom. Coins denoms are now case insensitive, i.e. 100fooToken equals to 100FOOTOKEN.
* [\#3195](https://github.com/cosmos/cosmos-sdk/issues/3195) Allows custom configuration for syncable strategy
* [\#3242](https://github.com/cosmos/cosmos-sdk/issues/3242) Fix infinite gas
meter utilization during aborted ante handler executions.
* [\#2222] [x/staking] `/stake` -> `/staking` module rename
* \#3292 [x/distribution] Enable or disable withdraw addresses with a parameter in the param store
meter utilization during aborted ante handler executions.
* [x/distribution] \#3292 Enable or disable withdraw addresses with a parameter in the param store
* [staking] \#2222 `/stake` -> `/staking` module rename
* [staking] \#1268 `LooseTokens` -> `NotBondedTokens`
* [staking] \#1402 Redelegation and unbonding-delegation structs changed to include multiple an array of entries
* [staking] \#3289 misc renames:
* `Validator.UnbondingMinTime` -> `Validator.UnbondingCompletionTime`
@ -110,7 +112,8 @@ IMPROVEMENTS
slashing, and staking modules.
* [\#3093](https://github.com/cosmos/cosmos-sdk/issues/3093) Ante handler does no longer read all accounts in one go when processing signatures as signature
verification may fail before last signature is checked.
* [x/stake] \#1402 Add for multiple simultaneous redelegations or unbonding-delegations within an unbonding period
* [staking] \#1402 Add for multiple simultaneous redelegations or unbonding-delegations within an unbonding period
* [staking] \#1268 staking spec rewrite
* Tendermint

View File

@ -347,12 +347,12 @@ func TestPoolParamsQuery(t *testing.T) {
pool := getStakingPool(t, port)
initialPool := staking.InitialPool()
initialPool.LooseTokens = initialPool.LooseTokens.Add(sdk.NewInt(100))
initialPool.BondedTokens = initialPool.BondedTokens.Add(sdk.NewInt(100)) // Delegate tx on GaiaAppGenState
initialPool.LooseTokens = initialPool.LooseTokens.Add(sdk.NewInt(50)) // freeFermionsAcc = 50 on GaiaAppGenState
initialPool.NotBondedTokens = initialPool.NotBondedTokens.Add(sdk.NewInt(100))
initialPool.BondedTokens = initialPool.BondedTokens.Add(sdk.NewInt(100)) // Delegate tx on GaiaAppGenState
initialPool.NotBondedTokens = initialPool.NotBondedTokens.Add(sdk.NewInt(50)) // freeFermionsAcc = 50 on GaiaAppGenState
require.Equal(t, initialPool.BondedTokens, pool.BondedTokens)
require.Equal(t, initialPool.LooseTokens, pool.LooseTokens)
require.Equal(t, initialPool.NotBondedTokens, pool.NotBondedTokens)
}
func TestValidatorsQuery(t *testing.T) {

View File

@ -291,7 +291,7 @@ func InitializeTestLCD(
accAuth.Coins = sdk.Coins{sdk.NewInt64Coin(stakingTypes.DefaultBondDenom, 100)}
acc := gapp.NewGenesisAccount(&accAuth)
genesisState.Accounts = append(genesisState.Accounts, acc)
genesisState.StakingData.Pool.LooseTokens = genesisState.StakingData.Pool.LooseTokens.Add(sdk.NewInt(100))
genesisState.StakingData.Pool.NotBondedTokens = genesisState.StakingData.Pool.NotBondedTokens.Add(sdk.NewInt(100))
}
appState, err := codec.MarshalJSONIndent(cdc, genesisState)

View File

@ -154,7 +154,7 @@ func GaiaAppGenState(cdc *codec.Codec, genDoc tmtypes.GenesisDoc, appGenTxs []js
for _, acc := range genesisState.Accounts {
for _, coin := range acc.Coins {
if coin.Denom == bondDenom {
stakingData.Pool.LooseTokens = stakingData.Pool.LooseTokens.
stakingData.Pool.NotBondedTokens = stakingData.Pool.NotBondedTokens.
Add(coin.Amount) // increase the supply
}
}

View File

@ -44,7 +44,7 @@ func makeGenesisState(t *testing.T, genTxs []auth.StdTx) GenesisState {
acc := auth.NewBaseAccountWithAddress(sdk.AccAddress(msg.ValidatorAddr))
acc.Coins = sdk.Coins{sdk.NewInt64Coin(bondDenom, 150)}
genAccs[i] = NewGenesisAccount(&acc)
stakingData.Pool.LooseTokens = stakingData.Pool.LooseTokens.Add(sdk.NewInt(150)) // increase the supply
stakingData.Pool.NotBondedTokens = stakingData.Pool.NotBondedTokens.Add(sdk.NewInt(150)) // increase the supply
}
// create the final app state

View File

@ -188,7 +188,7 @@ func appStateFn(r *rand.Rand, accs []simulation.Account, genesisTimestamp time.T
delegations = append(delegations, delegation)
}
stakingGenesis.Pool.LooseTokens = sdk.NewInt((amount * numAccs) + (numInitiallyBonded * amount))
stakingGenesis.Pool.NotBondedTokens = sdk.NewInt((amount * numAccs) + (numInitiallyBonded * amount))
stakingGenesis.Validators = validators
stakingGenesis.Bonds = delegations

View File

@ -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

45
docs/spec/SPEC-SPEC.md Normal file
View File

@ -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
```

View File

@ -1,4 +1,4 @@
## Transactions
## Messages
### MsgSend

View File

@ -1,4 +1,4 @@
# Transactions
# Messages
## MsgWithdrawDelegationRewardsAll

View File

@ -1,6 +1,6 @@
# Implementation (2/2)
## Transactions
## Messages
### Proposal Submission

View File

@ -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
```

View File

@ -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
```

View File

@ -1,2 +0,0 @@
- reserve pool
- AiB vesting

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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.

View File

@ -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)**

View File

@ -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 {

View File

@ -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
```

View File

@ -122,7 +122,7 @@ func CreateTestInputAdvanced(t *testing.T, isCheckTx bool, initCoins int64,
{sk.GetParams(ctx).BondDenom, sdk.NewInt(initCoins)},
})
require.Nil(t, err)
pool.LooseTokens = pool.LooseTokens.Add(sdk.NewInt(initCoins))
pool.NotBondedTokens = pool.NotBondedTokens.Add(sdk.NewInt(initCoins))
sk.SetPool(ctx, pool)
}

View File

@ -68,7 +68,7 @@ func getInitChainer(mapp *mock.App, keeper Keeper, stakingKeeper staking.Keeper,
mapp.InitChainer(ctx, req)
stakingGenesis := staking.DefaultGenesisState()
stakingGenesis.Pool.LooseTokens = sdk.NewInt(100000)
stakingGenesis.Pool.NotBondedTokens = sdk.NewInt(100000)
validators, err := staking.InitGenesis(ctx, stakingKeeper, stakingGenesis)
if err != nil {

View File

@ -60,7 +60,7 @@ func getInitChainer(mapp *mock.App, keeper staking.Keeper) sdk.InitChainer {
return func(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain {
mapp.InitChainer(ctx, req)
stakingGenesis := staking.DefaultGenesisState()
stakingGenesis.Pool.LooseTokens = sdk.NewInt(100000)
stakingGenesis.Pool.NotBondedTokens = sdk.NewInt(100000)
validators, err := staking.InitGenesis(ctx, keeper, stakingGenesis)
if err != nil {
panic(err)

View File

@ -76,7 +76,7 @@ func createTestInput(t *testing.T, defaults Params) (sdk.Context, bank.Keeper, s
sk := staking.NewKeeper(cdc, keyStaking, tkeyStaking, ck, paramsKeeper.Subspace(staking.DefaultParamspace), staking.DefaultCodespace)
genesis := staking.DefaultGenesisState()
genesis.Pool.LooseTokens = sdk.NewInt(initCoins.MulRaw(int64(len(addrs))).Int64())
genesis.Pool.NotBondedTokens = sdk.NewInt(initCoins.MulRaw(int64(len(addrs))).Int64())
_, err = staking.InitGenesis(ctx, sk, genesis)
require.Nil(t, err)

View File

@ -52,7 +52,7 @@ func getInitChainer(mapp *mock.App, keeper Keeper) sdk.InitChainer {
mapp.InitChainer(ctx, req)
stakingGenesis := DefaultGenesisState()
stakingGenesis.Pool.LooseTokens = sdk.NewInt(100000)
stakingGenesis.Pool.NotBondedTokens = sdk.NewInt(100000)
validators, err := InitGenesis(ctx, keeper, stakingGenesis)
if err != nil {

View File

@ -108,10 +108,16 @@ func handleMsgCreateValidator(ctx sdk.Context, msg types.MsgCreateValidator, k k
return ErrBadDenom(k.Codespace()).Result()
}
if _, err := msg.Description.EnsureLength(); err != nil {
return err.Result()
}
if ctx.ConsensusParams() != nil {
tmPubKey := tmtypes.TM2PB.PubKey(msg.PubKey)
if !common.StringInSlice(tmPubKey.Type, ctx.ConsensusParams().Validator.PubKeyTypes) {
return ErrValidatorPubKeyTypeUnsupported(k.Codespace(), tmPubKey.Type, ctx.ConsensusParams().Validator.PubKeyTypes).Result()
return ErrValidatorPubKeyTypeUnsupported(k.Codespace(),
tmPubKey.Type,
ctx.ConsensusParams().Validator.PubKeyTypes).Result()
}
}

View File

@ -308,11 +308,10 @@ func (k Keeper) HasReceivingRedelegation(ctx sdk.Context,
iterator := sdk.KVStorePrefixIterator(store, prefix)
defer iterator.Close()
found := false
if iterator.Valid() {
found = true
return true
}
return found
return false
}
// set a redelegation and associated index

View File

@ -176,7 +176,7 @@ func TestUnbondingDelegation(t *testing.T) {
func TestUnbondDelegation(t *testing.T) {
ctx, _, keeper := CreateTestInput(t, false, 0)
pool := keeper.GetPool(ctx)
pool.LooseTokens = sdk.NewInt(10)
pool.NotBondedTokens = sdk.NewInt(10)
//create a validator and a delegator to that validator
validator := types.NewValidator(addrVals[0], PKs[0], types.Description{})
@ -208,7 +208,7 @@ func TestUnbondDelegation(t *testing.T) {
require.Equal(t, int64(4), delegation.Shares.RoundInt64())
require.Equal(t, int64(4), validator.BondedTokens().Int64())
require.Equal(t, int64(6), pool.LooseTokens.Int64(), "%v", pool)
require.Equal(t, int64(6), pool.NotBondedTokens.Int64(), "%v", pool)
require.Equal(t, int64(4), pool.BondedTokens.Int64())
}
@ -218,7 +218,7 @@ func TestUndelegateSelfDelegation(t *testing.T) {
ctx, _, keeper := CreateTestInput(t, false, 0)
pool := keeper.GetPool(ctx)
pool.LooseTokens = sdk.NewInt(20)
pool.NotBondedTokens = sdk.NewInt(20)
//create a validator with a self-delegation
validator := types.NewValidator(addrVals[0], PKs[0], types.Description{})
@ -265,7 +265,7 @@ func TestUndelegateSelfDelegation(t *testing.T) {
func TestUndelegateFromUnbondingValidator(t *testing.T) {
ctx, _, keeper := CreateTestInput(t, false, 0)
pool := keeper.GetPool(ctx)
pool.LooseTokens = sdk.NewInt(20)
pool.NotBondedTokens = sdk.NewInt(20)
//create a validator with a self-delegation
validator := types.NewValidator(addrVals[0], PKs[0], types.Description{})
@ -342,7 +342,7 @@ func TestUndelegateFromUnbondingValidator(t *testing.T) {
func TestUndelegateFromUnbondedValidator(t *testing.T) {
ctx, _, keeper := CreateTestInput(t, false, 0)
pool := keeper.GetPool(ctx)
pool.LooseTokens = sdk.NewInt(20)
pool.NotBondedTokens = sdk.NewInt(20)
//create a validator with a self-delegation
validator := types.NewValidator(addrVals[0], PKs[0], types.Description{})
@ -421,7 +421,7 @@ func TestUndelegateFromUnbondedValidator(t *testing.T) {
func TestUnbondingAllDelegationFromValidator(t *testing.T) {
ctx, _, keeper := CreateTestInput(t, false, 0)
pool := keeper.GetPool(ctx)
pool.LooseTokens = sdk.NewInt(20)
pool.NotBondedTokens = sdk.NewInt(20)
//create a validator with a self-delegation
validator := types.NewValidator(addrVals[0], PKs[0], types.Description{})
@ -571,7 +571,7 @@ func TestRedelegation(t *testing.T) {
func TestRedelegateToSameValidator(t *testing.T) {
ctx, _, keeper := CreateTestInput(t, false, 0)
pool := keeper.GetPool(ctx)
pool.LooseTokens = sdk.NewInt(30)
pool.NotBondedTokens = sdk.NewInt(30)
// create a validator with a self-delegation
validator := types.NewValidator(addrVals[0], PKs[0], types.Description{})
@ -596,7 +596,7 @@ func TestRedelegateToSameValidator(t *testing.T) {
func TestRedelegateSelfDelegation(t *testing.T) {
ctx, _, keeper := CreateTestInput(t, false, 0)
pool := keeper.GetPool(ctx)
pool.LooseTokens = sdk.NewInt(30)
pool.NotBondedTokens = sdk.NewInt(30)
//create a validator with a self-delegation
validator := types.NewValidator(addrVals[0], PKs[0], types.Description{})
@ -651,7 +651,7 @@ func TestRedelegateSelfDelegation(t *testing.T) {
func TestRedelegateFromUnbondingValidator(t *testing.T) {
ctx, _, keeper := CreateTestInput(t, false, 0)
pool := keeper.GetPool(ctx)
pool.LooseTokens = sdk.NewInt(30)
pool.NotBondedTokens = sdk.NewInt(30)
//create a validator with a self-delegation
validator := types.NewValidator(addrVals[0], PKs[0], types.Description{})
@ -735,7 +735,7 @@ func TestRedelegateFromUnbondingValidator(t *testing.T) {
func TestRedelegateFromUnbondedValidator(t *testing.T) {
ctx, _, keeper := CreateTestInput(t, false, 0)
pool := keeper.GetPool(ctx)
pool.LooseTokens = sdk.NewInt(30)
pool.NotBondedTokens = sdk.NewInt(30)
//create a validator with a self-delegation
validator := types.NewValidator(addrVals[0], PKs[0], types.Description{})

View File

@ -102,7 +102,7 @@ func (k Keeper) BondedRatio(ctx sdk.Context) sdk.Dec {
// when minting new tokens
func (k Keeper) InflateSupply(ctx sdk.Context, newTokens sdk.Int) {
pool := k.GetPool(ctx)
pool.LooseTokens = pool.LooseTokens.Add(newTokens)
pool.NotBondedTokens = pool.NotBondedTokens.Add(newTokens)
k.SetPool(ctx, pool)
}

View File

@ -108,11 +108,11 @@ func (k Keeper) Slash(ctx sdk.Context, consAddr sdk.ConsAddress, infractionHeigh
tokensToBurn = sdk.MaxInt(tokensToBurn, sdk.ZeroInt()) // defensive.
// Deduct from validator's bonded tokens and update the validator.
// The deducted tokens are returned to pool.LooseTokens.
// The deducted tokens are returned to pool.NotBondedTokens.
validator = k.RemoveValidatorTokens(ctx, validator, tokensToBurn)
pool := k.GetPool(ctx)
// Burn the slashed tokens, which are now loose.
pool.LooseTokens = pool.LooseTokens.Sub(tokensToBurn)
pool.NotBondedTokens = pool.NotBondedTokens.Sub(tokensToBurn)
k.SetPool(ctx, pool)
// Log that a slash occurred!
@ -188,9 +188,9 @@ func (k Keeper) slashUnbondingDelegation(ctx sdk.Context, unbondingDelegation ty
k.SetUnbondingDelegation(ctx, unbondingDelegation)
pool := k.GetPool(ctx)
// Burn loose tokens
// Burn not-bonded tokens
// Ref https://github.com/cosmos/cosmos-sdk/pull/1278#discussion_r198657760
pool.LooseTokens = pool.LooseTokens.Sub(unbondingSlashAmount)
pool.NotBondedTokens = pool.NotBondedTokens.Sub(unbondingSlashAmount)
k.SetPool(ctx, pool)
}
@ -259,9 +259,9 @@ func (k Keeper) slashRedelegation(ctx sdk.Context, validator types.Validator, re
panic(fmt.Errorf("error unbonding delegator: %v", err))
}
// Burn loose tokens
// Burn not-bonded tokens
pool := k.GetPool(ctx)
pool.LooseTokens = pool.LooseTokens.Sub(tokensToBurn)
pool.NotBondedTokens = pool.NotBondedTokens.Sub(tokensToBurn)
k.SetPool(ctx, pool)
}

View File

@ -21,7 +21,7 @@ func setupHelper(t *testing.T, amt int64) (sdk.Context, Keeper, types.Params) {
params := keeper.GetParams(ctx)
pool := keeper.GetPool(ctx)
numVals := 3
pool.LooseTokens = sdk.NewInt(amt * int64(numVals))
pool.NotBondedTokens = sdk.NewInt(amt * int64(numVals))
// add numVals validators
for i := 0; i < numVals; i++ {
@ -103,7 +103,7 @@ func TestSlashUnbondingDelegation(t *testing.T) {
// balance decreased
require.Equal(t, sdk.NewInt64Coin(params.BondDenom, 5), ubd.Entries[0].Balance)
newPool := keeper.GetPool(ctx)
require.Equal(t, int64(5), oldPool.LooseTokens.Sub(newPool.LooseTokens).Int64())
require.Equal(t, int64(5), oldPool.NotBondedTokens.Sub(newPool.NotBondedTokens).Int64())
}
// tests slashRedelegation
@ -503,8 +503,8 @@ func TestSlashBoth(t *testing.T) {
require.Equal(t, sdk.NewInt(3), rdA.Entries[0].Balance.Amount)
// read updated pool
newPool := keeper.GetPool(ctx)
// loose tokens burned
require.Equal(t, int64(2), oldPool.LooseTokens.Sub(newPool.LooseTokens).Int64())
// not-bonded tokens burned
require.Equal(t, int64(2), oldPool.NotBondedTokens.Sub(newPool.NotBondedTokens).Int64())
// bonded tokens burned
require.Equal(t, int64(3), oldPool.BondedTokens.Sub(newPool.BondedTokens).Int64())
// read updated validator

View File

@ -125,7 +125,7 @@ func CreateTestInput(t *testing.T, isCheckTx bool, initCoins int64) (sdk.Context
{keeper.BondDenom(ctx), sdk.NewInt(initCoins)},
})
require.Nil(t, err)
pool.LooseTokens = pool.LooseTokens.Add(sdk.NewInt(initCoins))
pool.NotBondedTokens = pool.NotBondedTokens.Add(sdk.NewInt(initCoins))
keeper.SetPool(ctx, pool)
}

View File

@ -15,7 +15,7 @@ import (
// * Updates the active valset as keyed by LastValidatorPowerKey.
// * Updates the total power as keyed by LastTotalPowerKey.
// * Updates validator status' according to updated powers.
// * Updates the fee pool bonded vs loose tokens.
// * Updates the fee pool bonded vs not-bonded tokens.
// * Updates relevant indices.
// It gets called once after genesis, another time maybe after genesis transactions,
// then once at every EndBlock.

View File

@ -191,7 +191,7 @@ func (k Keeper) RemoveValidator(ctx sdk.Context, address sdk.ValAddress) {
// this happens if shares are zero but tokens are not.
// TODO: Remove once https://github.com/cosmos/cosmos-sdk/pull/2958 is merged
pool := k.GetPool(ctx)
pool.LooseTokens = pool.LooseTokens.Sub(validator.Tokens)
pool.NotBondedTokens = pool.NotBondedTokens.Sub(validator.Tokens)
k.SetPool(ctx, pool)
// delete the old validator record

View File

@ -75,7 +75,7 @@ func TestUpdateValidatorByPowerIndex(t *testing.T) {
pool := keeper.GetPool(ctx)
// create a random pool
pool.LooseTokens = sdk.NewInt(10000)
pool.NotBondedTokens = sdk.NewInt(10000)
pool.BondedTokens = sdk.NewInt(1234)
keeper.SetPool(ctx, pool)
@ -123,7 +123,7 @@ func TestUpdateBondedValidatorsDecreaseCliff(t *testing.T) {
keeper.SetParams(ctx, params)
// create a random pool
pool.LooseTokens = sdk.NewInt(10000)
pool.NotBondedTokens = sdk.NewInt(10000)
pool.BondedTokens = sdk.NewInt(1234)
keeper.SetPool(ctx, pool)

View File

@ -45,7 +45,7 @@ func AllInvariants(ck bank.Keeper, k staking.Keeper,
}
}
// SupplyInvariants checks that the total supply reflects all held loose tokens, bonded tokens, and unbonding delegations
// SupplyInvariants checks that the total supply reflects all held not-bonded tokens, bonded tokens, and unbonding delegations
// nolint: unparam
func SupplyInvariants(ck bank.Keeper, k staking.Keeper,
f auth.FeeCollectionKeeper, d distribution.Keeper, am auth.AccountKeeper) simulation.Invariant {
@ -85,12 +85,12 @@ func SupplyInvariants(ck bank.Keeper, k staking.Keeper,
// add yet-to-be-withdrawn
loose = loose.Add(d.GetOutstandingRewards(ctx).AmountOf(stakingTypes.DefaultBondDenom))
// Loose tokens should equal coin supply plus unbonding delegations
// Not-bonded tokens should equal coin supply plus unbonding delegations
// plus tokens on unbonded validators
if !sdk.NewDecFromInt(pool.LooseTokens).Equal(loose) {
if !sdk.NewDecFromInt(pool.NotBondedTokens).Equal(loose) {
return fmt.Errorf("loose token invariance:\n"+
"\tpool.LooseTokens: %v\n"+
"\tsum of account tokens: %v", pool.LooseTokens, loose)
"\tpool.NotBondedTokens: %v\n"+
"\tsum of account tokens: %v", pool.NotBondedTokens, loose)
}
// Bonded tokens should equal sum of tokens with bonded validators

View File

@ -15,8 +15,8 @@ var _, _, _ sdk.Msg = &MsgCreateValidator{}, &MsgEditValidator{}, &MsgDelegate{}
// MsgCreateValidator - struct for bonding transactions
type MsgCreateValidator struct {
Description
Commission CommissionMsg
Description Description `json:"description"`
Commission CommissionMsg `json:"commission"`
DelegatorAddr sdk.AccAddress `json:"delegator_address"`
ValidatorAddr sdk.ValAddress `json:"validator_address"`
PubKey crypto.PubKey `json:"pubkey"`
@ -62,11 +62,12 @@ func (msg MsgCreateValidator) GetSigners() []sdk.AccAddress {
return addrs
}
// TODO Remove use of custom struct (no longer necessary)
// get the bytes for the message signer to sign on
func (msg MsgCreateValidator) GetSignBytes() []byte {
b, err := MsgCdc.MarshalJSON(struct {
Description
Commission CommissionMsg
Description Description `json:"description"`
Commission CommissionMsg `json:"commission"`
DelegatorAddr sdk.AccAddress `json:"delegator_address"`
ValidatorAddr sdk.ValAddress `json:"validator_address"`
PubKey string `json:"pubkey"`

View File

@ -35,10 +35,9 @@ var _ params.ParamSet = (*Params)(nil)
// Params defines the high level settings for staking
type Params struct {
UnbondingTime time.Duration `json:"unbonding_time"`
MaxValidators uint16 `json:"max_validators"` // maximum number of validators
BondDenom string `json:"bond_denom"` // bondable coin denomination
UnbondingTime time.Duration `json:"unbonding_time"` // time duration of unbonding
MaxValidators uint16 `json:"max_validators"` // maximum number of validators
BondDenom string `json:"bond_denom"` // bondable coin denomination
}
// Implements params.ParamSet

View File

@ -10,7 +10,7 @@ import (
// Pool - dynamic parameters of the current state
type Pool struct {
LooseTokens sdk.Int `json:"loose_tokens"` // tokens which are not bonded in a validator
NotBondedTokens sdk.Int `json:"not_bonded_tokens"` // tokens which are not bonded in a validator
BondedTokens sdk.Int `json:"bonded_tokens"` // reserve of bonded tokens
}
@ -24,7 +24,7 @@ func (p Pool) Equal(p2 Pool) bool {
// initial pool for testing
func InitialPool() Pool {
return Pool{
LooseTokens: sdk.ZeroInt(),
NotBondedTokens: sdk.ZeroInt(),
BondedTokens: sdk.ZeroInt(),
}
}
@ -33,7 +33,7 @@ func InitialPool() Pool {
// Sum total of all staking tokens in the pool
func (p Pool) TokenSupply() sdk.Int {
return p.LooseTokens.Add(p.BondedTokens)
return p.NotBondedTokens.Add(p.BondedTokens)
}
//____________________________________________________________________
@ -50,18 +50,18 @@ func (p Pool) BondedRatio() sdk.Dec {
//_______________________________________________________________________
func (p Pool) looseTokensToBonded(bondedTokens sdk.Int) Pool {
func (p Pool) notBondedTokensToBonded(bondedTokens sdk.Int) Pool {
p.BondedTokens = p.BondedTokens.Add(bondedTokens)
p.LooseTokens = p.LooseTokens.Sub(bondedTokens)
if p.LooseTokens.IsNegative() {
panic(fmt.Sprintf("sanity check: loose tokens negative, pool: %v", p))
p.NotBondedTokens = p.NotBondedTokens.Sub(bondedTokens)
if p.NotBondedTokens.IsNegative() {
panic(fmt.Sprintf("sanity check: not-bonded tokens negative, pool: %v", p))
}
return p
}
func (p Pool) bondedTokensToLoose(bondedTokens sdk.Int) Pool {
func (p Pool) bondedTokensToNotBonded(bondedTokens sdk.Int) Pool {
p.BondedTokens = p.BondedTokens.Sub(bondedTokens)
p.LooseTokens = p.LooseTokens.Add(bondedTokens)
p.NotBondedTokens = p.NotBondedTokens.Add(bondedTokens)
if p.BondedTokens.IsNegative() {
panic(fmt.Sprintf("sanity check: bonded tokens negative, pool: %v", p))
}
@ -73,7 +73,7 @@ func (p Pool) bondedTokensToLoose(bondedTokens sdk.Int) Pool {
func (p Pool) HumanReadableString() string {
resp := "Pool \n"
resp += fmt.Sprintf("Loose Tokens: %s\n", p.LooseTokens)
resp += fmt.Sprintf("Not-Bonded Tokens: %s\n", p.NotBondedTokens)
resp += fmt.Sprintf("Bonded Tokens: %s\n", p.BondedTokens)
resp += fmt.Sprintf("Token Supply: %s\n", p.TokenSupply())
resp += fmt.Sprintf("Bonded Ratio: %v\n", p.BondedRatio())

View File

@ -18,22 +18,22 @@ func TestPoolEqual(t *testing.T) {
func TestAddBondedTokens(t *testing.T) {
pool := InitialPool()
pool.LooseTokens = sdk.NewInt(10)
pool.NotBondedTokens = sdk.NewInt(10)
pool.BondedTokens = sdk.NewInt(10)
pool = pool.looseTokensToBonded(sdk.NewInt(10))
pool = pool.notBondedTokensToBonded(sdk.NewInt(10))
require.True(sdk.IntEq(t, sdk.NewInt(20), pool.BondedTokens))
require.True(sdk.IntEq(t, sdk.NewInt(0), pool.LooseTokens))
require.True(sdk.IntEq(t, sdk.NewInt(0), pool.NotBondedTokens))
}
func TestRemoveBondedTokens(t *testing.T) {
pool := InitialPool()
pool.LooseTokens = sdk.NewInt(10)
pool.NotBondedTokens = sdk.NewInt(10)
pool.BondedTokens = sdk.NewInt(10)
pool = pool.bondedTokensToLoose(sdk.NewInt(5))
pool = pool.bondedTokensToNotBonded(sdk.NewInt(5))
require.True(sdk.IntEq(t, sdk.NewInt(5), pool.BondedTokens))
require.True(sdk.IntEq(t, sdk.NewInt(15), pool.LooseTokens))
require.True(sdk.IntEq(t, sdk.NewInt(15), pool.NotBondedTokens))
}

View File

@ -279,7 +279,7 @@ func (v Validator) UpdateStatus(pool Pool, NewStatus sdk.BondStatus) (Validator,
case sdk.Unbonded:
return v, pool
case sdk.Bonded:
pool = pool.looseTokensToBonded(v.Tokens)
pool = pool.notBondedTokensToBonded(v.Tokens)
}
case sdk.Unbonding:
@ -287,7 +287,7 @@ func (v Validator) UpdateStatus(pool Pool, NewStatus sdk.BondStatus) (Validator,
case sdk.Unbonding:
return v, pool
case sdk.Bonded:
pool = pool.looseTokensToBonded(v.Tokens)
pool = pool.notBondedTokensToBonded(v.Tokens)
}
case sdk.Bonded:
@ -295,7 +295,7 @@ func (v Validator) UpdateStatus(pool Pool, NewStatus sdk.BondStatus) (Validator,
case sdk.Bonded:
return v, pool
default:
pool = pool.bondedTokensToLoose(v.Tokens)
pool = pool.bondedTokensToNotBonded(v.Tokens)
}
}
@ -313,7 +313,7 @@ func (v Validator) RemoveTokens(pool Pool, tokens sdk.Int) (Validator, Pool) {
}
v.Tokens = v.Tokens.Sub(tokens)
if v.Status == sdk.Bonded {
pool = pool.bondedTokensToLoose(tokens)
pool = pool.bondedTokensToNotBonded(tokens)
}
return v, pool
}
@ -341,7 +341,7 @@ func (v Validator) AddTokensFromDel(pool Pool, amount sdk.Int) (Validator, Pool,
}
if v.Status == sdk.Bonded {
pool = pool.looseTokensToBonded(amount)
pool = pool.notBondedTokensToBonded(amount)
}
v.Tokens = v.Tokens.Add(amount)
@ -376,7 +376,7 @@ func (v Validator) RemoveDelShares(pool Pool, delShares sdk.Dec) (Validator, Poo
v.DelegatorShares = remainingShares
if v.Status == sdk.Bonded {
pool = pool.bondedTokensToLoose(issuedTokens)
pool = pool.bondedTokensToNotBonded(issuedTokens)
}
return v, pool, issuedTokens

View File

@ -81,7 +81,7 @@ func TestRemoveTokens(t *testing.T) {
}
pool := InitialPool()
pool.LooseTokens = sdk.NewInt(10)
pool.NotBondedTokens = sdk.NewInt(10)
pool.BondedTokens = validator.BondedTokens()
validator, pool = validator.UpdateStatus(pool, sdk.Bonded)
@ -91,23 +91,23 @@ func TestRemoveTokens(t *testing.T) {
validator, pool = validator.RemoveTokens(pool, sdk.NewInt(10))
require.Equal(t, int64(90), validator.Tokens.Int64())
require.Equal(t, int64(90), pool.BondedTokens.Int64())
require.Equal(t, int64(20), pool.LooseTokens.Int64())
require.Equal(t, int64(20), pool.NotBondedTokens.Int64())
// update validator to unbonded and remove some more tokens
validator, pool = validator.UpdateStatus(pool, sdk.Unbonded)
require.Equal(t, sdk.Unbonded, validator.Status)
require.Equal(t, int64(0), pool.BondedTokens.Int64())
require.Equal(t, int64(110), pool.LooseTokens.Int64())
require.Equal(t, int64(110), pool.NotBondedTokens.Int64())
validator, pool = validator.RemoveTokens(pool, sdk.NewInt(10))
require.Equal(t, int64(80), validator.Tokens.Int64())
require.Equal(t, int64(0), pool.BondedTokens.Int64())
require.Equal(t, int64(110), pool.LooseTokens.Int64())
require.Equal(t, int64(110), pool.NotBondedTokens.Int64())
}
func TestAddTokensValidatorBonded(t *testing.T) {
pool := InitialPool()
pool.LooseTokens = sdk.NewInt(10)
pool.NotBondedTokens = sdk.NewInt(10)
validator := NewValidator(addr1, pk1, Description{})
validator, pool = validator.UpdateStatus(pool, sdk.Bonded)
validator, pool, delShares := validator.AddTokensFromDel(pool, sdk.NewInt(10))
@ -120,7 +120,7 @@ func TestAddTokensValidatorBonded(t *testing.T) {
func TestAddTokensValidatorUnbonding(t *testing.T) {
pool := InitialPool()
pool.LooseTokens = sdk.NewInt(10)
pool.NotBondedTokens = sdk.NewInt(10)
validator := NewValidator(addr1, pk1, Description{})
validator, pool = validator.UpdateStatus(pool, sdk.Unbonding)
validator, pool, delShares := validator.AddTokensFromDel(pool, sdk.NewInt(10))
@ -134,7 +134,7 @@ func TestAddTokensValidatorUnbonding(t *testing.T) {
func TestAddTokensValidatorUnbonded(t *testing.T) {
pool := InitialPool()
pool.LooseTokens = sdk.NewInt(10)
pool.NotBondedTokens = sdk.NewInt(10)
validator := NewValidator(addr1, pk1, Description{})
validator, pool = validator.UpdateStatus(pool, sdk.Unbonded)
validator, pool, delShares := validator.AddTokensFromDel(pool, sdk.NewInt(10))
@ -156,7 +156,7 @@ func TestRemoveDelShares(t *testing.T) {
DelegatorShares: sdk.NewDec(100),
}
poolA := InitialPool()
poolA.LooseTokens = sdk.NewInt(10)
poolA.NotBondedTokens = sdk.NewInt(10)
poolA.BondedTokens = valA.BondedTokens()
require.Equal(t, valA.DelegatorShareExRate(), sdk.OneDec())
@ -166,12 +166,12 @@ func TestRemoveDelShares(t *testing.T) {
require.Equal(t, int64(90), valB.DelegatorShares.RoundInt64())
require.Equal(t, int64(90), valB.BondedTokens().Int64())
require.Equal(t, int64(90), poolB.BondedTokens.Int64())
require.Equal(t, int64(20), poolB.LooseTokens.Int64())
require.Equal(t, int64(20), poolB.NotBondedTokens.Int64())
// conservation of tokens
require.True(sdk.IntEq(t,
poolB.LooseTokens.Add(poolB.BondedTokens),
poolA.LooseTokens.Add(poolA.BondedTokens)))
poolB.NotBondedTokens.Add(poolB.BondedTokens),
poolA.NotBondedTokens.Add(poolA.BondedTokens)))
// specific case from random tests
poolTokens := sdk.NewInt(5102)
@ -185,7 +185,7 @@ func TestRemoveDelShares(t *testing.T) {
}
pool := Pool{
BondedTokens: sdk.NewInt(248305),
LooseTokens: sdk.NewInt(232147),
NotBondedTokens: sdk.NewInt(232147),
}
shares := sdk.NewDec(29)
_, newPool, tokens := validator.RemoveDelShares(pool, shares)
@ -193,32 +193,32 @@ func TestRemoveDelShares(t *testing.T) {
require.True(sdk.IntEq(t, sdk.NewInt(1286), tokens))
require.True(sdk.IntEq(t,
newPool.LooseTokens.Add(newPool.BondedTokens),
pool.LooseTokens.Add(pool.BondedTokens)))
newPool.NotBondedTokens.Add(newPool.BondedTokens),
pool.NotBondedTokens.Add(pool.BondedTokens)))
}
func TestUpdateStatus(t *testing.T) {
pool := InitialPool()
pool.LooseTokens = sdk.NewInt(100)
pool.NotBondedTokens = sdk.NewInt(100)
validator := NewValidator(addr1, pk1, Description{})
validator, pool, _ = validator.AddTokensFromDel(pool, sdk.NewInt(100))
require.Equal(t, sdk.Unbonded, validator.Status)
require.Equal(t, int64(100), validator.Tokens.Int64())
require.Equal(t, int64(0), pool.BondedTokens.Int64())
require.Equal(t, int64(100), pool.LooseTokens.Int64())
require.Equal(t, int64(100), pool.NotBondedTokens.Int64())
validator, pool = validator.UpdateStatus(pool, sdk.Bonded)
require.Equal(t, sdk.Bonded, validator.Status)
require.Equal(t, int64(100), validator.Tokens.Int64())
require.Equal(t, int64(100), pool.BondedTokens.Int64())
require.Equal(t, int64(0), pool.LooseTokens.Int64())
require.Equal(t, int64(0), pool.NotBondedTokens.Int64())
validator, pool = validator.UpdateStatus(pool, sdk.Unbonding)
require.Equal(t, sdk.Unbonding, validator.Status)
require.Equal(t, int64(100), validator.Tokens.Int64())
require.Equal(t, int64(0), pool.BondedTokens.Int64())
require.Equal(t, int64(100), pool.LooseTokens.Int64())
require.Equal(t, int64(100), pool.NotBondedTokens.Int64())
}
func TestPossibleOverflow(t *testing.T) {
@ -232,7 +232,7 @@ func TestPossibleOverflow(t *testing.T) {
DelegatorShares: delShares,
}
pool := Pool{
LooseTokens: sdk.NewInt(100),
NotBondedTokens: sdk.NewInt(100),
BondedTokens: poolTokens,
}
tokens := int64(71)