10 KiB
Transaction Overview
In this section we describe the processing of the transactions and the corresponding updates to the state. Transactions:
- TxCreateValidator
- TxEditValidator
- TxDelegation
- TxStartUnbonding
- TxCompleteUnbonding
- TxRedelegate
- TxCompleteRedelegation
Other important state changes:
- Update Validators
Other notes:
tx
denotes a reference to the transaction being processedsender
denotes the address of the sender of the transactiongetXxx
,setXxx
, andremoveXxx
functions are used to retrieve and modify objects from the storesdk.Dec
refers to a decimal type specified by the SDK.
TxCreateValidator
- triggers:
distribution.CreateValidatorDistribution
A validator is created using the TxCreateValidator
transaction.
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:
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
.
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:
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.DelegatorShares == 0 {
removeValidator(validator.Operator)
return
TxCompleteUnbonding
Complete the unbonding and transfer the coins to the delegate. Perform any slashing that occurred during the unbonding period.
type TxUnbondingComplete struct {
DelegatorAddr sdk.Address
ValidatorAddr sdk.Address
}
redelegationComplete(tx TxRedelegate):
unbonding = getUnbondingDelegation(tx.DelegatorAddr, tx.Validator)
if unbonding.CompleteTime >= CurrentBlockTime && unbonding.CompleteHeight >= CurrentBlockHeight
validator = GetValidator(tx.ValidatorAddr)
returnTokens = ExpectedTokens * tx.startSlashRatio/validator.SlashRatio
AddCoins(unbonding.DelegatorAddr, returnTokens)
removeUnbondingDelegation(unbonding)
return
TxRedelegation
The redelegation command allows delegators to instantly switch validators. Once the unbonding period has passed, the redelegation must be completed with txRedelegationComplete.
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
TxCompleteRedelegation
Note that unlike TxCompleteUnbonding slashing of redelegating shares does not take place during completion. Slashing on redelegated shares takes place actively as a slashing occurs.
type TxRedelegationComplete struct {
DelegatorAddr Address
ValidatorFrom Validator
ValidatorTo Validator
}
redelegationComplete(tx TxRedelegate):
redelegation = getRedelegation(tx.DelegatorAddr, tx.validatorFrom, tx.validatorTo)
if redelegation.CompleteTime >= CurrentBlockTime && redelegation.CompleteHeight >= CurrentBlockHeight
removeRedelegation(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.
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