cosmos-sdk/docs/spec/distribution/transactions.md

8.3 KiB

Transactions

TxWithdrawDelegation

When a delegator wishes to withdraw their transaction fees it must send TxWithdrawDelegation. Note that parts of this transaction logic are also triggered each with any change in individual delegations, such as an unbond, redelegation, or delegation of additional tokens to a specific validator.

Each time a withdrawal is made by a recipient the adjustment term must be modified for each block with a change in distributors shares since the time of last withdrawal. This is accomplished by iterating over all relevant ValidatorUpdate's stored in distribution state.

type TxWithdrawDelegation struct {
    delegatorAddr sdk.AccAddress
    withdrawAddr  sdk.AccAddress // address to make the withdrawal to
}

func WithdrawDelegator(delegatorAddr, withdrawAddr sdk.AccAddress) 
    entitlement = GetDelegatorEntitlement(delegatorAddr)
    AddCoins(withdrawAddr, totalEntitlment.TruncateDecimal())

func GetDelegatorEntitlement(delegatorAddr sdk.AccAddress) DecCoins
    
    // compile all the distribution scenarios
    delegations = GetDelegations(delegatorAddr)
    DelDistr = GetDelegationDistribution(delegation.DelegatorAddr,
                                         delegation.ValidatorAddr)
    vus = GetValidatorUpdates(DelDistr.WithdrawalHeight)
        
    // update all adjustment factors for each delegation since last withdrawal
    for vu = range vus 
        for delegation = range delegations 
            DelDistr = GetDelegationDistribution(delegation.DelegatorAddr,
                                                 delegation.ValidatorAddr)
            vu.ProcessPowerChangeDelegation(delegation, DelDistr) 
    
    // collect all entitled fees
    entitlement = 0
    for delegation = range delegations 
        global = GetGlobal() 
        pool = GetPool() 
        DelDistr = GetDelegationDistribution(delegation.DelegatorAddr,
                        delegation.ValidatorAddr)
        ValDistr = GetValidatorDistribution(delegation.ValidatorAddr)
        validator = GetValidator(delegation.ValidatorAddr)

        scenerio1 = NewDelegationFromGlobalPool(delegation, validator, 
                        pool, global, ValDistr, DelDistr)
        scenerio2 = NewDelegationFromProvisionPool(delegation, validator, 
                        ValDistr, DelDistr)
        entitlement += scenerio1.WithdrawalEntitlement()
        entitlement += scenerio2.WithdrawalEntitlement()
    
    return entitlement

func (vu ValidatorUpdate) ProcessPowerChangeDelegation(delegation sdk.Delegation, 
                      DelDistr DelegationDistribution) 

    // get the historical scenarios
    scenario1 = vu.DelegationFromGlobalPool(delegation, DelDistr) 
    scenario2 = vu.DelegationFromProvisionPool(delegation, DelDistr) 

    // process the adjustment factors 
    scenario1.UpdateAdjustmentForPowerChange(vu.Height) 
    scenario2.UpdateAdjustmentForPowerChange(vu.Height) 

TxWithdrawValidator

When a validator wishes to withdraw their transaction fees it must send TxWithdrawDelegation. Note that parts of this transaction logic is also triggered each with any change in individual delegations, such as an unbond, redelegation, or delegation of additional tokens to a specific validator. This transaction withdraws the validators commission rewards, as well as any rewards earning on their self-delegation.

type TxWithdrawValidator struct {
    ownerAddr    sdk.AccAddress // validator address to withdraw from 
    withdrawAddr sdk.AccAddress // address to make the withdrawal to
}

func WithdrawalValidator(ownerAddr, withdrawAddr sdk.AccAddress)

    // update the delegator adjustment factors and also withdrawal delegation fees
    entitlement = GetDelegatorEntitlement(ownerAddr)
    
    // update the validator adjustment factors for commission 
    ValDistr = GetValidatorDistribution(ownerAddr.ValidatorAddr)
    vus = GetValidatorUpdates(ValDistr.CommissionWithdrawalHeight)
    for vu = range vus 
        vu.ProcessPowerChangeCommission()

    // withdrawal validator commission rewards
    global = GetGlobal() 
    pool = GetPool() 
    ValDistr = GetValidatorDistribution(delegation.ValidatorAddr)
    validator = GetValidator(delegation.ValidatorAddr)

    scenerio1 = NewCommissionFromGlobalPool(validator, 
                    pool, global, ValDistr)
    scenerio2 = CommissionFromProposerPool(validator, ValDistr)
    entitlement += scenerio1.WithdrawalEntitlement()
    entitlement += scenerio2.WithdrawalEntitlement()
    
    AddCoins(withdrawAddr, totalEntitlment.TruncateDecimal())

