diff --git a/docs/spec/simple-distribution/end_block.md b/docs/spec/simple-distribution/end_block.md new file mode 100644 index 0000000000..bc6847ef4b --- /dev/null +++ b/docs/spec/simple-distribution/end_block.md @@ -0,0 +1,36 @@ +# End Block + +At each endblock, the fees received are sorted to the proposer, community fund, +and global pool. When the validator is the proposer of the round, that +validator (and their delegators) receives between 1% and 5% of fee rewards, the +reserve tax is then charged, then the remainder is distributed proportionally +by voting power to all bonded validators independent of whether they voted +(social distribution). Note the social distribution is applied to proposer +validator in addition to the proposer reward. + +The amount of proposer reward is calculated from pre-commits Tendermint +messages in order to incentivize validators to wait and include additional +pre-commits in the block. All provision rewards are added to a provision reward +pool which validator holds individually +(`ValidatorDistribution.ProvisionsRewardPool`). + +``` +func SortFees(feesCollected sdk.Coins, global Global, proposer ValidatorDistribution, + sumPowerPrecommitValidators, totalBondedTokens, communityTax sdk.Dec) + + feesCollectedDec = MakeDecCoins(feesCollected) + proposerReward = feesCollectedDec * (0.01 + 0.04 + * sumPowerPrecommitValidators / totalBondedTokens) + proposer.ProposerPool += proposerReward + + communityFunding = feesCollectedDec * communityTax + global.CommunityFund += communityFunding + + poolReceived = feesCollectedDec - proposerReward - communityFunding + global.Pool += poolReceived + global.EverReceivedPool += poolReceived + global.LastReceivedPool = poolReceived + + SetValidatorDistribution(proposer) + SetGlobal(global) +``` diff --git a/docs/spec/simple-distribution/future_improvements.md b/docs/spec/simple-distribution/future_improvements.md new file mode 100644 index 0000000000..954fb4d623 --- /dev/null +++ b/docs/spec/simple-distribution/future_improvements.md @@ -0,0 +1,16 @@ +## Future Improvements + +### Power Change + +Within the current implementation all power changes ever made are indefinitely stored +within the current state. In the future this state should be trimmed on an epoch basis. Delegators +which will have not withdrawn their fees will be penalized in some way, depending on what is +computationally feasible this may include: + - burning non-withdrawn fees + - requiring more expensive withdrawal costs which include proofs from archive nodes of historical state + +In addition or as an alternative it may make sense to implement a "rolling" epoch which cycles through +all the delegators in small groups (for example 5 delegators per block) and just runs the withdrawal transaction +at standard rates and takes transaction fees from the withdrawal amount. + + diff --git a/docs/spec/simple-distribution/overview.md b/docs/spec/simple-distribution/overview.md new file mode 100644 index 0000000000..62cec2e768 --- /dev/null +++ b/docs/spec/simple-distribution/overview.md @@ -0,0 +1,64 @@ +# Distribution + +## Overview + +This _simple_ distribution mechanism describes a functional way to passively +distribute rewards between validator and delegators. Note that this mechanism does +not distribute funds in as precisely as active reward distribution and will therefor +be upgraded in the future. + +The mechanism operates as follows. Collected rewards are pooled globally and +divided out passively to validators and delegators. Each validator has the +opportunity to charge commission to the delegators on the rewards collected on +behalf of the delegators by the validators. Fees are paid directly into a +global reward pool, and validator proposer-reward pool. Due to the nature of +passive accounting whenever changes to parameters which affect the rate of reward +distribution occurs, withdrawal of rewards must also occur when: + + - withdrawing one must withdrawal the maximum amount they are entitled + too, leaving nothing in the pool, + - bonding, unbonding, or re-delegating tokens to an existing account a + full withdrawal of the rewards must occur (as the rules for lazy accounting + change), + - a validator chooses to change the commission on rewards, all accumulated + commission rewards must be simultaneously withdrawn. + +The above scenarios are covered in `triggers.md`. + +The distribution mechanism outlines herein is used to lazily distribute the +following rewards between validators and associated delegators: + - multi-token fees to be socially distributed, + - proposer reward pool, + - inflated atom provisions, and + - validator commission on all rewards earned by their delegators stake + +Fees are pooled within a global pool, as well as validator specific +proposer-reward pools. The mechanisms used allow for validators and delegators +to independently and lazily withdrawn their rewards. + +Within this spec + +As a part of the lazy computations, each validator and delegator holds an +accumulation term which is used to estimate what their approximate fair portion +of tokens held in the global pool is owed to them. This approximation of owed +rewards would be equivalent to the active distribution under the situation that +there was a constant flow of incoming reward tokens every block. Because this +is not the case, the approximation of owed rewards will deviate from the active +distribution based on fluctuations of incoming reward tokens as well as timing +of reward withdrawal by other delegators and validators from the reward pool. + + +## Affect on Staking + +Charging commission on Atom provisions while also allowing for Atom-provisions +to be auto-bonded (distributed directly to the validators bonded stake) is +problematic within DPoS. Fundamentally these two mechnisms are mutually +exclusive. If there are atoms commissions and auto-bonding Atoms, the portion +of Atoms the reward distribution calculation would become very large as the Atom +portion for each delegator would change each block making a withdrawal of rewards +for a delegator require a calculation for every single block since the last +withdrawal. In conclusion we can only have atom commission and unbonded atoms +provisions, or bonded atom provisions with no Atom commission, and we elect to +implement the former. Stakeholders wishing to rebond their provisions may elect +to set up a script to periodically withdraw and rebond rewards. + diff --git a/docs/spec/simple-distribution/state.md b/docs/spec/simple-distribution/state.md new file mode 100644 index 0000000000..7865c085ec --- /dev/null +++ b/docs/spec/simple-distribution/state.md @@ -0,0 +1,100 @@ +## State + +### Global + +All globally tracked parameters for distribution are stored within +`Global`. Rewards are collected and added to the reward pool and +distributed to validators/delegators from here. + +Note that the reward pool holds decimal coins (`DecCoins`) to allow +for fractions of coins to be received from operations like inflation. +When coins are distributed from the pool they are truncated back to +`sdk.Coins` which are non-decimal. + + - Global: `0x00 -> amino(global)` + +```golang +// coins with decimal +type DecCoins []DecCoin + +type DecCoin struct { + Amount sdk.Dec + Denom string +} + +type Global struct { + PrevBondedTokens sdk.Dec // bonded token amount for the global pool on the previous block + Adjustment sdk.Dec // global adjustment factor for lazy calculations + Pool DecCoins // funds for all validators which have yet to be withdrawn + PrevReceivedPool DecCoins // funds added to the pool on the previous block + EverReceivedPool DecCoins // total funds ever added to the pool + CommunityFund DecCoins // pool for community funds yet to be spent +} +``` + +### Validator Distribution + +Validator distribution information for the relevant validator is updated each time: + 1. delegation amount to a validator are updated, + 2. a validator successfully proposes a block and receives a reward, + 3. any delegator withdraws from a validator, or + 4. the validator withdraws it's commission. + + - ValidatorDistribution: `0x02 | ValOwnerAddr -> amino(validatorDistribution)` + +```golang +type ValidatorDistribution struct { + CommissionWithdrawalHeight int64 // last time this validator withdrew commission + Adjustment sdk.Dec // global pool adjustment factor + ProposerAdjustment DecCoins // proposer pool adjustment factor + ProposerPool DecCoins // reward pool collected from being the proposer + EverReceivedProposerReward DecCoins // all rewards ever collected from being the proposer + PrevReceivedProposerReward DecCoins // previous rewards collected from being the proposer + PrevBondedTokens sdk.Dec // bonded token amount on the previous block + PrevDelegatorShares sdk.Dec // amount of delegator shares for the validator on the previous block +} +``` + +### Delegation Distribution + +Each delegation holds multiple adjustment factors to specify its entitlement to +the rewards from a validator. `AdjustmentPool` is used to passively calculate +each bonds entitled fees from the `RewardPool`. `AdjustmentPool` is used to +passively calculate each bonds entitled fees from +`ValidatorDistribution.ProposerRewardPool` + + - DelegatorDistribution: ` 0x02 | DelegatorAddr | ValOwnerAddr -> amino(delegatorDist)` + +```golang +type DelegatorDist struct { + WithdrawalHeight int64 // last time this delegation withdrew rewards + Adjustment sdk.Dec // fee provisioning adjustment factor + AdjustmentProposer DecCoins // proposers pool adjustment factor + PrevTokens sdk.Dec // bonded tokens held by the delegation on the previous block + PrevShares sdk.Dec // delegator shares held by the delegation on the previous block +} +``` + +### Power Change + +Every instance that the voting power changes, information about the state of +the validator set during the change must be recorded as a `PowerChange` for +other validators to run through. Each power change is indexed by its block +height. + + - PowerChange: `0x03 | amino(Height) -> amino(validatorDist)` + +```golang +type PowerChange struct { + Height int64 // block height at change + ValidatorBondedTokens sdk.Dec // following used to create distribution scenarios + ValidatorDelegatorShares sdk.Dec + ValidatorDelegatorShareExRate sdk.Dec + ValidatorCommission sdk.Dec + PoolBondedTokens sdk.Dec + Global Global + ValDistr ValidatorDistribution + DelegationShares sdk.Dec + DelDistr DelegatorDistribution +} +``` diff --git a/docs/spec/simple-distribution/transactions.md b/docs/spec/simple-distribution/transactions.md new file mode 100644 index 0000000000..1401c3b851 --- /dev/null +++ b/docs/spec/simple-distribution/transactions.md @@ -0,0 +1,399 @@ +# 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 owner 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) +``` + diff --git a/docs/spec/simple-distribution/triggers.md b/docs/spec/simple-distribution/triggers.md new file mode 100644 index 0000000000..8800609a44 --- /dev/null +++ b/docs/spec/simple-distribution/triggers.md @@ -0,0 +1,31 @@ +# Triggers + +## Create validator distribution + + - triggered-by: validator entering bonded validator group (`stake.bondValidator()`) + +Whenever a new validator is added to the Tendermint validator set they are +entitled to begin earning rewards of atom provisions and fees. At this point +`ValidatorDistribution.Pool()` must be zero (as the validator has not yet +earned any rewards) meaning that the initial value for `validator.Adjustment` +must be set to the value of `validator.SimplePool()` for the height which the +validator is added on the validator set. + +## Create or modify delegation distribution + + - triggered-by: `stake.TxDelegate`, `stake.TxBeginRedelegate`, `stake.TxBeginUnbonding` + +The pool of a new delegator bond will be 0 for the height at which the bond was +added. This is achieved by setting `DelegationDistribution.WithdrawalHeight` to +the height which the bond was added. Additionally the `AdjustmentPool` and +`AdjustmentProposerPool` must be set to the equivalent values of +`DelegationDistribution.SimplePool()` and +`DelegationDistribution.SimpleProposerPool()` for the height of delegation. + +## Commission rate change + + - triggered-by: `stake.TxEditValidator` + +If a validator changes its commission rate, all commission on fees must be +simultaneously withdrawn using the transaction `TxWithdrawValidator` +