Skip to main content
Version: Next

Module Manager

Synopsis

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 PreBlocker and BeginBlocker and EndBlocker.

Application Module Interfaces

Application module interfaces exist to facilitate the composition of modules together to form a functional Cosmos SDK application.

note

It is recommended to implement interfaces from the Core API appmodule package. This makes modules less dependent on the SDK. For legacy reason modules can still implement interfaces from the SDK module package.

There are 2 main application module interfaces:

The above interfaces are mostly embedding smaller interfaces (extension interfaces), that defines specific 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 interact with other modules, typically through keepers, which means there is a need for an interface where modules list their keepers and other methods that require a reference to another module's object. AppModule interface extension, such as HasBeginBlocker and HasEndBlocker, 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.

The usage of extension interfaces allows modules to define only the functionalities they need. For example, a module that does not need an EndBlock does not need to define the HasEndBlocker interface and thus the EndBlock method. AppModule and AppModuleGenesis are voluntarily small interfaces, that can take advantage of the Module patterns without having to define many placeholder functions.

AppModuleBasic

note

Use module.CoreAppModuleBasicAdaptor instead for creating an AppModuleBasic from an appmodule.AppModule.

The AppModuleBasic interface defines the independent methods modules need to implement.

types/module/module.go
loading...

Let us go through the methods:

  • RegisterLegacyAminoCodec(*codec.LegacyAmino): Registers the amino codec for the module, which is used to marshal and unmarshal structs to/from []byte in order to persist them in the module's KVStore.
  • RegisterInterfaces(codectypes.InterfaceRegistry): Registers a module's interface types and their concrete implementations as proto.Message.
  • RegisterGRPCGatewayRoutes(client.Context, *runtime.ServeMux): Registers gRPC routes for the module.

All the AppModuleBasic of an application are managed by the BasicManager.

HasName

types/module/module.go
loading...
  • HasName is an interface that has a method Name(). This method returns the name of the module as a string.

Genesis

tip

For easily creating an AppModule that only has genesis functionalities, use module.GenesisOnlyAppModule.

module.HasGenesisBasics

types/module/module.go
loading...

Let us go through the methods:

  • DefaultGenesis(codec.JSONCodec): Returns a default GenesisState for the module, marshalled to json.RawMessage. The default GenesisState need to be defined by the module developer and is primarily used for testing.
  • ValidateGenesis(codec.JSONCodec, client.TxEncodingConfig, json.RawMessage): Used to validate the GenesisState defined by a module, given in its json.RawMessage form. It will usually unmarshall the json before running a custom ValidateGenesis function defined by the module developer.

module.HasGenesis

HasGenesis is an extension interface for allowing modules to implement genesis functionalities.

types/module/module.go
loading...

module.HasABCIGenesis

HasABCIGenesis is an extension interface for allowing modules to implement genesis functionalities and returns validator set updates.

types/module/module.go
loading...

appmodule.HasGenesis

danger

appmodule.HasGenesis is experimental and should be considered unstable, it is recommended to not use this interface at this time.

core/appmodule/genesis.go
loading...

AppModule

The AppModule interface defines a module. Modules can declare their functionalities by implementing extensions interfaces. AppModules are managed by the module manager, which checks which extension interfaces are implemented by the module.

appmodule.AppModule

core/appmodule/module.go
loading...

module.AppModule

note

Previously the module.AppModule interface was containing all the methods that are defined in the extensions interfaces. This was leading to much boilerplate for modules that did not need all the functionalities.

types/module/module.go
loading...

HasInvariants

This interface defines one method. It allows to checks if a module can register invariants.

types/module/module.go
loading...
  • RegisterInvariants(sdk.InvariantRegistry): Registers the invariants of the module. If an invariant deviates from its predicted value, the InvariantRegistry triggers appropriate logic (most often the chain will be halted).

HasServices

This interface defines one method. It allows to checks if a module can register invariants.

appmodule.HasService

core/appmodule/module.go
loading...

module.HasServices

types/module/module.go
loading...
  • RegisterServices(Configurator): Allows a module to register services.

HasConsensusVersion

This interface defines one method for checking a module consensus version.

types/module/module.go
loading...
  • ConsensusVersion() uint64: Returns the consensus version of the module.

HasPreBlocker

