BaseApp 是每条 Cosmos SDK 链的执行引擎。它实现了 ABCI(应用区块链接口),这是 CometBFT 用来与应用通信的协议,并将这些调用转换为模块执行、交易处理和状态转换。 每条 Cosmos SDK 链都会嵌入 BaseApp。你的 app.go 会创建一个 BaseApp 实例,用模块、keeper 和中间件对其进行配置,而最终生成的结构体就是 CometBFT 直接通信的对象。BaseApp 为你的区块链应用提供执行基础设施的底层基础。没有它,每条链都需要自行实现 ABCI 处理、签名验证、gas 计量、消息路由、区块钩子编排以及状态提交。

架构位置

BaseApp 位于 CometBFT 与模块之间:
CometBFT (consensus engine)
    ↓  ABCI (InitChain, CheckTx, FinalizeBlock, Commit, ...)
BaseApp
    ↓  orchestrates block execution
ModuleManager
    ↓  dispatches to individual modules
Modules (x/auth, x/bank, x/staking, ...)
    ↓  read/write
State (KVStores)
CometBFT 通过调用 BaseApp 上的 ABCI 方法来驱动区块生命周期。BaseApp 会处理每一次调用,将其委托给已注册的生命周期钩子,并把消息路由到合适的模块处理器。模块承载业务逻辑,KVStore 则保存最终产生的状态。

关键字段

BaseApp 定义在 baseapp/baseapp.go 中。它持有运行一条链所需的一切引用:
type BaseApp struct {
    logger           log.Logger
    name             string                      // application name from abci.BlockInfo
    db               dbm.DB                      // common DB backend
    cms              storetypes.CommitMultiStore // Main (uncached) state
    storeLoader      StoreLoader                 // function to handle store loading
    grpcQueryRouter  *GRPCQueryRouter            // router for redirecting gRPC query calls
    msgServiceRouter *MsgServiceRouter           // router for redirecting Msg service messages
    txDecoder        sdk.TxDecoder               // unmarshal []byte into sdk.Tx
    mempool          mempool.Mempool
    anteHandler      sdk.AnteHandler             // ante handler for fee and auth
    postHandler      sdk.PostHandler             // post handler, optional
    // ...
    sealed           bool
    // ...
    chainID          string
    // ...
}
完整字段列表请参见 BaseApp 结构体定义。
  • cms(CommitMultiStore):根状态存储。所有模块子存储都挂载在这里,区块执行期间的所有状态读写都会经过它。
  • storeLoader:一个函数,用于在应用启动时打开并挂载各个模块存储。
  • grpcQueryRouter:将传入的 gRPC 查询路由到正确模块的查询处理器。
  • msgServiceRouter:将交易中的每条消息路由到正确模块的 MsgServer 处理器。
  • txDecoder:将来自 CometBFT 的原始交易字节解码为 sdk.Tx。
  • anteHandler:在消息执行前运行,用于处理横切关注点:签名验证、序列校验和手续费扣除。
  • postHandler:可选中间件,在消息执行后运行,可用于小费处理或执行后的状态调整等任务。
  • sealed:在调用 LoadLatestVersion 后会被设置为 true。如果在密封后调用 setter 方法,会触发 panic。

初始化与密封

BaseApp 强制执行一套配置生命周期:必须在调用 LoadLatestVersion 之前调用各个 setter 方法。LoadLatestVersion 运行时,会校验必需组件、初始化 check 状态,并将 sealed 设为 true。任何在密封后调用的 setter 都会触发 panic。首次启动时,CometBFT 会调用 InitChain。它会将创世文件中的 ConsensusParams 存入 ParamStore,包括区块 gas 上限、最大区块大小、证据规则,这些参数之后可以通过链上治理调整。它会通过分支根存储来初始化所有易失状态,将区块 gas meter 设为无限,以确保创世交易不受 gas 限制,并调用应用的 initChainer,后者会运行每个模块的 InitGenesis 来填充初始状态。下一节会介绍这如何影响 app.go 的结构。

交易解码

交易从 CometBFT 到达时是原始字节。在 BaseApp 能够校验或执行它们之前,必须先使用 TxDecoder 将其解码为 SDK 的交易类型:
[]byte tx
   ↓
TxDecoder
   ↓
