在 交易、消息与查询 页面中,你已经了解了交易是在链上授权并执行逻辑的实际机制。本页将说明在 Cosmos SDK 中,交易如何被验证、执行并提交。 在基于 Cosmos SDK 构建之前,重要的是将 SDK 应用架构 中的高层架构,与区块和交易在代码中实际如何执行对应起来。 以下组件对于理解 Cosmos SDK 中交易的生命周期至关重要:
  • CometBFT(共识引擎)— 对区块进行排序并提议区块
  • ABCI(应用区块链接口)— CometBFT 用来与 Cosmos SDK 应用通信的协议
  • SDK 应用(BaseApp + 模块)— 执行交易的确定性状态机
  • Protobuf schema — 定义交易、消息、状态和查询类型
本页将区块和交易生命周期映射回这些组件。

ABCI 概览

CometBFT 和 SDK 应用是两个相互独立的进程,各自承担不同职责。
  • CometBFT 负责共识:对交易排序、管理验证者以及驱动区块生产。
  • SDK 应用 负责状态:执行交易并更新链上的数据。
ABCI(应用区块链接口)是连接两者的协议:CometBFT 通过调用应用上的 ABCI 方法来驱动区块生命周期的各个阶段,应用则对此作出响应。 BaseApp 是 SDK 对 ABCI 接口的实现。它接收来自 CometBFT 的这些调用,并协调各模块之间的执行。模块接入 BaseApp,并在相应阶段执行自身逻辑。
+---------------------+          |         +-------------------------+
|      CometBFT       |          |         |      SDK Application    |
|     (Consensus)     |         ABCI       |    (BaseApp + modules)  |
+---------------------+          |         +-------------------------+
                                 |
InitChain (once)                 |
  Chain start -------------------|------> InitGenesis per module
                                 |
CheckTx (per submitted tx)       |
  Mempool validation ------------|------> decode · verify · validate
                                 |<------ accept → Mempool
                                 |
PrepareProposal (proposer only)  |
  Build block proposal ----------|------> select txs (MaxTxBytes, MaxGas)
                                 |
ProcessProposal (all validators) |
  Evaluate proposal -------------|------> verify txs → ACCEPT / REJECT
                                 |
FinalizeBlock (per block)        |
  Execute block -----------------|------> PreBlock hooks
                                 |        BeginBlock hooks
                                 |        For each tx:
                                 |          AnteHandler
                                 |          → message routing
                                 |          → MsgServer (module logic)
                                 |        EndBlock hooks
                                 |        Return AppHash
Commit                           |
  Persist state -----------------|------> persist state to disk
                                 |<------ return AppHash

InitChain(仅创世时)

InitChain 只会在链第一次启动时运行一次。BaseApp 会加载 genesis.json,该文件定义了链的初始状态,然后调用每个模块的 InitGenesis 来填充其存储。初始验证者集合也会在此建立。创世初始化发生在第一个区块开始之前。 关于 genesis.json 如何变成模块状态,参见 创世与链初始化。

CheckTx 与 mempool

在交易能够进入区块之前,它会经过 CheckTx:
User
  ↓
Node
  ↓
ABCI: CheckTx
  ↓
Mempool
交易以原始 protobuf 编码字节的形式发送。 关于这些字节如何被确定性编码,参见 编码与 Protobuf。 在 CheckTx 期间,SDK 应用的 BaseApp 会解码交易、验证签名和序列号、校验费用与 gas,并执行基础的消息校验。 关于账户序列模型,参见 账户。关于 gas 计量和费用相关的执行细节,参见 执行上下文、Gas 与事件。 如果验证失败,交易会被拒绝。如果通过验证,它就会进入 mempool。mempool 是节点内存中保存已验证交易的池,这些交易正在等待被纳入区块。 已验证交易会在 mempool 中等待,直到 CometBFT 为下一轮选出区块提议者。

PrepareProposal

每一轮中,CometBFT 会选择一个验证者来提议区块。PrepareProposal 只会在该验证者上被调用。BaseApp 会从 mempool 中选择交易,遵守区块的 MaxTxBytes 和 MaxGas 限制,并返回最终的交易列表。关于此处理器在何处配置,参见 区块提议与投票扩展。

