- 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
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.
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.
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 throughCheckTx:
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 callsProcessProposal. 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 callsFinalizeBlock once per block. Inside FinalizeBlock, BaseApp runs these phases in order:
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.
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.
Transaction execution
AfterBeginBlock, 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 viaBaseApp’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.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
AfterFinalizeBlock 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 duringFinalizeBlock, which guarantees consensus safety. If validators holding more than 1/3 of voting power disagree on the app hash, consensus halts.
Complete lifecycle overview
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.BaseApp implements ABCI and orchestrates execution.
- Transactions are validated in
CheckTxbefore entering the mempool PrepareProposalruns on the proposer to build the final tx set for the blockProcessProposalruns on all validators to accept or reject the proposed block- Each block is executed inside a single
FinalizeBlockcall - 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
FinalizeBlockcomputes and returns the app hash;Commitpersists state to disk