sdk.Tx
这一步发生在交易进入执行流水线之前。没有它,BaseApp 就无法检查消息、运行 AnteHandler,也无法将执行路由到正确的模块。

执行模式

BaseApp 并不会把所有内容都放在同一份可变状态上执行。它会针对不同的执行上下文,维护基于已提交根状态分支出来的 copy-on-write 视图:
  • CheckTx(ExecModeCheck):在交易进入 mempool 前校验交易,但不提交状态。
  • FinalizeBlock(ExecModeFinalize):在提议区块中执行交易,使用一份分支状态,并在结束时提交。
  • PrepareProposal(ExecModePrepareProposal):当节点是区块提议者时运行,用于组装候选区块。它针对一份永不提交的分支状态执行。
  • ProcessProposal(ExecModeProcessProposal):在每个验证者上运行,用于校验传入的提议。它同样针对一份永不提交的分支状态执行。
  • Simulate(ExecModeSimulate):运行交易以估算 gas,但不提交状态。
这种隔离确保了校验、提议处理和模拟不会意外修改已提交的应用状态。

交易执行流水线

当 BaseApp 处理一笔交易时,它会经过一条结构化流水线:
RunTx
  ├─ DecodeTx       → raw bytes → sdk.Tx
  ├─ AnteHandler    → signatures, sequence, fees, gas setup
  ├─ RunMsgs        → route each message to the correct module handler
  └─ PostHandler    → optional post-execution middleware
如果 AnteHandler 失败,消息执行就不会开始。如果任意消息失败,消息执行会以原子方式回滚;要么所有消息写入都提交,要么全部不提交。

AnteHandler

AnteHandler 是在交易中任何消息执行之前运行的中间件。它会验证加密签名,校验并递增账户序列号,扣除交易手续费,并为该笔交易设置 gas meter。 关于应用装配侧的内容,包括 SetAnteHandler、HandlerOptions 和构造顺序,请参见在 app.go 中挂载存储并设置 hooks。 如果 AnteHandler 失败,交易会被拒绝,其消息永远不会执行。如果 AnteHandler 成功,但后续某条消息失败,那么 AnteHandler 的状态写入,例如手续费扣除以及有序交易的序列递增,已经被刷新到 finalizeBlockState,并会随区块一起提交。即使消息失败,手续费仍然会被收取。 BaseApp.runTx() 还会处理执行期间发生的 Go panic,例如 keeper 遇到无效状态时。默认情况下,panic 会被捕获并记录为错误。应用可以通过 BaseApp.AddRunTxRecoveryHandler 注册自定义的 panic 恢复逻辑,它会向链中添加一个 RecoveryHandler。详情请参见 ADR-022 和 baseapp/recovery.go。

消息路由

当一笔交易包含消息时,BaseApp 会使用 MsgServiceRouter 将每条消息路由到合适的模块处理器。
type MsgServiceRouter struct {
    routes map[string]MsgServiceHandler
    // ...
}
路由过程分为三步:
  1. 注册:在应用启动期间,每个模块都会调用 RegisterService,按消息类型 URL 注册自己的消息处理器(例如 /cosmos.bank.v1beta1.MsgSend)。
  2. 查找:在执行时,Handler 会为传入消息的类型 URL 查找已注册的处理器。
  3. 执行:取回的处理器会调用模块的 MsgServer 实现,由它来校验输入、应用业务规则,并通过 keeper 更新状态。
这种路由完全基于类型 URL。模块在路由层面不需要彼此知晓;BaseApp 是中立的协调者。

查询

对于应用状态的只读访问,BaseApp 使用 GRPCQueryRouter 将传入的 gRPC 查询路由到正确的模块查询服务。查询会绕过交易执行流水线,直接读取已提交状态。它们不会经过 AnteHandler,不会以相同方式消耗 gas,也不会修改状态。

存储管理

BaseApp 拥有保存所有模块状态的 CommitMultiStore。应用启动时,每个模块都会注册自己的 store key,而 BaseApp 会挂载对应的存储:
app.MountKVStores(keys)
在执行每笔交易之前,BaseApp 会创建 multistore 的一个带缓存、copy-on-write 视图。该交易期间的所有写入都会发生在缓存中。如果交易成功,缓存会被刷新到底层存储。如果交易在任意时刻失败,缓存会被丢弃,不会应用任何状态变更。

CheckTx 与 mempool 校验

