分配
概覽
這個 簡單 的分配機制描述了一種被動地在驗證者和委託人之間分配獎勵的功能方法。請注意,這種機制在獎勵分配的精確性上不如主動獎勵分配機制,因此將來會進行升級。
該機制的運作方式如下:收集到的獎勵在全局範圍內進行匯總,並被動地分配給驗證者和委託人。 每個驗證者都有機會向委託人收取佣金,作為代表委託人收取獎勵的報酬。 費用直接收入全局獎勵池和驗證者提案人獎勵池。 由於被動記賬的特性,每當影響獎勵分配速率的參數發生變化時,也必須進行獎勵的提取。
- 每當提款時,必須提取自己有權獲得的最大金額,不留任何餘額在池中。
- 每當將代幣綁定、解綁或重新委託給現有賬戶時,必須完全提取獎勵(因為懶惰記賬的規則發生變化)。
- 每當驗證者選擇更改獎勵佣金時,必須同時提取所有累積的佣金獎勵。
本文所概述的分配機制用於在驗證者和相關委託人之間懶性地分配以下獎勵:
- 多代幣手續費以社會化方式分配
- 提案人獎勵池
- 通脹的Dst供應
- 驗證者從其委託人股份所獲得的所有獎勵中抽取佣金
手續費會匯集到全局共享池以及特定於驗證者的提案人獎勵池中。使用的機制允許驗證者和委託人獨立且惰性地提取他們的獎勵。
缺點
作為懶惰計算的一部分,每個委託人都持有一個特定於每個驗證者的累積條款,用於估計全局手續費池中持有的代幣中應支付給他們的大致公平份額。
entitlement = delegator-accumulation / all-delegators-accumulation
在每個區塊都有持續且相等的獎勵代幣流入的情況下,這種分配機制將等同於主動分配(每個區塊單獨分配給所有委託人)。 然而,這是不切實際的,因此會根據獎勵代幣流入的波動以及其他委託人獎勵提取的時機,與主動分配產生偏差。
如果您確實知道即將有大量獎勵流入,您將有動力等待這一事件之後再進行提取,這將增加您現有累積值 accum 的價值。欲了解更多詳情,請參閱 #2764 了解更多詳情。
對抵押的影響
在BPoS(權益證明)中,對Dst供應收取佣金的同時,還允許自動綁定Dst供應(直接分配給綁定的驗證者),這在本質上是有問題的。 這兩種機制在根本上是互斥的。如果佣金和自動綁定機制同時應用於抵押代幣,那麼任何驗證者與其委託人之間的抵押代幣分配將在每個區塊中發生變化。 這就需要對每個委託記錄進行每個區塊的計算,這在計算上是相當昂貴的。
總之,我們只能選擇在Dst佣金和未綁定的Dst供應之間,或者在無Dst佣金的綁定Dst供應之間進行選擇,我們選擇實施前者。 希望重新綁定其供應的利益相關者可以選擇設置一個腳本,定期提取並重新綁定獎勵。
內容
概念
在權益證明(PoS)區塊鏈中,從交易手續費中獲得的獎勵將支付給驗證者。手續費分配模塊將獎勵公平地分配給驗證者的相應委託人。
獎勵是按周期計算的。每當驗證者的委託發生變化時,周期就會更新,例如,當驗證者收到新的委託時。然後,可以通過計算委託開始前的整個周期的總獎勵,再減去當前的總獎勵來計算單個驗證者的獎勵。
當驗證者被移除或驗證者要求提款時,會支付佣金給驗證者。
在每個 BeginBlock
操作中,都會計算並增加佣金,以更新累計的手續費金額。
當委託發生變化或被移除,或者請求提款時,獎勵將分配給委託人。
在分配獎勵之前,將應用在當前委託期間內對驗證者所做的所有懲罰。
F1 手续费分配中的引用计数
在 F1 費用分配中,委託人收到的獎勵是在他們撤回委託時計算的。這個計算必須讀取獎勵總和除以他們在委託結束時的代幣份額所在時期,以及為撤回而創建的最後一個時期。
此外,由於處罰會改變委託人所持有的代幣數量(但我們採用懶惰計算,只在委託人取消委託時計算), 我們必須在委託人委託和撤回獎勵之間發生的任何處罰之前/之後的單獨時期內計算獎勵。 因此,像委託一樣,處罰也引用了由處罰事件結束的時期。
對於所有不再被任何委託或任何處罰引用的存儲歷史獎勵記錄,都可以安全地刪除,因為它們永遠不會被讀取(未來的委託和未來的處罰將始終引用未來的時期)。 這是通過跟踪每個歷史獎勵存儲條目的 ReferenceCount 來實現的。每次創建一個可能需要引用歷史記錄的新對象(委託或處罰)時,引用計數就會增加。 每次刪除一個以前需要引用歷史記錄的對象時,引用計數就會減少。 如果引用計數降至零,則刪除歷史記錄。
狀態
費用池
所有全球追踪的分配參數都存儲在 FeePool 中。獎勵從此處收集並添加到獎勵池,然後分配給驗證者/委託人。
請注意,獎勵池持有十進制硬幣 DecCoins
,以便從膨脹等操作中獲得硬幣的小數部分。當硬幣從池中分配時,它們將被截斷回非十進制的 sdk.Coins
。
- FeePool:
0x00 -> ProtocolBuffer(FeePool)
// coins with decimal
type DecCoins []DecCoin
type DecCoin struct {
Amount sdk.Dec
Denom string
}
+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/distribution/v1beta1/distribution.proto#L94-L101
驗證人分配
相關驗證人的驗證人分配信息每次都會更新:
- 向驗證人的委託金額更新,
- 驗證人成功提交區塊並獲得獎勵,
- 任何委託人從驗證人中撤回委託
-
驗證人撤回其佣金。
-
ValidatorDistInfo:
0x02 | ValOperatorAddrLen (1 byte) | ValOperatorAddr -> ProtocolBuffer(validatorDistribution)
type ValidatorDistInfo struct {
OperatorAddress sdk.AccAddress
SelfBondRewards sdk.DecCoins
ValidatorCommission types.ValidatorAccumulatedCommission
}
委託分配
每個委託分配只需要記錄它最後一次提取費用的高度。 因為每次委託的屬性更改(如綁定代幣等)時都必須提取費用, 所以它的屬性將保持不變,委託人的累積因子可以被被動地計算,只需知道最後一次提款的高度和當前的屬性。
- DelegationDistInfo:
0x02 | DelegatorAddrLen (1 byte) | DelegatorAddr | ValOperatorAddrLen (1 byte) | ValOperatorAddr -> ProtocolBuffer(delegatorDist)
type DelegationDistInfo struct {
WithdrawalHeight int64 // last time this delegation withdrew rewards
}
開始區塊
在每個BeginBlock中,上一個區塊中收到的所有費用都轉移到分配的ModuleAccount帳戶。 當委託人或驗證人撤回他們的獎勵時,它們會從ModuleAccount中扣除。 在開始區塊期間,對收集的費用的不同索賠進行以下更新:
- 上一個高度的區塊提議者及其委託人會收到1%至5%的費用獎勵。
- 社區儲備稅會被收取。
- 其餘部分按投票權比例分配給所有已抵押的驗證人。
為了激勵驗證人等待並在區塊中包含額外的預提交信息,區塊提議人的獎勵是從Tendermint預提交信息中計算出來的。
分配方案
假設 fees
是在上一個區塊中收集的總費用,包括用於抵押獎勵的通膨性獎勵。
所有費用都在區塊期間收集到特定的模組帳戶中。在 BeginBlock
階段,它們將被發送到 distribution
中。
不會發生其他代幣的發送。相反,每個帳戶有權獲得的獎勵將被存儲下來,並且可以通過 FundCommunityPool、WithdrawValidatorCommission 和 WithdrawDelegatorReward 消息觸發提款。
社區儲備獎勵
社區儲備池將獲得 community_tax * fees,以及在驗證人獲得總獎勵後剩餘的零頭,這些零頭總是向下舍入到最接近的整數值。
驗證人獎勵
提案人將獲得基礎獎勵 fees * baseproposerreward 和獎勵金 fees * bonusproposerreward * P,其中 P =(包括預提交的驗證人總權益 / 總驗證人權益)。 提案人包含的預提交越多,P 就越大。 P 永遠不會大於 1.00(因為只有綁定的驗證人才能提供有效的預提交),並且始終大於 2/3。
剩餘的費用將按照驗證人的共識權重比例分配給所有綁定的驗證人,包括提案人。
powFrac = validator power / total bonded validator power
proposerMul = baseproposerreward + bonusproposerreward * P
voteMul = 1 - communitytax - proposerMul
總體而言,提案人將獲得 fees * (voteMul * powFrac + proposerMul) 的獎勵。 所有其他驗證人將獲得 fees * voteMul * powFrac 的獎勵。
委託人的獎勵
每個驗證人的獎勵將分配給其委託人。驗證人還有一個自我委託,其在分配計算中被視為常規委託。
驗證人設置佣金率。佣金率是靈活的,但每個驗證人都設置了一個最高費率和最高每日增長率。這些最高值不能超過,以保護委託人免受驗證人佣金率的突然增加,以防止驗證人獲取所有獎勵。
操作者有資格獲得的未結算獎勵存儲在ValidatorAccumulatedCommission
中,委託人有資格獲得的獎勵存儲在ValidatorCurrentRewards
中。
F1費用分配方案用於計算每個委託人的獎勵,因為他們撤回或更新其委託,因此不在BeginBlock
中處理。
分配示例
對於這個示例分配,底層共識引擎根據參與抵押的總算力,按比例選擇區塊提議者。
所有驗證人在包含預提交(pre-commit)到其建議的區塊方面表現相同。 然後保持(包含預提交的數量) / (總抵押驗證人算力)的常數,以便驗證人的攤銷區塊獎勵為(驗證人算力 / 總抵押算力) * (1-社區稅率)的總獎勵。 因此,單個委託人的獎勵為:
(delegator proportion of the validator power / validator power) * (validator power / total bonded power)
* (1 - community tax rate) * (1 - validator commision rate)
= (delegator proportion of the validator power / total bonded power) * (1 -
community tax rate) * (1 - validat
# Messages
## MsgSetWithdrawAddress
By default, the withdraw address is the delegator address. To change its withdraw address, a delegator must send a `MsgSetWithdrawAddress` message.
Changing the withdraw address is possible only if the parameter `WithdrawAddrEnabled` is set to `true`.
The withdraw address cannot be any of the module accounts. These accounts are blocked from being withdraw addresses by being added to the distribution keeper's `blockedAddrs` array at initialization.
Response:
+++ https://github.com/cosmos/cosmos-sdk/blob/v0.42.4/proto/cosmos/distribution/v1beta1/tx.proto#L29-L37
```go
func (k Keeper) SetWithdrawAddr(ctx sdk.Context, delegatorAddr sdk.AccAddress, withdrawAddr sdk.AccAddress) error
if k.blockedAddrs[withdrawAddr.String()] {
fail with "`{withdrawAddr}` is not allowed to receive external funds"
}
if !k.GetWithdrawAddrEnabled(ctx) {
fail with `ErrSetWithdrawAddrDisabled`
}
k.SetDelegatorWithdrawAddr(ctx, delegatorAddr, withdrawAddr)
MsgWithdrawDelegatorReward
委託人可以提取其獎勵。 在分配模組內部,此交易同時刪除了先前的委託及其相關獎勵,就好像委託人只是開始了相同價值的新委託一樣。 獎勵立即從分配「ModuleAccount」發送到提取地址。 任何剩餘的部分(截斷小數)都會發送到社區池。 委託開始的高度設置為當前的驗證人週期,並將上一個週期的引用計數減少。 提取的金額從驗證人的「ValidatorOutstandingRewards」變量中扣除。
In the F1 distribution, the total rewards are calculated per validator period, and a delegator receives a piece of those rewards in proportion to their stake in the validator.
In basic F1, the total rewards that all the delegators are entitled to between to periods is calculated the following way.
Let R(X)
be the total accumulated rewards up to period X
divided by the tokens staked at that time. The delegator allocation is R(X) * delegator_stake
.
Then the rewards for all the delegators for staking between periods A
and B
are (R(B) - R(A)) * total stake
.
However, these calculated rewards don't account for slashing.
在F1分配中,總獎勵是根據驗證人週期進行計算,委託人按其在驗證人中的持股比例獲得其中一部分獎勵。
在基本的F1分配中,所有委託人在兩個週期之間有權獲得的總獎勵計算如下:
讓R(X)
表示到期間X為止累積的總獎勵,並除以當時抵押的代幣數量。委託人的分配為R(X) * 委託人抵押量
。
然後,在期間A和B之間抵押的所有委託人的獎勵為(R(B) - R(A)) * 總抵押數量
。
但是,這些計算出來的獎勵並未考慮減產。
考慮到減產需要迭代計算。
讓 F(X)
表示在期間 X
發生的懲罰事件中要減少的驗證人的比例。
如果驗證人在期間 P1, ..., PN
被削減,其中 A < P1
,PN < B
,則分配模塊將按以下方式計算每個委託人的獎勵 T(A, B)
:·
stake := initial stake
rewards := 0
previous := A
for P in P1, ..., PN`:
rewards = (R(P) - previous) * stake
stake = stake * F(P)
previous = P
rewards = rewards + (R(B) - R(PN)) * stake
歷史獎勵是通過回放所有的削減事件,並在每個步驟中減少委託人的持股量來進行追溯計算的。 最終計算出的抵押量與實際委託中抵押的代幣相當,但由於捨入誤差可能存在一定的誤差。
+++ https://github.com/cosmos/cosmos-sdk/blob/v0.42.4/proto/cosmos/distribution/v1beta1/tx.proto#L42-L50
WithdrawValidatorCommission
驗證人可以發送「WithdrawValidatorCommission」訊息來提取其累積佣金。 佣金在每個區塊的「BeginBlock」期間計算,因此無需迭代即可提取。 提取的金額從驗證人的「ValidatorOutstandingRewards」變數中扣除。 只能發送整數金額。如果累積獎勵有小數,則在發送提款之前將其截斷,剩餘部分留待以後提取。
資助社區基金池(FundCommunityPool)
這個消息直接將代幣從發送者發送到社區池。
如果無法將金額從發送者轉移到分配模塊帳戶,則交易將失敗。
func (k Keeper) FundCommunityPool(ctx sdk.Context, amount sdk.Coins, sender sdk.AccAddress) error {
if err := k.bankKeeper.SendCoinsFromAccountToModule(ctx, sender, types.ModuleName, amount); err != nil {
return err
}
feePool := k.GetFeePool(ctx)
feePool.CommunityPool = feePool.CommunityPool.Add(sdk.NewDecCoinsFromCoins(amount...)...)
k.SetFeePool(ctx, feePool)
return nil
}
常見的分配操作:
這些操作在許多不同的訊息中進行。
初始化委託。
每次更改委託時,都會提取獎勵並重新初始化委託。 初始化委託會增加驗證人的週期,並跟踪委託的起始週期。
// initialize starting info for a new delegation
func (k Keeper) initializeDelegation(ctx sdk.Context, val sdk.ValAddress, del sdk.AccAddress) {
// period has already been incremented - we want to store the period ended by this delegation action
previousPeriod := k.GetValidatorCurrentRewards(ctx, val).Period - 1
// increment reference count for the period we're going to track
k.incrementReferenceCount(ctx, val, previousPeriod)
validator := k.stakingKeeper.Validator(ctx, val)
delegation := k.stakingKeeper.Delegation(ctx, del, val)
// calculate delegation stake in tokens
// we don't store directly, so multiply delegation shares * (tokens per share)
// note: necessary to truncate so we don't allow withdrawing more rewards than owed
stake := validator.TokensFromSharesTruncated(delegation.GetShares())
k.SetDelegatorStartingInfo(ctx, val, del, types.NewDelegatorStartingInfo(previousPeriod, stake, uint64(ctx.BlockHeight())))
}
Hooks
本模塊可以調用和調用可用的鉤子。
創建或修改委託分配。
- triggered-by:
staking.MsgDelegate
,staking.MsgBeginRedelegate
,staking.MsgUndelegate
委託之前
- 委託獎勵會提取到委託人的提取地址。 獎勵包括當前週期,但不包括開始的週期。
- 驗證者週期會增加。 驗證者週期會增加,因為驗證者的權力和份額分配可能已經發生變化。
- 委託人開始週期的參考計數會減少。
委託之后
委派的起始高度設定為上一時段。 由於前鉤,這段時間是委託人獲得獎勵的最後一段時間。
驗證者創建
- triggered-by:
staking.MsgCreateValidator
當創建驗證者時,以下驗證者變量會被初始化:
- 歷史獎勵
- 當前累積獎勵
- 累積佣金
- 總未結算獎勵
- 週期
默認情況下,所有值都設置為0
,除了週期,它被設置為1
。
驗證者移除
- triggered-by:
staking.RemoveValidator
未結算的佣金被發送到驗證者的自我委託提款地址。 剩餘的委託人獎勵被發送到社區費用池。
注意:只有當驗證者沒有剩餘的委託時,才會將其刪除。 此時,所有未結算的委託人獎勵都已被提取。 任何剩餘的獎勵都是微不足道的金額。
驗證者被削減
-
triggered-by:
staking.Slash
-
當前驗證器週期參考計數增加。 參考計數增加是因為削減事件創建了對它的引用。
- 驗證器週期增加。
- 削減事件被存儲以供以後使用。 當計算委託人獎勵時,削減事件將被引用。
事件
分發模塊發出以下事件:
BeginBlocker
Type | Attribute Key | Attribute Value |
---|---|---|
proposer_reward | validator | {validatorAddress} |
proposer_reward | reward | {proposerReward} |
commission | amount | {commissionAmount} |
commission | validator | {validatorAddress} |
rewards | amount | {rewardAmount} |
rewards | validator | {validatorAddress} |
Handlers
MsgSetWithdrawAddress
Type | Attribute Key | Attribute Value |
---|---|---|
set_withdraw_address | withdraw_address | {withdrawAddress} |
message | module | distribution |
message | action | set_withdraw_address |
message | sender | {senderAddress} |
MsgWithdrawDelegatorReward
Type | Attribute Key | Attribute Value |
---|---|---|
withdraw_rewards | amount | {rewardAmount} |
withdraw_rewards | validator | {validatorAddress} |
message | module | distribution |
message | action | withdraw_delegator_reward |
message | sender | {senderAddress} |
MsgWithdrawValidatorCommission
Type | Attribute Key | Attribute Value |
---|---|---|
withdraw_commission | amount | {commissionAmount} |
message | module | distribution |
message | action | withdraw_validator_commission |
message | sender | {senderAddress} |
參數
分發模塊包含以下參數:
Key | Type | Example |
---|---|---|
communitytax | string (dec) | "0.020000000000000000" [0] |
baseproposerreward | string (dec) | "0.010000000000000000" [0] |
bonusproposerreward | string (dec) | "0.040000000000000000" [0] |
withdrawaddrenabled | bool | true |
- [0]
communitytax
,baseproposerreward
andbonusproposerreward
這些參數必須是正數,它們的總和不能超過1.00。