概述

这种简单的分配机制描述了一种在验证人和委托人之间被动分配奖励的可行方式。注意,该机制在资金分配精确度上不如主动式奖励分配机制,因此未来会升级。 该机制的运作方式如下。收集到的奖励会汇总到全局池中,并被动地分配给验证人和委托人。每个验证人都可以对其代委托人获得的奖励向委托人收取佣金。手续费会直接归集到全局奖励池和验证人提议者奖励池中。由于采用被动记账方式,只要发生会影响奖励分配速率的参数变更,就必须同时执行奖励提取。
  • 每次提取时,必须提取自己有权获得的最大金额,池中不能留下任何余额。
  • 每次向现有账户进行绑定、解绑或重新委托代币时,都必须先完整提取奖励(因为惰性记账的规则会发生变化)。
  • 每次验证人选择调整奖励佣金时,所有累计的佣金奖励都必须同时提取。
上述场景在 hooks.md 中有所说明。 此处概述的分配机制用于在验证人及其关联委托人之间惰性分配以下奖励:
  • 需要进行社会化分配的多代币手续费
  • 通胀产生的已质押资产增发
  • 验证人就其委托人质押所获得的全部奖励收取的佣金
手续费会汇集到全局池中。所采用的机制允许验证人和委托人彼此独立地按需、惰性提取自己的奖励。

局限性

作为惰性计算的一部分,每个委托人都会为每个验证人维护一个特定的累积项,用于估算全局手续费池中应归属于自己的近似公平代币份额。
entitlement = delegator-accumulation / all-delegators-accumulation
在每个区块都有恒定且相等数量的奖励代币流入的情况下,这种分配机制将与主动分配相同(即每个区块分别向所有委托人分发)。然而这并不现实,因此由于流入奖励代币的波动以及其他委托人提取奖励的时机不同,结果会偏离主动分配。 如果你恰好知道即将出现显著增加的奖励流入,那么你会有动机等到事件发生之后再提取,从而提高现有 accum 的价值。更多细节见 #2764。

对质押的影响

对 Atom 增发收益收取佣金,同时又允许 Atom 增发自动绑定(直接分配到验证人的已绑定质押),这在 BPoS 中会带来问题。从根本上说,这两种机制是互斥的。如果同时将佣金机制和自动绑定机制应用到质押代币上,那么任意验证人与其委托人之间的质押代币分配都会随着每个区块而变化。这样就需要对每条委托记录在每个区块都进行一次计算,而这被认为计算成本过高。 总之,我们只能在“对 Atom 收取佣金且增发的 atom 不绑定”和“增发的 atom 直接绑定但不收取 Atom 佣金”之间二选一,而我们选择实现前者。希望将其增发收益重新绑定的利益相关者,可以选择编写脚本,定期提取并重新绑定奖励。

目录

概念

在权益证明(PoS)区块链中,由交易手续费产生的奖励会支付给验证人。手续费分配模块会将这些奖励公平地分配给验证人的各个委托人。 奖励按 period 计算。每当验证人的委托发生变化时,例如验证人收到新的委托,period 就会更新。随后,单个验证人的奖励可以通过“委托开始前所在 period 的总奖励减去当前总奖励”来计算。更多信息参见 F1 手续费分配论文。 支付给验证人的佣金会在验证人被移除或验证人请求提取时发放。佣金会在每次 BeginBlock 操作时计算并累加,以更新累计的手续费金额。 分配给委托人的奖励会在委托发生变更、被移除或请求提取时发放。在分配奖励之前,会先应用该委托期间验证人发生的所有罚没。

F1 手续费分配中的引用计数

在 F1 手续费分配中,委托人获得的奖励会在其委托被提取时计算。该计算需要读取求和中的各项,这些项来自委托时结束的那个 period,以及为此次提取创建的最终 period,并基于各 period 中“奖励除以代币份额”的结果进行计算。 此外,由于罚没会改变一笔委托所拥有的代币数量(但我们对此采用惰性计算,只在委托人解除委托时计算),因此对于委托发生与奖励提取之间出现的任何罚没,我们必须分别计算罚没前后各 period 的奖励。因此,罚没和委托一样,都会引用由该罚没事件结束的 period。 任何不再被任何委托或任何罚没引用的历史奖励记录都可以安全删除,因为它们永远不会再被读取(未来的委托和未来的罚没总会引用未来的 periods)。这一点通过为每条历史奖励存储项跟踪一个 ReferenceCount 来实现。每当创建一个可能需要引用历史记录的新对象(委托或罚没)时,引用计数就会递增。每当删除一个此前需要引用历史记录的对象时,引用计数就会递减。如果引用计数降为零,对应的历史记录就会被删除。

外部社区池 Keeper

外部社区池 keeper 定义如下:
// ExternalCommunityPoolKeeper is the interface that an external community pool module keeper must fulfill
// for x/distribution to properly accept it as a community pool fund destination.
type ExternalCommunityPoolKeeper interface {
	// GetCommunityPoolModule gets the module name that funds should be sent to for the community pool.
	// This is the address that x/distribution will send funds to for external management.
	GetCommunityPoolModule()

string
	// FundCommunityPool allows an account to directly fund the community fund pool.
	FundCommunityPool(ctx sdk.Context, amount sdk.Coins, senderAddr sdk.AccAddress)

error
	// DistributeFromCommunityPool distributes funds from the community pool module account to
	// a receiver address.
	DistributeFromCommunityPool(ctx sdk.Context, amount sdk.Coins, receiveAddr sdk.AccAddress)

error
}
默认情况下,distribution 模块会使用内部实现的 community pool。也可以为该模块提供一个外部 community pool,使资金改为流向它而不是内部实现。Cosmos SDK 维护的参考外部 community pool 实现是 x/protocolpool。

状态

FeePool

分配相关的所有全局跟踪参数都存储在 FeePool 中。奖励会被收集并加入奖励池,然后从这里分配给验证人和委托人。 注意,奖励池保存的是十进制代币 (DecCoins),以便接收来自通胀等操作产生的零碎代币。当代币从池中分发时,会被截断回非十进制的 sdk.Coins。
  • FeePool: 0x00 -> ProtocolBuffer(FeePool)
// coins with decimal
type DecCoins []DecCoin

type DecCoin struct {
    Amount math.LegacyDec
    Denom  string
}
// Reference: https://github.com/cosmos/cosmos-sdk/blob/v0.47.0-rc1/proto/cosmos/distribution/v1beta1/distribution.proto#L116-L123

验证人分配

相关验证人的分配信息会在以下任一情况下更新:
  1. 向某个验证人的委托数量发生更新,
  2. 任意委托人从某个验证人处提取奖励,或
  3. 验证人提取其佣金。
  • ValidatorDistInfo: 0x02 | ValOperatorAddrLen (1 byte) | ValOperatorAddr -> ProtocolBuffer(validatorDistribution)
type ValidatorDistInfo struct {
    OperatorAddress     sdk.AccAddress
    SelfBondRewards     sdkmath.DecCoins
    ValidatorCommission types.ValidatorAccumulatedCommission
}

委托分配

每条委托分配记录只需要记录其上一次提取手续费的区块高度。由于委托每次在属性发生变化时(例如已绑定代币等)都必须提取手续费,因此其属性会保持不变,而委托人的 accumulation 因子只需依靠上次提取的高度和当前属性就可以被被动计算出来。
  • DelegationDistInfo: 0x02 | DelegatorAddrLen (1 byte) | DelegatorAddr | ValOperatorAddrLen (1 byte) | ValOperatorAddr -> ProtocolBuffer(delegatorDist)