func (vu ValidatorUpdate) ProcessPowerChangeCommission() 

    // get the historical scenarios
    scenario1 = vu.CommissionFromGlobalPool()
    scenario2 = vu.CommissionFromProposerPool()

    // process the adjustment factors 
    scenario1.UpdateAdjustmentForPowerChange(vu.Height) 
    scenario2.UpdateAdjustmentForPowerChange(vu.Height) 

Common calculations

Update total validator accum

The total amount of validator accum must be calculated in order to determine the amount of pool tokens which a validator is entitled to at a particular block. The accum is always additive to the existing accum. This term is to be updates each time rewards are withdrawn from the system.

func (g Global) UpdateTotalValAccum(height int64, totalBondedTokens Dec) 
    blocks = height - g.TotalValAccumUpdateHeight
    g.TotalValAccum += totalDelShares * blocks
    g.TotalValAccumUpdateHeight = height

Update validator's accums

The total amount of delegator accum must be updated in order to determine the amount of pool tokens which each delegator is entitled to, relative to the other delegators for that validator. The accum is always additive to the existing accum. This term is to be updated each time a withdrawal is made from a validator.

func (vd ValidatorDistribution) UpdateTotalDelAccum(height int64, totalDelShares Dec) 
    blocks = height - vd.TotalDelAccumUpdateHeight
    vd.TotalDelAccum += totalDelShares * blocks
    vd.TotalDelAccumUpdateHeight = height

Global pool to validator pool

Everytime a validator or delegator make a withdraw or the validator is the proposer and receives new tokens - the relavent validator must move tokens from the passive global pool to thier own pool.

func (vd ValidatorDistribution) TakeAccum(g Global, height int64, totalBonded, vdTokens Dec) g Global
    g.UpdateTotalValAccum(height, totalBondedShares)
    g.UpdateValAccum(height, totalBondedShares)
    
    // update the validators pool
    blocks = height - vd.GlobalWithdrawalHeight
    vd.GlobalWithdrawalHeight = height
    accum = blocks * vdTokens
    withdrawalTokens := g.Pool * accum / g.TotalValAccum 
    
    g.TotalValAccum -= accumm
    vd.Pool += withdrawalTokens
    g.Pool -= withdrawalTokens

    return g

Delegation's withdrawal

For delegations (including validator's self-delegation) all fees from fee pool are subject to commission rate from the owner of the validator.

func (dd DelegatorDist) WithdrawRewards(g Global, vd ValidatorDistribution,
    height int64, totalBonded, vdTokens, totalDelShares, commissionRate Dec) (g Global, withdrawn Dec)

    vd.UpdateTotalDelAccum(height, totalDelShares) 
    g = vd.TakeAccum(g, height, totalBonded, vdTokens) 
    
    blocks = height - dd.WithdrawalHeight
    dd.WithdrawalHeight = height
    accum = delegatorShares * blocks * (1 - commissionRate)
     
    withdrawalTokens := vd.Pool * accum / vd.TotalDelAccum
    vd.TotalDelAccum -= accum

    vd.Pool -= withdrawalTokens
    vd.TotalDelAccum -= accum
    return g, withdrawalTokens

Validators's commission withdrawal

Similar to a delegator's entitlement, but with recipient shares based on the commission portion of bonded tokens.

func (vd ValidatorDist) WithdrawCommission(g Global, vd ValidatorDistribution,
    height int64, totalBonded, vdTokens, totalDelShares, commissionRate Dec) (g Global, withdrawn Dec)

    vd.UpdateTotalDelAccum(height, totalDelShares) 
    g = vd.TakeAccum(g, height, totalBonded, vdTokens) 
    
    blocks = height - vd.CommissionWithdrawalHeight
    vd.CommissionWithdrawalHeight = height
    accum = delegatorShares * blocks * (commissionRate)
     
    withdrawalTokens := vd.Pool * accum / vd.TotalDelAccum
    vd.TotalDelAccum -= accum

    vd.Pool -= withdrawalTokens
    vd.TotalDelAccum -= accum

    return g, withdrawalTokens