在交易进入区块执行之前,它会先经过 CheckTx。BaseApp 会在 CheckTx 模式下运行 AnteHandler,以校验签名、检查序列号并验证手续费。每个验证者还会强制执行一个可配置的 minGasPrices 下限,低于最小 gas 价格的交易会在这里被拒绝,以此作为垃圾交易防护措施。未通过 CheckTx 的交易会被拒绝,无法进入 mempool。 CheckTx 不会执行消息。它确实会运行 AnteHandler,如果 ante 成功,产生的写入会被持久化到 BaseApp 的内部 CheckTx 状态,而不是已提交的链状态。这就是 mempool 在区块执行前跟踪交易有效性的方式。每次区块提交后,CometBFT 都会触发一次重新检查流程(ReCheckTx),针对新状态重新校验所有待处理的 mempool 交易,任何已变为无效的交易(例如它们的序列号已被竞争交易消耗)都会在此时被移除。

协调区块执行

当 CometBFT 调用 FinalizeBlock 时,BaseApp 会按顺序运行完整的区块执行流水线:
FinalizeBlock
  ├─ PreBlock    → 模块 pre-block 钩子
  ├─ BeginBlock  → 模块 begin-block 钩子
  ├─ 对每笔交易:
  │    ├─ AnteHandler   (签名校验、费用扣除、gas 设置)
  │    ├─ 消息路由与执行
  │    └─ 提交或回滚(每笔交易原子化)
  └─ EndBlock    → 模块 end-block 钩子
       → 返回 AppHash
PreBlock 会在任何区块逻辑之前运行。它处理那些必须在区块开始前生效的变更,例如激活链升级或修改共识参数。 BeginBlock 在 PreBlock 之后运行,负责每个区块的例行处理:铸造通胀奖励、分发质押奖励、重置按区块统计的计数器。 交易 会按照区块中的顺序依次执行。每笔交易的消息执行都是原子性的:如果任意消息失败,消息执行分支会回滚。AnteHandler 的副作用则可能已经生效。 EndBlock 会在所有交易完成后运行。它处理依赖区块累计状态的逻辑,例如在所有投票消息处理完之后统计治理投票结果,或在所有委托变更完成后更新验证者权重。 在 FinalizeBlock 完成后,BaseApp 会计算并返回应用哈希,即所有已提交状态的 Merkle 根。关于它与 multistore 及确定性执行的关系,请参见 App hash。随后当 CometBFT 调用 Commit 时,BaseApp 会将 finalizeBlockState 写入根存储,将 checkState 重置为新提交的状态,并将 finalizeBlockState 清空为 nil,为下一个区块做准备。

Module Manager

BaseApp 将 PreBlock、BeginBlock 和 EndBlock 暴露为生命周期钩子点。每个标准 SDK 应用都会将这些钩子连接到一个 ModuleManager,它持有全部已注册模块及其执行顺序。当某个钩子触发时,ModuleManager 会遍历其有序模块列表,并依次调用每个模块对应的钩子。顺序很重要:某些模块依赖其他模块先完成状态更新后才能运行。 app.go 页面展示了应用如何构建 ModuleManager、将其接入 BaseApp,以及在实际中如何配置顺序。参见 Module Manager in app.go。

区块提案与投票扩展

ABCI 2.0 新增了一个提案阶段,该阶段发生在共识轮次期间、FinalizeBlock 执行之前。BaseApp 为这个阶段暴露了四个处理器,并在构造时接入默认实现:
  • PrepareProposal:在当前区块提议者上调用,用于从 mempool 组装区块。默认实现会选择交易,直到达到区块 gas 上限。链可以覆盖该实现,以支持自定义排序、过滤,或注入协议级交易。
  • ProcessProposal:在每个验证者上调用,用于校验收到的提案。默认实现会接受任何结构上有效的提案。使用 PrepareProposal 注入数据的链,通常也会覆盖这里,以验证这些数据存在且有效。
  • ExtendVote / VerifyVoteExtension:允许验证者在其 precommit 投票中附加任意数据,并校验其他验证者的扩展。一个重要用例是预言机价格馈送:验证者将链下数据注入共识,使其在区块开始时可被链上使用。
这四项都可以在 app.go 中通过 SetPrepareProposal、SetProcessProposal、SetExtendVoteHandler 和 SetVerifyVoteExtensionHandler 进行配置。不需要自定义行为的链可以直接保留默认实现。