type DelegationDistInfo struct {
    WithdrawalHeight int64    // last time this delegation withdrew rewards
}

Params

distribution 模块使用前缀 0x09 在状态中存储其 params,可通过治理或具有 authority 的地址进行更新。
  • Params: 0x09 | ProtocolBuffer(Params)
// Reference: https://github.com/cosmos/cosmos-sdk/blob/v0.47.0-rc1/proto/cosmos/distribution/v1beta1/distribution.proto#L12-L42

Begin Block

在每次 BeginBlock 时,上一个区块收到的所有手续费都会转入 distribution 的 ModuleAccount 账户。当委托人或验证人提取奖励时,资金会从 ModuleAccount 中支出。在 begin block 期间,对已收集手续费的不同权利份额会按如下方式更新:
  • 扣除保留的社区税。
  • 剩余部分按投票权比例分配给所有已绑定验证人

分配方案

参数说明见 params。 设 fees 为上一个区块收集到的总手续费,其中包括对质押资产的通胀奖励。所有手续费都会在区块处理期间收集到一个特定的模块账户中。在 BeginBlock 期间,它们会被发送到 "distribution" ModuleAccount。不会发生其他代币转账。相反,每个账户应得的奖励会被记录下来,而提取可以通过消息 FundCommunityPool、WithdrawValidatorCommission 和 WithdrawDelegatorReward 触发。

分配给社区池的奖励

社区池会获得 community_tax * fees,再加上验证人获得奖励后剩余的零头;验证人奖励始终向下取整到最接近的整数值。

使用外部社区资金池

从 Cosmos SDK v0.53.0 开始,可以使用外部社区资金池(例如 x/protocolpool)来替代由 x/distribution 管理的社区资金池。 在决定使用外部社区资金池之前,请先查看下一节中的警告。
// ExternalCommunityPoolKeeper is the interface that an external community pool module keeper must fulfill
// for x/distribution to properly accept it as a community pool fund destination.
type ExternalCommunityPoolKeeper interface {
	// GetCommunityPoolModule gets the module name that funds should be sent to for the community pool.
	// This is the address that x/distribution will send funds to for external management.
	GetCommunityPoolModule()

string
	// FundCommunityPool allows an account to directly fund the community fund pool.
	FundCommunityPool(ctx sdk.Context, amount sdk.Coins, senderAddr sdk.AccAddress)

error
	// DistributeFromCommunityPool distributes funds from the community pool module account to
	// a receiver address.
	DistributeFromCommunityPool(ctx sdk.Context, amount sdk.Coins, receiveAddr sdk.AccAddress)

error
}
app.DistrKeeper = distrkeeper.NewKeeper(
    appCodec,
    runtime.NewKVStoreService(keys[distrtypes.StoreKey]),
    app.AccountKeeper,
    app.BankKeeper,
    app.StakingKeeper,
    authtypes.FeeCollectorName,
    authtypes.NewModuleAddress(govtypes.ModuleName).String(),
    distrkeeper.WithExternalCommunityPool(app.ProtocolPoolKeeper), // New option.
)

外部社区资金池使用警告

当 x/distribution 使用外部社区资金池时,以下 handlers 会返回错误: QueryService
  • CommunityPool
MsgService
  • CommunityPoolSpend
  • FundCommunityPool
如果你的服务依赖 x/distribution 提供的这些功能,请将它们更新为使用 x/protocolpool 中的对应实现。

分配给验证者的奖励

提议者不会获得额外奖励。所有费用都会按共识权重比例分配给所有已绑定的验证者,包括提议者在内。
powFrac = validator power / total bonded validator power
voteMul = 1 - community_tax
所有验证者都会获得 fees * voteMul * powFrac。

分配给委托者的奖励

每个验证者的奖励都会分配给其委托者。验证者也有自我委托,在分配计算中会像普通委托一样处理。 验证者会设置一个佣金率。佣金率是灵活的,但每个验证者都会设置一个最大费率和每日最大增幅。这些上限不能被突破,用于保护委托者,防止验证者突然提高佣金率并拿走全部奖励。 运营者有权获得的未提取奖励存储在 ValidatorAccumulatedCommission 中,而委托者有权获得的奖励存储在 ValidatorCurrentRewards 中。每个委托者的奖励会在其提取奖励或更新委托时,使用 F1 手续费分配方案 进行计算,因此不会在 BeginBlock 中处理。

分配示例

在这个分配示例中,底层共识引擎会按照区块提议者权重相对于全部已绑定权重的比例来选择区块提议者。 所有验证者在将预提交包含进其提议区块方面的表现都相同。于是令 (pre_commits included) / (total bonded validator power) 保持不变,这样验证者的摊销区块奖励就是总奖励中的 ( validator power / total bonded power) * (1 - community tax rate)。因此,单个委托者获得的奖励为:
(delegator proportion of the validator power / validator power) * (validator power / total bonded power)
  * (1 - community tax rate) * (1 - validator commission rate)
= (delegator proportion of the validator power / total bonded power) * (1 -
community tax rate) * (1 - validator commission rate)

消息

MsgSetWithdrawAddress

默认情况下,提取地址就是委托者地址。若要修改提取地址,委托者必须发送一条 MsgSetWithdrawAddress 消息。 只有在参数 WithdrawAddrEnabled 被设置为 true 时,才允许修改提取地址。 提取地址不能是任何模块账户。这些账户会在初始化时被加入 distribution keeper 的 blockedAddrs 数组,因此被禁止作为提取地址。 响应:
// Reference: https://github.com/cosmos/cosmos-sdk/blob/v0.47.0-rc1/proto/cosmos/distribution/v1beta1/tx.proto#L49-L60
func (k Keeper)

SetWithdrawAddr(ctx context.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

委托者可以提取自己的奖励。 在 distribution 模块内部,这笔交易会同时移除之前那笔带有关联奖励的委托,其效果等同于委托者以相同数值重新发起了一笔新的委托。 奖励会立即从 distribution 的 ModuleAccount 发送到提取地址。 任何余数(被截断的小数)都会发送到社区资金池。 委托的起始高度会被设置为当前验证者周期,前一个周期的引用计数会减一。 提取的金额会从该验证者的 ValidatorOutstandingRewards 变量中扣除。 在 F1 分配中,总奖励是按验证者周期计算的,而委托者会按其在验证者中的质押占比获得其中一部分奖励。 在基础 F1 中,所有委托者在两个周期之间应得的总奖励按如下方式计算。 设 R(X) 为截止到周期 X 的累计总奖励除以当时已质押的代币数。委托者的分配额为 R(X) * delegator_stake。 那么,所有委托者在周期 A 到 B 之间质押所获得的奖励就是 (R(B) - R(A)) * total stake。 不过,这样计算出的奖励并未考虑 slash 惩罚。 将 slash 纳入计算需要进行迭代。 设 F(X) 为验证者在周期 X 发生一次 slash 事件时应被削减的比例。 如果验证者在 P1, ..., PN 这些周期被 slash,其中 A < P1,PN < B,则 distribution 模块按如下方式计算单个委托者的奖励 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
历史奖励会通过回放所有 slash 并在每一步衰减委托者的 stake 来追溯计算。 最终计算出的 stake 与该委托实际质押的代币数量等价,但会因舍入误差而存在一定误差范围。 响应:
// Reference: https://github.com/cosmos/cosmos-sdk/blob/v0.47.0-rc1/proto/cosmos/distribution/v1beta1/tx.proto#L66-L77

WithdrawValidatorCommission

验证者可以发送 WithdrawValidatorCommission 消息来提取其累计佣金。 佣金会在每个区块的 BeginBlock 中计算,因此提取时不需要迭代。 提取的金额会从该验证者的 ValidatorOutstandingRewards 变量中扣除。 只能发送整数金额。如果累计奖励中包含小数,则在发送提取时会先截断金额,剩余部分保留到后续再提取。

FundCommunityPool

如果使用了 ExternalCommunityPool,此 handler 会返回错误。
此消息会将代币直接从发送方转入社区资金池。 如果金额无法从发送方转入 distribution 模块账户,则交易失败。
func (k Keeper)

FundCommunityPool(ctx context.Context, amount sdk.Coins, sender sdk.AccAddress)

error {
    if err := k.bankKeeper.SendCoinsFromAccountToModule(ctx, sender, types.ModuleName, amount); err != nil {
    return err
}

feePool, err := k.FeePool.Get(ctx)
    if err != nil {
    return err
}

feePool.CommunityPool = feePool.CommunityPool.Add(sdk.NewDecCoinsFromCoins(amount...)...)
    if err := k.FeePool.Set(ctx, feePool); err != nil {
    return err
}

return nil
}

常见分配操作

这些操作会在许多不同的消息处理中发生。

初始化委托

每次委托发生变化时,都会先提取奖励,然后重新初始化该委托。 初始化委托会使验证者周期递增,并记录该委托的起始周期。
// initialize starting info for a new delegation
func (k Keeper)

initializeDelegation(ctx context.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())))
}

