# Cosmos SDK Middlewares Problem Context: [Make middlewares less confusing](https://github.com/cosmos/cosmos-sdk/issues/11955) ## Before 0.46-beta Antehandlers is a set of functions which should execute linearly. ```go type AnteHandler func(ctx Context, tx Tx, simulate bool) (newCtx Context, err error) ``` In fact we never create directly the AnteHandlers, instead we create decorators, which are wrapped into AnteHandler closures: ```go func (d Decorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { // prepare // optional: defer / recover hacks to escape the linear flow (sic!) // optional: simulate logic (sic!) // optional: update context return next(ctx, tx, simulate) } // here we translate the decorator into AnteHandler: func ChainAnteDecorators(chain ...AnteDecorator) AnteHandler { // ... return func(ctx Context, tx Tx, simulate bool) (Context, error) { return chain[0].AnteHandle(ctx, tx, simulate, ChainAnteDecorators(chain[1:]...)) } } ``` Examples: + [ContextDecorator with GasMeter](https://github.com/cosmos/cosmos-sdk/blob/release%2Fv0.45.x/x/auth/ante/setup.go#L32) ## 0.46-beta: Middleware concept was created to modularize the execution for 3 major transaction flows: `CheckTx`, `DeliverTx` and `SimulateTx`. We compose middlewares using list: `A, B,...` and we run them sequentially: ``` A A_precode B B_precode ... DeliverTx() ... B_postcode A_postcode ``` We clearly separate the concerns for + CheckTx + DeliverTx + Simulate Wrapping code is more clean, and more modular, examples: + [gas middleware](https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-beta2/x/auth/middleware/gas.go#L43) + [fee grant](https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-beta2/x/auth/middleware/fee.go#L114) is still complex but it's more modular. More: [Middleware ADR](https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-beta2/docs/architecture/adr-045-check-delivertx-middlewares.md). ## Antehandlers vs 0.46-beta2 Middlewares I consider 0.46-beta2 middlewares approach superior to Antehandlers: + Modularization and clear separation of concern: checkTx, deliverTx, simulate, and helper functions. + Easier code + easy way to implement tendermint event indexer + Antehandlers are not linear - there are needed "hacks" to escape linearity (eg gas meter) + Implementing tx [priority](https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-beta2/x/auth/middleware/fee.go#L97) or advanced [fee market](https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-beta2/x/auth/middleware/fee.go#L97) with middleware is straightforward. With Antehandlers it will be hacky: priority must be set in tx Response type. So probably using `defer`. Personally, I think the 0.46-beta middleware design requires better documentation, rather than revert to AnteHandlers. Superior design could be based on Dependency Injection mentioned by Aaron. ## Alternatives _DRAFTS_ ```go type DeliverTxMiddleware interface { // comment with dependencies Pre(ctx context.Context, req Request) (Context, Request, error) Post(ctx context.Context, req Request, resp Response, err error) Recover(ctx context.Context, req Request, resp Response, panicErr interface{}) } type Executor struct { deliverTx func(ctx Context, req Request) (Response, ResponseCheckTx, error) ms []DeliverTxMiddleware } func (e Executor) DeliverTx(ctx, req) (Response, error){ _, _, resp, err := e.execute(ctx, req, e.ms) return resp, err } func (e Executor) execute(ctx context.Context, req Request, ms []DeliverTxMiddleware) (Context, Request, Response, error) { if len(ms) == 0 { return e.handler(ctx, req) } var err error var resp = Response{} var m = ms[0] defer func(){ if r := recover(); r != nil { m.Recover(ctx, req, resp, r) } } ctx, req, err = m.Pre(ctx, req) if err != nil { return ctx, req, resp, err } // recurse ctx, req, resp, err = e.Execute(ctx, req, ms[1:]) resp, err = m.Post(ctx, req, resp, err) return ctx, req, res, err } func ComposeDeliverTx(h tx.Handler, ms ...DeliverTxMiddleware) tx.Handler { return Executor{h, ms} } ``` Execution graph: ``` A.pre 🡪 B.pre 🡪 ... 🡾 RunMsgs A.post 🡨 B.post 🡨 ... 🡷 ``` ### Example Let's try to implement [gas handler]() using the Executor: ```go type gasHandler struct { sdkCtx sdk.Context } func (h *gasHandler) Pre(ctx context.Context, req tx.Request) (context.Context, tx.Request, error) { sdkCtx, err := gasContext(sdk.UnwrapSDKContext(ctx), req.Tx, false) if err != nil { return ctx, req, err } h.sdkCtx = sdkCtx return sdk.WrapSDKContext(sdkCtx), req, err } func (h *gasHandler) Post(ctx context.Context, req tx.Request, resp tx.Response, err error) (context.Context, tx.Response, error) { return ctx, populateGas(resp, h.sdkCtx), err } ``` vs 0.46-beta Middleware version: ```go type gasTxHandler struct { next tx.Handler } func (txh gasTxHandler) DeliverTx(ctx context.Context, req tx.Request) (tx.Response, error) { sdkCtx, err := gasContext(sdk.UnwrapSDKContext(ctx), req.Tx, false) if err != nil { return tx.Response{}, err } res, err := txh.next.DeliverTx(sdk.WrapSDKContext(sdkCtx), req) return populateGas(res, sdkCtx), err } ``` ### Comments + the draft above builds on the same idea as the Middleweare design + we clearly break down the code into `pre`, `post` and `recover` actions