串联起来看

BaseApp 是 Cosmos SDK 链的执行引擎:
CometBFT → ABCI → BaseApp → Modules → State
它实现了 ABCI,协调区块生命周期(PreBlock → BeginBlock → 交易 → EndBlock),通过 MsgServiceRouter 将消息路由到模块处理器,通过 GRPCQueryRouter 路由查询,在每笔交易前运行 AnteHandler,并通过写时复制缓存管理 multistore 以保证原子性。状态变更会在区块结束时提交;校验与模拟则运行在分支状态上,绝不会触及已提交数据。 下一节 app.go Overview 将说明如何实例化、配置并将 BaseApp 与各模块连接起来,从而生成一条完整且可运行的链。
BaseApp is the execution engine of every Cosmos SDK chain. It implements ABCI (Application Blockchain Interface), the protocol CometBFT uses to communicate with the application, and translates those calls into module execution, transaction processing, and state transitions. Every Cosmos SDK chain embeds BaseApp. Your app.go creates a BaseApp instance, configures it with modules, keepers, and middleware, and the resulting struct is what CometBFT communicates with directly. BaseApp provides the base layer of execution infrastructure to your blockchain application. Without it, every chain would need to independently implement ABCI handling, signature verification, gas metering, message routing, block hook orchestration, and state commitment.

Architectural position

BaseApp sits between CometBFT and the modules:
CometBFT (consensus engine)
    ↓  ABCI (InitChain, CheckTx, FinalizeBlock, Commit, ...)
BaseApp
    ↓  orchestrates block execution
ModuleManager
    ↓  dispatches to individual modules
Modules (x/auth, x/bank, x/staking, ...)
    ↓  read/write
State (KVStores)
CometBFT drives the block lifecycle by calling ABCI methods on BaseApp. BaseApp handles each call, delegating to registered lifecycle hooks and routing messages to the appropriate module handlers. Modules contain the business logic, and KVStores hold the resulting state.

Key fields

BaseApp is defined in baseapp/baseapp.go. It holds references to everything needed to run a chain:
type BaseApp struct {
    logger           log.Logger
    name             string                      // application name from abci.BlockInfo
    db               dbm.DB                      // common DB backend
    cms              storetypes.CommitMultiStore // Main (uncached) state
    storeLoader      StoreLoader                 // function to handle store loading
    grpcQueryRouter  *GRPCQueryRouter            // router for redirecting gRPC query calls
    msgServiceRouter *MsgServiceRouter           // router for redirecting Msg service messages
    txDecoder        sdk.TxDecoder               // unmarshal []byte into sdk.Tx
    mempool          mempool.Mempool
    anteHandler      sdk.AnteHandler             // ante handler for fee and auth
    postHandler      sdk.PostHandler             // post handler, optional
    // ...
    sealed           bool
    // ...
    chainID          string
    // ...
}
For a complete list of fields, see the BaseApp struct definition.
  • cms (CommitMultiStore): the root state store. All module substores are mounted here, and all state reads and writes during block execution pass through it.
  • storeLoader: a function that opens and mounts the individual module stores at application startup.
  • grpcQueryRouter: routes incoming gRPC queries to the correct module’s query handler.
  • msgServiceRouter: routes each message in a transaction to the correct module’s MsgServer handler.
  • txDecoder: decodes raw transaction bytes from CometBFT into an sdk.Tx.
  • anteHandler: runs before message execution to handle cross-cutting concerns: signature verification, sequence validation, and fee deduction.
  • postHandler: optional middleware that runs after message execution — used for tasks such as tipping or post-execution state adjustments.
  • sealed: set to true after LoadLatestVersion is called. Setter methods panic if called after sealing.

Initialization and sealing

BaseApp enforces a configuration lifecycle: setter methods must be called before LoadLatestVersion is invoked. When LoadLatestVersion runs, it validates required components, initializes the check state, and sets sealed to true. Any setter called after sealing panics. On first launch, CometBFT calls InitChain. It stores ConsensusParams from the genesis file — block gas limit, max block size, evidence rules — in the ParamStore, where they can later be adjusted via on-chain governance. It initializes all volatile states by branching the root store, sets the block gas meter to infinite so genesis transactions are not gas-constrained, and calls the application’s initChainer, which runs each module’s InitGenesis to populate initial state. How this shapes the structure of app.go is covered in the next section.

