一笔 Cosmos 交易在被打包前要过两道门
在 Cosmos 系链上,一笔交易从签名到落账要经历两种完全不同的校验。第一道发生在它进入内存池的时刻:节点收到广播来的交易后,会调用 ABCI 接口里的 CheckTx 方法,让应用层判断这笔交易值不值得收进内存池。CometBFT 的内存池实现写得很直接——交易有效性通过 CheckTx 检查后才入池。第二道发生在出块之后:共识把选定的交易交给应用真正执行,在 CometBFT v1.0 的接口里这一步叫 FinalizeBlock(更早的版本里是 DeliverTx)。两道门的强度和代价完全不同,理解这一点,就理解了为什么”客户端没报错”和”上链成功”是两回事。
CheckTx 只做便宜的事

CheckTx 的设计目标是便宜。它通常只做这些检查:签名对不对、发送者的序列号对不对、手续费是否达到本节点的最低门槛、交易引用的账户和权限是否基本成立。它不要求把交易完整执行一遍——那太贵了,每个节点对每笔网络流量都做全量执行的话,广播风暴就能拖垮整条链。Cosmos SDK 的应用会把”检查交易”处理成一个轻量函数:解码、验签、查序列号、估算费用,然后返回接受或拒绝。由于 CheckTx 的结果决定交易是否继续向其他节点传播,它同时充当了内存池的准入闸门和网络的流量滤网。
通过第一道门为什么还会失败
麻烦在于 CheckTx 看到的只是当前状态的一个快照,而交易真正执行时状态可能已经变了。两个最常见的场景:其一,发送者连着发三笔交易,三笔都通过了 CheckTx 进入内存池,但打包顺序与签名顺序不一致,序列号错位的交易会执行失败;其二,两笔交易争用同一份链上状态,先到的那笔改变了条件,后到的那笔即使手续费更高也会在 FinalizeBlock 阶段报错。此时交易已经被打包、 gas 已经被消耗,回执里记录的是一次失败执行——钱花了,状态没改。节点在区块提交后还会用新状态重新检查内存池里剩下的交易,把已经不再合法的成员筛掉,但这只能收敛,不能阻止竞态。
与以太坊对照以及排障含义
以太坊模型里没有独立于执行的入池校验接口,节点用规则化校验加基准费用规则决定交易去留,最终的成败只在执行层见分晓;Cosmos 则把一次轻量预检显式地写成了接口,代价是要向用户解释”被接受不等于会被执行”。对排障而言,CheckTx 拒绝的问题(验签失败、序列号旧、费用太低)应在发送端解决;执行失败的排查方向则是状态竞态与条款过期。开发者可以用 dry-run 类接口提前模拟执行结果,其精度接近 FinalizeBlock 而非 CheckTx,这也是官方建议把它用于估计 gas 的原因。
两道门背后的取舍:便宜闸门与确定性执行
把校验拆成两遍,本质是在带宽与算力之间做交换。如果只有入池检查、没有真正执行,恶意节点就能把一堆注定失败的垃圾塞进区块;如果对每笔广播流量都做全量执行,转发节点的开销会高到没人愿意跑全节点。CheckTx 加上 FinalizeBlock 的组合让普通节点只付轻检成本,只有提议者和执行者承担全价。CometBFT v1 的接口还允许把内存池整个移交给应用自己管理,此时共识节点不再调用 CheckTx,准入策略完全由应用侧代码决定——这说明两遍模型是默认值而非教条。不同链给 CheckTx 定的门槛也不同:手续费下限、消息数量上限、每条消息的 gas 估计都在这里生效,同一个交易在一个节点被收下、在另一个节点被拒,是内存池异质性的正常表现,而不是网络故障。理解了两道门的分工,就能理解为什么发送方应当监听最终回执而不是 RPC 广播返回值来判定成败:前者来自区块执行结果,后者只反映某台节点的 CheckTx 意见。本文只解释机制,不构成任何投资建议。
发表评论
还没有评论,来说两句吧。
评论区为展示样式,提交不会被处理。