* Refactor CliContext as Context
* Fix lint issues
* Fix goimports
* Fix gov tests
* Resolved ci-lint issues
* Add changelog
* Rename cliCtx to clientCtx
* Fix mocks and routes
* Add changelog
* Update changelog
* Apply suggestions from code review
Co-authored-by: Alessio Treglia <alessio@tendermint.com>
* merge client/rpc/ro{ot,utes}.go
* Update docs
* client/rpc: remove redundant client/rpc.RegisterRPCRoutes
* regenerate mocks
* Update ADRs
Co-authored-by: Alessio Treglia <alessio@tendermint.com>
Co-authored-by: Federico Kunze <31522760+fedekunze@users.noreply.github.com>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
16 KiB
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 BeginBlocker and EndBlocker. {synopsis}
Pre-requisite Readings
- Introduction to SDK Modules {prereq}
Application Module Interfaces
Application module interfaces exist to facilitate the composition of modules together to form a functional SDK application. There are 3 main application module interfaces:
AppModuleBasicfor independent module functionalities.AppModulefor inter-dependent module functionalities (except genesis-related functionalities).AppModuleGenesisfor 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 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 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.
AppModuleBasic
The AppModuleBasic interface defines the independent methods modules need to implement.
+++ 7d7821b9af/types/module/module.go (L46-L59)
Let us go through the methods:
Name(): Returns the name of the module as astring.RegisterCodec(*codec.Codec): Registers thecodecfor the module, which is used to marhsal and unmarshal structs to/from[]bytein order to persist them in the moduel'sKVStore.DefaultGenesis(): Returns a defaultGenesisStatefor the module, marshalled tojson.RawMessage. The defaultGenesisStateneed to be defined by the module developer and is primarily used for testing.ValidateGenesis(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.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 [../interfaces/rest.md] for more.GetTxCmd(*codec.Codec): Returns the rootTxcommand for the module. The subcommands of this root command are used by end-users to generate new transactions containingmessages defined in the module.GetQueryCmd(*codec.Codec): Return the rootquerycommand 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.
AppModuleGenesis
The AppModuleGenesis interface is a simple embedding of the AppModuleBasic interface with two added methods.
+++ 7d7821b9af/types/module/module.go (L126-L131)
Let us go through the two added methods:
InitGenesis(sdk.Context, 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): Exports the latest subset of the state managed by the module to be used in a new genesis file.ExportGenesisis 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, json.RawMessage) and ExportGenesis(sdk.Context) will generally be defined as methods of the concrete type implementing hte AppModule interface.
AppModule
The AppModule interface defines the inter-dependent methods modules need to implement.
+++ 7d7821b9af/types/module/module.go (L133-L149)
AppModules 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 theinvariantsof the module. If the invariants deviates from its predicted value, theInvariantRegistrytriggers appropriate logic (most often the chain will be halted).Route(): Returns the name of the module's route, formessages to be routed to the module bybaseapp.NewHandler(): Returns ahandlergiven theType()of themessage, in order to process themessage.QuerierRoute(): Returns the name of the module's query route, forqueriesto be routes to the module bybaseapp.NewQuerierHandler(): Returns aqueriergiven the querypath, in order to process thequery.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 beginning of each block. This is also where the module can inform the underlying consensus engine of validator set changes (e.g. thestakingmodule). Implement empty if no logic needs to be triggered at the beginning 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 need 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 NewHandler() function often calls a NewHandler(k keeper) function defined in handler.go and therefore needs to pass the module's keeper as 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:
+++ 7d7821b9af/types/module/module.go (L61-L63)
It implements the following methods:
NewBasicManager(modules ...AppModuleBasic): Constructor function. It takes a list of the application'sAppModuleBasicand builds a newBasicManager. This function is generally called in theinit()function ofapp.goto quickly initialize the independent elements of the application's modules (click here to see an example).RegisterCodec(cdc *codec.Codec): Registers thecodecs of each of the application'sAppModuleBasic. This function is usually called early on in the application's construction.DefaultGenesis(): Provides default genesis information for modules in the application by calling theDefaultGenesis()function of each module. It is used to construct a default genesis file for the application.ValidateGenesis(genesis map[string]json.RawMessage): Validates the genesis information modules by calling theValidateGenesis()function of each module.RegisterRESTRoutes(ctx client.Context, rtr *mux.Router): Registers REST routes for modules by calling theRegisterRESTRoutesfunction of each module. This function is usually called function from themain.gofunction of the application's command-line interface.AddTxCommands(rootTxCmd *cobra.Command, cdc *codec.Codec): Adds modules' transaction commands to the application'srootTxCommand. This function is usually called function from themain.gofunction of the application's command-line interface.AddQueryCommands(rootQueryCmd *cobra.Command, cdc *codec.Codec): Adds modules' query commands to the application'srootQueryCommand. This function is usually called function from themain.gofunction 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:
+++ 7d7821b9af/types/module/module.go (L190-L198)
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.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.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 beginning 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): Registers module routes to the application'srouter, in order to routemessages to the appropriatehandler, and module query routes to the application'squeryRouter, in order to routequeriesto the appropriatequerier.InitGenesis(ctx sdk.Context, genesisData map[string]json.RawMessage): Calls theInitGenesisfunction of each module when the application is first started, in the order defined inOrderInitGenesis. Returns anabci.ResponseInitChainto the underlying consensus engine, which can contain validator updates.ExportGenesis(ctx sdk.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.BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock): At the beginning of each block, this function is called frombaseappand, in turn, calls theBeginBlockfunction 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.ResponseBeginBlockwhich contains the aforementioned events.EndBlock(ctx sdk.Context, req abci.RequestEndBlock): At the end of each block, this function is called frombaseappand, in turn, calls theEndBlockfunction 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.ResponseEndBlockwhich contains the aforementioned events, as well as validator set updates (if any).
Next {hide}
Learn more about messages and queries {hide}