# BaseApp

This document describes BaseApp, the abstraction that implements the core functionalities of a Cosmos SDK application.

# Pre-requisite Readings

# Introduction

BaseApp is a base type that implements the core of a Cosmos SDK application, namely:

  • The Application Blockchain Interface, for the state-machine to communicate with the underlying consensus engine (e.g. Tendermint).
  • Service Routers, to route messages and queries to the appropriate module.
  • Different states, as the state-machine can have different volatile states updated based on the ABCI message received.

The goal of BaseApp is to provide the fundamental layer of a Cosmos SDK application that developers can easily extend to build their own custom application. Usually, developers will create a custom type for their application, like so:

Copy type App struct { // reference to a BaseApp *baseapp.BaseApp // list of application store keys // list of application keepers // module manager }

Extending the application with BaseApp gives the former access to all of BaseApp's methods. This allows developers to compose their custom application with the modules they want, while not having to concern themselves with the hard work of implementing the ABCI, the service routers and state management logic.

# Type Definition

The BaseApp type holds many important parameters for any Cosmos SDK based application.

Copy // BaseApp reflects the ABCI application implementation. type BaseApp struct { // nolint: maligned // initialized on creation logger log.Logger name string // application name from abci.Info db dbm.DB // common DB backend cms sdk.CommitMultiStore // Main (uncached) state storeLoader StoreLoader // function to handle store loading, may be overridden with SetStoreLoader() queryRouter sdk.QueryRouter // router for redirecting query calls grpcQueryRouter *GRPCQueryRouter // router for redirecting gRPC query calls interfaceRegistry types.InterfaceRegistry txHandler tx.Handler // txHandler for {Deliver,Check}Tx and simulations initChainer sdk.InitChainer // initialize state with validators and state blob beginBlocker sdk.BeginBlocker // logic to run before any txs endBlocker sdk.EndBlocker // logic to run after all txs, and to determine valset changes addrPeerFilter sdk.PeerFilter // filter peers by address and port idPeerFilter sdk.PeerFilter // filter peers by node ID fauxMerkleMode bool // if true, IAVL MountStores uses MountStoresDB for simulation speed. // manages snapshots, i.e. dumps of app state at certain intervals snapshotManager *snapshots.Manager snapshotInterval uint64 // block interval between state sync snapshots snapshotKeepRecent uint32 // recent state sync snapshots to keep // volatile states: // // checkState is set on InitChain and reset on Commit // deliverState is set on InitChain and BeginBlock and set to nil on Commit checkState *state // for CheckTx deliverState *state // for DeliverTx // an inter-block write-through cache provided to the context during deliverState interBlockCache sdk.MultiStorePersistentCache // absent validators from begin block voteInfos []abci.VoteInfo // paramStore is used to query for ABCI consensus parameters from an // application parameter store. paramStore ParamStore // The minimum gas prices a validator is willing to accept for processing a // transaction. This is mainly used for DoS and spam prevention. minGasPrices sdk.DecCoins // initialHeight is the initial height at which we start the baseapp initialHeight int64 // flag for sealing options and parameters to a BaseApp sealed bool // block height at which to halt the chain and gracefully shutdown haltHeight uint64 // minimum block time (in Unix seconds) at which to halt the chain and gracefully shutdown haltTime uint64 // minRetainBlocks defines the minimum block height offset from the current // block being committed, such that all blocks past this offset are pruned // from Tendermint. It is used as part of the process of determining the // ResponseCommit.RetainHeight value during ABCI Commit. A value of 0 indicates // that no blocks should be pruned. // // Note: Tendermint block pruning is dependant on this parameter in conunction // with the unbonding (safety threshold) period, state pruning and state sync // snapshot parameters to determine the correct minimum value of // ResponseCommit.RetainHeight. minRetainBlocks uint64 // application's version string version string // application's protocol version that increments on every upgrade // if BaseApp is passed to the upgrade keeper's NewKeeper method. appVersion uint64 // trace set will return full stack traces for errors in ABCI Log field trace bool // indexEvents defines the set of events in the form {eventType}.{attributeKey}, // which informs Tendermint what to index. If empty, all events will be indexed. indexEvents map[string]struct{} // abciListeners for hooking into the ABCI message processing of the BaseApp // and exposing the requests and responses to external consumers abciListeners []ABCIListener }

Let us go through the most important components.

Note: Not all parameters are described, only the most important ones. Refer to the type definition for the full list.

First, the important parameters that are initialized during the bootstrapping of the application:

  • CommitMultiStore: This is the main store of the application, which holds the canonical state that is committed at the end of each block. This store is not cached, meaning it is not used to update the application's volatile (un-committed) states. The CommitMultiStore is a multi-store, meaning a store of stores. Each module of the application uses one or multiple KVStores in the multi-store to persist their subset of the state.
  • Database: The db is used by the CommitMultiStore to handle data persistence.
  • gRPC Query Router: The grpcQueryRouter facilitates the routing of gRPC queries to the appropriate module for it to be processed. These queries are not ABCI messages themselves, but they are relayed to the relevant module's gRPC Query service.
  • TxDecoder (opens new window): It is used to decode raw transaction bytes relayed by the underlying Tendermint engine.
  • ParamStore: The parameter store used to get and set application consensus parameters.
  • TxHandler: This handler is used to set middlewares. Middlewares can, for instace, handle signature verification, fee payment, and other pre-message execution checks when a transaction is received. It's executed during CheckTx/RecheckTx and DeliverTx.
  • InitChainer, BeginBlocker and EndBlocker: These are the functions executed when the application receives the InitChain, BeginBlock and EndBlock ABCI messages from the underlying Tendermint engine.

Then, parameters used to define volatile states (i.e. cached states):

  • checkState: This state is updated during CheckTx, and reset on Commit.
  • deliverState: This state is updated during DeliverTx, and set to nil on Commit and gets re-initialized on BeginBlock.

Finally, a few more important parameters:

  • voteInfos: This parameter carries the list of validators whose precommit is missing, either because they did not vote or because the proposer did not include their vote. This information is carried by the Context and can be used by the application for various things like punishing absent validators.
  • minGasPrices: This parameter defines the minimum gas prices accepted by the node. This is a local parameter, meaning each full-node can set a different minGasPrices. It is used in the TxHandler during CheckTx, mainly as a spam protection mechanism. The transaction enters the mempool (opens new window) only if the gas prices of the transaction are greater than one of the minimum gas price in minGasPrices (e.g. if minGasPrices == 1uatom,1photon, the gas-price of the transaction must be greater than 1uatom OR 1photon).
  • appVersion: Version of the application. It is set in the application's constructor function.

# Constructor

Copy func NewBaseApp( name string, logger log.Logger, db dbm.DB, txDecoder sdk.TxDecoder, options ...func(*BaseApp), ) *BaseApp { // ... }

The BaseApp constructor function is pretty straightforward. The only thing worth noting is the possibility to provide additional options (opens new window) to the BaseApp, which will execute them in order. The options are generally setter functions for important parameters, like SetPruning() to set pruning options or SetMinGasPrices() to set the node's min-gas-prices.

Naturally, developers can add additional options based on their application's needs.

# State Updates

The BaseApp maintains two primary volatile states and a root or main state. The main state is the canonical state of the application and the volatile states, checkState and deliverState, are used to handle state transitions in-between the main state made during Commit.

Internally, there is only a single CommitMultiStore which we refer to as the main or root state. From this root state, we derive two volatile states by using a mechanism called store branching (performed by CacheWrap function). The types can be illustrated as follows:

Types

# InitChain State Updates

During InitChain, the two volatile states, checkState and deliverState are set by branching the root CommitMultiStore. Any subsequent reads and writes happen on branched versions of the CommitMultiStore. To avoid unnecessary roundtrip to the main state, all reads to the branched store are cached.

InitChain

# CheckTx State Updates

During CheckTx, the checkState, which is based off of the last committed state from the root store, is used for any reads and writes. Here we only execute the wired middlewares CheckTx and verify a service router exists for every message in the transaction. Note, that if a middleware's CheckTx fails, the state transitions won't be reflected in the checkState -- i.e. checkState is only updated on success.

CheckTx

# BeginBlock State Updates

During BeginBlock, the deliverState is set for use in subsequent DeliverTx ABCI messages. The deliverState is based off of the last committed state from the root store and is branched. Note, the deliverState is set to nil on Commit.

BeginBlock

# DeliverTx State Updates

The state flow for DeliverTx is nearly identical to CheckTx except state transitions occur on the deliverState and messages in a transaction are executed. Similarly to CheckTx, state transitions occur on a doubly branched state -- deliverState. Successful message execution results in writes being committed to deliverState. Note, if message execution fails, state transitions from the middlewares are persisted.

DeliverTx

# Commit State Updates

During Commit all the state transitions that occurred in the deliverState are finally written to the root CommitMultiStore which in turn is committed to disk and results in a new application root hash. These state transitions are now considered final. Finally, the checkState is set to the newly committed state and deliverState is set to nil to be reset on BeginBlock.

Commit

# ParamStore

During InitChain, the RequestInitChain provides ConsensusParams which contains parameters related to block execution such as maximum gas and size in addition to evidence parameters. If these parameters are non-nil, they are set in the BaseApp's ParamStore. Behind the scenes, the ParamStore is actually managed by an x/params module Subspace. This allows the parameters to be tweaked via on-chain governance.

# Service Routers

When messages and queries are received by the application, they must be routed to the appropriate module in order to be processed. Routing is done via BaseApp, with queryRouter for queries and grpcQueryRouter for gRPC queries.

# gRPC Query Router

Queries need to be routed to the appropriate module's Query service. To do so, BaseApp holds a grpcQueryRouter, which maps modules' fully-qualified service methods (string, defined in their Protobuf Query gRPC) to their QueryServer implementation. The grpcQueryRouter is called during the initial stages of query processing, which can be either by directly sending a gRPC query to the gRPC endpoint, or via the Query ABCI message on the Tendermint RPC endpoint.

The grpcQueryRouter is initialized with all the query routes using the application's module manager (via the RegisterServices method), which itself is initialized with all the application's modules in the application's constructor.

# Main ABCI Messages

The Application-Blockchain Interface (opens new window) (ABCI) is a generic interface that connects a state-machine with a consensus engine to form a functional full-node. It can be wrapped in any language, and needs to be implemented by each application-specific blockchain built on top of an ABCI-compatible consensus engine like Tendermint.

The consensus engine handles two main tasks:

  • The networking logic, which mainly consists in gossiping block parts, transactions and consensus votes.
  • The consensus logic, which results in the deterministic ordering of transactions in the form of blocks.

It is not the role of the consensus engine to define the state or the validity of transactions. Generally, transactions are handled by the consensus engine in the form of []bytes, and relayed to the application via the ABCI to be decoded and processed. At keys moments in the networking and consensus processes (e.g. beginning of a block, commit of a block, reception of an unconfirmed transaction, ...), the consensus engine emits ABCI messages for the state-machine to act on.

Developers building on top of the Cosmos SDK need not implement the ABCI themselves, as BaseApp comes with a built-in implementation of the interface. Let us go through the main ABCI messages that BaseApp implements: CheckTx and DeliverTx

# CheckTx

CheckTx is sent by the underlying consensus engine when a new unconfirmed (i.e. not yet included in a valid block) transaction is received by a full-node. The role of CheckTx is to guard the full-node's mempool (where unconfirmed transactions are stored until they are included in a block) from spam transactions. Unconfirmed transactions are relayed to peers only if they pass CheckTx.

CheckTx() can perform both stateful and stateless checks, but developers should strive to make the checks lightweight because gas fees are not charged for the resources (CPU, data load...) used during the CheckTx.

In the Cosmos SDK, after decoding transactions, CheckTx() is implemented to do the following checks:

  1. Extract the sdk.Msgs from the transaction.
  2. Perform stateless checks by calling ValidateBasic() on each of the sdk.Msgs. This is done first, as stateless checks are less computationally expensive than stateful checks. If ValidateBasic() fail, CheckTx returns before running stateful checks, which saves resources.
  3. Perform non-module related stateful checks on the account. This step is mainly about checking that the sdk.Msg signatures are valid, that enough fees are provided and that the sending account has enough funds to pay for said fees. Note that no precise gas counting occurs here, as sdk.Msgs are not processed. Usually, the middleware will check that the gas provided with the transaction is superior to a minimum reference gas amount based on the raw transaction size, in order to avoid spam with transactions that provide 0 gas.

CheckTx does not process sdk.Msgs - they only need to be processed when the canonical state need to be updated, which happens during DeliverTx.

Steps 2. and 3. are performed by the middlewares' CheckTx(). During each step of CheckTx(), a special volatile state called checkState is updated. This state is used to keep track of the temporary changes triggered by the CheckTx() calls of each transaction without modifying the main canonical state . For example, when a transaction goes through CheckTx(), the transaction's fees are deducted from the sender's account in checkState. If a second transaction is received from the same account before the first is processed, and the account has consumed all its funds in checkState during the first transaction, the second transaction will fail CheckTx() and be rejected. In any case, the sender's account will not actually pay the fees until the transaction is actually included in a block, because checkState never gets committed to the main state. The checkState is reset to the latest state of the main state each time a blocks gets committed.

CheckTx returns a response to the underlying consensus engine of type abci.ResponseCheckTx (opens new window). The response contains:

  • Code (uint32): Response Code. 0 if successful.
  • Data ([]byte): Result bytes, if any.
  • Log (string): The output of the application's logger. May be non-deterministic.
  • Info (string): Additional information. May be non-deterministic.
  • GasWanted (int64): Amount of gas requested for transaction. It is provided by users when they generate the transaction.
  • GasUsed (int64): Amount of gas consumed by transaction. During CheckTx, this value is computed by multiplying the standard cost of a transaction byte by the size of the raw transaction. Next is an example: Copy ctx.GasMeter().ConsumeGas(params.TxSizeCostPerByte*sdk.Gas(len(ctx.TxBytes())), "txSize")
  • Events ([]cmn.KVPair): Key-Value tags for filtering and indexing transactions (eg. by account). See events for more.
  • Codespace (string): Namespace for the Code.

# RecheckTx

After Commit, CheckTx is run again on all transactions that remain in the node's local mempool excluding the transactions that are included in the block. To prevent the mempool from rechecking all transactions every time a block is committed, the configuration option mempool.recheck=false can be set. As of Tendermint v0.32.1, an additional Type parameter is made available to the CheckTx function that indicates whether an incoming transaction is new (CheckTxType_New), or a recheck (CheckTxType_Recheck). This allows certain checks like signature verification can be skipped during CheckTxType_Recheck.

# DeliverTx

When the underlying consensus engine receives a block proposal, each transaction in the block needs to be processed by the application. To that end, the underlying consensus engine sends a DeliverTx message to the application for each transaction in a sequential order.

Before the first transaction of a given block is processed, a volatile state called deliverState is initialized during BeginBlock. This state is updated each time a transaction is processed via DeliverTx, and committed to the main state when the block is committed, after what it is set to nil.

DeliverTx performs the exact same steps as CheckTx, with a little caveat at step 3 and the addition of a fifth step:

  1. The GasTxMiddleware does not check that the transaction's gas-prices is sufficient. That is because the min-gas-prices value gas-prices is checked against is local to the node, and therefore what is enough for one full-node might not be for another. This means that the proposer can potentially include transactions for free, although they are not incentivised to do so, as they earn a bonus on the total fee of the block they propose.
  2. For each sdk.Msg in the transaction, route to the appropriate module's Protobuf Msg service. Additional stateful checks are performed, and the branched multistore held in deliverState's context is updated by the module's keeper. If the Msg service returns successfully, the branched multistore held in context is written to deliverState CacheMultiStore.

During the additional fifth step outlined in (2), each read/write to the store increases the value of GasConsumed. You can find the default cost of each operation:

Copy // KVGasConfig returns a default gas config for KVStores. func KVGasConfig() GasConfig { return GasConfig{ HasCost: 1000, DeleteCost: 1000, ReadCostFlat: 1000, ReadCostPerByte: 3, WriteCostFlat: 2000, WriteCostPerByte: 30, IterNextCostFlat: 30, } }

At any point, if GasConsumed > GasWanted, the function returns with Code != 0 and DeliverTx fails.

DeliverTx returns a response to the underlying consensus engine of type abci.ResponseDeliverTx (opens new window). The response contains:

  • Code (uint32): Response Code. 0 if successful.
  • Data ([]byte): Result bytes, if any.
  • Log (string): The output of the application's logger. May be non-deterministic.
  • Info (string): Additional information. May be non-deterministic.
  • GasWanted (int64): Amount of gas requested for transaction. It is provided by users when they generate the transaction.
  • GasUsed (int64): Amount of gas consumed by transaction. During DeliverTx, this value is computed by multiplying the standard cost of a transaction byte by the size of the raw transaction, and by adding gas each time a read/write to the store occurs.
  • Events ([]cmn.KVPair): Key-Value tags for filtering and indexing transactions (eg. by account). See events for more.
  • Codespace (string): Namespace for the Code.

# Middlewares

Middlewares implement the tx.Handler interface. They are called within BaseApp CheckTx and DeliverTx, allowing to run custom logic before or after the transaction is processed. They are primarily used to authenticate the transaction before the transaction's internal messages are processed, but also to perform additional checks on the transaction itself.

Copy package tx import ( context "context" codectypes "github.com/cosmos/cosmos-sdk/codec/types" abci "github.com/tendermint/tendermint/abci/types" sdk "github.com/cosmos/cosmos-sdk/types" ) // RequestSimulateTx is the request type for the tx.Handler.RequestSimulateTx // method. type RequestSimulateTx struct { TxBytes []byte } // ResponseSimulateTx is the response type for the tx.Handler.RequestSimulateTx // method. type ResponseSimulateTx struct { GasInfo sdk.GasInfo Result *sdk.Result } // Request is the tx request type used in middlewares. // At least one of Tx or TxBytes must be set. If only TxBytes is set, then // Tx will be populated by the TxDecoderMiddleware. If only Tx is set, then // some middlewares (such as signature verification) will fail. // // In practice, the middleware stack is called from {Check,Deliver}Tx, which // only passes the TxBytes. Then, the TxDecoderMiddleware decodes the bytes // into the Tx field. type Request struct { Tx sdk.Tx TxBytes []byte } // Response is the tx response type used in middlewares. type Response struct { GasWanted uint64 GasUsed uint64 // MsgResponses is an array containing each Msg service handler's response // type, packed in an Any. This will get proto-serialized into the `Data` field // in the ABCI Check/DeliverTx responses. MsgResponses []*codectypes.Any Log string Events []abci.Event } // RequestCheckTx is the additional request type used in middlewares CheckTx // method. type RequestCheckTx struct { Type abci.CheckTxType } // RequestCheckTx is the additional response type used in middlewares CheckTx // method. type ResponseCheckTx struct { Priority int64 } // TxHandler defines the baseapp's CheckTx, DeliverTx and Simulate respective // handlers. It is designed as a middleware stack. type Handler interface { CheckTx(ctx context.Context, req Request, checkReq RequestCheckTx) (Response, ResponseCheckTx, error) DeliverTx(ctx context.Context, req Request) (Response, error) SimulateTx(ctx context.Context, req Request) (Response, error) } // TxMiddleware defines one layer of the TxHandler middleware stack. type Middleware func(Handler) Handler

Middlewares are theoretically optional, but still a very important component of public blockchain networks. They have 3 primary purposes:

  • Be a primary line of defense against spam and second line of defense (the first one being the mempool) against transaction replay with fees deduction and sequence checking.
  • Perform preliminary stateful validity checks like ensuring signatures are valid or that the sender has enough funds to pay for fees.
  • Play a role in the incentivisation of stakeholders via the collection of transaction fees.

BaseApp holds a txHandler as parameter that is initialized in the application's constructor. The most widely used middlewares are the auth module middlewares (opens new window).

NewDefaultTxHandler groups a number a middlewares that are commonly used:

Copy package middleware import ( sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/types/tx" "github.com/cosmos/cosmos-sdk/types/tx/signing" authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing" "github.com/cosmos/cosmos-sdk/x/auth/types" ) // ComposeMiddlewares compose multiple middlewares on top of a tx.Handler. The // middleware order in the variadic arguments is from outer to inner. // // Example: Given a base tx.Handler H, and two middlewares A and B, the // middleware stack: // ``` // A.pre // B.pre // H // B.post // A.post // ``` // is created by calling `ComposeMiddlewares(H, A, B)`. func ComposeMiddlewares(txHandler tx.Handler, middlewares ...tx.Middleware) tx.Handler { for i := len(middlewares) - 1; i >= 0; i-- { txHandler = middlewares[i](txHandler) } return txHandler } type TxHandlerOptions struct { Debug bool // TxDecoder is used to decode the raw tx bytes into a sdk.Tx. TxDecoder sdk.TxDecoder // IndexEvents defines the set of events in the form {eventType}.{attributeKey}, // which informs Tendermint what to index. If empty, all events will be indexed. IndexEvents map[string]struct{} LegacyRouter sdk.Router MsgServiceRouter *MsgServiceRouter AccountKeeper AccountKeeper BankKeeper types.BankKeeper FeegrantKeeper FeegrantKeeper SignModeHandler authsigning.SignModeHandler SigGasConsumer func(meter sdk.GasMeter, sig signing.SignatureV2, params types.Params) error ExtensionOptionChecker ExtensionOptionChecker TxFeeChecker TxFeeChecker } // NewDefaultTxHandler defines a TxHandler middleware stacks that should work // for most applications. func NewDefaultTxHandler(options TxHandlerOptions) (tx.Handler, error) { if options.TxDecoder == nil { return nil, sdkerrors.Wrap(sdkerrors.ErrLogic, "txDecoder is required for middlewares") } if options.AccountKeeper == nil { return nil, sdkerrors.Wrap(sdkerrors.ErrLogic, "account keeper is required for middlewares") } if options.BankKeeper == nil { return nil, sdkerrors.Wrap(sdkerrors.ErrLogic, "bank keeper is required for middlewares") } if options.SignModeHandler == nil { return nil, sdkerrors.Wrap(sdkerrors.ErrLogic, "sign mode handler is required for middlewares") } var sigGasConsumer = options.SigGasConsumer if sigGasConsumer == nil { sigGasConsumer = DefaultSigVerificationGasConsumer } var extensionOptionChecker = options.ExtensionOptionChecker if extensionOptionChecker == nil { extensionOptionChecker = rejectExtensionOption } var txFeeChecker = options.TxFeeChecker if txFeeChecker == nil { txFeeChecker = checkTxFeeWithValidatorMinGasPrices } return ComposeMiddlewares( NewRunMsgsTxHandler(options.MsgServiceRouter, options.LegacyRouter), NewTxDecoderMiddleware(options.TxDecoder), // Set a new GasMeter on sdk.Context. // // Make sure the Gas middleware is outside of all other middlewares // that reads the GasMeter. In our case, the Recovery middleware reads // the GasMeter to populate GasInfo. GasTxMiddleware, // Recover from panics. Panics outside of this middleware won't be // caught, be careful! RecoveryTxMiddleware, // Choose which events to index in Tendermint. Make sure no events are // emitted outside of this middleware. NewIndexEventsTxMiddleware(options.IndexEvents), // Reject all extension options other than the ones needed by the feemarket. NewExtensionOptionsMiddleware(extensionOptionChecker), ValidateBasicMiddleware, TxTimeoutHeightMiddleware, ValidateMemoMiddleware(options.AccountKeeper), ConsumeTxSizeGasMiddleware(options.AccountKeeper), // No gas should be consumed in any middleware above in a "post" handler part. See // ComposeMiddlewares godoc for details. // `DeductFeeMiddleware` and `IncrementSequenceMiddleware` should be put outside of `WithBranchedStore` middleware, // so their storage writes are not discarded when tx fails. DeductFeeMiddleware(options.AccountKeeper, options.BankKeeper, options.FeegrantKeeper, txFeeChecker), SetPubKeyMiddleware(options.AccountKeeper), ValidateSigCountMiddleware(options.AccountKeeper), SigGasConsumeMiddleware(options.AccountKeeper, sigGasConsumer), SigVerificationMiddleware(options.AccountKeeper, options.SignModeHandler), IncrementSequenceMiddleware(options.AccountKeeper), // Creates a new MultiStore branch, discards downstream writes if the downstream returns error. // These kinds of middlewares should be put under this: // - Could return error after messages executed succesfully. // - Storage writes should be discarded together when tx failed. WithBranchedStore, // Consume block gas. All middlewares whose gas consumption after their `next` handler // should be accounted for, should go below this middleware. ConsumeBlockGasMiddleware, NewTipMiddleware(options.BankKeeper), ), nil }

Click here for more on these middlewares.

# RunMsgsTxHandler

RunMsgsTxHandler is a middleware that runs the sdk.Msgs in the transaction. When being called from DeliverTx or SimulateTx, it retrieves the sdk.Msg's fully-qualified type name, by checking the type_url of the Protobuf Any representing the sdk.Msg. Then, using its msgServiceRouter, it checks for the existence of Msg service method related to that type_url. At this point the Msg service RPC is executed, before returning.

Note: When its CheckTx method is called, the RunMsgsTxHandler does not do anything as messages are not run during CheckTx.

# Other ABCI Messages

# InitChain

The InitChain ABCI message (opens new window) is sent from the underlying Tendermint engine when the chain is first started. It is mainly used to initialize parameters and state like:

Finally, the InitChain(req abci.RequestInitChain) method of BaseApp calls the initChainer() of the application in order to initialize the main state of the application from the genesis file and, if defined, call the InitGenesis function of each of the application's modules.

# BeginBlock

The BeginBlock ABCI message (opens new window) is sent from the underlying Tendermint engine when a block proposal created by the correct proposer is received, before DeliverTx is run for each transaction in the block. It allows developers to have logic be executed at the beginning of each block. In the Cosmos SDK, the BeginBlock(req abci.RequestBeginBlock) method does the following:

  • Initialize deliverState with the latest header using the req abci.RequestBeginBlock passed as parameter via the setDeliverState function. Copy // setDeliverState sets the BaseApp's deliverState with a cache-wrapped multi-store // (i.e. a CacheMultiStore) and a new Context with the cache-wrapped multi-store, // and provided header. It is set on InitChain and BeginBlock and set to nil on // Commit. func (app *BaseApp) setDeliverState(header abci.Header) { ms := app.cms.CacheMultiStore() app.deliverState = &state{ ms: ms, ctx: sdk.NewContext(ms, header, false, app.logger), } } This function also resets the main gas meter.
  • Initialize the block gas meter with the maxGas limit. The gas consumed within the block cannot go above maxGas. This parameter is defined in the application's consensus parameters.
  • Run the application's beginBlocker(), which mainly runs the BeginBlocker() method of each of the application's modules.
  • Set the VoteInfos (opens new window) of the application, i.e. the list of validators whose precommit for the previous block was included by the proposer of the current block. This information is carried into the Context so that it can be used during DeliverTx and EndBlock.

# EndBlock

The EndBlock ABCI message (opens new window) is sent from the underlying Tendermint engine after DeliverTx as been run for each transaction in the block. It allows developers to have logic be executed at the end of each block. In the Cosmos SDK, the bulk EndBlock(req abci.RequestEndBlock) method is to run the application's EndBlocker(), which mainly runs the EndBlocker() method of each of the application's modules.

# Commit

The Commit ABCI message (opens new window) is sent from the underlying Tendermint engine after the full-node has received precommits from 2/3+ of validators (weighted by voting power). On the BaseApp end, the Commit(res abci.ResponseCommit) function is implemented to commit all the valid state transitions that occurred during BeginBlock, DeliverTx and EndBlock and to reset state for the next block.

To commit state-transitions, the Commit function calls the Write() function on deliverState.ms, where deliverState.ms is a branched multistore of the main store app.cms. Then, the Commit function sets checkState to the latest header (obtained from deliverState.ctx.BlockHeader) and deliverState to nil.

Finally, Commit returns the hash of the commitment of app.cms back to the underlying consensus engine. This hash is used as a reference in the header of the next block.

# Info

The Info ABCI message (opens new window) is a simple query from the underlying consensus engine, notably used to sync the latter with the application during a handshake that happens on startup. When called, the Info(res abci.ResponseInfo) function from BaseApp will return the application's name, version and the hash of the last commit of app.cms.

# Query

The Query ABCI message (opens new window) is used to serve queries received from the underlying consensus engine, including queries received via RPC like Tendermint RPC. It used to be the main entrypoint to build interfaces with the application, but with the introduction of gRPC queries in Cosmos SDK v0.40, its usage is more limited. The application must respect a few rules when implementing the Query method, which are outlined here (opens new window).

Each Tendermint query comes with a path, which is a string which denotes what to query. If the path matches a gRPC fully-qualified service method, then BaseApp will defer the query to the grpcQueryRouter and let it handle it like explained above. Otherwise, the path represents a query that is not (yet) handled by the gRPC router. BaseApp splits the path string with the / delimiter. By convention, the first element of the split string (split[0]) contains the category of query (app, p2p, store or custom ). The BaseApp implementation of the Query(req abci.RequestQuery) method is a simple dispatcher serving these 4 main categories of queries:

  • Application-related queries like querying the application's version, which are served via the handleQueryApp method.
  • Direct queries to the multistore, which are served by the handlerQueryStore method. These direct queries are different from custom queries which go through app.queryRouter, and are mainly used by third-party service provider like block explorers.
  • P2P queries, which are served via the handleQueryP2P method. These queries return either app.addrPeerFilter or app.ipPeerFilter that contain the list of peers filtered by address or IP respectively. These lists are first initialized via options in BaseApp's constructor.
  • Custom queries, which encompass legacy queries (before the introduction of gRPC queries), are served via the handleQueryCustom method. The handleQueryCustom branches the multistore before using the queryRoute obtained from app.queryRouter to map the query to the appropriate module's legacy querier.

# Next

Learn more about transactions