MsgUpdateParams

Distribution 模块参数可以通过 MsgUpdateParams 更新,这可以通过治理提案完成,且签名者始终是 gov 模块账户地址。
// Reference: https://github.com/cosmos/cosmos-sdk/blob/v0.47.0-rc1/proto/cosmos/distribution/v1beta1/tx.proto#L133-L147
在以下情况下,消息处理可能失败:
  • 签名者不是 gov 模块账户地址。

Hooks

该模块可调用和被调用的可用 hooks。

创建或修改委托分配

  • 触发来源:staking.MsgDelegate、staking.MsgBeginRedelegate、staking.MsgUndelegate

之前

  • 委托奖励会被提取到委托者的提取地址。 这些奖励包含当前周期,但不包含起始周期。
  • 验证者周期会递增。 验证者周期递增是因为验证者的权重和份额分配可能已经发生变化。
  • 委托者起始周期的引用计数会减一。

之后

委托的起始高度会被设置为前一个周期。 由于 Before hook 的存在,这个周期就是该委托者上一次获得奖励的最后一个周期。

创建验证者

  • 触发来源:staking.MsgCreateValidator
当创建一个验证者时,会初始化以下验证者变量:
  • 历史奖励
  • 当前累计奖励
  • 累计佣金
  • 未提取奖励总额
  • 周期
默认情况下,除周期被设置为 1 外,所有值都初始化为 0。

移除验证者

  • 触发来源:staking.RemoveValidator
未提取的佣金会发送到验证者自我委托的提取地址。 剩余的委托者奖励会发送到社区手续费资金池。 注意:只有当验证者没有任何剩余委托时,才会被移除。 到那时,所有未提取的委托者奖励都应已被提取。 任何剩余奖励都只是零头金额。

验证者被罚没

  • 触发来源:staking.Slash
  • 当前验证者周期的引用计数会增加。 引用计数之所以增加,是因为罚没事件创建了一个指向它的引用。
  • 验证者周期会递增。
  • 罚没事件会被存储,以供后续使用。 在计算委托人奖励时,会引用该罚没事件。

事件

分配模块会发出以下事件:

BeginBlocker

类型属性键属性值
proposer_rewardvalidator{validatorAddress}
proposer_rewardreward{proposerReward}
commissionamount{commissionAmount}
commissionvalidator{validatorAddress}
rewardsamount{rewardAmount}
rewardsvalidator{validatorAddress}

处理器

MsgSetWithdrawAddress

类型属性键属性值
set_withdraw_addresswithdraw_address{withdrawAddress}
messagemoduledistribution
messageactionset_withdraw_address
messagesender{senderAddress}

MsgWithdrawDelegatorReward

类型属性键属性值
withdraw_rewardsamount{rewardAmount}
withdraw_rewardsvalidator{validatorAddress}
messagemoduledistribution
messageactionwithdraw_delegator_reward
messagesender{senderAddress}

MsgWithdrawValidatorCommission

类型属性键属性值
withdraw_commissionamount{commissionAmount}
messagemoduledistribution
messageactionwithdraw_validator_commission
messagesender{senderAddress}

参数

分配模块包含以下参数:
键类型示例
communitytaxstring (dec)“0.020000000000000000” [0]
withdrawaddrenabledbooltrue
  • [0] communitytax 必须为正数,且不能超过 1.00。
  • baseproposerreward 和 bonusproposerreward 是在 v0.47 中已弃用的参数,不再使用。
储备池是通过 CommunityTax 收取、供治理使用的已归集资金池。 当前在 Cosmos SDK 中,通过 CommunityTax 收集的代币虽然已记账,但无法支出。

客户端

CLI

用户可以使用 CLI 查询并与 distribution 模块交互。

查询

query 命令允许用户查询 distribution 状态。
simd query distribution --help
commission
commission 命令允许用户按地址查询验证者佣金奖励。
simd query distribution commission [address] [flags]
示例:
simd query distribution commission cosmosvaloper1...
示例输出:
commission:
- amount: "1000000.000000000000000000"
  denom: stake
community-pool
community-pool 命令允许用户查询社区资金池中的所有代币余额。
simd query distribution community-pool [flags]
示例:
simd query distribution community-pool
示例输出:
pool:
- amount: "1000000.000000000000000000"
  denom: stake
params
params 命令允许用户查询 distribution 模块的参数。
simd query distribution params [flags]
示例:
simd query distribution params
示例输出:
base_proposer_reward: "0.000000000000000000"
bonus_proposer_reward: "0.000000000000000000"
community_tax: "0.020000000000000000"
withdraw_addr_enabled: true
rewards
rewards 命令允许用户查询委托人奖励。用户也可以选择附带验证者地址,以查询从特定验证者获得的奖励。
simd query distribution rewards [delegator-addr] [validator-addr] [flags]
示例:
simd query distribution rewards cosmos1...
示例输出:
rewards:
- reward:
  - amount: "1000000.000000000000000000"
    denom: stake
  validator_address: cosmosvaloper1..
total:
- amount: "1000000.000000000000000000"
  denom: stake
slashes
slashes 命令允许用户查询给定区块范围内的所有罚没记录。
simd query distribution slashes [validator] [start-height] [end-height] [flags]
示例:
simd query distribution slashes cosmosvaloper1... 1 1000
示例输出:
pagination:
  next_key: null
  total: "0"
slashes:
- validator_period: 20,
  fraction: "0.009999999999999999"
validator-outstanding-rewards
validator-outstanding-rewards 命令允许用户查询某个验证者及其所有委托中全部未提取的奖励。
simd query distribution validator-outstanding-rewards [validator] [flags]
示例:
simd query distribution validator-outstanding-rewards cosmosvaloper1...
示例输出:
rewards:
- amount: "1000000.000000000000000000"
  denom: stake
validator-distribution-info
validator-distribution-info 命令允许用户查询验证者的佣金和自委托奖励。
simd query distribution validator-distribution-info cosmosvaloper1...
示例输出:
commission:
- amount: "100000.000000000000000000"
  denom: stake
