9.6 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 {
OwnerAddr sdk.Address
ConsensusPubKey crypto.PubKey
GovernancePubKey crypto.PubKey
SelfDelegation coin.Coin
Description Description
Commission sdk.Dec
CommissionMax sdk.Dec
CommissionMaxChange sdk.Dec
}
createValidator(tx TxCreateValidator):
validator = getValidator(tx.OwnerAddr)
if validator != nil return // only one validator per address
validator = NewValidator(OwnerAddr, ConsensusPubKey, GovernancePubKey, Description)
init validator poolShares, delegatorShares set to 0
init validator commision fields from tx
validator.PoolShares = 0
setValidator(validator)
txDelegate = TxDelegate(tx.OwnerAddr, tx.OwnerAddr, tx.SelfDelegation)
delegate(txDelegate, validator) // see delegate function in [TxDelegate](TxDelegate)
return
TxEditValidator
If either the Description
(excluding DateBonded
which is constant),
Commission
, or the GovernancePubKey
need to be updated, the
TxEditCandidacy
transaction should be sent from the owner account:
type TxEditCandidacy struct {
GovernancePubKey crypto.PubKey
Commission sdk.Dec
Description Description
}
editCandidacy(tx TxEditCandidacy):
validator = getValidator(tx.ValidatorAddr)
if tx.Commission > CommissionMax || tx.Commission < 0 then fail
if rateChange(tx.Commission) > CommissionMaxChange then fail
validator.Commission = tx.Commission
if tx.GovernancePubKey != nil validator.GovernancePubKey = tx.GovernancePubKey
if tx.Description != nil validator.Description = tx.Description
setValidator(store, validator)
return
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 == Revoked 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.Owner && validator.Revoked == 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.Revoked = true
validator = updateValidator(validator)
if validator.DelegatorShares == 0 {
removeValidator(validator.Owner)
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.Owner)
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
ownerAddr = iterator.Value()
if bytes.Equal(ownerAddr, newValidator.Owner) {
validator = newValidator
else
validator = getValidator(ownerAddr)
// if not previously a validator (and unrevoked),
// kick the cliff validator / bond this new validator
if validator.Status() != Bonded && !validator.Revoked {
kickCliffValidator = true
validator = bondValidator(ctx, store, validator)
if bytes.Equal(ownerAddr, newValidator.Owner) {
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