ProcessProposal

当其他验证者收到提议的区块后,CometBFT 会调用 ProcessProposal。BaseApp 会验证每笔交易,并返回 ACCEPT 或 REJECT。此时不会写入状态。一旦超过三分之二的投票权接受该区块并达成共识,CometBFT 就会调用 FinalizeBlock。关于这些处理器在执行模型中的视角,参见 区块提议与投票扩展。

FinalizeBlock

CometBFT 每个区块调用一次 FinalizeBlock。在 FinalizeBlock 内部,BaseApp 会按顺序运行以下阶段:
PreBlock → BeginBlock → transaction execution → EndBlock

PreBlock

PreBlock 在 BeginBlock 之前运行,通常用于那些必须在区块开始前影响共识关键状态的逻辑,例如激活链升级或修改共识参数。由于这些变更需要在任何区块逻辑运行之前生效,因此不能放在 BeginBlock 内进行。模块可以通过其 AppModule 上的 HasPreBlocker 扩展接口来实现这一点(通常位于 x/<module>/module.go),应用的 ModuleManager 会在 FinalizeBlock 期间调用所有已注册的 PreBlocker。 如果某个 PreBlocker 修改了共识参数,它会通过在其 ResponsePreBlock 中返回 ConsensusParamsChanged=true 来发出信号。随后,BaseApp 会在继续进入 BeginBlock 之前,刷新当前上下文中的共识参数:
app.finalizeBlockState.ctx = app.finalizeBlockState.ctx.WithConsensusParams(app.GetConsensusParams())

BeginBlock

BeginBlock 在 PreBlock 之后运行,处理那些必须在任何交易执行之前发生、且与区块中具体交易无关的每区块维护逻辑。常见用途包括铸造通胀奖励、分发质押奖励,以及重置每区块状态。模块通过 x/<module>/module.go 中的 BeginBlock 函数实现这一点。由于 BeginBlock 和 EndBlock 会在每个区块上运行,这些钩子中的复杂或高开销逻辑可能会拖慢区块执行,因此应保持其工作尽量轻量。

交易执行

在 BeginBlock 之后,BaseApp 会遍历区块中的每笔交易,并通过一个固定流水线来执行。

第 1 步:AnteHandler

在 Cosmos SDK 链的 app.go 中配置的 AnteHandler,会对每笔交易首先运行。对于标准的有序交易,它会验证签名、检查序列号、扣除费用并计量 gas。完整的中间件模型参见 BaseApp。 如果 AnteHandler 失败,交易会中止,其消息不会被执行。

第 2 步:消息路由与执行

交易中的每条消息都会通过 BaseApp 的 MsgServiceRouter 路由到相应模块的 protobuf Msg 服务。消息是模块特定的,通常定义在模块的 tx.proto 中。BaseApp 会将这些消息路由到模块已注册的 protobuf Msg 服务处理器,该处理器再调用模块的 MsgServer 实现。关于路由器在执行流水线中的作用,参见 消息路由。 MsgServer 包含该消息类型的执行逻辑。它会验证消息内容、应用业务规则并更新状态。状态通过模块的 keeper 进行读写,keeper 负责管理对模块 KV 存储的访问,并封装其存储键。模块简介 解释了 MsgServer 与 Keeper 如何划分职责。 消息会按照它们在交易中出现的顺序依次执行。

第 3 步:原子性

消息执行具有原子性:要么所有消息都成功,要么消息执行产生的写入一项也不会被提交。
Tx
  ├─ Msg 1
  ├─ Msg 2
  └─ Msg 3
如果任意一条消息失败,该交易的消息执行分支会被丢弃,交易返回错误。随后会执行区块中的下一笔交易。BaseApp 在内部使用缓存存储来实现这一点。在消息执行开始之前,AnteHandler 的副作用可能已经生效。 如果链启用了无序交易,则会绕过常规的序列号检查,重放保护将使用超时时间戳加无序 nonce 跟踪。关于面向客户端的流程,参见 生成无序交易。

EndBlock

EndBlock 在区块中的所有交易执行完毕后运行。它用于依赖区块累计状态的逻辑,例如在所有投票交易都处理完之后统计治理投票,或者在区块中的所有委托变更完成后重新计算验证者权重。模块通过 x/<module>/module.go 中的 EndBlock 函数实现这一点。