operator_address: cosmosvaloper1...
self_bond_rewards:
- amount: "100000.000000000000000000"
  denom: stake
validator-historical-rewards
validator-historical-rewards 命令允许用户查询某个验证者在指定周期的历史奖励。
simd query distribution validator-historical-rewards [validator] [period] [flags]
示例:
simd query distribution validator-historical-rewards cosmosvaloper1... 5
示例输出:
rewards:
  cumulative_reward_ratio:
  - amount: "1000000.000000000000000000"
    denom: stake
  reference_count: 2
validator-current-rewards
validator-current-rewards 命令允许用户查询某个验证者的当前奖励。
simd query distribution validator-current-rewards [validator] [flags]
示例:
simd query distribution validator-current-rewards cosmosvaloper1...
示例输出:
rewards:
  period: "3"
  rewards:
  - amount: "1000000.000000000000000000"
    denom: stake
delegator-starting-info
delegator-starting-info 命令允许用户查询某个委托人在给定验证者上的起始信息。
simd query distribution delegator-starting-info [delegator-address] [validator-address] [flags]
示例:
simd query distribution delegator-starting-info cosmos1... cosmosvaloper1...
示例输出:
starting_info:
  creation_height: "10"
  previous_period: "2"
  stake: "1000000.000000000000000000"

交易

tx 命令允许用户与 distribution 模块交互。
simd tx distribution --help
fund-community-pool
fund-community-pool 命令允许用户向社区资金池发送资金。
simd tx distribution fund-community-pool [amount] [flags]
示例:
simd tx distribution fund-community-pool 100stake --from cosmos1...
set-withdraw-addr
set-withdraw-addr 命令允许用户为与某个委托人地址关联的奖励设置提取地址。
simd tx distribution set-withdraw-addr [withdraw-addr] [flags]
示例:
simd tx distribution set-withdraw-addr cosmos1... --from cosmos1...
withdraw-all-rewards
withdraw-all-rewards 命令允许用户提取某个委托人的全部奖励。
simd tx distribution withdraw-all-rewards [flags]
示例:
simd tx distribution withdraw-all-rewards --from cosmos1...
withdraw-rewards
withdraw-rewards 命令允许用户从给定的委托地址提取全部奖励, 如果给定的委托地址是验证者操作员地址,并且用户提供了 --commission 标志,也可以选择提取验证者佣金。
simd tx distribution withdraw-rewards [validator-addr] [flags]
示例:
simd tx distribution withdraw-rewards cosmosvaloper1... --from cosmos1... --commission

gRPC

用户可以使用 gRPC 端点查询 distribution 模块。

Params

Params 端点允许用户查询 distribution 模块的参数。 示例:
grpcurl -plaintext \
    localhost:9090 \
    cosmos.distribution.v1beta1.Query/Params
示例输出:
{
  "params": {
    "communityTax": "20000000000000000",
    "baseProposerReward": "00000000000000000",
    "bonusProposerReward": "00000000000000000",
    "withdrawAddrEnabled": true
  }
}

ValidatorDistributionInfo

ValidatorDistributionInfo 用于查询验证者的佣金和自委托奖励。 示例:
grpcurl -plaintext \
    -d '{"validator_address":"cosmosvalop1..."}' \
    localhost:9090 \
    cosmos.distribution.v1beta1.Query/ValidatorDistributionInfo
示例输出:
{
  "commission": {
    "commission": [
      {
        "denom": "stake",
        "amount": "1000000000000000"
      }
    ]
  },
  "self_bond_rewards": [
    {
      "denom": "stake",
      "amount": "1000000000000000"
    }
  ],
  "validator_address": "cosmosvalop1..."
}

ValidatorOutstandingRewards

ValidatorOutstandingRewards 端点允许用户查询某个验证者地址的奖励。 示例:
grpcurl -plaintext \
    -d '{"validator_address":"cosmosvalop1.."}' \
    localhost:9090 \
    cosmos.distribution.v1beta1.Query/ValidatorOutstandingRewards
示例输出:
{
  "rewards": {
    "rewards": [
      {
        "denom": "stake",
        "amount": "1000000000000000"
      }
    ]
  }
}

ValidatorCommission

ValidatorCommission 端点允许用户查询某个验证者的累计佣金。 示例:
grpcurl -plaintext \
    -d '{"validator_address":"cosmosvalop1.."}' \
    localhost:9090 \
    cosmos.distribution.v1beta1.Query/ValidatorCommission
示例输出:
{
  "commission": {
    "commission": [
      {
        "denom": "stake",
        "amount": "1000000000000000"
      }
    ]
  }
}

ValidatorSlashes

ValidatorSlashes 端点允许用户查询某个验证者的罚没事件。 示例:
grpcurl -plaintext \
    -d '{"validator_address":"cosmosvalop1.."}' \
    localhost:9090 \
    cosmos.distribution.v1beta1.Query/ValidatorSlashes
示例输出:
{
  "slashes": [
    {
      "validator_period": "20",
      "fraction": "0.009999999999999999"
    }
  ],
  "pagination": {
    "total": "1"
  }
}

DelegationRewards

DelegationRewards 端点允许用户查询某笔委托累计获得的总奖励。 示例:
grpcurl -plaintext \
    -d '{"delegator_address":"cosmos1...","validator_address":"cosmosvalop1..."}' \
    localhost:9090 \
    cosmos.distribution.v1beta1.Query/DelegationRewards
示例输出:
{
  "rewards": [
    {
      "denom": "stake",
      "amount": "1000000000000000"
    }
  ]
}

DelegationTotalRewards

DelegationTotalRewards 端点允许用户查询每个验证者累计获得的总奖励。 示例:
grpcurl -plaintext \
    -d '{"delegator_address":"cosmos1..."}' \
    localhost:9090 \
    cosmos.distribution.v1beta1.Query/DelegationTotalRewards
示例输出:
{
  "rewards": [
    {
      "validatorAddress": "cosmosvaloper1...",
      "reward": [
        {
          "denom": "stake",
          "amount": "1000000000000000"
        }
      ]
    }
  ],
  "total": [
    {
      "denom": "stake",
      "amount": "1000000000000000"
    }
  ]
}

DelegatorValidators

DelegatorValidators 端点允许用户查询给定委托人的所有验证者。 示例:
grpcurl -plaintext \
    -d '{"delegator_address":"cosmos1..."}' \
    localhost:9090 \
    cosmos.distribution.v1beta1.Query/DelegatorValidators
示例输出:
{
  "validators": ["cosmosvaloper1..."]
}

DelegatorWithdrawAddress

DelegatorWithdrawAddress 端点允许用户查询委托人的提现地址。 示例:
grpcurl -plaintext \
    -d '{"delegator_address":"cosmos1..."}' \
    localhost:9090 \
    cosmos.distribution.v1beta1.Query/DelegatorWithdrawAddress
示例输出:
{
  "withdrawAddress": "cosmos1..."
}

CommunityPool

CommunityPool 端点允许用户查询社区资金池中的代币。 示例:
grpcurl -plaintext \
    localhost:9090 \
    cosmos.distribution.v1beta1.Query/CommunityPool
示例输出:
{
  "pool": [
    {
      "denom": "stake",
      "amount": "1000000000000000000"
    }
  ]
}

ValidatorHistoricalRewards

ValidatorHistoricalRewards 端点允许用户查询某个验证者在指定周期的历史奖励。这对于通过检查内部的分配状态来调试奖励计算非常有用。 示例:
grpcurl -plaintext \
    -d '{"validator_address":"cosmosvaloper1...","period":"5"}' \
    localhost:9090 \
    cosmos.distribution.v1beta1.Query/ValidatorHistoricalRewards
