Handlers

Pre-requisite Readings {hide}

handler type

The handler type defined in the Cosmos SDK specifies the typical structure of a handler function.

Copypackage types // Handler defines the core of the state transition function of an application. type Handler func(ctx Context, msg Msg) Result // AnteHandler authenticates transactions, before their internal messages are handled. // If newCtx.IsZero(), ctx is used instead. type AnteHandler func(ctx Context, tx Tx, simulate bool) (newCtx Context, err error) // AnteDecorator wraps the next AnteHandler to perform custom pre- and post-processing. type AnteDecorator interface { AnteHandle(ctx Context, tx Tx, simulate bool, next AnteHandler) (newCtx Context, err error) } // ChainDecorator chains AnteDecorators together with each AnteDecorator // wrapping over the decorators further along chain and returns a single AnteHandler. // // NOTE: The first element is outermost decorator, while the last element is innermost // decorator. Decorator ordering is critical since some decorators will expect // certain checks and updates to be performed (e.g. the Context) before the decorator // is run. These expectations should be documented clearly in a CONTRACT docline // in the decorator's godoc. // // NOTE: Any application that uses GasMeter to limit transaction processing cost // MUST set GasMeter with the FIRST AnteDecorator. Failing to do so will cause // transactions to be processed with an infinite gasmeter and open a DOS attack vector. // Use `ante.SetUpContextDecorator` or a custom Decorator with similar functionality. func ChainAnteDecorators(chain ...AnteDecorator) AnteHandler { if (chain[len(chain)-1] != Terminator{}) { chain = append(chain, Terminator{}) } if len(chain) == 1 { return func(ctx Context, tx Tx, simulate bool) (Context, error) { return chain[0].AnteHandle(ctx, tx, simulate, nil) } } return func(ctx Context, tx Tx, simulate bool) (Context, error) { return chain[0].AnteHandle(ctx, tx, simulate, ChainAnteDecorators(chain[1:]...)) } } // Terminator AnteDecorator will get added to the chain to simplify decorator code // Don't need to check if next == nil further up the chain // ______ // <((((((\\\ // / . }\ // ;--..--._|} // (\ '--/\--' ) // \\ | '-' :'| // \\ . -==- .-| // \\ \.__.' \--._ // [\\ __.--| // _/'--. // \ \\ .'-._ ('-----'/ __/ \ // \ \\ / __>| | '--. | // \ \\ | \ | / / / // \ '\ / \ | | _/ / // \ \ \ | | / / // snd \ \ \ / type Terminator struct{} // Simply return provided Context and nil error func (t Terminator) AnteHandle(ctx Context, _ Tx, _ bool, _ AnteHandler) (Context, error) { return ctx, nil } Expandtypes / handler.goView source

Let us break it down:

  • The Msg is the actual object being processed.
  • The Context 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] returned to baseapp, which contains (among other things) information on the execution of the handler, gas consumption and events. Copy// Result is the union of ResponseFormat and ResponseCheckTx. type Result struct { // Code is the response code, is stored back on the chain. Code CodeType // Codespace is the string referring to the domain of an error Codespace CodespaceType // Data is any data returned from the app. // Data has to be length prefixed in order to separate // results from multiple msgs executions Data []byte // Log contains the txs log information. NOTE: nondeterministic. Log string // GasWanted is the maximum units of work we allow this tx to perform. GasWanted uint64 // GasUsed is the amount of gas actually consumed. NOTE: unimplemented GasUsed uint64 // Events contains a slice of Event objects that were emitted during some // execution. Events Events }Expandtypes / result.goView source

Implementation of a module handlers

Module handlers are typically implemented in a ./handler.go file inside the module's folder. The module manager is used to add the module's handlers to the application's router via the NewHandler() method. Typically, the manager's NewHandler() method simply calls a NewHandler() method defined in handler.go, which looks like the following:

Copyfunc NewHandler(keeper Keeper) sdk.Handler { return func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { ctx = ctx.WithEventManager(sdk.NewEventManager()) switch msg := msg.(type) { case MsgType1: return handleMsgType1(ctx, keeper, msg) case MsgType2: return handleMsgType2(ctx, keeper, msg) default: return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized %s message type: %T", ModuleName, msg) } } } Expand

First, the handler function sets a new EventManager to the context to isolate events per msg. Then, this simple switch returns a handler function specific to the type of the received message. These handler functions are the ones that actually process messages, 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 getter functions.
  • Then, if the checks are successfull, the handler calls the keeper's setter functions to actually perform the state transition.

Before returning, handler functions generally emit one or multiple events via the EventManager held in the ctx:

Copyctx.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), ), ) Expand

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 to learn more about events.

Finally, the handler function returns a sdk.Result which contains the aforementioned events and an optional Data field.

Copy// Result is the union of ResponseFormat and ResponseCheckTx. type Result struct { // Code is the response code, is stored back on the chain. Code CodeType // Codespace is the string referring to the domain of an error Codespace CodespaceType // Data is any data returned from the app. // Data has to be length prefixed in order to separate // results from multiple msgs executions Data []byte // Log contains the txs log information. NOTE: nondeterministic. Log string // GasWanted is the maximum units of work we allow this tx to perform. GasWanted uint64 // GasUsed is the amount of gas actually consumed. NOTE: unimplemented GasUsed uint64 // Events contains a slice of Event objects that were emitted during some // execution. Events Events }Expandtypes / result.goView source

Next is an example of how to return a Result from the gov module:

Copy return sdk.Result{ Data: GetProposalIDBytes(proposal.ProposalID), Events: ctx.EventManager().Events(), }Expandx / gov / handler.goView source

For a deeper look at handlers, see this example implementation of a handler function from the nameservice tutorial.

Next {hide}

Learn about queriers {hide}