Commit

在 FinalizeBlock 返回之后,CometBFT 会调用 Commit。这会将状态变更持久化到节点的本地磁盘。

确定性执行

在所有验证者之间,区块执行必须是确定性的。区块必须包含相同且顺序一致的交易,交易也必须使用规范的 protobuf 二进制编码。状态转换必须是确定性的,这可确保每个验证者在 FinalizeBlock 期间计算出相同的 app hash,从而保证共识安全性。如果持有超过三分之一投票权的验证者在 app hash 上存在分歧,共识就会停止。

完整生命周期概览

CometBFT
  ↓ ABCI InitChain
BaseApp → x/<module>/InitGenesis

For each submitted transaction (async):
  ↓ ABCI CheckTx
    → decode, verify, validate
    → insert into mempool

For every block:
  ↓ ABCI PrepareProposal  (proposer only)
    → select txs from mempool (MaxTxBytes, MaxGas)
    → return tx list to CometBFT
  ↓ ABCI ProcessProposal  (all validators)
    → verify txs, check gas limit
    → ACCEPT or REJECT
  ↓ ABCI FinalizeBlock
    → PreBlock
    → x/<module>/BeginBlock
    For each tx (in the block):
      → AnteHandler
      → Message routing
      → Message execution (atomic)
    → x/<module>/EndBlock
  ↓ ABCI Commit
BaseApp commits KVStores
在每个阶段运行的钩子(AnteHandler、BeginBlocker、EndBlocker 和 InitChainer)会在任何区块执行之前,于你的链的 app.go 中完成注册。app.go 是配置层,用于将各个模块接入 BaseApp。
CometBFT 通过 ABCI 驱动区块处理。BaseApp 实现了 ABCI,并负责协调执行流程。
  • 交易在进入内存池之前,会先在 CheckTx 中完成校验
  • PrepareProposal 在提议者节点上运行,用于为区块构建最终交易集合
  • ProcessProposal 在所有验证者上运行,用于接受或拒绝提议的区块
  • 每个区块都在一次 FinalizeBlock 调用中执行
  • 在 FinalizeBlock 内部:PreBlock → BeginBlock → 交易 → EndBlock
  • 每笔交易都会经过 AnteHandler → 消息路由 → 消息执行
  • 单笔交易中的消息执行具有原子性:要么全部提交,要么全部不提交
  • FinalizeBlock 会计算并返回应用哈希;Commit 会将状态持久化到磁盘
Protobuf 可确保规范化编码,使所有验证者都以完全一致的方式解释交易。下一节 模块简介 将从区块执行转向实际实现链逻辑的模块结构。
In the Transactions, Messages, and Queries page, you learned that transactions are the actual mechanism that authorizes and executes logic on the chain. This page explains how transactions are validated, executed, and committed in the Cosmos SDK. Before building with the Cosmos SDK, it’s important to connect the high-level architecture from SDK Application Architecture with how blocks and transactions actually execute in code. The following components are essential for understanding the lifecycle of a transaction in the Cosmos SDK:
  • CometBFT (consensus engine) — orders and proposes blocks
  • ABCI (Application-Blockchain Interface) — the protocol CometBFT uses to talk to the Cosmos SDK application
  • SDK application (BaseApp + modules) — the deterministic state machine that executes transactions
  • Protobuf schemas — define transactions, messages, state, and query types
This page maps the block and transaction lifecycle back to those components.

ABCI overview

CometBFT and the SDK application are two separate processes with distinct responsibilities.
  • CometBFT handles consensus: ordering transactions, managing validators, and driving block production.
  • The SDK application handles state: executing transactions and updating the chain’s data.
The ABCI (Application Blockchain Interface) is the protocol that connects them: CometBFT calls ABCI methods on the application to drive each phase of the block lifecycle, and the application responds. BaseApp is the SDK’s implementation of the ABCI interface. It receives these calls from CometBFT and orchestrates execution across modules. Modules plug into BaseApp and execute their logic during the appropriate phases.
+---------------------+          |         +-------------------------+
|      CometBFT       |          |         |      SDK Application    |
|     (Consensus)     |         ABCI       |    (BaseApp + modules)  |
+---------------------+          |         +-------------------------+
                                 |
