> ## Documentation Index
> Fetch the complete documentation index at: https://docs.cosmos.network/llms.txt
> Use this file to discover all available pages before exploring further.

# Vote Extensions

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 the `VoteExtensionsEnableHeight` 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:

```go theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
cp := ctx.ConsensusParams()
if cp.Abci != nil && req.Height > cp.Abci.VoteExtensionsEnableHeight {
    // vote extensions are available
}
```

`ConsensusParams().Abci` is a pointer and must be nil-checked before use.

## ExtendVote

The Cosmos SDK defines [`ExtendVoteHandler`](https://github.com/cosmos/cosmos-sdk/blob/release/v0.54.x/types/abci.go#L48):

```go theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
type ExtendVoteHandler func(Context, *abci.RequestExtendVote) (*abci.ResponseExtendVote, error)
```

Register a handler in `app.go` via `baseapp.SetExtendVoteHandler` (defined in [`baseapp/options.go`](https://github.com/cosmos/cosmos-sdk/blob/release/v0.54.x/baseapp/options.go)):

```go theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
app.SetExtendVoteHandler(myExtendVoteHandler)
```

If `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

Keep extensions small — large extensions increase consensus latency. See [CometBFT QA results](/cometbft/latest/docs/qa/CometBFT-QA-38#vote-extensions-testbed) for benchmarks.

## VerifyVoteExtension

The SDK defines [`VerifyVoteExtensionHandler`](https://github.com/cosmos/cosmos-sdk/blob/release/v0.54.x/types/abci.go#L52):

```go theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
type VerifyVoteExtensionHandler func(Context, *abci.RequestVerifyVoteExtension) (*abci.ResponseVerifyVoteExtension, error)
```

Register it in `app.go`:

```go theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
app.SetVerifyVoteExtensionHandler(myVerifyVoteExtensionHandler)
```

`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 in `PrepareProposal` or `ProcessProposal`, validate that they are properly signed. The SDK provides [`baseapp.ValidateVoteExtensions`](https://github.com/cosmos/cosmos-sdk/blob/release/v0.54.x/baseapp/abci_utils.go) for this:

```go theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
err := baseapp.ValidateVoteExtensions(ctx, valStore, req.Height, ctx.ChainID(), req.LocalLastCommit)
if err != nil {
    return nil, err
}
```

`ValidateVoteExtensions` verifies that each vote extension in the commit is correctly signed by its validator. `valStore` is a [`baseapp.ValidatorStore`](https://github.com/cosmos/cosmos-sdk/blob/release/v0.54.x/baseapp/abci_utils.go), an interface with a single method:

```go theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
type ValidatorStore interface {
    GetPubKeyByConsAddr(context.Context, sdk.ConsAddress) (cmtprotocrypto.PublicKey, error)
}
```

Call `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 height `H` 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:

```go theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
injectedVoteExtTx := StakeWeightedPrices{
    StakeWeightedPrices: stakeWeightedPrices,
    ExtendedCommitInfo:  req.LocalLastCommit,
}
bz, err := json.Marshal(injectedVoteExtTx)
if err != nil {
    return nil, err
}
proposalTxs = append([][]byte{bz}, proposalTxs...)
```

`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](https://github.com/cosmos/cosmos-sdk/blob/release/v0.54.x/docs/architecture/adr-064-abci-2.0.md#vote-extension-propagation--verification).

## Recovery via PreBlocker

The SDK's `PreBlocker` runs before any message execution in `FinalizeBlock`. Use it to recover injected vote extensions and make the results available to modules during the block:

```go theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
func (h *ProposalHandler) PreBlocker(ctx sdk.Context, req *abci.RequestFinalizeBlock) (*sdk.ResponsePreBlock, error) {
    res := &sdk.ResponsePreBlock{}
    if len(req.Txs) == 0 {
        return res, nil
    }
    cp := ctx.ConsensusParams()
    if cp.Abci != nil && req.Height > cp.Abci.VoteExtensionsEnableHeight {
        var injectedVoteExtTx StakeWeightedPrices
        if err := json.Unmarshal(req.Txs[0], &injectedVoteExtTx); err != nil {
            return nil, err
        }
        if err := h.keeper.SetOraclePrices(ctx, injectedVoteExtTx.StakeWeightedPrices); err != nil {
            return nil, err
        }
    }
    return res, nil
}
```

Register the PreBlocker in `app.go` (see [`baseapp/options.go`](https://github.com/cosmos/cosmos-sdk/blob/release/v0.54.x/baseapp/options.go)):

```go theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
app.SetPreBlocker(proposalHandler.PreBlocker)
```

The `sdk.PreBlocker` type is defined in [`types/abci.go`](https://github.com/cosmos/cosmos-sdk/blob/release/v0.54.x/types/abci.go):

```go theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
type PreBlocker func(Context, *abci.RequestFinalizeBlock) (*ResponsePreBlock, error)
```

State written to the context inside `PreBlocker` is available to all `BeginBlock` and message handlers in the same block.
