400 lines
15 KiB
Markdown
400 lines
15 KiB
Markdown
# 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
|
|
`PowerChange`'s stored in distribution state.
|
|
|
|
|
|
```golang
|
|
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)
|
|
pcs = GetPowerChanges(DelDistr.WithdrawalHeight)
|
|
|
|
// update all adjustment factors for each delegation since last withdrawal
|
|
for pc = range pcs
|
|
for delegation = range delegations
|
|
DelDistr = GetDelegationDistribution(delegation.DelegatorAddr,
|
|
delegation.ValidatorAddr)
|
|
pc.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 (pc PowerChange) ProcessPowerChangeDelegation(delegation sdk.Delegation,
|
|
DelDistr DelegationDistribution)
|
|
|
|
// get the historical scenarios
|
|
scenario1 = pc.DelegationFromGlobalPool(delegation, DelDistr)
|
|
scenario2 = pc.DelegationFromProvisionPool(delegation, DelDistr)
|
|
|
|
// process the adjustment factors
|
|
scenario1.UpdateAdjustmentForPowerChange(pc.Height)
|
|
scenario2.UpdateAdjustmentForPowerChange(pc.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.
|
|
|
|
```golang
|
|
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)
|
|
pcs = GetPowerChanges(ValDistr.CommissionWithdrawalHeight)
|
|
for pc = range pcs
|
|
pc.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 (pc PowerChange) ProcessPowerChangeCommission()
|
|
|
|
// get the historical scenarios
|
|
scenario1 = pc.CommissionFromGlobalPool()
|
|
scenario2 = pc.CommissionFromProposerPool()
|
|
|
|
// process the adjustment factors
|
|
scenario1.UpdateAdjustmentForPowerChange(pc.Height)
|
|
scenario2.UpdateAdjustmentForPowerChange(pc.Height)
|
|
```
|
|
|
|
## Common Calculations
|
|
|
|
### Distribution scenario
|
|
|
|
A common form of abstracted calculations exists between validators and
|
|
delegations attempting to withdrawal their rewards, either from `Global.Pool`
|
|
or from `ValidatorDistribution.ProposerPool`. With the following interface
|
|
fulfilled the entitled fees for the various scenarios can be calculated.
|
|
|
|
```golang
|
|
type DistributionScenario interface {
|
|
DistributorTokens() DecCoins // current tokens from distributor
|
|
DistributorCumulativeTokens() DecCoins // total tokens ever received
|
|
DistributorPrevReceivedTokens() DecCoins // last value of tokens received
|
|
DistributorShares() sdk.Dec // current shares
|
|
DistributorPrevShares() sdk.Dec // shares last block
|
|
|
|
RecipientAdjustment() sdk.Dec
|
|
RecipientShares() sdk.Dec // current shares
|
|
RecipientPrevShares() sdk.Dec // shares last block
|
|
|
|
ModifyAdjustments(withdrawal sdk.Dec) // proceedure to modify adjustment factors
|
|
}
|
|
```
|
|
|
|
#### Entitled reward from distribution scenario
|
|
|
|
The entitlement to the distributor's tokens held can be accounted for lazily.
|
|
To begin this calculation we must determine the recipient's _simple pool_ and
|
|
_projected pool_. The simple pool represents a lazy accounting of what a
|
|
recipient's entitlement to the distributor's tokens would be if all recipients
|
|
for that distributor had static shares (equal to the current shares), and no
|
|
recipients had ever withdrawn their entitled rewards. The projected pool
|
|
represents the anticipated recipient's entitlement to the distributors tokens
|
|
based on the current blocks token input (for example fees reward received) to
|
|
the distributor, and the distributor's tokens and shares of the previous block
|
|
assuming that neither had changed in the current block. Using the simple and
|
|
projected pools we can determine all cumulative changes which have taken place
|
|
outside of the recipient and adjust the recipient's _adjustment factor_ to
|
|
account for these changes and ultimately keep track of the correct entitlement
|
|
to the distributors tokens.
|
|
|
|
```
|
|
func (d DistributionScenario) RecipientCount(height int64) sdk.Dec
|
|
return v.RecipientShares() * height
|
|
|
|
func (d DistributionScenario) GlobalCount(height int64) sdk.Dec
|
|
return d.DistributorShares() * height
|
|
|
|
func (d DistributionScenario) SimplePool() DecCoins
|
|
return d.RecipientCount() / d.GlobalCount() * d.DistributorCumulativeTokens
|
|
|
|
func (d DistributionScenario) ProjectedPool(height int64) DecCoins
|
|
return d.RecipientPrevShares() * (height-1)
|
|
/ (d.DistributorPrevShares() * (height-1))
|
|
* d.DistributorCumulativeTokens
|
|
+ d.RecipientShares() / d.DistributorShares()
|
|
* d.DistributorPrevReceivedTokens()
|
|
```
|
|
|
|
The `DistributionScenario` _adjustment_ terms account for changes in
|
|
recipient/distributor shares and recipient withdrawals. The adjustment factor
|
|
must be modified whenever the recipient withdraws from the distributor or the
|
|
distributor's/recipient's shares are changed.
|
|
- When the shares of the recipient is changed the adjustment factor is
|
|
increased/decreased by the difference between the _simple_ and _projected_
|
|
pools. In other words, the cumulative difference in the shares if the shares
|
|
has been the new shares as opposed to the old shares for the entire duration of
|
|
the blockchain up the previous block.
|
|
- When a recipient makes a withdrawal the adjustment factor is increased by the
|
|
withdrawal amount.
|
|
|
|
```
|
|
func (d DistributionScenario) UpdateAdjustmentForPowerChange(height int64)
|
|
simplePool = d.SimplePool()
|
|
projectedPool = d.ProjectedPool(height)
|
|
AdjustmentChange = simplePool - projectedPool
|
|
if AdjustmentChange > 0
|
|
d.ModifyAdjustments(AdjustmentChange)
|
|
|
|
func (d DistributionScenario) WithdrawalEntitlement() DecCoins
|
|
entitlement = d.SimplePool() - d.RecipientAdjustment()
|
|
d.ModifyAdjustments(entitlement)
|
|
return entitlement
|
|
```
|
|
|
|
### Distribution scenarios
|
|
|
|
Note that the distribution scenario structures are found in `state.md`.
|
|
|
|
#### Delegation's entitlement to Global.Pool
|
|
|
|
For delegations (including validator's self-delegation) all fees from fee pool
|
|
are subject to commission rate from the operator of the validator. The global
|
|
shares should be taken as true number of global bonded shares. The recipients
|
|
shares should be taken as the bonded tokens less the validator's commission.
|
|
|
|
```
|
|
type DelegationFromGlobalPool struct {
|
|
DelegationShares sdk.Dec
|
|
ValidatorCommission sdk.Dec
|
|
ValidatorBondedTokens sdk.Dec
|
|
ValidatorDelegatorShareExRate sdk.Dec
|
|
PoolBondedTokens sdk.Dec
|
|
Global Global
|
|
ValDistr ValidatorDistribution
|
|
DelDistr DelegatorDistribution
|
|
}
|
|
|
|
func (d DelegationFromGlobalPool) DistributorTokens() DecCoins
|
|
return d.Global.Pool
|
|
|
|
func (d DelegationFromGlobalPool) DistributorCumulativeTokens() DecCoins
|
|
return d.Global.EverReceivedPool
|
|
|
|
func (d DelegationFromGlobalPool) DistributorPrevReceivedTokens() DecCoins
|
|
return d.Global.PrevReceivedPool
|
|
|
|
func (d DelegationFromGlobalPool) DistributorShares() sdk.Dec
|
|
return d.PoolBondedTokens
|
|
|
|
func (d DelegationFromGlobalPool) DistributorPrevShares() sdk.Dec
|
|
return d.Global.PrevBondedTokens
|
|
|
|
func (d DelegationFromGlobalPool) RecipientShares() sdk.Dec
|
|
return d.DelegationShares * d.ValidatorDelegatorShareExRate() *
|
|
d.ValidatorBondedTokens() * (1 - d.ValidatorCommission)
|
|
|
|
func (d DelegationFromGlobalPool) RecipientPrevShares() sdk.Dec
|
|
return d.DelDistr.PrevTokens
|
|
|
|
func (d DelegationFromGlobalPool) RecipientAdjustment() sdk.Dec
|
|
return d.DelDistr.Adjustment
|
|
|
|
func (d DelegationFromGlobalPool) ModifyAdjustments(withdrawal sdk.Dec)
|
|
d.ValDistr.Adjustment += withdrawal
|
|
d.DelDistr.Adjustment += withdrawal
|
|
d.global.Adjustment += withdrawal
|
|
SetValidatorDistribution(d.ValDistr)
|
|
SetDelegatorDistribution(d.DelDistr)
|
|
SetGlobal(d.Global)
|
|
```
|
|
|
|
#### Delegation's entitlement to ValidatorDistribution.ProposerPool
|
|
|
|
Delegations (including validator's self-delegation) are still subject
|
|
commission on the rewards gained from the proposer pool. Global shares in this
|
|
context is actually the validators total delegations shares. The recipient's
|
|
shares is taken as the effective delegation shares less the validator's
|
|
commission.
|
|
|
|
```
|
|
type DelegationFromProposerPool struct {
|
|
DelegationShares sdk.Dec
|
|
ValidatorCommission sdk.Dec
|
|
ValidatorDelegatorShares sdk.Dec
|
|
ValDistr ValidatorDistribution
|
|
DelDistr DelegatorDistribution
|
|
}
|
|
|
|
func (d DelegationFromProposerPool) DistributorTokens() DecCoins
|
|
return d.ValDistr.ProposerPool
|
|
|
|
func (d DelegationFromProposerPool) DistributorCumulativeTokens() DecCoins
|
|
return d.ValDistr.EverReceivedProposerReward
|
|
|
|
func (d DelegationFromProposerPool) DistributorPrevReceivedTokens() DecCoins
|
|
return d.ValDistr.PrevReceivedProposerReward
|
|
|
|
func (d DelegationFromProposerPool) DistributorShares() sdk.Dec
|
|
return d.ValidatorDelegatorShares
|
|
|
|
func (d DelegationFromProposerPool) DistributorPrevShares() sdk.Dec
|
|
return d.ValDistr.PrevDelegatorShares
|
|
|
|
func (d DelegationFromProposerPool) RecipientShares() sdk.Dec
|
|
return d.DelegationShares * (1 - d.ValidatorCommission)
|
|
|
|
func (d DelegationFromProposerPool) RecipientPrevShares() sdk.Dec
|
|
return d.DelDistr.PrevShares
|
|
|
|
func (d DelegationFromProposerPool) RecipientAdjustment() sdk.Dec
|
|
return d.DelDistr.AdjustmentProposer
|
|
|
|
func (d DelegationFromProposerPool) ModifyAdjustments(withdrawal sdk.Dec)
|
|
d.ValDistr.AdjustmentProposer += withdrawal
|
|
d.DelDistr.AdjustmentProposer += withdrawal
|
|
SetValidatorDistribution(d.ValDistr)
|
|
SetDelegatorDistribution(d.DelDistr)
|
|
```
|
|
|
|
#### Validators's commission entitlement to Global.Pool
|
|
|
|
Similar to a delegator's entitlement, but with recipient shares based on the
|
|
commission portion of bonded tokens.
|
|
|
|
```
|
|
type CommissionFromGlobalPool struct {
|
|
ValidatorBondedTokens sdk.Dec
|
|
ValidatorCommission sdk.Dec
|
|
PoolBondedTokens sdk.Dec
|
|
Global Global
|
|
ValDistr ValidatorDistribution
|
|
}
|
|
|
|
func (c CommissionFromGlobalPool) DistributorTokens() DecCoins
|
|
return c.Global.Pool
|
|
|
|
func (c CommissionFromGlobalPool) DistributorCumulativeTokens() DecCoins
|
|
return c.Global.EverReceivedPool
|
|
|
|
func (c CommissionFromGlobalPool) DistributorPrevReceivedTokens() DecCoins
|
|
return c.Global.PrevReceivedPool
|
|
|
|
func (c CommissionFromGlobalPool) DistributorShares() sdk.Dec
|
|
return c.PoolBondedTokens
|
|
|
|
func (c CommissionFromGlobalPool) DistributorPrevShares() sdk.Dec
|
|
return c.Global.PrevBondedTokens
|
|
|
|
func (c CommissionFromGlobalPool) RecipientShares() sdk.Dec
|
|
return c.ValidatorBondedTokens() * c.ValidatorCommission
|
|
|
|
func (c CommissionFromGlobalPool) RecipientPrevShares() sdk.Dec
|
|
return c.ValDistr.PrevBondedTokens * c.ValidatorCommission
|
|
|
|
func (c CommissionFromGlobalPool) RecipientAdjustment() sdk.Dec
|
|
return c.ValDistr.Adjustment
|
|
|
|
func (c CommissionFromGlobalPool) ModifyAdjustments(withdrawal sdk.Dec)
|
|
c.ValDistr.Adjustment += withdrawal
|
|
c.Global.Adjustment += withdrawal
|
|
SetValidatorDistribution(c.ValDistr)
|
|
SetGlobal(c.Global)
|
|
```
|
|
|
|
#### Validators's commission entitlement to ValidatorDistribution.ProposerPool
|
|
|
|
Similar to a delegators entitlement to the proposer pool, but with recipient
|
|
shares based on the commission portion of the total delegator shares.
|
|
|
|
```
|
|
type CommissionFromProposerPool struct {
|
|
ValidatorDelegatorShares sdk.Dec
|
|
ValidatorCommission sdk.Dec
|
|
ValDistr ValidatorDistribution
|
|
}
|
|
|
|
func (c CommissionFromProposerPool) DistributorTokens() DecCoins
|
|
return c.ValDistr.ProposerPool
|
|
|
|
func (c CommissionFromProposerPool) DistributorCumulativeTokens() DecCoins
|
|
return c.ValDistr.EverReceivedProposerReward
|
|
|
|
func (c CommissionFromProposerPool) DistributorPrevReceivedTokens() DecCoins
|
|
return c.ValDistr.PrevReceivedProposerReward
|
|
|
|
func (c CommissionFromProposerPool) DistributorShares() sdk.Dec
|
|
return c.ValidatorDelegatorShares
|
|
|
|
func (c CommissionFromProposerPool) DistributorPrevShares() sdk.Dec
|
|
return c.ValDistr.PrevDelegatorShares
|
|
|
|
func (c CommissionFromProposerPool) RecipientShares() sdk.Dec
|
|
return c.ValidatorDelegatorShares * (c.ValidatorCommission)
|
|
|
|
func (c CommissionFromProposerPool) RecipientPrevShares() sdk.Dec
|
|
return c.ValDistr.PrevDelegatorShares * (c.ValidatorCommission)
|
|
|
|
func (c CommissionFromProposerPool) RecipientAdjustment() sdk.Dec
|
|
return c.ValDistr.AdjustmentProposer
|
|
|
|
func (c CommissionFromProposerPool) ModifyAdjustments(withdrawal sdk.Dec)
|
|
c.ValDistr.AdjustmentProposer += withdrawal
|
|
SetValidatorDistribution(c.ValDistr)
|
|
```
|
|
|