空块为什么也要出?CometBFT 的 timeout_commit 与出块节奏 图 1
空块为什么也要出?CometBFT 的 timeout_commit 与出块节奏 · 图 1

出块节奏不是”有交易才有块”

很多人以为链上没交易就不会出块。Cosmos 系公链常用的共识引擎 CometBFT 默认恰恰相反:即使一个时隙里没有任何交易,节点们照样就一个空区块走完投票流程并把高度加一。这个行为由配置文件 consensus 段落里的一组超时参数控制,理解它们才能解释为什么有些 Cosmos 链的区块高度像心跳一样匀速增长,也能看懂节点日志里那些等待与跳过的记录。

五个超时参数的分工

投票等待与提交节奏流水线的概念示意(图片由 Agnes 生成,非产品界面或数据图)

CometBFT 官方配置文档列出的默认值是这样的:timeout_propose 为三秒,是等待提案的时间,每轮递增 timeout_propose_delta 五百毫秒;收到超过三分之二的预投票后进入 timeout_prevote,默认一秒;收到超过三分之二的预确认后进入 timeout_precommit,同样默认一秒;区块提交之后再等 timeout_commit,默认一秒,才开始下一个高度。最后这个参数的注释写得很直白:这是提交区块后、进入新高度前的等待,目的是多收一些预确认签名——即便已经凑够三分之二。正是它决定了无交易时链的最小出块间隔。

空块是被共识认证过的

create_empty_blocks 默认为 true,配置文档同时说明:该值开启时,按默认共识参数大约每秒出一个块,修改 timeout_commit 就能调节节奏,比如设成十秒就得到约十秒的块。空块不是占位符,它同样经过提案、预投票、预确认的完整流程,有自己的区块头、高度和签名集合。这一点对索引系统很重要:事件订阅和区块扫描逻辑不能假设”新高度等于新交易”,需要按每条区块的交易列表判空,也不能用高度直接换算时间戳差值来推断活动量。

skip_timeout_commit 与事件驱动链

配置里还有一个 skip_timeout_commit 开关:设为 true 时,只要集齐预确认就立刻进入下一个高度,等同于把提交超时归零。需要更低延迟的链可以关掉固定节拍,让出块完全由事件驱动;代价是空闲期网络行为更突发,验证者之间收签名的窗口更短。两类设置在验证者运维上的表现不同:匀速链的监控报警看心跳间隔偏移即可,事件驱动链则要区分”没交易所以没块”和”共识卡住”这两种静止,通常要额外看提案与投票 gossip 的流量。

关掉空块之后:证明块的存在

配置文档的另一半解释了 create_empty_blocks = false 时会发生什么:区块只在收到交易时创建,但在这条路径上仍然可能出现”看起来空”的块。文档说明,当上一个区块改变了应用状态哈希时,共识引擎会额外创建一个所谓证明块——新状态的应用哈希只有写进新区块的区块头,轻客户端才能针对新状态出具默克尔证明。换句话说,即便链切换到”有交易才出块”模式,状态变了的那个高度后面仍可能跟一个内容为空的区块,它是给轻客户端证明链条准备的,不是垃圾数据。读这类链的浏览器时看到高度连涨而交易列表为空,先想到这两种可能:超时节拍下的常规空块,或者状态变化后的证明块。

对普通用户意味着什么

浏览器里 Cosmos 系链几乎恒定的出块速度、空区块也计入高度的现象,都不是巧合而是这套超时参数的直接结果。想核对一条链的真实节奏,最可靠的办法是看它的创世与配置文档里的这些参数值,再对照区块链浏览器相邻高度的时间差;不同链可能把 timeout_commit 调成完全不同的数字,把某条链的心跳经验套到另一条链上经常出错。参数名以官方配置文档为准,各链发行版可能有本地改动。

本文为机制说明,不构成投资建议;节点参数调整影响链的运行表现,操作请以官方文档为据。