H 的 pre-commit 投票上的任意字节。它们属于 ABCI 2.0 的一部分,并从 CometBFT v0.38 和 Cosmos SDK v0.50 开始可用。
启用投票扩展
投票扩展由VoteExtensionsEnableHeight 共识参数控制。在配置的高度,CometBFT 会开始对每个验证者调用 ExtendVote 和 VerifyVoteExtension。在高度 H 生成的扩展,会在高度 H+1 通过 PrepareProposal 提供给区块提议者。
要在某个处理器中检查投票扩展是否已启用:
ConsensusParams().Abci 是一个指针,使用前必须先进行 nil 检查。
ExtendVote
Cosmos SDK 定义了ExtendVoteHandler:
app.go 中通过 baseapp.SetExtendVoteHandler 注册处理器(定义见 baseapp/options.go):
ExtendVoteHandler,它必须返回一个非 nil 的 VoteExtension。空字节切片也是有效的。
ExtendVote 仅在本地验证者上调用,因此不需要是确定性的。常见用途包括:
- 为预言机提交价格
- 为加密 mempool 共享加密份额
VerifyVoteExtension
SDK 定义了VerifyVoteExtensionHandler:
app.go 中注册:
VerifyVoteExtension 会在每个验证者上针对每个对等节点的 pre-commit 调用。它必须是确定性的,即同一个扩展在每个验证者上都必须产生相同结果。如果应用定义了 ExtendVoteHandler,也应该同时定义 VerifyVoteExtensionHandler。
始终在此处理器中校验传入扩展的大小。
校验投票扩展签名
在PrepareProposal 或 ProcessProposal 中处理投票扩展之前,请先校验它们是否已被正确签名。SDK 为此提供了 baseapp.ValidateVoteExtensions:
ValidateVoteExtensions 会验证提交中的每个投票扩展是否都由对应验证者正确签名。valStore 是一个 baseapp.ValidatorStore,这是一个只包含单个方法的接口:
PrepareProposal(针对 req.LocalLastCommit)和 ProcessProposal(针对从注入交易中恢复出的 ExtendedCommitInfo)中调用 ValidateVoteExtensions。
投票扩展传播
高度H 的投票扩展只会在高度 H+1 的 PrepareProposal 中,通过 req.LocalLastCommit 提供给区块提议者。它们不会在 ProcessProposal 期间提供给其他验证者。
如果所有验证者都需要在 H+1 使用扩展数据,提议者必须将其注入区块提案。由于 PrepareProposal 中的 Txs 字段类型为 [][]byte,因此任何字节切片都可以被前置到提案中,包括序列化后的扩展摘要:
FinalizeBlock 会忽略任何未实现 sdk.Tx 的字节切片,因此注入的扩展会在消息执行期间被安全跳过。
关于传播设计的更多细节,参见 ABCI 2.0 ADR。
通过 PreBlocker 恢复
SDK 的PreBlocker 会在 FinalizeBlock 中任何消息执行之前运行。可以用它恢复注入的投票扩展,并在当前区块期间将结果提供给各模块使用:
app.go 中注册 PreBlocker(见 baseapp/options.go):
sdk.PreBlocker 类型定义在 types/abci.go 中:
PreBlocker 内部写入上下文的状态,在同一区块中的所有 BeginBlock 和消息处理器中都可用。
Vote extensions are arbitrary bytes that validators can attach to their pre-commit vote at block height
H. They are part of ABCI 2.0 and are available starting from CometBFT v0.38 and Cosmos SDK v0.50.
Enabling vote extensions
Vote extensions are controlled by theVoteExtensionsEnableHeight consensus parameter. At the configured height, CometBFT begins calling ExtendVote and VerifyVoteExtension on every validator. Extensions produced at height H are available to the block proposer at height H+1 via PrepareProposal.
To check whether vote extensions are active in a handler:
ConsensusParams().Abci is a pointer and must be nil-checked before use.
ExtendVote
The Cosmos SDK definesExtendVoteHandler:
app.go via baseapp.SetExtendVoteHandler (defined in baseapp/options.go):
ExtendVoteHandler is set, it must return a non-nil VoteExtension. An empty byte slice is valid.
ExtendVote is called only on the local validator and does not need to be deterministic. Common uses include:
- Submitting prices for an oracle
- Sharing encryption shares for an encrypted mempool
VerifyVoteExtension
The SDK definesVerifyVoteExtensionHandler:
app.go:
VerifyVoteExtension is called on every validator for every peer’s pre-commit. It must be deterministic — the same extension must produce the same result on every validator. If an application defines ExtendVoteHandler, it should also define a VerifyVoteExtensionHandler.
Always validate the size of incoming extensions in this handler.
Validating vote extension signatures
Before processing vote extensions inPrepareProposal or ProcessProposal, validate that they are properly signed. The SDK provides baseapp.ValidateVoteExtensions for this:
ValidateVoteExtensions verifies that each vote extension in the commit is correctly signed by its validator. valStore is a baseapp.ValidatorStore, an interface with a single method:
ValidateVoteExtensions in both PrepareProposal (on req.LocalLastCommit) and ProcessProposal (on the ExtendedCommitInfo recovered from the injected transaction) before trusting any extension data.
Vote extension propagation
Vote extensions from heightH are provided only to the block proposer at height H+1 via req.LocalLastCommit in PrepareProposal. They are not provided to other validators during ProcessProposal.
If all validators need to use extension data at H+1, the proposer must inject it into the block proposal. Since the Txs field in PrepareProposal is a [][]byte, any byte slice — including a serialized extensions summary — can be prepended to the proposal:
FinalizeBlock ignores any byte slice that does not implement sdk.Tx, so injected extensions are safely skipped during message execution.
For more details on propagation design, see the ABCI 2.0 ADR.
Recovery via PreBlocker
The SDK’sPreBlocker runs before any message execution in FinalizeBlock. Use it to recover injected vote extensions and make the results available to modules during the block:
app.go (see baseapp/options.go):
sdk.PreBlocker type is defined in types/abci.go:
PreBlocker is available to all BeginBlock and message handlers in the same block.