InitChain (once)                 |
  Chain start -------------------|------> InitGenesis per module
                                 |
CheckTx (per submitted tx)       |
  Mempool validation ------------|------> decode · verify · validate
                                 |<------ accept → Mempool
                                 |
PrepareProposal (proposer only)  |
  Build block proposal ----------|------> select txs (MaxTxBytes, MaxGas)
                                 |
ProcessProposal (all validators) |
  Evaluate proposal -------------|------> verify txs → ACCEPT / REJECT
                                 |
FinalizeBlock (per block)        |
  Execute block -----------------|------> PreBlock hooks
                                 |        BeginBlock hooks
                                 |        For each tx:
                                 |          AnteHandler
                                 |          → message routing
                                 |          → MsgServer (module logic)
                                 |        EndBlock hooks
                                 |        Return AppHash
Commit                           |
  Persist state -----------------|------> persist state to disk
                                 |<------ return AppHash

InitChain (genesis only)

InitChain runs once when the chain starts for the first time. BaseApp loads genesis.json, which defines the chain’s initial state, and calls each module’s InitGenesis to populate its store. The initial validator set is established. Genesis runs before the first block begins. For how genesis.json becomes module state, see Genesis and chain initialization.

CheckTx and the mempool

Before a transaction can enter a block, it goes through CheckTx:
User
  ↓
Node
  ↓
ABCI: CheckTx
  ↓
Mempool
Transactions are sent as raw protobuf-encoded bytes. For how those bytes are encoded deterministically, see Encoding and Protobuf. During CheckTx, the SDK application’s BaseApp decodes the transaction, verifies signatures and sequences, validates fees and gas, and performs basic message validation. For the account sequence model, see Accounts. For gas metering and fee-related execution details, see Execution Context, Gas, and Events. If validation fails, the transaction is rejected. If it passes, it enters the mempool. The mempool is a node’s in-memory pool of validated transactions waiting to be included in a block. Validated transactions wait in the mempool until CometBFT selects a block proposer for the next round.

PrepareProposal

Each round, CometBFT selects one validator to propose a block. PrepareProposal is called on that validator only. BaseApp selects transactions from the mempool respecting the block’s MaxTxBytes and MaxGas limits and returns the final transaction list. For where this handler is configured, see Block proposal and vote extensions.

ProcessProposal

Once the other validators receive the proposed block, CometBFT calls ProcessProposal. BaseApp verifies each transaction and returns ACCEPT or REJECT. No state is written. Once more than two-thirds of voting power accepts the block and consensus is reached, CometBFT calls FinalizeBlock. For the execution-model view of these handlers, see Block proposal and vote extensions.

FinalizeBlock

CometBFT calls FinalizeBlock once per block. Inside FinalizeBlock, BaseApp runs these phases in order:
PreBlock → BeginBlock → transaction execution → EndBlock

PreBlock

PreBlock runs before BeginBlock and is generally used for logic that must affect consensus-critical state before the block begins, such as activating a chain upgrade or modifying consensus parameters. Because these changes need to take effect before any block logic runs, they cannot happen inside BeginBlock. Modules may implement this via the HasPreBlocker extension interface on their AppModule (typically in x/<module>/module.go), and the application’s ModuleManager invokes all registered PreBlockers during FinalizeBlock. If a PreBlocker modifies consensus parameters, it signals this by returning ConsensusParamsChanged=true in its ResponsePreBlock. BaseApp then refreshes the consensus params in the current context before proceeding to BeginBlock:
app.finalizeBlockState.ctx = app.finalizeBlockState.ctx.WithConsensusParams(app.GetConsensusParams())

BeginBlock

BeginBlock runs after PreBlock and handles per-block housekeeping that must happen before any transactions execute, regardless of the transactions in the block. Common uses include minting inflation rewards, distributing staking rewards, and resetting per-block state. Modules implement this via the BeginBlock function in x/<module>/module.go. Because BeginBlock and EndBlock run on every block, complex or expensive logic in these hooks can slow block execution; keep their work lightweight.

Transaction execution

After BeginBlock, BaseApp iterates over each transaction in the block and runs it through a fixed pipeline.

