# Modules
This section tackles modules. Taking a look at the following sections is recommended to better understand modules in the Cosmos SDK:
You can find a code example illustrating module creation and introduction for your checkers blockchain at the end of this section.
Each Cosmos chain is a purpose-built blockchain. Cosmos SDK modules define the unique properties of each chain. Modules can be considered state machines within the larger state machine. They contain the storage layout or state and the state transition functions, which are the message methods.
Modules define most of the logic of Cosmos SDK applications.
When a transaction is relayed from the underlying Tendermint consensus engine, BaseApp
decomposes the Messages
contained within the transaction. BaseApp
routes messages to the appropriate module for processing. Interpretation and execution occur when the appropriate module message handler receives the message.
Developers compose together modules using the Cosmos SDK to build custom application-specific blockchains.
# Module scope
Modules include core functionality that every blockchain node needs:
- A boilerplate implementation of the Application Blockchain Interface (ABCI) that communicates with the underlying Tendermint consensus engine.
- A general-purpose data store that persists the module state called
multistore
. - A server and interfaces to facilitate interactions with the node.
Modules implement the majority of the application logic while the core attends to wiring and infrastructure concerns and enables modules to be composed into higher-order modules.
A module defines a subset of the overall state using:
- One or more keys/value stores known as
KVStore
- A subset of message types that are needed by the application and do not exist yet.
Modules also define interactions with other modules that do already exist.
Most of the work For developers involved in building a Cosmos SDK application revolves around building custom modules required by their application that do not exist yet and integrating them with modules that already exist into one coherent application. Existing modules can come either from the Cosmos SDK itself or from third-party developers. You can download them from an online module repository.
# Module components
It is a best practice to define a module in the x/moduleName
folder. For example, the module called Checkers
would go in x/checkers
. If you head over to the Cosmos SDK's base code, you can see that it also defines its modules (opens new window) in an x/
folder.
Modules implement several elements:
Msg
service to handle messages and one gRPC Query
service to handle queries# Interfaces
A module must implement three application module interfaces to be integrated with the rest of the application:
AppModuleBasic
: implements non-dependent elements of the module.AppModule
: interdependent, specialized elements of the module that are unique to the application.AppModuleGenesis
: interdependent, genesis/initialization elements of the module that establish the initial state of the blockchain at inception.
You define AppModule
and AppModuleBasic
, and their functions in your module's x/moduleName/module.go
file.
# Protobuf services
Each module defines two Protobuf services:
Msg
: a set of RPC methods related one-to-one to Protobuf request types to handle messages.Query
: gRPC query service to handle queries.
If the topic is new to you, check out an introduction to Protocol Buffers (opens new window).
# Msg
service
Regarding the Msg
service keep in mind:
- A best practice is to define the
Msg
Protobuf service in thetx.proto
file. - Each module should implement the
RegisterServices
method as part of theAppModule
interface. This lets the application know which messages and queries the module can handle. - Service methods should use a keeper, which encapsulates knowledge about the storage layout and presents methods for updating the state.
# gRPC Query
service
For the gRPC Query
service keep in mind:
- A best practice is to define the
Query
Protobuf service in thequery.proto
file. - Allows users to query the state using gRPC.
- Each gRPC endpoint corresponds to a service method, named with the
rpc
prefix inside the gRPCQuery
service. - Can be configured under the
grpc.enable
andgrpc.address
fields inapp.toml
.
Protobuf generates a QueryServer
interface containing all the service methods for each module. Modules implement this QueryServer
interface by providing the concrete implementation of each service method in separate files. These implementation methods are the handlers of the corresponding gRPC query endpoints. This division of concerns across different files makes the setup safe from a re-generation of files by Protobuf.
gRPC (opens new window) is a modern, open-source, high-performance framework that supports multiple languages. It is the recommended standard for external clients such as wallets, browsers, and backend services to interact with a node.
gRPC-Gateway REST endpoints support external clients that may not wish to use gRPC. The Cosmos SDK provides a gRPC-gateway REST endpoint for each gRPC service.
Have a look at the gRPC-Gateway documentation (opens new window) on more on the gRPC-Gateway plugin.
# Command-line commands
Each module defines commands for a command-line interface (CLI). Commands related to a module are defined in a folder called client/cli
. The CLI divides commands into two categories: transactions and queries. These are the same as those which you defined in tx.go
and query.go
respectively.
# Keeper
Keepers are the gatekeepers to the module’s store(s). It is mandatory to go through a module’s keeper to access the store(s). A keeper encapsulates the knowledge about the layout of the storage within the store and contains methods to update and inspect it. If you come from a module-view-controller (MVC) world, then it helps to think of the keeper as the controller.
Other modules may need access to a store, but other modules are also potentially malicious or poorly written. For this reason, developers need to consider who/what should have access to their module store(s). To prevent a module from randomly accessing another module at runtime, a module that needs access to another module needs to declare its intent to use another module at construction. At this point, such a module is granted a runtime key that lets it access the other module. Only modules that hold this key to a store can access the store. This is part of what is called an object-capability model.
Keepers are defined in keeper.go
. A keeper's type definition generally consists of keys to the module's own store in the multistore
, references to other modules' keepers, and a reference to the application's codec.
# Core modules
The Cosmos SDK includes a set of core modules that address common concerns with well-solved, standardized implementations. Core modules address application needs such as tokens, staking, and governance.
Core modules offer several advantages over ad-hoc solutions:
- Standardization is established early, which helps ensure good interoperability with wallets, analytics, other modules, and other Cosmos SDK applications.
- Duplication of effort is significantly reduced because application developers focus on what is unique about their application.
- Core modules are working examples of Cosmos SDK modules that provide strong hints about suggested structure, style, and best practices.
Developers create coherent applications by selecting and composing core modules first and then implementing the custom logic.
Why not explore the list of core modules and the application concerns they address (opens new window)?
# Design principles when building modules
The following design principles are of importance when building modules:
Msg
service to handle messages and one gRPC Query
service to handle queries# Recommended folder structure
These ideas are meant to be applied as suggestions. Application developers are encouraged to improve and contribute to the module structure and development design.
# Structure
A typical Cosmos SDK module can be structured as follows:
- The serializable data types and Protobuf interfaces:
{module_name}.proto
: the module's common message type definitions.event.proto
: the module's message type definitions related to events.genesis.proto
: the module's message type definitions related to the genesis state.query.proto
: the module'sQuery
service and related message type definitions.tx.proto
: the module'sMsg
service and related message type definitions.
- Then the rest of the code elements:
client/
: the module's CLI client functionality implementation and the module's integration testing suite.exported/
: the module's exported types - typically interface types. If a module relies on keepers from another module, it is expected to receive the keepers as interface contracts through theexpected_keepers.go
file (see below) to avoid a direct dependency on the module implementing the keepers. However, these interface contracts can define methods that operate on and/or return types that are specific to the module that is implementing the keepers. This is whereexported/
comes into play. The interface types that are defined inexported/
use canonical types that allow for the module to receive the keepers as interface contracts through theexpected_keepers.go
file. This pattern allows for code to remain DRY (opens new window) and also alleviates import cycle chaos.keeper/
: the module'sKeeper
andMsgServer
implementations.module/
: the module'sAppModule
andAppModuleBasic
implementations.simulation/
: the module's simulation package defines functions used by the blockchain simulator application (simapp
).spec/
: the module's specification documents outlining important concepts, state storage structure, and message and event type definitions.- The root directory includes type definitions for messages, events, and genesis state including the type definitions generated by Protocol Buffers.
abci.go
: the module'sBeginBlocker
andEndBlocker
implementations. This file is only required ifBeginBlocker
and/orEndBlocker
need to be defined.codec.go
: the module's registry methods for interface types.errors.go
: the module's sentinel errors.events.go
: the module's event types and constructors.expected_keepers.go
: the module's expected other keeper interfaces.genesis.go
: the module's genesis state methods and helper functions.keys.go
: the module's store keys and associated helper functions.msgs.go
: the module's message type definitions and associated methods.params.go
: the module's parameter type definitions and associated methods.*.pb.go
: the module's type definitions generated by Protocol Buffers as defined in the respective*.proto
files above.
# Errors
Modules are encouraged to define and register their own errors to provide better context on failed message or handler execution. Errors should be common or general errors, which can be further wrapped to provide additional specific execution context.
For more details take a look at the Cosmos SDK documentation on errors when building modules (opens new window).
# Registration
Modules should define and register their custom errors in x/{module}/errors.go
. Registration of errors is handled via the types/errors
package.
Each custom module error must provide the codespace, which is typically the module name (for example, "distribution") and is unique per module, and a uint32
code. The codespace and code together provide a globally unique Cosmos SDK error.
The only restrictions on error codes are the following:
- It must be greater than one, as a code value of one is reserved for internal errors.
- It must be unique within the module.
The Cosmos SDK provides a core set of common errors. These errors are defined in types/errors/errors.go
(opens new window).
# Wrapping
The custom module errors can be returned as their concrete type, as they already fulfill the error interface. Module errors can be wrapped to provide further context and meaning to failed execution.
Regardless of whether an error is wrapped or not, the Cosmos SDK's errors package provides an API to determine if an error is of a particular kind via Is
.
# ABCI
If a module error is registered, the Cosmos SDK errors package allows ABCI information to be extracted through the ABCIInfo
API. The package also provides ResponseCheckTx
and ResponseDeliverTx
as auxiliary APIs to automatically get CheckTx
and DeliverTx
responses from an error.
# Next up
Have a look at the code example below or head straight to the next section for an introduction to Protobuf.
Now your application is starting to take shape.
# The checkers
module
When you create your checkers blockchain application, you ought to include a majority of the standard modules like auth
, bank
, and so on. With the Cosmos SDK boilerplate in place, the checkers part of your checkers application will most likely reside in a single checkers
module. This is the module that you author.
# Game wager
Earlier the goal was to let players play with money. With the introduction of modules like bank
you can start handling that.
The initial ideas are:
- The wager amount is declared when creating a game.
- Each player is billed the amount when doing their first move, which is interpreted as "challenge accepted". The amount should not be deducted on the game creation. If the opponent rejects the game or the game times out, the first player gets refunded.
- Subsequent moves by a player do not cost anything.
- If a game ends in a win or times out on a forfeit, the winning player gets the total wager amount.
- If a game ends in a draw, then both players get back their amount.
How would this look like in terms of code? You need to add the wager to:
The game:
The message to create a game:
# Wager payment
Now you need to decide how the tokens are moved. When a player accepts a challenge, the amount is deducted from the player's balance. But where does it go? You could decide to burn the tokens and re-mint them at a later date, but this would make the total supply fluctuate wildly for no apparent benefit.
It is possible to transfer from a player to a module. The module acts as the escrow account for all games. So when playing for the first time, a player would:
Notice how "stake"
identifies the likely name of the base token of your application, the token that is used with the consensus. Conversely, when paying a winner you would have:
Notice how:
- It is a standard error when the player cannot pay, which is easily fixed by the player.
- It is a panic (an internal error) when the escrow account cannot pay, because if the escrow cannot pay it means there is a logic problem somewhere.
If you want to go beyond these code samples and instead see more in detail how to define all this, head to My Own Chain.