Transaction decoding

Transactions arrive from CometBFT as raw bytes. Before BaseApp can validate or execute them, it must decode them into the SDK’s transaction type using the TxDecoder:
[]byte tx
   ↓
TxDecoder
   ↓
sdk.Tx
This step happens before the transaction enters the execution pipeline. Without it, BaseApp cannot inspect messages, run the AnteHandler, or route execution to the correct module.

Execution modes

BaseApp does not execute everything against the same mutable state. It maintains branched, copy-on-write views of the committed root state for different execution contexts:
  • CheckTx (ExecModeCheck): validates a transaction before it enters the mempool, without committing state.
  • FinalizeBlock (ExecModeFinalize): executes transactions in a proposed block against a branched state that is committed at the end.
  • PrepareProposal (ExecModePrepareProposal): runs when the node is the block proposer, assembling a candidate block. Executes against a branched state that is never committed.
  • ProcessProposal (ExecModeProcessProposal): runs on every validator to validate an incoming proposal. Also executes against a branched state that is never committed.
  • Simulate (ExecModeSimulate): runs a transaction for gas estimation without committing state.
This separation ensures that validation, proposal handling, and simulation cannot accidentally mutate committed application state.

The transaction execution pipeline

When BaseApp processes a transaction, it runs through a structured pipeline:
RunTx
  ├─ DecodeTx       → raw bytes → sdk.Tx
  ├─ AnteHandler    → signatures, sequence, fees, gas setup
  ├─ RunMsgs        → route each message to the correct module handler
  └─ PostHandler    → optional post-execution middleware
If the AnteHandler fails, message execution does not begin. If any message fails, message execution reverts atomically; all message writes commit or none do.

AnteHandler

The AnteHandler is middleware that runs before any message in a transaction executes. It verifies cryptographic signatures, validates and increments the account sequence number, deducts transaction fees, and sets up the gas meter for the transaction. For the application wiring side, including SetAnteHandler, HandlerOptions, and constructor ordering, see Mounting stores and setting hooks in app.go. If the AnteHandler fails, the transaction is rejected and its messages never execute. If the AnteHandler succeeds but a message later fails, the AnteHandler’s state writes, such as fee deduction and sequence increment for ordered transactions, are already flushed to finalizeBlockState and will be committed with the block. Fees are charged even for transactions whose messages fail. BaseApp.runTx() also handles Go panics that occur during execution — for example, when a keeper encounters an invalid state. By default, panics are caught and logged as errors. Applications can register custom panic recovery logic via BaseApp.AddRunTxRecoveryHandler, which adds a RecoveryHandler to the chain. See ADR-022 and baseapp/recovery.go for details.

Message routing

When a transaction contains messages, BaseApp routes each one to the appropriate module handler using the MsgServiceRouter.
type MsgServiceRouter struct {
    routes map[string]MsgServiceHandler
    // ...
}
The routing process has three steps:
  1. Registration: During app startup, each module calls RegisterService, which registers its message handlers keyed by message type URL (e.g., /cosmos.bank.v1beta1.MsgSend).
  2. Lookup: At execution time, Handler looks up the registered handler for the incoming message’s type URL.
  3. Execution: The retrieved handler invokes the module’s MsgServer implementation, which validates inputs, applies business rules, and updates state through the keeper.
This routing is entirely type-URL-based. Modules do not need to know about each other at the routing level; BaseApp is the neutral coordinator.

Queries

For read-only access to application state, BaseApp uses the GRPCQueryRouter to route incoming gRPC queries to the correct module query service. Queries bypass the transaction execution pipeline and directly read committed state. They do not go through the AnteHandler, do not consume gas in the same way, and do not mutate state.

Store management

BaseApp owns the CommitMultiStore that holds all module state. At app startup, each module registers its store key, and BaseApp mounts the corresponding store:
app.MountKVStores(keys)
Before executing each transaction, BaseApp creates a cached, copy-on-write view of the multistore. All writes during that transaction occur in the cache. If the transaction succeeds, the cache is flushed to the underlying store. If the transaction fails at any point, the cache is discarded and no state changes are applied.

CheckTx and mempool validation