Step 1: AnteHandler

Configured in a Cosmos SDK chain’s app.go, the AnteHandler runs first for every transaction. For standard ordered transactions, it verifies signatures, checks sequence numbers, deducts fees, and meters gas. See BaseApp for the full middleware model. If the AnteHandler fails, the transaction aborts and its messages do not execute.

Step 2: Message routing and execution

Each message in a transaction is routed via BaseApp’s MsgServiceRouter to the appropriate module’s protobuf Msg service. Messages are module-specific and typically defined in a module’s tx.proto. BaseApp routes these messages to the module’s registered protobuf Msg service handler, which calls the module’s MsgServer implementation. See Message routing for the router’s role in the execution pipeline. The MsgServer contains the execution logic for that message type. It validates the message content, applies business rules, and updates state. State is read and written through the module’s keeper, which manages access to the module’s KV store and encapsulates its storage keys. Intro to Modules explains how MsgServer and Keeper divide responsibilities. Messages execute sequentially in the order they appear in the transaction.

Step 3: Atomicity

Message execution is atomic: all messages succeed or none of the message execution writes are committed.
Tx
  ├─ Msg 1
  ├─ Msg 2
  └─ Msg 3
If any message fails, the message execution branch for that transaction is discarded and the transaction returns an error. The next transaction in the block is then executed. BaseApp uses cached stores internally to implement this. AnteHandler side effects may already have been applied before message execution begins. If the chain enables unordered transactions, the normal sequence check is bypassed and replay protection uses a timeout timestamp plus unordered nonce tracking. For the client-facing flow, see Generating an Unordered Transaction.

EndBlock

EndBlock runs after all transactions in the block have executed. It is used for logic that depends on the block’s cumulative state, like tallying governance votes after all vote transactions have been processed, or recalculating validator power after all delegation changes in the block. Modules implement this via the EndBlock function in x/<module>/module.go.

Commit

After FinalizeBlock returns, CometBFT calls Commit. This persists the state changes to the node’s local disk.

Deterministic execution

Across all validators, the block execution is deterministic. Blocks must contain the same ordered transactions, and transactions must use canonical protobuf binary encoding. State transitions must be deterministic, which ensures that every validator computes the same app hash during FinalizeBlock, which guarantees consensus safety. If validators holding more than 1/3 of voting power disagree on the app hash, consensus halts.

Complete lifecycle overview

CometBFT
  ↓ ABCI InitChain
BaseApp → x/<module>/InitGenesis

For each submitted transaction (async):
  ↓ ABCI CheckTx
    → decode, verify, validate
    → insert into mempool

For every block:
  ↓ ABCI PrepareProposal  (proposer only)
    → select txs from mempool (MaxTxBytes, MaxGas)
    → return tx list to CometBFT
  ↓ ABCI ProcessProposal  (all validators)
    → verify txs, check gas limit
    → ACCEPT or REJECT
  ↓ ABCI FinalizeBlock
    → PreBlock
    → x/<module>/BeginBlock
    For each tx (in the block):
      → AnteHandler
      → Message routing
      → Message execution (atomic)
    → x/<module>/EndBlock
  ↓ ABCI Commit
BaseApp commits KVStores
The hooks that run at each phase (the AnteHandler, BeginBlocker, EndBlocker, and InitChainer) are registered in your chain’s app.go before any block executes. app.go is the configuration layer that wires modules into BaseApp.
CometBFT drives block processing through ABCI. BaseApp implements ABCI and orchestrates execution.
  • Transactions are validated in CheckTx before entering the mempool
  • PrepareProposal runs on the proposer to build the final tx set for the block
  • ProcessProposal runs on all validators to accept or reject the proposed block
  • Each block is executed inside a single FinalizeBlock call
  • Within FinalizeBlock: PreBlock → BeginBlock → transactions → EndBlock
  • Each transaction runs through AnteHandler → message routing → message execution
  • Message execution within a transaction is atomic: all messages commit or none do
  • FinalizeBlock computes and returns the app hash; Commit persists state to disk
Protobuf ensures canonical encoding so all validators interpret transactions identically. The next section, Intro to Modules, turns from block execution to the module structure that actually implements chain logic.