# Anatomy of an SDK Application
This document describes the core parts of a Cosmos SDK application. Throughout the document, a placeholder application named
app will be used.
# Node Client
The Daemon, or Full-Node Client, is the core process of an SDK-based blockchain. Participants in the network run this process to initialize their state-machine, connect with other full-nodes and update their state-machine as new blocks come in.
The blockchain full-node presents itself as a binary, generally suffixed by
-d for "daemon" (e.g.
gaia). This binary is built by running a simple
main.go function placed in
./cmd/appd/. This operation usually happens through the Makefile.
Once the main binary is built, the node can be started by running the
start command. This command function primarily does three things:
- Create an instance of the state-machine defined in
- Initialize the state-machine with the latest known state, extracted from the
dbstored in the
~/.app/datafolder. At this point, the state-machine is at height
- Create and start a new Tendermint instance. Among other things, the node will perform a handshake with its peers. It will get the latest
blockHeightfrom them, and replay blocks to sync to this height if it is greater than the local
0, the node is starting from genesis and Tendermint sends an
InitChainmessage via the ABCI to the
app, which triggers the
# Core Application File
In general, the core of the state-machine is defined in a file called
app.go. It mainly contains the type definition of the application and functions to create and initialize it.
# Type Definition of the Application
The first thing defined in
app.go is the
type of the application. It is generally comprised of the following parts:
- A reference to
baseapp. The custom application defined in
app.gois an extension of
baseapp. When a transaction is relayed by Tendermint to the application,
baseapp's methods to route them to the appropriate module.
baseappimplements most of the core logic for the application, including all the ABCI methods (opens new window) and the routing logic.
- A list of store keys. The store, which contains the entire state, is implemented as a
multistore(i.e. a store of stores) in the Cosmos SDK. Each module uses one or multiple stores in the multistore to persist their part of the state. These stores can be accessed with specific keys that are declared in the
apptype. These keys, along with the
keepers, are at the heart of the object-capabilities model of the Cosmos SDK.
- A list of module's
keepers. Each module defines an abstraction called
keeper, which handles reads and writes for this module's store(s). The
keeper's methods of one module can be called from other modules (if authorized), which is why they are declared in the application's type and exported as interfaces to other modules so that the latter can only access the authorized functions.
- A reference to an
appCodec. The application's
appCodecis used to serialize and deserialize data structures in order to store them, as stores can only persist
bytes. The default codec is Protocol Buffers.
- A reference to a
legacyAminocodec. Some parts of the SDK have not been migrated to use the
appCodecabove, and are still hardcoded to use Amino. Other parts explicity use Amino for backwards compatibility. For these reasons, the application still holds a reference to the legacy Amino codec. Please note that the Amino codec will be removed from the SDK in the upcoming releases.
- A reference to a module manager and a basic module manager. The module manager is an object that contains a list of the application's module. It facilitates operations related to these modules, like registering their
Msgservice and gRPC
Queryservice, or setting the order of execution between modules for various functions like
See an example of application type definition from
simapp, the SDK's own app used for demo and testing purposes:
# Constructor Function
This function constructs a new application of the type defined in the section above. It must fulfill the
AppCreator signature in order to be used in the
start command of the application's daemon command.
Here are the main actions performed by this function:
- Instantiate a new
codecand initialize the
codecof each of the application's module using the basic manager
- Instantiate a new application with a reference to a
baseappinstance, a codec and all the appropriate store keys.
- Instantiate all the
keepers defined in the application's
NewKeeperfunction of each of the application's modules. Note that
keepersmust be instantiated in the correct order, as the
NewKeeperof one module might require a reference to another module's
- Instantiate the application's module manager with the
AppModuleobject of each of the application's modules.
- With the module manager, initialize the application's
Msgroutes and legacy query routes. When a transaction is relayed to the application by Tendermint via the ABCI, it is routed to the appropriate module's
Msgservice using the routes defined here. Likewise, when a gRPC query request is received by the application, it is routed to the appropriate module's
gRPC query serviceusing the gRPC routes defined here. The SDK still supports legacy
Msgs and legacy Tendermint queries, which are routed using respectively the legacy
Msgroutes and the legacy query routes.
- With the module manager, register the application's modules' invariants. Invariants are variables (e.g. total supply of a token) that are evaluated at the end of each block. The process of checking invariants is done via a special module called the
InvariantsRegistry. The value of the invariant should be equal to a predicted value defined in the module. Should the value be different than the predicted one, special logic defined in the invariant registry will be triggered (usually the chain is halted). This is useful to make sure no critical bug goes unnoticed and produces long-lasting effects that would be hard to fix.
- With the module manager, set the order of execution between the
EndBlockerfunctions of each of the application's modules. Note that not all modules implement these functions.
- Set the remainer of application's parameters:
InitChainer: used to initialize the application when it is first started.
EndBlocker: called at the beginning and the end of every block).
anteHandler: used to handle fees and signature verification.
- Mount the stores.
- Return the application.
Note that this function only creates an instance of the app, while the actual state is either carried over from the
~/.app/data folder if the node is restarted, or generated from the genesis file if the node is started for the first time.
See an example of application constructor from
InitChainer is a function that initializes the state of the application from a genesis file (i.e. token balances of genesis accounts). It is called when the application receives the
InitChain message from the Tendermint engine, which happens when the node is started at
appBlockHeight == 0 (i.e. on genesis). The application must set the
InitChainer in its constructor via the
SetInitChainer (opens new window) method.
In general, the
InitChainer is mostly composed of the
InitGenesis function of each of the application's modules. This is done by calling the
InitGenesis function of the module manager, which in turn will call the
InitGenesis function of each of the modules it contains. Note that the order in which the modules'
InitGenesis functions must be called has to be set in the module manager using the module manager's
SetOrderInitGenesis method. This is done in the application's constructor, and the
SetOrderInitGenesis has to be called before the
See an example of an
# BeginBlocker and EndBlocker
The SDK offers developers the possibility to implement automatic execution of code as part of their application. This is implemented through two function called
EndBlocker. They are called when the application receives respectively the
EndBlock messages from the Tendermint engine, which happens at the beginning and at the end of each block. The application must set the
EndBlocker in its constructor via the
SetBeginBlocker (opens new window) and
SetEndBlocker (opens new window) methods.
In general, the
EndBlocker functions are mostly composed of the
EndBlock functions of each of the application's modules. This is done by calling the
EndBlock functions of the module manager, which in turn will call the
EndBlock functions of each of the modules it contains. Note that the order in which the modules'
EndBlock functions must be called has to be set in the module manager using the
SetOrderEndBlock methods respectively. This is done via the module manager in the application's constructor, and the
SetOrderEndBlock methods have to be called before the
As a sidenote, it is important to remember that application-specific blockchains are deterministic. Developers must be careful not to introduce non-determinism in
EndBlocker, and must also be careful not to make them too computationally expensive, as gas does not constrain the cost of
See an example of
EndBlocker functions from
# Register Codec
EncodingConfig structure is the last important part of the
app.go file. The goal of this structure is to define the codecs that will be used throughout the app.
Here are descriptions of what each of the four fields means:
InterfaceRegistryis used by the Protobuf codec to handle interfaces that are encoded and decoded (we also say "unpacked") using
google.protobuf.Any(opens new window).
Anycould be thought as a struct that contains a
type_url(name of a concrete type implementing the interface) and a
value(its encoded bytes).
InterfaceRegistryprovides a mechanism for registering interfaces and implementations that can be safely unpacked from
Any. Each of the application's modules implements the
RegisterInterfacesmethod that can be used to register the module's own interfaces and implementations.
- You can read more about Any in ADR-19.
- To go more into details, the SDK uses an implementation of the Protobuf specification called
gogoprotobuf(opens new window). By default, the gogo protobuf implementation of
Any(opens new window) uses global type registration (opens new window) to decode values packed in
Anyinto concrete Go types. This introduces a vulnerability where any malicious module in the dependency tree could registry a type with the global protobuf registry and cause it to be loaded and unmarshaled by a transaction that referenced it in the
type_urlfield. For more information, please refer to ADR-019.
Marshaler: the default codec used throughout the SDK. It is composed of a
BinaryCodecused to encode and decode state, and a
JSONCodecused to output data to the users (for example in the CLI). By default, the SDK uses Protobuf as
TxConfigdefines an interface a client can utilize to generate an application-defined concrete transaction type. Currently, the SDK handles two transaction types:
SIGN_MODE_DIRECT(which uses Protobuf binary as over-the-wire encoding) and
SIGN_MODE_LEGACY_AMINO_JSON(which depends on Amino). Read more about transactions here.
Amino: Some legacy parts of the SDK still use Amino for backwards-compatibility. Each module exposes a
RegisterLegacyAminomethod to register the module's specific types within Amino. This
Aminocodec should not be used by app developers anymore, and will be removed in future releases.
The SDK exposes a
MakeTestEncodingConfig function used to create a
EncodingConfig for the app constructor (
NewApp). It uses Protobuf as a default
NOTE: this function is marked deprecated and should only be used to create an app or in tests. We are working on refactoring codec management in a post Stargate release.
See an example of a
Modules are the heart and soul of SDK applications. They can be considered as state-machines within the state-machine. When a transaction is relayed from the underlying Tendermint engine via the ABCI to the application, it is routed by
baseapp to the appropriate module in order to be processed. This paradigm enables developers to easily build complex state-machines, as most of the modules they need often already exist. For developers, most of the work involved in building an SDK application revolves around building custom modules required by their application that do not exist yet, and integrating them with modules that do already exist into one coherent application. In the application directory, the standard practice is to store modules in the
x/ folder (not to be confused with the SDK's
x/ folder, which contains already-built modules).
# Application Module Interface
Modules must implement interfaces defined in the Cosmos SDK,
AppModule. The former implements basic non-dependant elements of the module, such as the
codec, while the latter handles the bulk of the module methods (including methods that require references to other modules'
keepers). Both the
AppModuleBasic types are defined in a file called
AppModule exposes a collection of useful methods on the module that facilitates the composition of modules into a coherent application. These methods are are called from the
module manager(../building-modules/module-manager.md#manager), which manages the application's collection of modules.
Each module defines two Protobuf services (opens new window): one
Msg service to handle messages, and one gRPC
Query service to handle queries. If we consider the module as a state-machine, then a
Msg service is a set of state transition RPC methods.
Msg service method is 1:1 related to a Protobuf request type, which must implement
sdk.Msgs are bundled in transactions, and each transaction contains one or multiple messages.
When a valid block of transactions is received by the full-node, Tendermint relays each one to the application via
DeliverTx (opens new window). Then, the application handles the transaction:
- Upon receiving the transaction, the application first unmarshalls it from
- Then, it verifies a few things about the transaction like fee payment and signatures before extracting the
Msg(s) contained in the transaction.
sdk.Msgs are encoded using Protobuf
Anys. By analyzing each
sdk.Msgto the corresponding module's
- If the message is successfully processed, the state is updated.
For a more details look at a transaction lifecycle.
Module developers create custom
Msg services when they build their own module. The general practice is to define the
Msg Protobuf service in a
tx.proto file. For example, the
x/bank module defines a service with two methods to transfer tokens:
Service methods use
keeper in order to update the module state.
Each module should also implement the
RegisterServices method as part of the
AppModule interface. This method should call the
RegisterMsgServer function provided by the generated Protobuf code.
Query services are introduced in the v0.40 Stargate release. They allow users to query the state using gRPC (opens new window). They are enabled by default, and can be configued under the
grpc.address fields inside
Query services are defined in the module's Protobuf definition files, specifically inside
query.proto definition file exposes a single
Query Protobuf service (opens new window). Each gRPC query endpoint corresponds to a service method, starting with the
rpc keyword, inside the
Protobuf generates a
QueryServer interface for each module, containing all the service methods. A module's
keeper then needs to implement this
QueryServer interface, by providing the concrete implementation of each service method. This concrete implementation is the handler of the corresponding gRPC query endpoint.
Finally, each module should also implement the
RegisterServices method as part of the
AppModule interface. This method should call the
RegisterQueryServer function provided by the generated Protobuf code.
Keepers are the gatekeepers of their module's store(s). To read or write in a module's store, it is mandatory to go through one of its
keeper's methods. This is ensured by the object-capabilities model of the Cosmos SDK. Only objects that hold the key to a store can access it, and only the module's
keeper should hold the key(s) to the module's store(s).
Keepers are generally defined in a file called
keeper.go. It contains the
keeper's type definition and methods.
keeper type definition generally consists of:
- Key(s) to the module's store(s) in the multistore.
- Reference to other module's
keepers. Only needed if the
keeperneeds to access other module's store(s) (either to read or write from them).
- A reference to the application's codec. The
keeperneeds it to marshal structs before storing them, or to unmarshal them when it retrieves them, because stores only accept
Along with the type definition, the next important component of the
keeper.go file is the
keeper's constructor function,
NewKeeper. This function instantiates a new
keeper of the type defined above, with a
keys and potentially references to other modules'
keepers as parameters. The
NewKeeper function is called from the application's constructor. The rest of the file defines the
keeper's methods, primarily getters and setters.
# Command-Line, gRPC Services and REST Interfaces
Each module defines command-line commands, gRPC services and REST routes to be exposed to end-user via the application's interfaces. This enables end-users to create messages of the types defined in the module, or to query the subset of the state managed by the module.
Generally, the commands related to a module are defined in a folder called
client/cli in the module's folder. The CLI divides commands in two category, transactions and queries, defined in
client/cli/query.go respectively. Both commands are built on top of the Cobra Library (opens new window):
- Transactions commands let users generate new transactions so that they can be included in a block and eventually update the state. One command should be created for each message type defined in the module. The command calls the constructor of the message with the parameters provided by the end-user, and wraps it into a transaction. The SDK handles signing and the addition of other transaction metadata.
- Queries let users query the subset of the state defined by the module. Query commands forward queries to the application's query router, which routes them to the appropriate querier the
gRPC (opens new window) is a modern open source high performance RPC framework that has support in multiple languages. It is the recommended way for external clients (such as wallets, browsers and other backend services) to interact with a node.
Each module can expose gRPC endpoints, called service methods (opens new window) and are defined in the module's Protobuf
query.proto file. A service method is defined by its name, input arguments and output response. The module then needs to:
- define a
AppModuleBasicto wire the client gRPC requests to the correct handler inside the module.
- for each service method, define a corresponding handler. The handler implements the core logic necessary to serve the gRPC request, and is located in the
# gRPC-gateway REST Endpoints
Some external clients may not wish to use gRPC. The SDK provides in this case a gRPC gateway service, which exposes each gRPC service as a correspoding REST endpoint. Please refer to the grpc-gateway (opens new window) documentation to learn more.
The REST endpoints are defined in the Protobuf files, along with the gRPC services, using Protobuf annotations. Modules that want to expose REST queries should add
google.api.http annotations to their
rpc methods. By default, all REST endpoints defined in the SDK have an URL starting with the
The SDK also provides a development endpoint to generate Swagger (opens new window) definition files for these REST endpoints. This endpoint can be enabled inside the
app.toml config file, under the
# Legacy API REST Endpoints
The module's Legacy REST interface lets users generate transactions and query the state through REST calls to the application's Legacy API Service. REST routes are defined in a file
client/rest/rest.go, which is composed of:
RegisterRoutesfunction, which registers each route defined in the file. This function is called from the main application's interface for each module used within the application. The router used in the SDK is Gorilla's mux (opens new window).
- Custom request type definitions for each query or transaction creation function that needs to be exposed. These custom request types build on the base
requesttype of the Cosmos SDK:
- One handler function for each request that can be routed to the given module. These functions implement the core logic necessary to serve the request.
These Legacy API endpoints are present in the SDK for backward compatibility purposes and will be removed in the next release.
# Application Interface
Interfaces let end-users interact with full-node clients. This means querying data from the full-node or creating and sending new transactions to be relayed by the full-node and eventually included in a block.
The main interface is the Command-Line Interface. The CLI of an SDK application is built by aggregating CLI commands defined in each of the modules used by the application. The CLI of an application is the same as the daemon (e.g.
appd), and defined in a file called
appd/main.go. The file contains:
main()function, which is executed to build the
appdinterface client. This function prepares each command and adds them to the
rootCmdbefore building them. At the root of
appd, the function adds generic commands like
config, query commands, tx commands and
- Query commands are added by calling the
queryCmdfunction. This function returns a Cobra command that contains the query commands defined in each of the application's modules (passed as an array of
main()function), as well as some other lower level query commands such as block or validator queries. Query command are called by using the command
appd query [query]of the CLI.
- Transaction commands are added by calling the
txCmdfunction. Similar to
queryCmd, the function returns a Cobra command that contains the tx commands defined in each of the application's modules, as well as lower level tx commands like transaction signing or broadcasting. Tx commands are called by using the command
appd tx [tx]of the CLI.
See an example of an application's main command-line file from the nameservice tutorial (opens new window)
# Dependencies and Makefile
A patch introduced in
go-grpc v1.34.0 made gRPC incompatible with the
gogoproto library, making some gRPC queries (opens new window) panic. As such, the SDK requires that
go-grpc <=v1.33.2 is installed in your
To make sure that gRPC is working properly, it is highly recommended to add the following line in your application's
Please see issue #8392 (opens new window) for more info.
This section is optional, as developers are free to choose their dependency manager and project building method. That said, the current most used framework for versioning control is
go.mod (opens new window). It ensures each of the libraries used throughout the application are imported with the correct version. See an example from the nameservice tutorial (opens new window):
For building the application, a Makefile (opens new window) is generally used. The Makefile primarily ensures that the
go.mod is run before building the two entrypoints to the application,
appd. See an example of Makefile from the nameservice tutorial (opens new window)
Learn more about the Lifecycle of a transaction