2018-09-24 15:23:58 -07:00
|
|
|
# Transaction Overview
|
2018-05-08 14:35:24 -07:00
|
|
|
|
2018-05-25 15:52:34 -07:00
|
|
|
In this section we describe the processing of the transactions and the
|
2018-08-16 13:47:59 -07:00
|
|
|
corresponding updates to the state. Transactions:
|
2018-09-24 15:23:58 -07:00
|
|
|
|
|
|
|
* TxCreateValidator
|
|
|
|
* TxEditValidator
|
|
|
|
* TxDelegation
|
|
|
|
* TxStartUnbonding
|
|
|
|
* TxRedelegate
|
2018-05-25 15:52:34 -07:00
|
|
|
|
2018-05-29 11:50:35 -07:00
|
|
|
Other important state changes:
|
2018-09-24 15:23:58 -07:00
|
|
|
|
|
|
|
* Update Validators
|
2018-05-29 11:50:35 -07:00
|
|
|
|
2018-05-25 15:52:34 -07:00
|
|
|
Other notes:
|
2018-08-16 13:47:59 -07:00
|
|
|
|
2018-09-24 15:23:58 -07:00
|
|
|
* `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.
|
|
|
|
|
|
|
|
## TxCreateValidator
|
2018-05-08 14:35:24 -07:00
|
|
|
|
2018-09-24 15:23:58 -07:00
|
|
|
* triggers: `distribution.CreateValidatorDistribution`
|
2018-08-08 03:10:21 -07:00
|
|
|
|
2018-05-25 15:52:34 -07:00
|
|
|
A validator is created using the `TxCreateValidator` transaction.
|
2018-05-08 14:35:24 -07:00
|
|
|
|
|
|
|
```golang
|
2018-05-25 15:52:34 -07:00
|
|
|
type TxCreateValidator struct {
|
2018-09-24 15:23:58 -07:00
|
|
|
Description Description
|
|
|
|
Commission Commission
|
2018-08-16 13:47:59 -07:00
|
|
|
|
2018-09-24 15:23:58 -07:00
|
|
|
DelegatorAddr sdk.AccAddress
|
|
|
|
ValidatorAddr sdk.ValAddress
|
|
|
|
PubKey crypto.PubKey
|
|
|
|
Delegation sdk.Coin
|
|
|
|
}
|
2018-05-08 14:35:24 -07:00
|
|
|
|
2018-05-25 15:52:34 -07:00
|
|
|
createValidator(tx TxCreateValidator):
|
2018-09-24 15:23:58 -07:00
|
|
|
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
|
2018-08-16 13:47:59 -07:00
|
|
|
|
2018-09-24 15:23:58 -07:00
|
|
|
err := validateDenom(tx.Delegation.Denom)
|
|
|
|
if err != nil return err // denomination must be valid
|
2018-08-16 13:47:59 -07:00
|
|
|
|
2018-09-24 15:23:58 -07:00
|
|
|
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
|
2018-05-25 15:52:34 -07:00
|
|
|
setValidator(validator)
|
2018-09-24 15:23:58 -07:00
|
|
|
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
|
2018-08-16 13:47:59 -07:00
|
|
|
|
2018-09-24 15:23:58 -07:00
|
|
|
tags := createTags(tx)
|
|
|
|
return tags
|
2018-08-16 13:47:59 -07:00
|
|
|
```
|
2018-05-08 14:35:24 -07:00
|
|
|
|
2018-09-24 15:23:58 -07:00
|
|
|
## TxEditValidator
|
2018-05-08 14:35:24 -07:00
|
|
|
|
2018-09-24 15:23:58 -07:00
|
|
|
If either the `Description`, `Commission`, or the `ValidatorAddr` need to be
|
|
|
|
updated, the `TxEditCandidacy` transaction should be sent from the operator
|
|
|
|
account:
|
2018-05-08 14:35:24 -07:00
|
|
|
|
|
|
|
```golang
|
|
|
|
type TxEditCandidacy struct {
|
2018-09-24 15:23:58 -07:00
|
|
|
Description Description
|
|
|
|
ValidatorAddr sdk.ValAddress
|
|
|
|
CommissionRate sdk.Dec
|
2018-05-08 14:35:24 -07:00
|
|
|
}
|
2018-08-16 13:47:59 -07:00
|
|
|
|
2018-05-08 14:35:24 -07:00
|
|
|
editCandidacy(tx TxEditCandidacy):
|
2018-09-24 15:23:58 -07:00
|
|
|
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)
|
2018-08-16 13:47:59 -07:00
|
|
|
|
2018-09-24 15:23:58 -07:00
|
|
|
tags := createTags(tx)
|
|
|
|
return tags
|
2018-05-08 14:35:24 -07:00
|
|
|
```
|
2018-08-16 13:47:59 -07:00
|
|
|
|
2018-08-08 03:10:21 -07:00
|
|
|
### TxDelegate
|
2018-08-16 13:47:59 -07:00
|
|
|
|
2018-08-08 03:10:21 -07:00
|
|
|
- triggers: `distribution.CreateOrModDelegationDistribution`
|
2018-05-08 14:35:24 -07:00
|
|
|
|
2018-05-25 15:52:34 -07:00
|
|
|
Within this transaction the delegator provides coins, and in return receives
|
|
|
|
some amount of their validator's delegator-shares that are assigned to
|
2018-08-16 13:47:59 -07:00
|
|
|
`Delegation.Shares`.
|
2018-05-08 14:35:24 -07:00
|
|
|
|
|
|
|
```golang
|
|
|
|
type TxDelegate struct {
|
2018-08-16 13:47:59 -07:00
|
|
|
DelegatorAddr sdk.Address
|
|
|
|
ValidatorAddr sdk.Address
|
|
|
|
Amount sdk.Coin
|
2018-05-08 14:35:24 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
delegate(tx TxDelegate):
|
2018-05-25 15:52:34 -07:00
|
|
|
pool = getPool()
|
2018-09-14 11:52:23 -07:00
|
|
|
if validator.Status == Jailed return
|
2018-05-08 14:35:24 -07:00
|
|
|
|
2018-05-25 15:52:34 -07:00
|
|
|
delegation = getDelegatorBond(DelegatorAddr, ValidatorAddr)
|
|
|
|
if delegation == nil then delegation = NewDelegation(DelegatorAddr, ValidatorAddr)
|
2018-08-16 13:47:59 -07:00
|
|
|
|
2018-05-25 15:52:34 -07:00
|
|
|
validator, pool, issuedDelegatorShares = validator.addTokensFromDel(tx.Amount, pool)
|
|
|
|
delegation.Shares += issuedDelegatorShares
|
2018-08-16 13:47:59 -07:00
|
|
|
|
2018-05-25 15:52:34 -07:00
|
|
|
setDelegation(delegation)
|
|
|
|
updateValidator(validator)
|
|
|
|
setPool(pool)
|
2018-08-16 13:47:59 -07:00
|
|
|
return
|
2018-05-08 14:35:24 -07:00
|
|
|
```
|
|
|
|
|
2018-05-29 13:03:13 -07:00
|
|
|
### TxStartUnbonding
|
2018-05-08 14:35:24 -07:00
|
|
|
|
|
|
|
Delegator unbonding is defined with the following transaction:
|
|
|
|
|
|
|
|
```golang
|
2018-05-29 13:03:13 -07:00
|
|
|
type TxStartUnbonding struct {
|
2018-08-16 13:47:59 -07:00
|
|
|
DelegatorAddr sdk.Address
|
|
|
|
ValidatorAddr sdk.Address
|
|
|
|
Shares string
|
2018-05-08 14:35:24 -07:00
|
|
|
}
|
|
|
|
|
2018-08-16 13:47:59 -07:00
|
|
|
startUnbonding(tx TxStartUnbonding):
|
2018-05-25 15:52:34 -07:00
|
|
|
delegation, found = getDelegatorBond(store, sender, tx.PubKey)
|
2018-08-16 13:47:59 -07:00
|
|
|
if !found == nil return
|
|
|
|
|
2018-06-13 21:58:36 -07:00
|
|
|
if bond.Shares < tx.Shares
|
2018-05-29 13:03:13 -07:00
|
|
|
return ErrNotEnoughBondShares
|
2018-05-08 14:35:24 -07:00
|
|
|
|
2018-06-13 21:58:36 -07:00
|
|
|
validator, found = GetValidator(tx.ValidatorAddr)
|
2018-05-25 15:52:34 -07:00
|
|
|
if !found {
|
2018-08-16 13:47:59 -07:00
|
|
|
return err
|
2018-05-08 14:35:24 -07:00
|
|
|
|
2018-06-13 21:58:36 -07:00
|
|
|
bond.Shares -= tx.Shares
|
2018-05-08 14:35:24 -07:00
|
|
|
|
2018-06-13 21:58:36 -07:00
|
|
|
revokeCandidacy = false
|
2018-05-25 15:52:34 -07:00
|
|
|
if bond.Shares.IsZero() {
|
2018-05-08 14:35:24 -07:00
|
|
|
|
2018-09-14 11:52:23 -07:00
|
|
|
if bond.DelegatorAddr == validator.Operator && validator.Jailed == false
|
2018-05-25 15:52:34 -07:00
|
|
|
revokeCandidacy = true
|
2018-05-08 14:35:24 -07:00
|
|
|
|
2018-05-29 13:03:13 -07:00
|
|
|
removeDelegation( bond)
|
2018-05-25 15:52:34 -07:00
|
|
|
else
|
|
|
|
bond.Height = currentBlockHeight
|
|
|
|
setDelegation(bond)
|
2018-05-08 14:35:24 -07:00
|
|
|
|
2018-06-13 21:58:36 -07:00
|
|
|
pool = GetPool()
|
|
|
|
validator, pool, returnAmount = validator.removeDelShares(pool, tx.Shares)
|
2018-05-29 13:03:13 -07:00
|
|
|
setPool( pool)
|
|
|
|
|
|
|
|
unbondingDelegation = NewUnbondingDelegation(sender, returnAmount, currentHeight/Time, startSlashRatio)
|
|
|
|
setUnbondingDelegation(unbondingDelegation)
|
2018-05-08 14:35:24 -07:00
|
|
|
|
2018-05-25 15:52:34 -07:00
|
|
|
if revokeCandidacy
|
2018-09-14 11:52:23 -07:00
|
|
|
validator.Jailed = true
|
2018-05-25 15:52:34 -07:00
|
|
|
|
2018-05-29 13:03:13 -07:00
|
|
|
validator = updateValidator(validator)
|
2018-05-08 14:35:24 -07:00
|
|
|
|
2018-10-18 12:58:18 -07:00
|
|
|
if validator.Status == Unbonded && validator.DelegatorShares == 0 {
|
2018-08-16 13:47:59 -07:00
|
|
|
removeValidator(validator.Operator)
|
2018-05-25 15:52:34 -07:00
|
|
|
|
|
|
|
return
|
2018-05-08 14:35:24 -07:00
|
|
|
```
|
|
|
|
|
2018-05-25 15:52:34 -07:00
|
|
|
### TxRedelegation
|
2018-05-08 14:35:24 -07:00
|
|
|
|
2018-05-29 13:03:13 -07:00
|
|
|
The redelegation command allows delegators to instantly switch validators. Once
|
2018-10-18 12:58:18 -07:00
|
|
|
the unbonding period has passed, the redelegation is automatically completed in the EndBlocker.
|
2018-05-08 14:35:24 -07:00
|
|
|
|
|
|
|
```golang
|
|
|
|
type TxRedelegate struct {
|
2018-05-25 15:52:34 -07:00
|
|
|
DelegatorAddr Address
|
|
|
|
ValidatorFrom Validator
|
|
|
|
ValidatorTo Validator
|
2018-08-14 17:15:02 -07:00
|
|
|
Shares sdk.Dec
|
2018-05-29 13:03:13 -07:00
|
|
|
CompletedTime int64
|
2018-05-08 14:35:24 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
redelegate(tx TxRedelegate):
|
2018-05-29 13:03:13 -07:00
|
|
|
|
2018-05-25 15:52:34 -07:00
|
|
|
pool = getPool()
|
2018-08-16 13:47:59 -07:00
|
|
|
delegation = getDelegatorBond(tx.DelegatorAddr, tx.ValidatorFrom.Operator)
|
2018-05-29 13:03:13 -07:00
|
|
|
if delegation == nil
|
2018-08-16 13:47:59 -07:00
|
|
|
return
|
|
|
|
|
|
|
|
if delegation.Shares < tx.Shares
|
|
|
|
return
|
2018-05-25 15:52:34 -07:00
|
|
|
delegation.shares -= Tx.Shares
|
|
|
|
validator, pool, createdCoins = validator.RemoveShares(pool, tx.Shares)
|
|
|
|
setPool(pool)
|
2018-08-16 13:47:59 -07:00
|
|
|
|
|
|
|
redelegation = newRedelegation(tx.DelegatorAddr, tx.validatorFrom,
|
2018-05-29 13:03:13 -07:00
|
|
|
tx.validatorTo, tx.Shares, createdCoins, tx.CompletedTime)
|
2018-05-25 15:52:34 -07:00
|
|
|
setRedelegation(redelegation)
|
2018-08-16 13:47:59 -07:00
|
|
|
return
|
2018-05-08 14:35:24 -07:00
|
|
|
```
|
|
|
|
|
2018-05-29 11:50:35 -07:00
|
|
|
### 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)
|
|
|
|
|
2018-06-13 21:58:36 -07:00
|
|
|
kickCliffValidator = false
|
|
|
|
oldCliffValidatorAddr = getCliffValidator(ctx)
|
2018-05-29 11:50:35 -07:00
|
|
|
|
|
|
|
// add the actual validator power sorted store
|
2018-06-13 21:58:36 -07:00
|
|
|
maxValidators = GetParams(ctx).MaxValidators
|
|
|
|
iterator = ReverseSubspaceIterator(ValidatorsByPowerKey) // largest to smallest
|
|
|
|
bondedValidatorsCount = 0
|
2018-05-29 11:50:35 -07:00
|
|
|
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
|
|
|
|
|
2018-08-16 13:47:59 -07:00
|
|
|
operatorAddr = iterator.Value()
|
|
|
|
if bytes.Equal(operatorAddr, newValidator.Operator) {
|
2018-05-29 11:50:35 -07:00
|
|
|
validator = newValidator
|
|
|
|
else
|
2018-08-16 13:47:59 -07:00
|
|
|
validator = getValidator(operatorAddr)
|
2018-05-29 11:50:35 -07:00
|
|
|
|
2018-09-14 11:52:23 -07:00
|
|
|
// if not previously a validator (and unjailed),
|
2018-05-29 11:50:35 -07:00
|
|
|
// kick the cliff validator / bond this new validator
|
2018-09-14 11:52:23 -07:00
|
|
|
if validator.Status() != Bonded && !validator.Jailed {
|
2018-05-29 11:50:35 -07:00
|
|
|
kickCliffValidator = true
|
|
|
|
|
|
|
|
validator = bondValidator(ctx, store, validator)
|
2018-08-16 13:47:59 -07:00
|
|
|
if bytes.Equal(operatorAddr, newValidator.Operator) {
|
2018-05-29 11:50:35 -07:00
|
|
|
updatedVal = validator
|
|
|
|
|
|
|
|
bondedValidatorsCount++
|
|
|
|
iterator.Next()
|
|
|
|
|
|
|
|
// perform the actual kicks
|
|
|
|
if oldCliffValidatorAddr != nil && kickCliffValidator {
|
2018-06-13 21:58:36 -07:00
|
|
|
validator = getValidator(store, oldCliffValidatorAddr)
|
2018-05-29 11:50:35 -07:00
|
|
|
unbondValidator(ctx, store, validator)
|
|
|
|
return
|
|
|
|
|
|
|
|
// perform all the store operations for when a validator status becomes unbonded
|
2018-06-13 21:58:36 -07:00
|
|
|
unbondValidator(ctx Context, store KVStore, validator Validator)
|
|
|
|
pool = GetPool(ctx)
|
2018-05-29 11:50:35 -07:00
|
|
|
|
|
|
|
// set the status
|
2018-06-13 21:58:36 -07:00
|
|
|
validator, pool = validator.UpdateStatus(pool, Unbonded)
|
2018-05-29 11:50:35 -07:00
|
|
|
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
|
2018-08-16 13:47:59 -07:00
|
|
|
bondValidator(ctx Context, store KVStore, validator Validator) Validator
|
2018-06-13 21:58:36 -07:00
|
|
|
pool = GetPool(ctx)
|
2018-05-29 11:50:35 -07:00
|
|
|
|
|
|
|
// set the status
|
2018-06-13 21:58:36 -07:00
|
|
|
validator, pool = validator.UpdateStatus(pool, Bonded)
|
2018-05-29 11:50:35 -07:00
|
|
|
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
|
|
|
|
```
|