Module Manager
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.
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:
appmodule.AppModule/module.AppModulefor inter-dependent module functionalities (except genesis-related functionalities).
The above interfaces are mostly embedding smaller interfaces (extension interfaces), that defines specific functionalities:
- (legacy)
module.HasGenesisBasics: The legacy interface for stateless genesis methods. - (legacy)
module.HasGenesisfor inter-dependent genesis-related module functionalities. - (legacy)
module.HasABCIGenesisfor inter-dependent genesis-related module functionalities. appmodule.HasPreBlocker: The extension interface that contains information about theAppModuleandPreBlock.appmodule.HasBeginBlocker: The extension interface that contains information about theAppModuleandBeginBlock.appmodule.HasEndBlocker: The extension interface that contains information about theAppModuleandEndBlock.appmodule.HasService/module.HasServices: The extension interface for modules to register services.module.HasABCIEndBlock: The extension interface that contains information about theAppModule,EndBlockand returns an updated validator set.- (legacy)
module.HasInvariants: The extension interface for registering invariants. - (legacy)
module.HasConsensusVersion: The extension interface for declaring a module consensus version.
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.
HasAminoCodec​
https://github.com/cosmos/cosmos-sdk/blob/eee5e21e1c8d0995b6d4f83b7f55ec0b58d27ba7/core/appmodule/module.go#L74-L78
RegisterLegacyAminoCodec(*codec.LegacyAmino): Registers theaminocodec for the module, which is used to marshal and unmarshal structs to/from[]bytein order to persist them in the module'sKVStore.
HasRegisterInterfaces​
https://github.com/cosmos/cosmos-sdk/blob/eee5e21e1c8d0995b6d4f83b7f55ec0b58d27ba7/core/appmodule/v2/module.go#L103-L106
RegisterInterfaces(codectypes.InterfaceRegistry): Registers a module's interface types and their concrete implementations asproto.Message.
HasGRPCGateway​
https://github.com/cosmos/cosmos-sdk/blob/eee5e21e1c8d0995b6d4f83b7f55ec0b58d27ba7/types/module/module.go#L84-L87
RegisterGRPCGatewayRoutes(client.Context, *runtime.ServeMux): Registers gRPC routes for the module.
Genesis​
For easily creating an AppModule that only has genesis functionalities, implement module.HasGenesis/HasABCIGenesis.
module.HasGenesisBasics​
https://github.com/cosmos/cosmos-sdk/blob/v0.50.0-alpha.0/types/module/module.go#L76-L79
Let us go through the methods:
DefaultGenesis(codec.JSONCodec): Returns a defaultGenesisStatefor the module, marshalled tojson.RawMessage. The defaultGenesisStateneed to be defined by the module developer and is primarily used for testing.ValidateGenesis(codec.JSONCodec, client.TxEncodingConfig, json.RawMessage): Used to validate theGenesisStatedefined by a module, given in itsjson.RawMessageform. It will usually unmarshall thejsonbefore running a customValidateGenesisfunction defined by the module developer.
module.HasGenesis​
HasGenesis is an extension interface for allowing modules to implement genesis functionalities.
https://github.com/cosmos/cosmos-sdk/blob/28fa3b8/types/module/module.go#L184-L189
module.HasABCIGenesis​
HasABCIGenesis is an extension interface for allowing modules to implement genesis functionalities and returns validator set updates.
https://github.com/cosmos/cosmos-sdk/blob/28fa3b8/types/module/module.go#L94-L98
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​
https://github.com/cosmos/cosmos-sdk/blob/28fa3b8/core/appmodule/module.go#L11-L20
module.AppModule​
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.
https://github.com/cosmos/cosmos-sdk/blob/28fa3b8/core/appmodule/v2/module.go#L14-L20
HasInvariants​
This interface defines one method. It allows to checks if a module can register invariants.
https://github.com/cosmos/cosmos-sdk/blob/v0.50.0-alpha.0/types/module/module.go#L202-L205
RegisterInvariants(sdk.InvariantRegistry): Registers theinvariantsof the module. If an invariant deviates from its predicted value, theInvariantRegistrytriggers 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​
https://github.com/cosmos/cosmos-sdk/blob/28fa3b8/core/appmodule/module.go#L22-L40
module.HasServices​
https://github.com/cosmos/cosmos-sdk/blob/v0.50.0-alpha.0/types/module/module.go#L208-L211
RegisterServices(Configurator): Allows a module to register services.
HasConsensusVersion​
This interface defines one method for checking a module consensus version.
https://github.com/cosmos/cosmos-sdk/blob/28fa3b8/core/appmodule/v2/migrations.go#L6-L12
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.
https://github.com/cosmos/cosmos-sdk/blob/28fa3b8dfcb3208d3b1cfbae08eda519e4cc1560/core/appmodule/v2/module.go#L30-L38
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 needs to return validator set updates (staking), they can use HasABCIEndBlock
https://github.com/cosmos/cosmos-sdk/blob/28fa3b8dfcb3208d3b1cfbae08eda519e4cc1560/core/appmodule/v2/module.go#L40-L48
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 HasUpdateValidators is an extension interface from module.AppModule. All modules that have an EndBlock which return validator set updates implement this interface.
https://github.com/cosmos/cosmos-sdk/blob/28fa3b8dfcb3208d3b1cfbae08eda519e4cc1560/core/appmodule/v2/module.go#L87-L94
UpdateValidators(context.Context) ([]abci.ValidatorUpdate, error): This method gives module developers the option to inform the underlying consensus engine of validator set changes (e.g. thestakingmodule).
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 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.
// example
type AppModule struct {
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.
Module Manager​
The module manager is used to manage collections of appmodule.AppModule and AppModule and all the extensions interfaces.
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:
https://github.com/cosmos/cosmos-sdk/blob/v0.50.0-alpha.0/types/module/module.go#L267-L276
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'sAppModules and builds a newManager. It is generally called from the application's main constructor function.SetOrderInitGenesis(moduleNames ...string): Sets the order in which theInitGenesisfunction 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, thegenutilmodule must occur afterstakingmodule so that the pools are properly initialized with tokens from genesis accounts, thegenutilsmodule must also occur afterauthso that it can access the params from auth, IBC'scapabilitymodule should be initialized before all other modules so that it can initialize any capabilities.SetOrderExportGenesis(moduleNames ...string): Sets the order in which theExportGenesisfunction 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 thePreBlock()function of each module will be called beforeBeginBlock()of all modules. 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.SetOrderPrecommiters(moduleNames ...string): Sets the order in which thePrecommit()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 thePrepareCheckState()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 inDefaultMigrationsOrder.RegisterInvariants(ir sdk.InvariantRegistry): Registers the invariants of module implementing theHasInvariantsinterface.RegisterServices(cfg Configurator): Registers the services of modules implementing theHasServicesinterface.InitGenesis(ctx context.Context, genesisData map[string]json.RawMessage): Calls theInitGenesisfunction of each module when the application is first started, in the order defined inOrderInitGenesis. Returns anabci.InitChainResponseto the underlying consensus engine, which can contain validator updates.ExportGenesis(ctx context.Context): Calls theExportGenesisfunction 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.ExportGenesisForModules(ctx context.Context, modulesToExport []string): Behaves the same asExportGenesis, except takes a list of modules to export.BeginBlock(ctx context.Context) error: At the beginning of each block, this function is called fromBaseAppand, in turn, calls theBeginBlockfunction of each modules implementing theappmodule.HasBeginBlockerinterface, in the order defined inOrderBeginBlockers. 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 fromBaseAppand, in turn, calls theEndBlockfunction of each modules implementing theappmodule.HasEndBlockerinterface, in the order defined inOrderEndBlockers. It creates a child context with an event manager to aggregate events emitted from all modules. The function returns anabciwhich 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 fromBaseAppand, in turn, calls theEndBlockfunction of each modules implementing themodule.HasABCIEndBlockinterface, in the order defined inOrderEndBlockers. It creates a child context with an event manager to aggregate events emitted from all modules. The function returns anabciwhich contains the aforementioned events, as well as validator set updates (if any).Precommit(ctx context.Context): DuringCommit, this function is called fromBaseAppimmediately before thedeliverStateis written to the underlyingrootMultiStoreand, in turn calls thePrecommitfunction of each modules implementing theHasPrecommitinterface, in the order defined inOrderPrecommiters. It creates a child context where the underlyingCacheMultiStoreis that of the newly committed block'sfinalizeblockstate.PrepareCheckState(ctx context.Context): DuringCommit, this function is called fromBaseAppimmediately after thedeliverStateis written to the underlyingrootMultiStoreand, in turn calls thePrepareCheckStatefunction of each module implementing theHasPrepareCheckStateinterface, in the order defined inOrderPrepareCheckStaters. It creates a child context where the underlyingCacheMultiStoreis that of the next block'scheckState. Writes to this state will be present in thecheckStateof the next block, and therefore this method can be used to prepare thecheckStatefor the next block.- (Optional)
RegisterLegacyAminoCodec(cdc *codec.LegacyAmino): Registers thecodec.LegacyAminos of each of the application module. 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'sAppModule.- (Optional)
RegisterGRPCGatewayRoutes(clientCtx client.Context, rtr *runtime.ServeMux): Registers gRPC routes for modules. DefaultGenesis(cdc codec.JSONCodec): Provides default genesis information for modules in the application by calling theDefaultGenesis(cdc codec.JSONCodec)function of each module. It only calls the modules that implements theHasGenesisBasicsinterfaces.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 modules implementing theHasGenesisBasicsinterface.
Here's an example of a concrete integration within an simapp:
https://github.com/cosmos/cosmos-sdk/blob/v0.50.0-alpha.0/simapp/app.go#L411-L434
This is the same example from runtime (the package that powers app di):
https://github.com/cosmos/cosmos-sdk/blob/v0.50.0-alpha.0/runtime/module.go#L61
https://github.com/cosmos/cosmos-sdk/blob/v0.50.0-alpha.0/runtime/module.go#L82