费用分配
概述
PoA 模块实现了一种基于验证者权重的自定义费用分配机制。与标准的 Cosmos SDKx/distribution 模块不同,PoA 使用基于检查点的系统按比例向验证者分配费用,但不会自动发放。
费用如何累积
费用在 PoA 系统中的流转方式与标准 Cosmos SDK 不同:-
区块费用:每个区块收集的交易手续费默认进入
fee_collector模块账户;如果已配置,也可以进入 PoA 模块账户(参见费用路由设置) -
检查点系统:在以下情况下,系统会为验证者更新已分配费用:
- 任意验证者权重发生变化
- 任意验证者提取费用
x/poa/keeper/distribution.go:18
分配算法
基于检查点的分配
PoA 模块使用检查点系统,在验证者权重变化时公平地分配费用。系统不会在每个区块主动执行分配,而是在离散的检查点高效完成费用分配。 检查点触发条件:- 任意验证者权重变化(通过
MsgUpdateValidators) - 任意费用提取(通过
MsgWithdrawFees)
- = 检查点 时的未分配费用
- = PoA 模块账户在时间 的当前余额
- = 所有验证者此前已分配费用之和(如果尚未执行过检查点,则为 0)
- = 在检查点 分配给验证者 的份额
- = 验证者 在检查点 的投票权重
- = 所有验证者权重之和
- = 检查点前验证者 的累计费用
- = 检查点后验证者 的累计费用
- = 本次检查点分配的份额
检查点序列示例
初始状态(检查点前):- PoA 模块账户余额: 代币
- 总已分配额: 代币(来自之前的检查点)
- 验证者 A:,已分配 代币
- 验证者 B:,已分配 代币
- 总权重:
MsgUpdateValidators,将权重分布调整为 30/70
触发检查点(在权重变更生效前):
- 计算未分配额: 代币
-
按当前权重(50/50)分配份额:
- 验证者 A: 代币
- 验证者 B: 代币
-
更新累计费用:
- 验证者 A: 代币
- 验证者 B: 代币
- 更新总已分配额: 代币
- 验证者 A:(未来区块使用的新权重)
- 验证者 B:(未来区块使用的新权重)
- 现在 1000 代币都已完成分配()
- 每个验证者都拥有更新后的 可供提取
DecCoins(十进制币种)来防止舍入残余累积。每个验证者都会跟踪那些小到暂时无法提取的小数金额。
提取费用
MsgWithdrawFees (x/poa/keeper/msg_server.go:91)
任何验证者操作员都可以提取累计费用:
- 提交提取请求:由操作员地址签名
- 执行检查点:系统会先对所有验证者执行检查点(分配所有待处理费用)
- 截断:将十进制币种截断为整数币种
- 转账:从 PoA 模块账户向操作员地址转账
- 更新跟踪:总已分配额按提取数量减少
- 余数:小数余量保留在验证者的已分配余额中
x/poa/keeper/distribution.go:106
提取公式
当验证者 提取费用时: 其中:- = 提取数量(截断为整数币种)
- = 提取前验证者的已分配费用
- = 提取后验证者的已分配费用(十进制余量)
- = 向下取整函数(截断小数)
- = 所有验证者更新后的总已分配额
费用路由设置
PoA 拥有自己的模块账户用于收集费用。建议启用 PoA 模块账户,以便让费用记账保持隔离且准确。如果未启用,费用默认会进入标准的fee_collector 账户。
要启用 PoA 模块账户,需要完成两处接线变更:
1. 注册 PoA 模块账户
在传给authkeeper.NewAccountKeeper 的 maccPerms 映射中注册 poatypes.ModuleName:
simapp/app.go
2. 配置 Ante Handler
在NewDeductFeeDecorator 上使用 WithFeeRecipientModule,将费用路由到 PoA 模块账户:
simapp/ante.go
WithFeeRecipientModule 向后兼容;如果省略它,默认仍会使用标准的 fee_collector 行为。
安全性注意事项
- 十进制精度:
- 使用 DecCoins 以防止残余累积
- 验证者会跟踪小数金额
- 余数会在多次提取之间保留
- 防止舍入误差持续累积
Fee Distribution
Overview
The PoA module implements a custom fee distribution mechanism based on validator power. Unlike the standard Cosmos SDK x/distribution module, PoA uses a checkpoint-based system to allocate fees proportionally to validators without automatic distribution.How Fees Accumulate
Fees flow through the PoA system differently than standard Cosmos SDK:-
Block Fees: Transaction fees collected in each block go to the
fee_collectormodule account by default, or to the PoA module account if configured (see Fee Routing Setup) -
Checkpoint System: Allocated fees are updated for validators when:
- Any validator power changes
- Any validator withdraws fees
x/poa/keeper/distribution.go:18
Distribution Algorithm
Checkpoint-Based Allocation
The PoA module uses a checkpoint system to allocate fees fairly when validator power changes. Rather than distributing fees actively at every block, allocation efficiently happens at discrete checkpoints. Checkpoint Triggers:- Any validator power change (via
MsgUpdateValidators) - Any fee withdrawal (via
MsgWithdrawFees)
- = unallocated fees at checkpoint
- = current balance in the PoA module account
- = sum of all previously- allocated fees across all validators (0 if no checkpoints have been done)
- = share allocated to validator at checkpoint
- = voting power of validator at checkpoint
- = sum of all validator powers
- = validator ‘s accumulated fees before checkpoint
- = validator ‘s accumulated fees after checkpoint
- = share allocated in this checkpoint
Example Checkpoint Sequence
Initial State (before checkpoint):- PoA module account balance: tokens
- Total allocated: tokens (from previous checkpoints)
- Validator A: , tokens allocated
- Validator B: , tokens allocated
- Total power:
MsgUpdateValidators to change power distribution to 30/70
Checkpoint Triggered (before power change takes effect):
- Calculate unallocated: tokens
-
Allocate shares based on current power (50/50):
- Validator A: tokens
- Validator B: tokens
-
Update accumulated fees:
- Validator A: tokens
- Validator B: tokens
- Update total allocated: tokens
- Validator A: (new power for future blocks)
- Validator B: (new power for future blocks)
- All 1000 tokens now allocated ()
- Each validator has updated available for withdrawal
DecCoins (decimal coins) to prevent rounding dust accumulation. Each validator tracks fractional amounts that are too small to withdraw.
Withdrawing Fees
MsgWithdrawFees (x/poa/keeper/msg_server.go:91)
Any validator operator can withdraw accumulated fees:
- Submit Withdrawal: Signed by operator address
- Checkpoint: System checkpoints all validators first (allocates any pending fees)
- Truncate: Decimal coins truncated to whole coins
- Transfer: Coins transferred from the PoA module account to operator address
- Update Tracking: Total allocated decreases by withdrawn amount
- Remainder: Decimal remainder stays in validator’s allocated balance
x/poa/keeper/distribution.go:106
Withdrawal Formula
When validator withdraws fees: Where:- = amount withdrawn (truncated to integer coins)
- = validator’s allocated fees before withdrawal
- = validator’s allocated fees after withdrawal (decimal remainder)
- = floor function (truncate decimals)
- = updated total allocated across all validators
Fee Routing Setup
PoA has its own module account for collecting fees. Enabling the PoA module account is recommended to keep fee accounting isolated and accurate. If not enabled, fees are deposited into the standardfee_collector account by default.
To enable the PoA module account, two wiring changes are required:
1. Register the PoA Module Account
Registerpoatypes.ModuleName in the maccPerms map passed to authkeeper.NewAccountKeeper:
simapp/app.go
2. Configure the Ante Handler
UseWithFeeRecipientModule on NewDeductFeeDecorator to route fees to the PoA module account:
simapp/ante.go
WithFeeRecipientModule is backwards compatible — omitting it defaults to the standard fee_collector behavior.
Security Considerations
- Decimal Precision:
- Uses DecCoins to prevent dust accumulation
- Validators track fractional amounts
- Remainders preserved across withdrawals
- Prevents rounding errors from accumulating