-
Notifications
You must be signed in to change notification settings - Fork 3.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
R4R: Docs: Building-modules section 2nd part #5037
Changes from 14 commits
a6788cd
d96abfd
f38fd48
c7ddc70
f7b6fc3
8b1ac72
5162d2e
b4d1657
190c5d3
d2a2d91
f3d307a
c09597f
e20c6d3
628ad72
01e79c8
bc6afa6
c44e476
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
# BeginBlocker and EndBlocker | ||
|
||
## Pre-requisite Reading | ||
|
||
- [Module Manager](./module-manager.md) | ||
|
||
## Synopsis | ||
|
||
`BeginBlocker` and `EndBlocker` are optional methods module developers can implement in their module. They will be triggered at the beginning and at the end of each block respectively. | ||
|
||
## BeginBlocker and EndBlocker | ||
|
||
`BeginBlocker` and `EndBlocker` are a way for module developers to add automatic execution of logic to their module. This is a powerful tool that should be used carefully, as complex automatic functions can slow down or even halt the chain. | ||
|
||
When needed, `BeginBlocker` and `EndBlocker` are implemented as part of the [`AppModule` interface](./module-manager.md#appmodule). The `BeginBlock` and `EndBlock` methods of the interface implemented in `module.go` generally defer to `BeginBlocker` and `EndBlocker` methods respectively, which are usually implemented in a `abci.go` file. | ||
|
||
The actual implementation of `BeginBlocker` and `EndBlocker` are very similar to that of a [`handler`](./handler.md): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would be good to add a line describing |
||
|
||
- They generally use the [`keeper`](./keeper.md) and [`ctx`](../core/context.md) to retrieve information about the latest state. | ||
- If needed, they use the `keeper` and `ctx` to trigger state-transitions. | ||
- If needed, they can emit [`events`](../core/events.md) via the `ctx`'s `EventManager`. | ||
|
||
A specificity of the `EndBlocker` is that it can return validator updates to the underlying consensus engine in the form of an [`[]abci.ValidatorUpdates`](https://tendermint.com/docs/app-dev/abci-spec.html#validatorupdate). | ||
|
||
For more, see an [example implementation of `BeginBlocker` from the `distr` module](https://github.com/cosmos/cosmos-sdk/blob/master/x/distribution/abci.go) and an [example implementation of `EndBlocker`]( https://github.com/cosmos/cosmos-sdk/blob/master/x/staking/handler.go#L44) | ||
|
||
## Next | ||
|
||
Learn about [`keeper`s](./keeper.md). |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,47 @@ | ||
# Module Genesis | ||
|
||
## `GenesisState` | ||
## Pre-requisite Reading | ||
|
||
## `ValidateGenesis` | ||
- [Module Manager](./module-manager.md) | ||
- [Keepers](./keeper.md) | ||
|
||
## Synopsis | ||
|
||
Modules generally handle a subset of the state and, as such, they need to define the related subset of the genesis file as well as methods to initialize, verify and export it. | ||
|
||
- [Type Definition](#type-definition) | ||
+ [InitGenesis](#initgenesis) | ||
+ [DefaultGenesis](#defaultgenesis) | ||
- [Other Genesis Functions](#other-genesis-functions) | ||
+ [ValidateGenesis](#validategenesis) | ||
+ [ExportGenesis](#exportgenesis) | ||
|
||
## Type Definition | ||
|
||
The subset of the genesis state defined from a given module is generally defined in a `internal/types/genesis.go` file, along with the `DefaultGenesis` and `ValidateGenesis` methods. The struct defining the subset of the genesis state defined by the module is usually called `GenesisState` and contains all the module-related values that need to be initialized during the genesis process. | ||
|
||
See an example of `GenesisState` type definition from the [nameservice tutoria](https://github.com/cosmos/sdk-application-tutorial/blob/master/x/nameservice/genesis.go#L10-L12). | ||
|
||
### `DefaultGenesis` | ||
|
||
The `DefaultGenesis()` method is a simple method that calls the constructor function for `GenesisState` with the default value for each parameter. See an example [here](https://github.com/cosmos/sdk-application-tutorial/blob/master/x/nameservice/genesis.go#L14-L16). | ||
|
||
### `ValidateGenesis` | ||
|
||
The `ValidateGenesis(genesisState GenesisState)` method is called to verify that the provided `genesisState` is correct. It should perform validity checks on each of the parameter listed in `GenesisState`. See an example [here](https://github.com/cosmos/sdk-application-tutorial/blob/master/x/nameservice/genesis.go#L18-L31). | ||
|
||
## Other Genesis Methods | ||
|
||
Other than the methods related directly to `GenesisState`, module developers are expected to implement two other methods as part of the [`AppModuleGenesis` interface](./module-manager.md#appmodulegenesis) (only if the module needs to initialize a subset of state in genesis). These methods are [`InitGenesis`](#initgenesis) and [`ExportGenesis`](#exportgenesis). | ||
|
||
### `InitGenesis` | ||
|
||
The `InitGenesis` method is executed during [`InitChain`](../core/baseapp.md#initchain) when the application is first started. Given a `GenesisState`, it initializes the subset of the state managed by the module by using the module's [`keeper`](./keeper.md) setter function on each parameter within the `GenesisState`. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. probably want to mention |
||
|
||
See an [example of `InitGenesis` from the nameservice tutorial](https://github.com/cosmos/sdk-application-tutorial/blob/master/x/nameservice/genesis.go#L39-L44). | ||
|
||
### `ExportGenesis` | ||
|
||
The `ExportGenesis` method is executed whenever an export of the state is made. It takes the latest known version of the subset of the state managed by the module and creates a new `GenesisState` out of it. This is mainly used when the chain needs to be upgraded via a hard fork. | ||
|
||
See an [example of `ExportGenesis` from the nameservice tutorial](https://github.com/cosmos/sdk-application-tutorial/blob/master/x/nameservice/genesis.go#L46-L57). |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,73 @@ | ||
# Handlers | ||
# Handlers | ||
|
||
## Pre-requisite Reading | ||
|
||
- [Module Manager](./module-manager.md) | ||
- [Messages and Queries](./messages-and-queries.md) | ||
|
||
## Synopsis | ||
|
||
A `Handler` designates a function that processes [`message`s](./messages-and-queries.md#messages). `handler`s are specific to the module in which they are defined, and only process `message`s defined within said module. They are called from `baseapp` during [`DeliverTx`](../core/baseapp.md#delivertx). | ||
fedekunze marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
- [`handler` type](#handler-type) | ||
- [Implementation of a module `handler`s](#implementation-of-a-module-handlers) | ||
|
||
## `handler` type | ||
|
||
The [`handler` type](https://github.com/cosmos/cosmos-sdk/blob/master/types/handler.go#L4) defined in the Cosmos SDK specifies the typical structure of a `handler` function: | ||
|
||
```go | ||
type Handler func(ctx Context, msg Msg) Result | ||
``` | ||
|
||
Let us break it down: | ||
|
||
- The [`msg`](./messages-and-queries.md#messages) is the actual object being processed. | ||
fedekunze marked this conversation as resolved.
Show resolved
Hide resolved
|
||
- The [`Context`](../core/context.md) contains all the necessary information needed to process the `msg`, as well as a cache-wrapped copy of the latest state. If the `msg` is succesfully processed, the modified version of the temporary state contained in the `ctx` will be written to the main state. | ||
- The [`Result`](https://github.com/cosmos/cosmos-sdk/blob/master/types/result.go#L14-L38) returned to `baseapp`, which contains (among other things) information on the execution of the `handler`, [`gas`](../basics/accounts-fees-gas.md#gas) consumption and [`events`](./events.md). | ||
|
||
## Implementation of a module `handler`s | ||
|
||
Module `handler`s are typically implemented in a `handler.go` file inside the module's folder. The [module manager](./module-manager.md) is used to add the module's `handler`s to the [application's `router`](../core/baseapp.md#message-routing) via the `NewHandler()` method. Typically, the manager's `NewHandler()` method simply calls a `NewHandler()` method defined in `handler.go`, which looks like the following: | ||
|
||
```go | ||
func NewHandler(keeper Keeper) sdk.Handler { | ||
return func(ctx sdk.Context, msg sdk.Msg) sdk.Result { | ||
switch msg := msg.(type) { | ||
case MsgType1: | ||
return handleMsgType1(ctx, keeper, msg) | ||
case MsgType2: | ||
return handleMsgType2(ctx, keeper, msg) | ||
default: | ||
errMsg := fmt.Sprintf("Unrecognized nameservice Msg type: %v", msg.Type()) | ||
return sdk.ErrUnknownRequest(errMsg).Result() | ||
} | ||
} | ||
} | ||
``` | ||
|
||
This simple switch returns a `handler` function specific to the type of the received `message`. These `handler` functions are the ones that actually process `message`s, and usually follow the following 2 steps: | ||
|
||
- First, they perform *stateful* checks to make sure the `message` is valid. At this stage, the `message`'s `ValidateBasic()` method has already been called, meaning *stateless* checks on the message (like making sure parameters are correctly formatted) have already been performed. Checks performed in the `handler` can be more expensive and require access to the state. For example, a `handler` for a `transfer` message might check that the sending account has enough funds to actually perform the transfer. To access the state, the `handler` needs to call the [`keeper`'s](./keeper.md) getter functions. | ||
- Then, if the checks are successfull, the `handler` calls the [`keeper`'s](./keeper.md) setter functions to actually perform the state transition. | ||
|
||
Before returning, `handler` functions generally emit one or multiple [`events`](../core/events.md) via the `EventManager` held in the `ctx`: | ||
|
||
```go | ||
ctx.EventManager().EmitEvent( | ||
sdk.NewEvent( | ||
eventType, // e.g. sdk.EventTypeMessage for a message, types.CustomEventType for a custom event defined in the module | ||
sdk.NewAttribute(attributeKey, attributeValue), | ||
), | ||
) | ||
``` | ||
|
||
These `events` are relayed back to the underlying consensus engine and can be used by service providers to implement services around the application. Click [here](../core/events.md) to learn more about `events`. | ||
|
||
Finally, the `handler` function returns a [`sdk.Result`](https://github.com/tendermint/tendermint/blob/master/abci/types/result.go) which contains the aforementioned `events` and an optional `Data` field. | ||
|
||
For a deeper look at `handler`s, see this [example implementation of a `handler` function](https://github.com/cosmos/sdk-application-tutorial/blob/master/x/nameservice/handler.go) from the nameservice tutorial. | ||
|
||
## Next | ||
|
||
Learn about [queriers](./querier.md). |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,88 @@ | ||
# Invariants | ||
|
||
## What is an invariant | ||
## Pre-requisite Reading | ||
|
||
## Invariant Registry | ||
- [Keepers](./keeper.md) | ||
|
||
## Synopsis | ||
|
||
An invariant is a property of the application that should always be true. An `Invariant` is a function that checks for a particular invariant. These functions are useful to detect bugs early on and act upon them to limit the potential consequences (e.g. by halting the chain). They are also useful in the development process of the application to detect bugs via simulations. | ||
|
||
- [Implementing `Invariant`s](#implementing-invariants) | ||
fedekunze marked this conversation as resolved.
Show resolved
Hide resolved
|
||
- [Invariant Registry](#invariant-registry) | ||
|
||
## Implementing `Invariant`s | ||
|
||
An `Invariant` is a function that checks for a particular invariant within a module. Module `Invariant`s must follow the [`Invariant`s type](https://github.com/cosmos/cosmos-sdk/blob/master/types/invariant.go#L9): | ||
|
||
```go | ||
type Invariant func(ctx Context) (string, bool) | ||
``` | ||
|
||
where the `string` return value is the invariant message, which can be used when printing logs, and the `bool` return value is the actual result of the invariant check. | ||
|
||
In practice, each module implements `Invariant`s in a `internal/keeper/invariants.go` file within the module's folder. The standard is to implement one `Invariant` function per logical grouping of invariants with the following model: | ||
|
||
```go | ||
// Example for an Invariant that checks balance-related invariants | ||
|
||
func BalanceInvariants(k Keeper) sdk.Invariant { | ||
return func(ctx sdk.Context) (string, bool) { | ||
// Implement checks for balance-related invariants | ||
} | ||
} | ||
``` | ||
|
||
Additionally, module developers should generally implement an `AllInvariants` function that runs all the `Invariant`s functions of the module: | ||
|
||
```go | ||
// AllInvariants runs all invariants of the module. | ||
// In this example, the module implements two Invariants: BalanceInvariants and DepositsInvariants | ||
|
||
func AllInvariants(k Keeper) sdk.Invariant { | ||
|
||
return func(ctx sdk.Context) (string, bool) { | ||
res, stop := BalanceInvariants(k)(ctx) | ||
if stop { | ||
return res, stop | ||
} | ||
|
||
return DepositsInvariant(k)(ctx) | ||
} | ||
} | ||
``` | ||
|
||
Finally, module developers need to implement the `RegisterInvariants` method as part of the [`AppModule` interface](./module-manager.md#appmodule). Indeed, the `RegisterInvariants` method of the module, implemented in the `module.go` file, typically only defers the call to a `RegisterInvariants` method implemented in `internal/keeper/invariants.go`. The `RegisterInvariants` method registers a route for each `Invariant` function in the [`InvariantRegistry`](#invariant-registry): | ||
|
||
|
||
```go | ||
// RegisterInvariants registers all staking invariants | ||
func RegisterInvariants(ir sdk.InvariantRegistry, k Keeper) { | ||
ir.RegisterRoute(types.ModuleName, "module-accounts", | ||
BalanceInvariants(k)) | ||
ir.RegisterRoute(types.ModuleName, "nonnegative-power", | ||
DepositsInvariant(k)) | ||
} | ||
``` | ||
|
||
For more, see an example of [`Invariant`s implementation from the `staking` module](https://github.com/cosmos/cosmos-sdk/blob/master/x/staking/keeper/invariants.go). | ||
|
||
## Invariant Registry | ||
|
||
The `InvariantRegistry` is a registry where the `Invariant`s of all the modules of an application are registered. There is only one `InvariantRegistry` per **application**, meaning module developers need not implement their own `InvariantRegistry` when building a module. All module developers need to do is to register their modules' invariants in the `InvariantRegistry`, as explained in the section above. | ||
|
||
Typically, the `InvariantRegistry` is implemented as a specific module (the most used implementation is that of the [`crisis` module](https://github.com/cosmos/cosmos-sdk/blob/master/x/crisis/)). This module must implement an object that follow the [`sdk.InvariantRegistry` interface](https://github.com/cosmos/cosmos-sdk/blob/master/types/invariant.go#L14-L17). | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this paragraph is confusing. -- does the SDK contain the only instance of the InvariantRegistry ? Does the crisis module contain another instance? |
||
|
||
```go | ||
type InvariantRegistry interface { | ||
RegisterRoute(moduleName, route string, invar Invariant) | ||
} | ||
``` | ||
|
||
Typically, this interface is implemented in the module's `keeper`. You can see an example implementation of an `InvariantRegistry` from the `crisis` module [here](https://github.com/cosmos/cosmos-sdk/blob/master/x/crisis/internal/keeper/keeper.go). | ||
|
||
`Invariant`s can be checked manually via [`message`s](./messages-and-queries), but most often they are checked automatically at the end of each block (see an example [here](https://github.com/cosmos/cosmos-sdk/blob/master/x/crisis/abci.go)). In both cases, if one of the `Invariant`s returns false, the `InvariantRegistry` can trigger special logic (e.g. have the application panic and print the `Invariant`s message in the log). | ||
|
||
## Next | ||
|
||
Learn about the recommended [structure for modules](./structure.md). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe add something along the lines of "They will be triggered at the beginning and at the end of each block by the
BeginBlock
andEndBlock
ABCI messages sent from Tendermint."