示例输出:
{
  "rewards": {
    "cumulativeRewardRatio": [
      {
        "denom": "stake",
        "amount": "1000000000000000"
      }
    ],
    "referenceCount": 2
  }
}

ValidatorCurrentRewards

ValidatorCurrentRewards 端点允许用户查询某个验证者的当前奖励。 示例:
grpcurl -plaintext \
    -d '{"validator_address":"cosmosvaloper1..."}' \
    localhost:9090 \
    cosmos.distribution.v1beta1.Query/ValidatorCurrentRewards
示例输出:
{
  "rewards": {
    "rewards": [
      {
        "denom": "stake",
        "amount": "1000000000000000"
      }
    ],
    "period": "3"
  }
}

DelegatorStartingInfo

DelegatorStartingInfo 端点允许用户查询某个委托人在给定验证者上的起始信息。结合 ValidatorHistoricalRewards,可以先获取上一周期和质押数量,再查询该周期的累计奖励比率,从而验证奖励计算。 示例:
grpcurl -plaintext \
    -d '{"delegator_address":"cosmos1...","validator_address":"cosmosvaloper1..."}' \
    localhost:9090 \
    cosmos.distribution.v1beta1.Query/DelegatorStartingInfo
示例输出:
{
  "startingInfo": {
    "previousPeriod": "2",
    "stake": "1000000000000000000",
    "creationHeight": "10"
  }
}

Overview

This simple distribution mechanism describes a functional way to passively distribute rewards between validators and delegators. Note that this mechanism does not distribute funds in as precisely as active reward distribution mechanisms and will therefore 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. Fees are collected 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.
  • Whenever withdrawing, one must withdraw the maximum amount they are entitled to, leaving nothing in the pool.
  • Whenever 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).
  • Whenever a validator chooses to change the commission on rewards, all accumulated commission rewards must be simultaneously withdrawn.
The above scenarios are covered in hooks.md. The distribution mechanism outlined herein is used to lazily distribute the following rewards between validators and associated delegators:
  • multi-token fees to be socially distributed
  • inflated staked asset provisions
  • validator commission on all rewards earned by their delegators stake
Fees are pooled within a global pool. The mechanisms used allow for validators and delegators to independently and lazily withdraw their rewards.

Shortcomings

As a part of the lazy computations, each delegator holds an accumulation term specific to each validator which is used to estimate what their approximate fair portion of tokens held in the global fee pool is owed to them.
entitlement = delegator-accumulation / all-delegators-accumulation
Under the circumstance that there was constant and equal flow of incoming reward tokens every block, this distribution mechanism would be equal to the active distribution (distribute individually to all delegators each block). However, this is unrealistic so deviations from the active distribution will occur based on fluctuations of incoming reward tokens as well as timing of reward withdrawal by other delegators. If you happen to know that incoming rewards are about to significantly increase, you are incentivized to not withdraw until after this event, increasing the worth of your existing accum. See #2764 for further details.

Effect 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 BPoS. Fundamentally, these two mechanisms are mutually exclusive. If both commission and auto-bonding mechanisms are simultaneously applied to the staking-token then the distribution of staking-tokens between any validator and its delegators will change with each block. This then necessitates a calculation for each delegation records for each block - which is considered computationally expensive. 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.

Contents

Concepts

In Proof of Stake (PoS) blockchains, rewards gained from transaction fees are paid to validators. The fee distribution module fairly distributes the rewards to the validators’ constituent delegators. Rewards are calculated per period. The period is updated each time a validator’s delegation changes, for example, when the validator receives a new delegation. The rewards for a single validator can then be calculated by taking the total rewards for the period before the delegation started, minus the current total rewards. To learn more, see the F1 Fee Distribution paper. The commission to the validator is paid when the validator is removed or when the validator requests a withdrawal. The commission is calculated and incremented at every BeginBlock operation to update accumulated fee amounts. The rewards to a delegator are distributed when the delegation is changed or removed, or a withdrawal is requested. Before rewards are distributed, all slashes to the validator that occurred during the current delegation are applied.

Reference Counting in F1 Fee Distribution

In F1 fee distribution, the rewards a delegator receives are calculated when their delegation is withdrawn. This calculation must read the terms of the summation of rewards divided by the share of tokens from the period which they ended when they delegated, and the final period that was created for the withdrawal. Additionally, as slashes change the amount of tokens a delegation will have (but we calculate this lazily, only when a delegator un-delegates), we must calculate rewards in separate periods before / after any slashes which occurred in between when a delegator delegated and when they withdrew their rewards. Thus slashes, like delegations, reference the period which was ended by the slash event. All stored historical rewards records for periods which are no longer referenced by any delegations or any slashes can thus be safely removed, as they will never be read (future delegations and future slashes will always reference future periods). This is implemented by tracking a ReferenceCount along with each historical reward storage entry. Each time a new object (delegation or slash) is created which might need to reference the historical record, the reference count is incremented. Each time one object which previously needed to reference the historical record is deleted, the reference count is decremented. If the reference count hits zero, the historical record is deleted.

External Community Pool Keepers

An external pool community keeper is defined as:
// ExternalCommunityPoolKeeper is the interface that an external community pool module keeper must fulfill
// for x/distribution to properly accept it as a community pool fund destination.
type ExternalCommunityPoolKeeper interface {
	// GetCommunityPoolModule gets the module name that funds should be sent to for the community pool.
	// This is the address that x/distribution will send funds to for external management.
	GetCommunityPoolModule()

string
	// FundCommunityPool allows an account to directly fund the community fund pool.
	FundCommunityPool(ctx sdk.Context, amount sdk.Coins, senderAddr sdk.AccAddress)

error
	// DistributeFromCommunityPool distributes funds from the community pool module account to
	// a receiver address.
	DistributeFromCommunityPool(ctx sdk.Context, amount sdk.Coins, receiveAddr sdk.AccAddress)

error
}
By default, the distribution module will use a community pool implementation that is internal. An external community pool can be provided to the module which will have funds be diverted to it instead of the internal implementation. The reference external community pool maintained by the Cosmos SDK is x/protocolpool.

State

FeePool

All globally tracked parameters for distribution are stored within FeePool. 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.
  • FeePool: 0x00 -> ProtocolBuffer(FeePool)
// coins with decimal
type DecCoins []DecCoin

type DecCoin struct {
    Amount math.LegacyDec
    Denom  string
}
// Reference: https://github.com/cosmos/cosmos-sdk/blob/v0.47.0-rc1/proto/cosmos/distribution/v1beta1/distribution.proto#L116-L123

Validator Distribution

Validator distribution information for the relevant validator is updated each time:
  1. delegation amount to a validator is updated,
  2. any delegator withdraws from a validator, or
  3. the validator withdraws its commission.
  • ValidatorDistInfo: 0x02 | ValOperatorAddrLen (1 byte) | ValOperatorAddr -> ProtocolBuffer(validatorDistribution)
type ValidatorDistInfo struct {
    OperatorAddress     sdk.AccAddress
    SelfBondRewards     sdkmath.DecCoins
    ValidatorCommission types.ValidatorAccumulatedCommission
}

Delegation Distribution

