- 区块链
【免费下载链接】cosmos-sdk
Framework for building performant, customizable blockchains with native interoperability
导读
本文以 Cosmos SDK 架构决策记录 ADR-048 Consensus Fees 为主体,系统讲解一份曾被提出、最终被否决(Status: Rejected)的共识级多档 Gas 价格设计方案:它试图将 Gas 定价从"每个验证人自行配置min-gas-prices"升级为"链上共识统一维护的分层动态价格",并借助 Tendermint mempool 的优先级能力实现交易分层。读完本文,你将理解三档定价的参数模型、EIP-1559 式区块负载调整算法、AuthInfo扩展选项与fee字段语义变更、交易优先级组合规则,以及该提案与当前 Cosmos SDK 实际 fee 检查实现之间的异同。
一、背景与动机:为什么要引入共识级 Gas 价格
在当前的 Cosmos SDK 架构中,Gas 价格保护完全依赖每个验证人节点自身的本地配置。ADR-048 明确指出:
Currently, each validator configures its own
minimal-gas-pricesinapp.yaml. But setting a proper minimal gas price is critical to protect network from DoS attack, and it's hard for all the validators to pick a sensible value, so we propose to maintain a gas price in consensus level.
该机制的现状与源码完全吻合。在当前仓库中,验证人通过app.toml配置minimum-gas-prices(见 server/config/toml.go 中的模板minimum-gas-prices = "{{ .BaseConfig.MinGasPrices }}"),该字段在 server/config/config.go 中定义,其注释写明这是"validator's minimum gas prices";ValidateBasic甚至会强制要求该字段非空(server/config/config.go,否则返回 "set min gas price in app.toml or flag or env variable")。同时 baseapp/config/gas.go 中的GasConfig.MinGasPrices注释也明确:这是"验证人愿意接受处理一笔交易的最低 Gas 价格,主要用于 DoS 与垃圾交易防护"。
问题在于:
- 设置合理的
min-gas-prices对抵御 DoS 攻击至关重要,但全网验证人很难就一个统一的价格达成一致; - 验证人之间配置不一致,会导致同一笔交易在不同节点上得到不同待遇,mempool 行为割裂;
- 静态价格无法随网络负载变化,区块拥堵时缺乏价格信号的自动调节。
因此 ADR-048 提出将 Gas 价格提升到共识层统一维护。此外,该提案还指出自 Tendermint 0.34.20 起 mempool 已支持优先级排序,可以借此实现更精细的 Gas 费用体系。
二、多档价格体系(Multi-Tier Price System):三档设计
ADR-048 的核心设计是在共识层维护一个多档(multi-tier)Gas 价格体系,以提供最大灵活性:
| 档位 | 价格类型 | 调整方式 |
|---|---|---|
| Tier 1 | 恒定价格 | 仅能通过治理提案偶尔修改 |
| Tier 2 | 动态价格 | 依据上一区块负载调整 |
| Tier 3 | 动态价格 | 依据上一区块负载调整,但调整速度更快 |
设计约束:高档位的 Gas 价格必须高于低档位(The gas price of higher tier should be bigger than the lower tier);交易手续费按共识层计算出的精确 Gas 价格收取(The transaction fees are charged with the exact gas price calculated on consensus)。
2.1 参数模型(protobuf Schema)
每档的价格策略通过协议参数表达,ADR-048 给出的参数 schema 如下:
message TierParams { uint32 priority = 1 // priority in tendermint mempool Coin initial_gas_price = 2 // uint32 parent_gas_target = 3 // the target saturation of block uint32 change_denominator = 4 // decides the change speed Coin min_gas_price = 5 // optional lower bound of the price adjustment Coin max_gas_price = 6 // optional upper bound of the price adjustment } message Params { repeated TierParams tiers = 1; }各字段语义如下:
priority:该档交易在 Tendermint mempool 中对应的优先级数值;initial_gas_price:该档 Gas 价格的初始值(首个区块使用);parent_gas_target:区块的目标饱和度(即区块 Gas 用量的期望值,用于负载比较);change_denominator:价格调整速度的分母,数值越大调整越平缓;min_gas_price:可选的价格调整下限,防止价格过低;max_gas_price:可选的价格调整上限,防止价格失控上涨。
2.2 实现层面的字段对齐
在 ADR-048 的"Implementation"一节,TierParams被细化为更贴近实现的形态,其中change_denominator被替换为change_speed(Decimal类型,0 表示不随区块负载调整):
class TierParams: 'gas price strategy parameters of one tier' priority: int # priority in tendermint mempool initial_gas_price: Coin parent_gas_target: int change_speed: Decimal # 0 means don't adjust for block load.这也解释了三档体系的本质差异:Tier 1 的change_speed为 0(不调整),Tier 2/3 的change_speed非零且不同,从而形成"恒定 / 慢速动态 / 快速动态"的梯度。
三、交易扩展选项与fee字段语义变更
3.1 通过AuthInfo扩展选项指定档位
为了让用户(或钱包/工具)为交易指定服务档位,ADR-048 提出在AuthInfo中增加一个扩展选项:
message ExtensionOptionsTieredTx { uint32 fee_tier = 1 }其中fee_tier的值就是tiers参数列表的下标索引。这与当前仓库AuthInfo中已经预留的扩展点完全兼容——在 proto/cosmos/tx/v1beta1/tx.proto 中,AuthInfo定义了extension_options(repeated google.protobuf.Any extension_options = 1023;),注释明确说明"arbitrary options that can be added by chains",即各链可以塞入自定义扩展选项而不破坏标准交易结构。ADR-048 正是计划利用这一机制承载ExtensionOptionsTieredTx。
3.2fee从"实收金额"变为"费用上限"
这是本提案对交易语义最重要的变更:
We also change the semantic of existing
feefield ofTx, instead of charging user the exactfeeamount, we treat it as a fee cap, while the actual amount of fee charged is decided dynamically.
变更后的行为:
- 用户提交的
fee不再是实际扣除金额,而是费用上限(fee cap); - 实际收费金额由共识层动态决定(即该档位当前 Gas 价格 × 实际 Gas 用量);
- 若用户给出的
fee折算价格低于当前共识价格,交易不会被包含进当前区块,理想情况下应留在 mempool 中,等待共识 Gas 价格下降后被打包; - mempool 最终可以清理(prune)这些长期滞留的旧交易。
这一设计相当于把"价格发现"交给了共识层,让低出价交易自动"排队等降价"。
四、交易优先级(Tx Prioritization)
4.1 优先级规则
交易按档位排序,档位越高优先级越高;同一档位内部遵循 Tendermint 默认顺序(当前为 FIFO):
Transactions are prioritized based on the tier, the higher the tier, the higher the priority. Within the same tier, follow the default Tendermint order (currently FIFO).
ADR-048 特别提醒:mempool 的交易排序逻辑不属于共识范畴,恶意验证人可以自行篡改(Be aware of that the mempool tx ordering logic is not part of consensus and can be modified by malicious validator)。因此这套机制只能作为激励性引导,不能作为共识保证。
4.2 与优先级机制的灵活组合
档位机制可以与多种优先级规则自由组合,ADR-048 给出了三类示例:
- 协议为用户档位追加额外档位:用户可设置 tier 0、10 或 20,但协议内部会创建 tier 0~29 的完整梯度。例如 IBC 交易自动进入
user_tier + 5:用户选 tier 1 时,IBC 交易进入 tier 15; - 为特殊交易类型保留档位区间:例如 tier 4、5 仅保留给特殊交易类型(如 tier 5 专用于 evidence 交易);普通
bank.Send若试图设置 tier 5,会被降级到该普通交易可用的最高档(如 tier 3); - 按交易类型强制指定档位:例如 tier 100 专用于 evidence 交易,且所有 evidence 交易无条件进入该档。
这套组合能力使协议可以对"用户自愿选档"之外的交易(跨链消息、作恶举报等)实施协议级强制优先级。
五、min-gas-prices的弃用
若共识级 Gas 价格上线,原有的每验证人min-gas-prices配置将被弃用(deprecate),因为两套机制并存会造成混淆:
Deprecate the current per-validator
min-gas-pricesconfiguration, since it would confusing for it to work together with the consensus gas price.
需要强调的是,这只是一个被否决提案中的设想。当前仓库的实际情况是minimum-gas-prices仍是验证人节点必配项,server/config/config.go 的ValidateBasic要求其非空,且 fee 检查逻辑完全围绕它构建(详见下文第八节)。这也正是"共识级定价"与"本地定价"两条路线在 Cosmos SDK 演进历史中的真实分歧点。
六、区块负载调整算法(Adjust For Block Load)
对于 Tier 2 / Tier 3,Gas 价格依据上一区块 Gas 用量动态调整,算法逻辑与以太坊 EIP-1559 相似:区块用量高于目标时涨价,低于目标时降价,且价格变化量与用量偏差成正比。
ADR-048 给出的 Python 伪代码:
def adjust_gas_price(gas_price, parent_gas_used, tier): if parent_gas_used == tier.parent_gas_target: return gas_price elif parent_gas_used > tier.parent_gas_target: gas_used_delta = parent_gas_used - tier.parent_gas_target gas_price_delta = max(gas_price * gas_used_delta // tier.parent_gas_target // tier.change_speed, 1) return gas_price + gas_price_delta else: gas_used_delta = parent_gas_target - parent_gas_used gas_price_delta = gas_price * gas_used_delta // parent_gas_target // tier.change_speed return gas_price - gas_price_delta算法要点:
- 恰好命中目标(
parent_gas_used == parent_gas_target):价格保持不变; - 超载(用量高于目标):按超量比例涨价,且价格增量至少为 1(
max(..., 1)),防止负载很高时价格不涨; - 空闲(用量低于目标):按缺量比例降价,降价不设下限(可以降到 0,除非配置了
min_gas_price下限); - 价格调整步长由
gas_price * gas_used_delta // parent_gas_target // change_speed决定,change_speed越大调整越温和——这正是 Tier 2 与 Tier 3"调整速度不同"的实现来源。
七、区块段预留(Block Segment Reservation)
ADR-048 指出一个关键风险:如果高档位交易可以无限挤压低档位交易,用户将被迫全部使用高档位,体系最终退化回单档位(the system degraded to a single tier)。因此理想方案是为每个档位预留区块段(block segment),保证低档位交易不会被完全挤掉:
Ideally we should reserve block segments for each tier, so the lower tiered transactions won't be completely squeezed out by higher tier transactions.
该能力需要 Tendermint 提供支持(We need help from tendermint to implement this),属于依赖外部共识引擎的能力项。
八、整体实现流程(Implementation)
ADR-048 用 Python 风格伪代码勾勒了完整实现,涵盖"档位识别 → 价格调整 → 费用检查 → 优先级回传 → 区块用量记录"的闭环。
8.1 档位识别
interface TieredTx: def tier(self) -> int: pass def tx_tier(tx): if isinstance(tx, TieredTx): return tx.tier() else: # default tier for custom transactions return 0 # NOTE: we can add more rules here per "Tx Prioritization" section自定义交易默认进入 tier 0;后续可按第四节"Tx Prioritization"的规则在此追加更多判定逻辑(如按交易类型、按 IBC 标记强制提档)。
8.2 共识状态
class State: 'consensus state' # total gas used in last block, None when it's the first block parent_gas_used: Optional[int] # gas prices of last block for all tiers gas_prices: List[Coin]State记录两个共识状态:上一区块总 Gas 用量(创世后的首个区块为None),以及上一区块各档位的 Gas 价格列表。
8.3 区块生命周期
def begin_block(): 'Adjust gas prices' for i, tier in enumerate(Params.tiers): if State.parent_gas_used is None: # initialized gas price for the first block State.gas_prices[i] = tier.initial_gas_price else: # adjust gas price according to gas used in previous block State.gas_prices[i] = adjust_gas_price(State.gas_prices[i], State.parent_gas_used, tier) def end_block(): 'Update block gas used' State.parent_gas_used = block_gas_meter.consumed()BeginBlock依据上一区块负载调整各档位价格(首区块用initial_gas_price初始化),EndBlock记录本区块实际 Gas 用量供下一区块使用。
8.4 CheckTx 费用检查与优先级回传
def mempoolFeeTxHandler_checkTx(ctx, tx): # the minimal-gas-price configured by validator, zero in deliver_tx context validator_price = ctx.MinGasPrice() consensus_price = State.gas_prices[tx_tier(tx)] min_price = max(validator_price, consensus_price) # zero means infinity for gas price cap if tx.gas_price() > 0 and tx.gas_price() < min_price: return 'insufficient fees' return next_CheckTx(ctx, tx) def txPriorityHandler_checkTx(ctx, tx): res, err := next_CheckTx(ctx, tx) # pass priority to tendermint res.Priority = Params.tiers[tx_tier(tx)].priority return res, err这段伪代码体现了提案中CheckTx 阶段的最小价格取max(验证人本地价, 共识价)(在 deliver_tx 上下文下验证人价格为 0,即完全由共识价决定);费用不足直接返回insufficient fees;同时把档位对应的priority写进 ABCI CheckTx 响应,交给 Tendermint mempool 做排序。
九、DoS 攻击防护(DoS Attack Protection)
ADR-048 分析了新体系下的 DoS 防护效果:
- 高档位饱和攻击成本高昂:攻击者若要占满区块、阻止其他交易执行,必须持续使用最高档位交易,其成本将显著高于默认档位(significantly higher than the default tier),从而形成经济威慑;
- 低档位垃圾流可被反制:若攻击者用低档位交易刷屏,诚实用户只需发送高档位交易即可在排序上压过攻击者(user can mitigate by sending higher tier transactions)。
十、影响评估(Consequences)
10.1 向后兼容性(Backwards Compatibility)
该提案引入三类不兼容变更:
- 新的协议参数(
Params.tiers); - 新的共识状态(
parent_gas_used、各档gas_prices); - 交易体中新增/变更字段(
AuthInfo扩展选项、fee语义)。
10.2 积极影响(Positive)
- 默认档位维持可预期的 Gas 价格体验:客户端使用 tier 0 即可获得与今天一致的固定价格预期;
- 高档位价格可自适应区块负载:拥堵时自动涨价、空闲时自动回落;
- 无优先级冲突:提案仅占用 3 个优先级水平,不会与基于交易类型的自定义优先级规则冲突;
- 可组合性强:档位可以与其他优先级规则自由叠加。
10.3 负面影响(Negative)
- 生态需升级:钱包与工具必须适配新的
tier参数,且fee字段语义已变更,存量客户端需要重新理解"费用上限"的含义。
十一、与当前仓库实现的对照:一个被否决但影响深远的提案
需要明确:ADR-048 的最终状态是 Rejected(已否决),其"共识级多档 Gas 价格"并未在 Cosmos SDK 主线落地。但将它与当前仓库实现对照,能更清晰地理解其设计的真实价值与取舍:
1. 费用检查:本地定价仍是现状。当前默认费用检查逻辑是 x/auth/ante/validator_tx_fee.go 中的checkTxFeeWithValidatorMinGasPrices:仅在ctx.IsCheckTx()时读取ctx.MinGasPrices(),按fee = ceil(minGasPrice * gasLimit)计算必需费用,不足则返回ErrInsufficientFee("insufficient fees")——这与 ADR-048 伪代码中"低出价返回 insufficient fees"的路径一致,但价格来源仍是各验证人本地配置而非共识状态。
2. 优先级回传:思路已部分落地。该文件中的getTxPriority(x/auth/ante/validator_tx_fee.go)会按交易"最小币种面额的 Gas 价格"计算一个朴素优先级;DeductFeeDecorator(x/auth/ante/fee.go)通过TxFeeChecker接口拿到(fee, priority, error)后调用ctx.WithPriority(priority)(x/auth/ante/fee.go)注入优先级,优先级在 types/context.go 中以int64保存,并最终体现在 ABCI CheckTx 响应中供 Tendermint mempool 排序。而仓库自带的PriorityNonceMempool(types/mempool/priority_nonce.go)正是按"优先级 + 发送者 nonce"二维部分有序存储交易的 mempool 实现——ADR-048 依赖的"mempool 优先级"能力,在现仓库中已有完整落地。
3. 扩展点:机制就绪,但未被用于档位。AuthInfo的extension_options(proto/cosmos/tx/v1beta1/tx.proto)为"链级自定义选项"预留了标准入口,理论上仍可承载类似ExtensionOptionsTieredTx的方案,只是共识级定价本身未获采纳。
综上,ADR-048 是一份"设计了完整参数模型与算法、却因共识复杂度与生态迁移成本被否决"的架构提案;它提出的分层定价、负载自适应、fee 上限语义、优先级组合等思想,与当前仓库"本地最低价 + 优先级 mempool"的实现形成鲜明对照,也为后续链上 Gas 机制设计提供了宝贵参考。
相关资源
- ADR-048 原文
- 验证人最低 Gas 价格配置解析
- app.toml 配置模板(minimum-gas-prices)
- BaseApp Gas 配置结构
- 默认费用检查实现 checkTxFeeWithValidatorMinGasPrices
- DeductFeeDecorator 与 TxFeeChecker 接口
- Context 的 MinGasPrices 与 Priority
- PriorityNonceMempool 优先级内存池
- AuthInfo 扩展选项定义
- 区块链
【免费下载链接】cosmos-sdk
Framework for building performant, customizable blockchains with native interoperability
相关推荐
Cosmos SDK 技术解析:Gas费用机制详解
Cosmos SDK 技术解析:Gas费用机制详解 前言 在区块链系统中,资源管理是一个核心问题。Cosmos SDK通过Gas机制来有效管理系统资源,确保网络
区块链Gas Town Formula 解析架构:三级优先级(Project/Town/System)设计与源码实现
Gas Town Formula 解析架构:三级优先级(Project/Town/System)设计与源码实现 导读 :本指南围绕 Gas Town 多智能体工
人工智能AI AgentAgent 编排代码智能体CLIFlexPrice 价格级桶化(Price-Level Bucketing)设计全解:把计量窗口从 Meter 迁移到 Price
FlexPrice 价格级桶化(Price Level Bucketing)设计全解:把计量窗口从 Meter 迁移到 Price 本文基于 FlexPrice
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考