The HasPreBlocker is an extension interface from appmodule.AppModule. All modules that have an PreBlock method implement this interface.

HasBeginBlocker

The HasBeginBlocker is an extension interface from appmodule.AppModule. All modules that have an BeginBlock method implement this interface.

core/appmodule/module.go
loading...
  • BeginBlock(context.Context) error: This method gives module developers the option to implement logic that is automatically triggered at the beginning of each block.

HasEndBlocker

The HasEndBlocker is an extension interface from appmodule.AppModule. All modules that have an EndBlock method implement this interface. If a module need to return validator set updates (staking), they can use HasABCIEndBlock

core/appmodule/module.go
loading...
  • EndBlock(context.Context) error: This method gives module developers the option to implement logic that is automatically triggered at the end of each block.

HasABCIEndBlock

The HasABCIEndBlock is an extension interface from module.AppModule. All modules that have an EndBlock which return validator set updates implement this interface.

types/module/module.go
loading...
  • EndBlock(context.Context) ([]abci.ValidatorUpdate, error): This method gives module developers the option to inform the underlying consensus engine of validator set changes (e.g. the staking module).

HasPrecommit

HasPrecommit is an extension interface from appmodule.AppModule. All modules that have a Precommit method implement this interface.

core/appmodule/module.go
loading...
  • Precommit(context.Context): This method gives module developers the option to implement logic that is automatically triggered during `Commit' of each block using the finalizeblockstate of the block to be committed. Implement empty if no logic needs to be triggered during Commit of each block for this module.

HasPrepareCheckState

HasPrepareCheckState is an extension interface from appmodule.AppModule. All modules that have a PrepareCheckState method implement this interface.

core/appmodule/module.go
loading...
  • PrepareCheckState(context.Context): This method gives module developers the option to implement logic that is automatically triggered during `Commit' of each block using the checkState of the next block. Implement empty if no logic needs to be triggered during Commit of each block for this module.

Implementing the Application Module Interfaces

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

Module managers are used to manage collections of AppModuleBasic and AppModule.

BasicManager

The BasicManager is a structure that lists all the AppModuleBasic of an application:

types/module/module.go
loading...

It implements the following methods:

  • NewBasicManager(modules ...AppModuleBasic): Constructor function. It takes a list of the application's AppModuleBasic and builds a new BasicManager. This function is generally called in the init() function of app.go to quickly initialize the independent elements of the application's modules (click here to see an example).
  • NewBasicManagerFromManager(manager *Manager, customModuleBasics map[string]AppModuleBasic): Constructor function. It creates a new BasicManager from a Manager. The BasicManager will contain all AppModuleBasic from the AppModule manager using CoreAppModuleBasicAdaptor whenever possible. Module's AppModuleBasic can be overridden by passing a custom AppModuleBasic map
  • RegisterLegacyAminoCodec(cdc *codec.LegacyAmino): Registers the codec.LegacyAminos of each of the application's AppModuleBasic. 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's AppModuleBasic.
  • DefaultGenesis(cdc codec.JSONCodec): Provides default genesis information for modules in the application by calling the DefaultGenesis(cdc codec.JSONCodec) function of each module. It only calls the modules that implements the HasGenesisBasics interfaces.
  • ValidateGenesis(cdc codec.JSONCodec, txEncCfg client.TxEncodingConfig, genesis map[string]json.RawMessage): Validates the genesis information modules by calling the ValidateGenesis(codec.JSONCodec, client.TxEncodingConfig, json.RawMessage) function of modules implementing the HasGenesisBasics interface.
  • RegisterGRPCGatewayRoutes(clientCtx client.Context, rtr *runtime.ServeMux): Registers gRPC routes for modules.
  • AddTxCommands(rootTxCmd *cobra.Command): Adds modules' transaction commands (defined as GetTxCmd() *cobra.Command) to the application's rootTxCommand. This function is usually called function from the main.go function of the application's command-line interface.
  • AddQueryCommands(rootQueryCmd *cobra.Command): Adds modules' query commands (defined as GetQueryCmd() *cobra.Command) to the application's rootQueryCommand. This function is usually called function from the main.go function of the application's command-line interface.

Manager

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:

types/module/module.go
loading...

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's AppModules and builds a new Manager. It is generally called from the application's main constructor function.
  • SetOrderInitGenesis(moduleNames ...string): Sets the order in which the InitGenesis 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. To initialize modules successfully, module dependencies should be considered. For example, the genutil module must occur after staking module so that the pools are properly initialized with tokens from genesis accounts, the genutils module must also occur after auth so that it can access the params from auth, IBC's capability module should be initialized before all other modules so that it can initialize any capabilities.
  • SetOrderExportGenesis(moduleNames ...string): Sets the order in which the ExportGenesis function of each module will be called in case of an export. This function is generally called from the application's main constructor function.
  • SetOrderPreBlockers(moduleNames ...string): Sets the order in which the PreBlock() function of each module will be called before BeginBlock() of all modules. This function is generally called from the application's main constructor function.
  • SetOrderBeginBlockers(moduleNames ...string): Sets the order in which the BeginBlock() 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 the EndBlock() 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.
  • SetOrderPrecommiters(moduleNames ...string): Sets the order in which the Precommit() function of each module will be called during commit of each block. This function is generally called from the application's main constructor function.
  • SetOrderPrepareCheckStaters(moduleNames ...string): Sets the order in which the PrepareCheckState() function of each module will be called during commit of each block. This function is generally called from the application's main constructor function.
  • SetOrderMigrations(moduleNames ...string): Sets the order of migrations to be run. If not set then migrations will be run with an order defined in DefaultMigrationsOrder.
  • RegisterInvariants(ir sdk.InvariantRegistry): Registers the invariants of module implementing the HasInvariants interface.
  • RegisterServices(cfg Configurator): Registers the services of modules implementing the HasServices interface.
  • InitGenesis(ctx context.Context, cdc codec.JSONCodec, genesisData map[string]json.RawMessage): Calls the InitGenesis function of each module when the application is first started, in the order defined in OrderInitGenesis. Returns an abci.ResponseInitChain to the underlying consensus engine, which can contain validator updates.
  • ExportGenesis(ctx context.Context, cdc codec.JSONCodec): Calls the ExportGenesis function of each module, in the order defined in OrderExportGenesis. 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.
  • ExportGenesisForModules(ctx context.Context, cdc codec.JSONCodec, modulesToExport []string): Behaves the same as ExportGenesis, except takes a list of modules to export.
  • BeginBlock(ctx context.Context) error: At the beginning of each block, this function is called from BaseApp and, in turn, calls the BeginBlock function of each modules implementing the appmodule.HasBeginBlocker interface, in the order defined in OrderBeginBlockers. It creates a child context with an event manager to aggregate events emitted from each modules.
  • EndBlock(ctx context.Context) error: At the end of each block, this function is called from BaseApp and, in turn, calls the EndBlock function of each modules implementing the appmodule.HasEndBlocker interface, in the order defined in OrderEndBlockers. It creates a child context with an event manager to aggregate events emitted from all modules. The function returns an abci which contains the aforementioned events, as well as validator set updates (if any).
  • EndBlock(context.Context) ([]abci.ValidatorUpdate, error): At the end of each block, this function is called from BaseApp and, in turn, calls the EndBlock function of each modules implementing the module.HasABCIEndBlock interface, in the order defined in OrderEndBlockers. It creates a child context with an event manager to aggregate events emitted from all modules. The function returns an abci which contains the aforementioned events, as well as validator set updates (if any).
  • Precommit(ctx context.Context): During Commit, this function is called from BaseApp immediately before the deliverState is written to the underlying rootMultiStore and, in turn calls the Precommit function of each modules implementing the HasPrecommit interface, in the order defined in OrderPrecommiters. It creates a child context where the underlying CacheMultiStore is that of the newly committed block's finalizeblockstate.
  • PrepareCheckState(ctx context.Context): During Commit, this function is called from BaseApp immediately after the deliverState is written to the underlying rootMultiStore and, in turn calls the PrepareCheckState function of each module implementing the HasPrepareCheckState interface, in the order defined in OrderPrepareCheckStaters. It creates a child context where the underlying CacheMultiStore is that of the next block's checkState. Writes to this state will be present in the checkState of the next block, and therefore this method can be used to prepare the checkState for the next block.

Here's an example of a concrete integration within an simapp:

simapp/app.go
loading...

This is the same example from runtime (the package that powers app v2):

runtime/module.go
loading...
runtime/module.go
loading...