Each delegation distribution only needs to record the height at which it last withdrew fees. Because a delegation must withdraw fees each time it’s properties change (aka bonded tokens etc.) its properties will remain constant and the delegator’s accumulation factor can be calculated passively knowing only the height of the last withdrawal and its current properties.
  • DelegationDistInfo: 0x02 | DelegatorAddrLen (1 byte) | DelegatorAddr | ValOperatorAddrLen (1 byte) | ValOperatorAddr -> ProtocolBuffer(delegatorDist)
type DelegationDistInfo struct {
    WithdrawalHeight int64    // last time this delegation withdrew rewards
}

Params

The distribution module stores its params in state with the prefix of 0x09, it can be updated with governance or the address with authority.
  • Params: 0x09 | ProtocolBuffer(Params)
// Reference: https://github.com/cosmos/cosmos-sdk/blob/v0.47.0-rc1/proto/cosmos/distribution/v1beta1/distribution.proto#L12-L42

Begin Block

At each BeginBlock, all fees received in the previous block are transferred to the distribution ModuleAccount account. When a delegator or validator withdraws their rewards, they are taken out of the ModuleAccount. During begin block, the different claims on the fees collected are updated as follows:
  • The reserve community tax is charged.
  • The remainder is distributed proportionally by voting power to all bonded validators

The Distribution Scheme

See params for description of parameters. Let fees be the total fees collected in the previous block, including inflationary rewards to the stake. All fees are collected in a specific module account during the block. During BeginBlock, they are sent to the "distribution" ModuleAccount. No other sending of tokens occurs. Instead, the rewards each account is entitled to are stored, and withdrawals can be triggered through the messages FundCommunityPool, WithdrawValidatorCommission and WithdrawDelegatorReward.

Reward to the Community Pool

The community pool gets community_tax * fees, plus any remaining dust after validators get their rewards that are always rounded down to the nearest integer value.

Using an External Community Pool

Starting with Cosmos SDK v0.53.0, an external community pool, such as x/protocolpool, can be used in place of the x/distribution managed community pool. Please view the warning in the next section before deciding to use an external community pool.
// ExternalCommunityPoolKeeper is the interface that an external community pool module keeper must fulfill
// for x/distribution to properly accept it as a community pool fund destination.
type ExternalCommunityPoolKeeper interface {
	// GetCommunityPoolModule gets the module name that funds should be sent to for the community pool.
	// This is the address that x/distribution will send funds to for external management.
	GetCommunityPoolModule()

string
	// FundCommunityPool allows an account to directly fund the community fund pool.
	FundCommunityPool(ctx sdk.Context, amount sdk.Coins, senderAddr sdk.AccAddress)

error
	// DistributeFromCommunityPool distributes funds from the community pool module account to
	// a receiver address.
	DistributeFromCommunityPool(ctx sdk.Context, amount sdk.Coins, receiveAddr sdk.AccAddress)

error
}
app.DistrKeeper = distrkeeper.NewKeeper(
    appCodec,
    runtime.NewKVStoreService(keys[distrtypes.StoreKey]),
    app.AccountKeeper,
    app.BankKeeper,
    app.StakingKeeper,
    authtypes.FeeCollectorName,
    authtypes.NewModuleAddress(govtypes.ModuleName).String(),
    distrkeeper.WithExternalCommunityPool(app.ProtocolPoolKeeper), // New option.
)

External Community Pool Usage Warning

When using an external community pool with x/distribution, the following handlers will return an error: QueryService
  • CommunityPool
MsgService
  • CommunityPoolSpend
  • FundCommunityPool
If you have services that rely on this functionality from x/distribution, please update them to use the x/protocolpool equivalents.

Reward To the Validators

The proposer receives no extra rewards. All fees are distributed among all the bonded validators, including the proposer, in proportion to their consensus power.
powFrac = validator power / total bonded validator power
voteMul = 1 - community_tax
All validators receive fees * voteMul * powFrac.

Rewards to Delegators

Each validator’s rewards are distributed to its delegators. The validator also has a self-delegation that is treated like a regular delegation in distribution calculations. The validator sets a commission rate. The commission rate is flexible, but each validator sets a maximum rate and a maximum daily increase. These maximums cannot be exceeded and protect delegators from sudden increases of validator commission rates to prevent validators from taking all of the rewards. The outstanding rewards that the operator is entitled to are stored in ValidatorAccumulatedCommission, while the rewards the delegators are entitled to are stored in ValidatorCurrentRewards. The F1 fee distribution scheme is used to calculate the rewards per delegator as they withdraw or update their delegation, and is thus not handled in BeginBlock.

Example Distribution

For this example distribution, the underlying consensus engine selects block proposers in proportion to their power relative to the entire bonded power. All validators are equally performant at including pre-commits in their proposed blocks. Then hold (pre_commits included) / (total bonded validator power) constant so that the amortized block reward for the validator is ( validator power / total bonded power) * (1 - community tax rate) of the total rewards. Consequently, the reward for a single delegator is:
(delegator proportion of the validator power / validator power) * (validator power / total bonded power)
  * (1 - community tax rate) * (1 - validator commission rate)
= (delegator proportion of the validator power / total bonded power) * (1 -
community tax rate) * (1 - validator commission rate)

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:
// Reference: https://github.com/cosmos/cosmos-sdk/blob/v0.47.0-rc1/proto/cosmos/distribution/v1beta1/tx.proto#L49-L60
func (k Keeper)

SetWithdrawAddr(ctx context.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

A delegator can withdraw its rewards. Internally in the distribution module, this transaction simultaneously removes the previous delegation with associated rewards, the same as if the delegator simply started a new delegation of the same value. The rewards are sent immediately from the distribution ModuleAccount to the withdraw address. Any remainder (truncated decimals) are sent to the community pool. The starting height of the delegation is set to the current validator period, and the reference count for the previous period is decremented. The amount withdrawn is deducted from the ValidatorOutstandingRewards variable for the validator. 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. Taking the slashes into account requires iteration. Let F(X) be the fraction a validator is to be slashed for a slashing event that happened at period X. If the validator was slashed at periods P1, ..., PN, where A < P1, PN < B, the distribution module calculates the individual delegator’s rewards, T(A, B), as follows:
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
The historical rewards are calculated retroactively by playing back all the slashes and then attenuating the delegator’s stake at each step. The final calculated stake is equivalent to the actual staked coins in the delegation with a margin of error due to rounding errors. Response:
// Reference: https://github.com/cosmos/cosmos-sdk/blob/v0.47.0-rc1/proto/cosmos/distribution/v1beta1/tx.proto#L66-L77

WithdrawValidatorCommission

The validator can send the WithdrawValidatorCommission message to withdraw their accumulated commission. The commission is calculated in every block during BeginBlock, so no iteration is required to withdraw. The amount withdrawn is deducted from the ValidatorOutstandingRewards variable for the validator. Only integer amounts can be sent. If the accumulated awards have decimals, the amount is truncated before the withdrawal is sent, and the remainder is left to be withdrawn later.

FundCommunityPool

This handler will return an error if an ExternalCommunityPool is used.
This message sends coins directly from the sender to the community pool. The transaction fails if the amount cannot be transferred from the sender to the distribution module account.
func (k Keeper)

FundCommunityPool(ctx context.Context, amount sdk.Coins, sender sdk.AccAddress)

error {
    if err := k.bankKeeper.SendCoinsFromAccountToModule(ctx, sender, types.ModuleName, amount); err != nil {
    return err
}

feePool, err := k.FeePool.Get(ctx)
    if err != nil {
    return err
}

feePool.CommunityPool = feePool.CommunityPool.Add(sdk.NewDecCoinsFromCoins(amount...)...)
    if err := k.FeePool.Set(ctx, feePool); err != nil {
    return err
}

return nil
}

Common distribution operations

These operations take place during many different messages.

Initialize delegation

Each time a delegation is changed, the rewards are withdrawn and the delegation is reinitialized. Initializing a delegation increments the validator period and keeps track of the starting period of the delegation.
// initialize starting info for a new delegation
func (k Keeper)

initializeDelegation(ctx context.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())))
}

