Cosmos SDK modules need to implement the AppModule
interfaces, in order to be managed by the application's module manager. The module manager plays an important role in message
and query
routing, and allows application developers to set the order of execution of a variety of functions like BeginBlocker
and EndBlocker
. {synopsis}
- Introduction to SDK Modules {prereq}
Application module interfaces exist to facilitate the composition of modules together to form a functional SDK application. There are 3 main application module interfaces:
AppModuleBasic
for independent module functionalities.AppModule
for inter-dependent module functionalities (except genesis-related functionalities).AppModuleGenesis
for inter-dependent genesis-related module functionalities.
The AppModuleBasic
interface exists to define independent methods of the module, i.e. those that do not depend on other modules in the application. This allows for the construction of the basic application structure early in the application definition, generally in the init()
function of the main application file.
The AppModule
interface exists to define inter-dependent module methods. Many modules need to interract with other modules, typically through keeper
s, which means there is a need for an interface where modules list their keeper
s and other methods that require a reference to another module's object. AppModule
interface also enables the module manager to set the order of execution between module's methods like BeginBlock
and EndBlock
, which is important in cases where the order of execution between modules matters in the context of the application.
Lastly the interface for genesis functionality AppModuleGenesis
is separated out from full module functionality AppModule
so that modules which
are only used for genesis can take advantage of the Module
patterns without having to define many placeholder functions.
The AppModuleBasic
interface defines the independent methods modules need to implement.
Let us go through the methods:
Name()
: Returns the name of the module as astring
.RegisterLegacyAminoCodec(*codec.LegacyAmino)
: Registers theamino
codec for the module, which is used to marshal and unmarshal structs to/from[]byte
in order to persist them in the module'sKVStore
.RegisterInterfaces(codectypes.InterfaceRegistry)
: Registers a module's interface types and their concrete implementations asproto.Message
.DefaultGenesis(codec.JSONCodec)
: Returns a defaultGenesisState
for the module, marshalled tojson.RawMessage
. The defaultGenesisState
need to be defined by the module developer and is primarily used for testing.ValidateGenesis(codec.JSONCodec, client.TxEncodingConfig, json.RawMessage)
: Used to validate theGenesisState
defined by a module, given in itsjson.RawMessage
form. It will usually unmarshall thejson
before running a customValidateGenesis
function defined by the module developer.RegisterRESTRoutes(client.Context, *mux.Router)
: Registers the REST routes for the module. These routes will be used to map REST request to the module in order to process them. See gRPC and REST for more.RegisterGRPCGatewayRoutes(client.Context, *runtime.ServeMux)
: Registers gRPC routes for the module.GetTxCmd()
: Returns the rootTx
command for the module. The subcommands of this root command are used by end-users to generate new transactions containingmessage
s defined in the module.GetQueryCmd()
: Return the rootquery
command for the module. The subcommands of this root command are used by end-users to generate new queries to the subset of the state defined by the module.
All the AppModuleBasic
of an application are managed by the BasicManager
.
The AppModuleGenesis
interface is a simple embedding of the AppModuleBasic
interface with two added methods.
Let us go through the two added methods:
InitGenesis(sdk.Context, codec.JSONCodec, json.RawMessage)
: Initializes the subset of the state managed by the module. It is called at genesis (i.e. when the chain is first started).ExportGenesis(sdk.Context, codec.JSONCodec)
: Exports the latest subset of the state managed by the module to be used in a new genesis file.ExportGenesis
is called for each module when a new chain is started from the state of an existing chain.
It does not have its own manager, and exists separately from AppModule
only for modules that exist only to implement genesis functionalities, so that they can be managed without having to implement all of AppModule
's methods. If the module is not only used during genesis, InitGenesis(sdk.Context, codec.JSONCodec, json.RawMessage)
and ExportGenesis(sdk.Context, codec.JSONCodec)
will generally be defined as methods of the concrete type implementing the AppModule
interface.
The AppModule
interface defines the inter-dependent methods that modules need to implement.
AppModule
s are managed by the module manager. This interface embeds the AppModuleGenesis
interface so that the manager can access all the independent and genesis inter-dependent methods of the module. This means that a concrete type implementing the AppModule
interface must either implement all the methods of AppModuleGenesis
(and by extension AppModuleBasic
), or include a concrete type that does as parameter.
Let us go through the methods of AppModule
:
RegisterInvariants(sdk.InvariantRegistry)
: Registers theinvariants
of the module. If an invariant deviates from its predicted value, theInvariantRegistry
triggers appropriate logic (most often the chain will be halted).Route()
: Returns the route formessage
s to be routed to the module byBaseApp
.QuerierRoute()
(deprecated): Returns the name of the module's query route, forqueries
to be routes to the module byBaseApp
.LegacyQuerierHandler(*codec.LegacyAmino)
(deprecated): Returns aquerier
given the querypath
, in order to process thequery
.RegisterServices(Configurator)
: Allows a module to register services.BeginBlock(sdk.Context, abci.RequestBeginBlock)
: This method gives module developers the option to implement logic that is automatically triggered at the beginning of each block. Implement empty if no logic needs to be triggered at the beginning of each block for this module.EndBlock(sdk.Context, abci.RequestEndBlock)
: This method gives module developers the option to implement logic that is automatically triggered at the end of each block. This is also where the module can inform the underlying consensus engine of validator set changes (e.g. thestaking
module). Implement empty if no logic needs to be triggered at the end of each block for this module.
Typically, the various application module interfaces are implemented in a file called module.go
, located in the module's folder (e.g. ./x/module/module.go
).
Almost every module needs to implement the AppModuleBasic
and AppModule
interfaces. If the module is only used for genesis, it will implement AppModuleGenesis
instead of AppModule
. The concrete type that implements the interface can add parameters that are required for the implementation of the various methods of the interface. For example, the Route()
function often calls a NewMsgServerImpl(k keeper)
function defined in keeper/msg_server.go
and therefore needs to pass the module's keeper
as a parameter.
// example
type AppModule struct {
AppModuleBasic
keeper Keeper
}
In the example above, you can see that the AppModule
concrete type references an AppModuleBasic
, and not an AppModuleGenesis
. That is because AppModuleGenesis
only needs to be implemented in modules that focus on genesis-related functionalities. In most modules, the concrete AppModule
type will have a reference to an AppModuleBasic
and implement the two added methods of AppModuleGenesis
directly in the AppModule
type.
If no parameter is required (which is often the case for AppModuleBasic
), just declare an empty concrete type like so:
type AppModuleBasic struct{}
Module managers are used to manage collections of AppModuleBasic
and AppModule
.
The BasicManager
is a structure that lists all the AppModuleBasic
of an application:
It implements the following methods:
NewBasicManager(modules ...AppModuleBasic)
: Constructor function. It takes a list of the application'sAppModuleBasic
and builds a newBasicManager
. This function is generally called in theinit()
function ofapp.go
to quickly initialize the independent elements of the application's modules (click here to see an example).RegisterLegacyAminoCodec(cdc *codec.LegacyAmino)
: Registers thecodec.LegacyAmino
s of each of the application'sAppModuleBasic
. This function is usually called early on in the application's construction.RegisterInterfaces(registry codectypes.InterfaceRegistry)
: Registers interface types and implementations of each of the application'sAppModuleBasic
.DefaultGenesis(cdc codec.JSONCodec)
: Provides default genesis information for modules in the application by calling theDefaultGenesis(cdc codec.JSONCodec)
function of each module. It is used to construct a default genesis file for the application.ValidateGenesis(cdc codec.JSONCodec, txEncCfg client.TxEncodingConfig, genesis map[string]json.RawMessage)
: Validates the genesis information modules by calling theValidateGenesis(codec.JSONCodec, client.TxEncodingConfig, json.RawMessage)
function of each module.RegisterRESTRoutes(ctx client.Context, rtr *mux.Router)
: Registers REST routes for modules by calling theRegisterRESTRoutes
function of each module. This function is usually called function from themain.go
function of the application's command-line interface.RegisterGRPCGatewayRoutes(clientCtx client.Context, rtr *runtime.ServeMux)
: Registers gRPC routes for modules.AddTxCommands(rootTxCmd *cobra.Command)
: Adds modules' transaction commands to the application'srootTxCommand
. This function is usually called function from themain.go
function of the application's command-line interface.AddQueryCommands(rootQueryCmd *cobra.Command)
: Adds modules' query commands to the application'srootQueryCommand
. This function is usually called function from themain.go
function of the application's command-line interface.
The Manager
is a structure that holds all the AppModule
of an application, and defines the order of execution between several key components of these modules:
The module manager is used throughout the application whenever an action on a collection of modules is required. It implements the following methods:
NewManager(modules ...AppModule)
: Constructor function. It takes a list of the application'sAppModule
s and builds a newManager
. It is generally called from the application's main constructor function.SetOrderInitGenesis(moduleNames ...string)
: Sets the order in which theInitGenesis
function of each module will be called when the application is first started. This function is generally called from the application's main constructor function.SetOrderExportGenesis(moduleNames ...string)
: Sets the order in which theExportGenesis
function of each module will be called in case of an export. This function is generally called from the application's main constructor function.SetOrderBeginBlockers(moduleNames ...string)
: Sets the order in which theBeginBlock()
function of each module will be called at the beginning of each block. This function is generally called from the application's main constructor function.SetOrderEndBlockers(moduleNames ...string)
: Sets the order in which theEndBlock()
function of each module will be called at the end of each block. This function is generally called from the application's main constructor function.RegisterInvariants(ir sdk.InvariantRegistry)
: Registers the invariants of each module.RegisterRoutes(router sdk.Router, queryRouter sdk.QueryRouter, legacyQuerierCdc *codec.LegacyAmino)
: Registers legacyMsg
andquerier
routes.RegisterServices(cfg Configurator)
: Registers all module services.InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, genesisData map[string]json.RawMessage)
: Calls theInitGenesis
function of each module when the application is first started, in the order defined inOrderInitGenesis
. Returns anabci.ResponseInitChain
to the underlying consensus engine, which can contain validator updates.ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec)
: Calls theExportGenesis
function of each module, in the order defined inOrderExportGenesis
. The export constructs a genesis file from a previously existing state, and is mainly used when a hard-fork upgrade of the chain is required.BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock)
: At the beginning of each block, this function is called fromBaseApp
and, in turn, calls theBeginBlock
function of each module, in the order defined inOrderBeginBlockers
. It creates a child context with an event manager to aggregate events emitted from all modules. The function returns anabci.ResponseBeginBlock
which contains the aforementioned events.EndBlock(ctx sdk.Context, req abci.RequestEndBlock)
: At the end of each block, this function is called fromBaseApp
and, in turn, calls theEndBlock
function of each module, in the order defined inOrderEndBlockers
. It creates a child context with an event manager to aggregate events emitted from all modules. The function returns anabci.ResponseEndBlock
which contains the aforementioned events, as well as validator set updates (if any).
Here's an example of a concrete integration within an application:
Learn more about message
s and queries
{hide}