Before a transaction reaches block execution, it goes through CheckTx. BaseApp runs the AnteHandler in CheckTx mode to validate signatures, check sequence numbers, and verify fees. Each validator also enforces a configurable minGasPrices floor, and transactions offering less than the minimum gas price are rejected here as a spam protection measure. Transactions that fail CheckTx are rejected and do not enter the mempool. CheckTx does not execute messages. It does run the AnteHandler, and if ante succeeds the resulting writes are persisted to BaseApp’s internal CheckTx state rather than to committed chain state. This is how the mempool tracks transaction validity before block execution. After each block commits, CometBFT triggers a recheck pass (ReCheckTx) that re-validates all pending mempool transactions against the new state, and any transactions that became invalid (for example, because their sequence number was consumed by a competing transaction) are evicted at this point.

Coordinating block execution

When CometBFT calls FinalizeBlock, BaseApp runs the full block execution pipeline in order:
FinalizeBlock
  ├─ PreBlock    → module pre-block hooks
  ├─ BeginBlock  → module begin-block hooks
  ├─ For each transaction:
  │    ├─ AnteHandler   (signature verification, fee deduction, gas setup)
  │    ├─ Message routing and execution
  │    └─ Commit or revert (atomic per-transaction)
  └─ EndBlock    → module end-block hooks
       → returns AppHash
PreBlock runs before any block logic. It handles changes that must take effect before the block begins, such as activating a chain upgrade or modifying consensus parameters. BeginBlock runs after PreBlock and handles per-block housekeeping: minting inflation rewards, distributing staking rewards, resetting per-block counters. Transactions execute sequentially in block order. Message execution for each transaction is atomic: if any message fails, the message execution branch reverts. AnteHandler side effects may already have been applied. EndBlock runs after all transactions. It handles logic that depends on the block’s cumulative state — for example, tallying governance votes after all vote messages have been processed, or updating validator power after all delegation changes. After FinalizeBlock completes, BaseApp computes and returns the app hash — the Merkle root of all committed state. See App hash for how it relates to the multistore and deterministic execution. When CometBFT subsequently calls Commit, BaseApp writes finalizeBlockState to the root store, resets checkState to the newly committed state, and clears finalizeBlockState to nil in preparation for the next block.

Module Manager

BaseApp exposes PreBlock, BeginBlock, and EndBlock as lifecycle hook points. Every standard SDK application wires these to a ModuleManager, which holds the full set of registered modules and their execution ordering. When a hook fires, ModuleManager iterates its ordered module list and calls each module’s corresponding hook in sequence. Ordering matters: some modules depend on others having already updated state before they run. The app.go page shows how the application constructs the ModuleManager, wires it into BaseApp, and configures ordering in practice. See Module Manager in app.go.

Block proposal and vote extensions

ABCI 2.0 added a proposal phase that runs during consensus rounds, before FinalizeBlock executes. BaseApp exposes four handlers for this phase, with default implementations wired at construction:
  • PrepareProposal: called on the current block proposer to assemble a block from the mempool. The default selects transactions up to the block gas limit. Chains can override this to implement custom ordering, filtering, or injection of protocol-level transactions.
  • ProcessProposal: called on every validator to validate an incoming proposal. The default accepts any structurally valid proposal. Chains that use PrepareProposal to inject data typically also override this to verify that data is present and valid.
  • ExtendVote / VerifyVoteExtension: allow validators to attach arbitrary data to their precommit votes and verify other validators’ extensions. One major use case is oracle price feeds: validators inject off-chain data into consensus so it becomes available on-chain at block start.
All four are configurable in app.go via SetPrepareProposal, SetProcessProposal, SetExtendVoteHandler, and SetVerifyVoteExtensionHandler. Chains that do not need custom behavior can leave the defaults in place.

Putting it all together

BaseApp is the execution engine of a Cosmos SDK chain:
CometBFT → ABCI → BaseApp → Modules → State
It implements ABCI, coordinates the block lifecycle (PreBlock → BeginBlock → transactions → EndBlock), routes messages to module handlers via the MsgServiceRouter, routes queries via the GRPCQueryRouter, runs the AnteHandler before each transaction, and manages the multistore with copy-on-write caching for atomicity. State changes are committed at block end; validation and simulation run against branched state and never touch committed data. The next section, app.go Overview, explains how BaseApp is instantiated, configured, and wired with modules to produce a complete, running chain.