MsgUpdateParams

Distribution module params can be updated through MsgUpdateParams, which can be done using governance proposal and the signer will always be gov module account address.
// Reference: https://github.com/cosmos/cosmos-sdk/blob/v0.47.0-rc1/proto/cosmos/distribution/v1beta1/tx.proto#L133-L147
The message handling can fail if:
  • signer is not the gov module account address.

Hooks

Available hooks that can be called by and from this module.

Create or modify delegation distribution

  • triggered-by: staking.MsgDelegate, staking.MsgBeginRedelegate, staking.MsgUndelegate

Before

  • The delegation rewards are withdrawn to the withdraw address of the delegator. The rewards include the current period and exclude the starting period.
  • The validator period is incremented. The validator period is incremented because the validator’s power and share distribution might have changed.
  • The reference count for the delegator’s starting period is decremented.

After

The starting height of the delegation is set to the previous period. Because of the Before-hook, this period is the last period for which the delegator was rewarded.

Validator created

  • triggered-by: staking.MsgCreateValidator
When a validator is created, the following validator variables are initialized:
  • Historical rewards
  • Current accumulated rewards
  • Accumulated commission
  • Total outstanding rewards
  • Period
By default, all values are set to a 0, except period, which is set to 1.

Validator removed

  • triggered-by: staking.RemoveValidator
Outstanding commission is sent to the validator’s self-delegation withdrawal address. Remaining delegator rewards get sent to the community fee pool. Note: The validator gets removed only when it has no remaining delegations. At that time, all outstanding delegator rewards will have been withdrawn. Any remaining rewards are dust amounts.

Validator is slashed

  • triggered-by: staking.Slash
  • The current validator period reference count is incremented. The reference count is incremented because the slash event has created a reference to it.
  • The validator period is incremented.
  • The slash event is stored for later use. The slash event will be referenced when calculating delegator rewards.

Events

The distribution module emits the following events:

BeginBlocker

TypeAttribute KeyAttribute Value
proposer_rewardvalidator{validatorAddress}
proposer_rewardreward{proposerReward}
commissionamount{commissionAmount}
commissionvalidator{validatorAddress}
rewardsamount{rewardAmount}
rewardsvalidator{validatorAddress}

Handlers

MsgSetWithdrawAddress

TypeAttribute KeyAttribute Value
set_withdraw_addresswithdraw_address{withdrawAddress}
messagemoduledistribution
messageactionset_withdraw_address
messagesender{senderAddress}

MsgWithdrawDelegatorReward

TypeAttribute KeyAttribute Value
withdraw_rewardsamount{rewardAmount}
withdraw_rewardsvalidator{validatorAddress}
messagemoduledistribution
messageactionwithdraw_delegator_reward
messagesender{senderAddress}

MsgWithdrawValidatorCommission

TypeAttribute KeyAttribute Value
withdraw_commissionamount{commissionAmount}
messagemoduledistribution
messageactionwithdraw_validator_commission
messagesender{senderAddress}

Parameters

The distribution module contains the following parameters:
KeyTypeExample
communitytaxstring (dec)“0.020000000000000000” [0]
withdrawaddrenabledbooltrue
  • [0] communitytax must be positive and cannot exceed 1.00.
  • baseproposerreward and bonusproposerreward were parameters that are deprecated in v0.47 and are not used.
The reserve pool is the pool of collected funds for use by governance taken via the CommunityTax. Currently with the Cosmos SDK, tokens collected by the CommunityTax are accounted for but unspendable.

Client

CLI

A user can query and interact with the distribution module using the CLI.

Query

The query commands allow users to query distribution state.
simd query distribution --help
commission
The commission command allows users to query validator commission rewards by address.
simd query distribution commission [address] [flags]
Example:
simd query distribution commission cosmosvaloper1...
Example Output:
commission:
- amount: "1000000.000000000000000000"
  denom: stake
community-pool
The community-pool command allows users to query all coin balances within the community pool.
simd query distribution community-pool [flags]
Example:
simd query distribution community-pool
Example Output:
pool:
- amount: "1000000.000000000000000000"
  denom: stake
params
The params command allows users to query the parameters of the distribution module.
simd query distribution params [flags]
Example:
simd query distribution params
Example Output:
base_proposer_reward: "0.000000000000000000"
bonus_proposer_reward: "0.000000000000000000"
community_tax: "0.020000000000000000"
withdraw_addr_enabled: true
rewards
The rewards command allows users to query delegator rewards. Users can optionally include the validator address to query rewards earned from a specific validator.
simd query distribution rewards [delegator-addr] [validator-addr] [flags]
Example:
simd query distribution rewards cosmos1...
Example Output:
rewards:
- reward:
  - amount: "1000000.000000000000000000"
    denom: stake
  validator_address: cosmosvaloper1..
total:
- amount: "1000000.000000000000000000"
  denom: stake
slashes
The slashes command allows users to query all slashes for a given block range.
simd query distribution slashes [validator] [start-height] [end-height] [flags]
Example:
simd query distribution slashes cosmosvaloper1... 1 1000
Example Output:
pagination:
  next_key: null
  total: "0"
slashes:
- validator_period: 20,
  fraction: "0.009999999999999999"
validator-outstanding-rewards
The validator-outstanding-rewards command allows users to query all outstanding (un-withdrawn) rewards for a validator and all their delegations.
simd query distribution validator-outstanding-rewards [validator] [flags]
Example:
simd query distribution validator-outstanding-rewards cosmosvaloper1...
Example Output:
rewards:
- amount: "1000000.000000000000000000"
  denom: stake
validator-distribution-info
The validator-distribution-info command allows users to query validator commission and self-delegation rewards for validator.
simd query distribution validator-distribution-info cosmosvaloper1...
Example Output:
commission:
- amount: "100000.000000000000000000"
  denom: stake
operator_address: cosmosvaloper1...
self_bond_rewards:
- amount: "100000.000000000000000000"
  denom: stake
validator-historical-rewards
The validator-historical-rewards command allows users to query historical rewards for a validator at a specific period.
simd query distribution validator-historical-rewards [validator] [period] [flags]
Example:
simd query distribution validator-historical-rewards cosmosvaloper1... 5
Example Output:
rewards:
  cumulative_reward_ratio:
  - amount: "1000000.000000000000000000"
    denom: stake
  reference_count: 2
validator-current-rewards
The validator-current-rewards command allows users to query current rewards for a validator.
simd query distribution validator-current-rewards [validator] [flags]
Example:
simd query distribution validator-current-rewards cosmosvaloper1...
Example Output:
rewards:
  period: "3"
  rewards:
  - amount: "1000000.000000000000000000"
    denom: stake
delegator-starting-info
The delegator-starting-info command allows users to query the starting info for a delegator on a given validator.
simd query distribution delegator-starting-info [delegator-address] [validator-address] [flags]
Example:
simd query distribution delegator-starting-info cosmos1... cosmosvaloper1...
Example Output:
starting_info:
  creation_height: "10"
  previous_period: "2"
  stake: "1000000.000000000000000000"

Transactions

The tx commands allow users to interact with the distribution module.
simd tx distribution --help
fund-community-pool
The fund-community-pool command allows users to send funds to the community pool.
simd tx distribution fund-community-pool [amount] [flags]
Example:
simd tx distribution fund-community-pool 100stake --from cosmos1...
set-withdraw-addr
The set-withdraw-addr command allows users to set the withdraw address for rewards associated with a delegator address.
simd tx distribution set-withdraw-addr [withdraw-addr] [flags]
Example:
simd tx distribution set-withdraw-addr cosmos1... --from cosmos1...
withdraw-all-rewards
The withdraw-all-rewards command allows users to withdraw all rewards for a delegator.
simd tx distribution withdraw-all-rewards [flags]
Example:
simd tx distribution withdraw-all-rewards --from cosmos1...
withdraw-rewards
The withdraw-rewards command allows users to withdraw all rewards from a given delegation address, and optionally withdraw validator commission if the delegation address given is a validator operator and the user proves the --commission flag.
simd tx distribution withdraw-rewards [validator-addr] [flags]
Example:
simd tx distribution withdraw-rewards cosmosvaloper1... --from cosmos1... --commission

gRPC

A user can query the distribution module using gRPC endpoints.

Params

The Params endpoint allows users to query parameters of the distribution module. Example:
grpcurl -plaintext \
    localhost:9090 \
    cosmos.distribution.v1beta1.Query/Params
Example Output:
{
  "params": {
    "communityTax": "20000000000000000",
    "baseProposerReward": "00000000000000000",
    "bonusProposerReward": "00000000000000000",
    "withdrawAddrEnabled": true
  }
}

ValidatorDistributionInfo

The ValidatorDistributionInfo queries validator commission and self-delegation rewards for validator. Example:
grpcurl -plaintext \
    -d '{"validator_address":"cosmosvalop1..."}' \
    localhost:9090 \
    cosmos.distribution.v1beta1.Query/ValidatorDistributionInfo
Example Output:
{
  "commission": {
    "commission": [
      {
        "denom": "stake",
        "amount": "1000000000000000"
      }
    ]
  },
  "self_bond_rewards": [
    {
      "denom": "stake",
      "amount": "1000000000000000"
    }
  ],
  "validator_address": "cosmosvalop1..."
}

ValidatorOutstandingRewards

The ValidatorOutstandingRewards endpoint allows users to query rewards of a validator address. Example:
grpcurl -plaintext \
    -d '{"validator_address":"cosmosvalop1.."}' \
    localhost:9090 \
    cosmos.distribution.v1beta1.Query/ValidatorOutstandingRewards
Example Output:
{
  "rewards": {
    "rewards": [
      {
        "denom": "stake",
        "amount": "1000000000000000"
      }
    ]
  }
}

ValidatorCommission

The ValidatorCommission endpoint allows users to query accumulated commission for a validator. Example:
grpcurl -plaintext \
    -d '{"validator_address":"cosmosvalop1.."}' \
    localhost:9090 \
    cosmos.distribution.v1beta1.Query/ValidatorCommission
Example Output:
{
  "commission": {
    "commission": [
      {
        "denom": "stake",
        "amount": "1000000000000000"
      }
    ]
  }
}

ValidatorSlashes

The ValidatorSlashes endpoint allows users to query slash events of a validator. Example:
grpcurl -plaintext \
    -d '{"validator_address":"cosmosvalop1.."}' \
    localhost:9090 \
    cosmos.distribution.v1beta1.Query/ValidatorSlashes
Example Output:
{
  "slashes": [
    {
      "validator_period": "20",
      "fraction": "0.009999999999999999"
    }
  ],
  "pagination": {
    "total": "1"
  }
}

DelegationRewards

The DelegationRewards endpoint allows users to query the total rewards accrued by a delegation. Example:
grpcurl -plaintext \
    -d '{"delegator_address":"cosmos1...","validator_address":"cosmosvalop1..."}' \
    localhost:9090 \
    cosmos.distribution.v1beta1.Query/DelegationRewards
Example Output:
{
  "rewards": [
    {
      "denom": "stake",
      "amount": "1000000000000000"
    }
  ]
}

DelegationTotalRewards

The DelegationTotalRewards endpoint allows users to query the total rewards accrued by each validator. Example:
grpcurl -plaintext \
    -d '{"delegator_address":"cosmos1..."}' \
    localhost:9090 \
    cosmos.distribution.v1beta1.Query/DelegationTotalRewards
Example Output:
{
  "rewards": [
    {
      "validatorAddress": "cosmosvaloper1...",
      "reward": [
        {
          "denom": "stake",
          "amount": "1000000000000000"
        }
      ]
    }
  ],
  "total": [
    {
      "denom": "stake",
      "amount": "1000000000000000"
    }
  ]
}

DelegatorValidators

The DelegatorValidators endpoint allows users to query all validators for given delegator. Example:
grpcurl -plaintext \
    -d '{"delegator_address":"cosmos1..."}' \
    localhost:9090 \
    cosmos.distribution.v1beta1.Query/DelegatorValidators
Example Output:
{
  "validators": ["cosmosvaloper1..."]
}

DelegatorWithdrawAddress

The DelegatorWithdrawAddress endpoint allows users to query the withdraw address of a delegator. Example:
grpcurl -plaintext \
    -d '{"delegator_address":"cosmos1..."}' \
    localhost:9090 \
    cosmos.distribution.v1beta1.Query/DelegatorWithdrawAddress
Example Output:
{
  "withdrawAddress": "cosmos1..."
}

CommunityPool

The CommunityPool endpoint allows users to query the community pool coins. Example:
grpcurl -plaintext \
    localhost:9090 \
    cosmos.distribution.v1beta1.Query/CommunityPool
Example Output:
{
  "pool": [
    {
      "denom": "stake",
      "amount": "1000000000000000000"
    }
  ]
}

ValidatorHistoricalRewards

The ValidatorHistoricalRewards endpoint allows users to query historical rewards for a validator at a specific period. This is useful for debugging reward calculations by inspecting internal distribution state. Example:
grpcurl -plaintext \
    -d '{"validator_address":"cosmosvaloper1...","period":"5"}' \
    localhost:9090 \
    cosmos.distribution.v1beta1.Query/ValidatorHistoricalRewards
Example Output:
{
  "rewards": {
    "cumulativeRewardRatio": [
      {
        "denom": "stake",
        "amount": "1000000000000000"
      }
    ],
    "referenceCount": 2
  }
}

ValidatorCurrentRewards

The ValidatorCurrentRewards endpoint allows users to query current rewards for a validator. Example:
grpcurl -plaintext \
    -d '{"validator_address":"cosmosvaloper1..."}' \
    localhost:9090 \
    cosmos.distribution.v1beta1.Query/ValidatorCurrentRewards
Example Output:
{
  "rewards": {
    "rewards": [
      {
        "denom": "stake",
        "amount": "1000000000000000"
      }
    ],
    "period": "3"
  }
}

DelegatorStartingInfo

The DelegatorStartingInfo endpoint allows users to query the starting info for a delegator on a given validator. Combined with ValidatorHistoricalRewards, this enables verification of reward calculations by retrieving the previous period and stake, then looking up cumulative reward ratios for that period. Example:
grpcurl -plaintext \
    -d '{"delegator_address":"cosmos1...","validator_address":"cosmosvaloper1..."}' \
    localhost:9090 \
    cosmos.distribution.v1beta1.Query/DelegatorStartingInfo
Example Output:
{
  "startingInfo": {
    "previousPeriod": "2",
    "stake": "1000000000000000000",
    "creationHeight": "10"
  }
}