github.com/cosmos/cosmos-sdk@v0.50.10/docs/architecture/adr-010-modular-antehandler.md (about)

     1  # ADR 010: Modular AnteHandler
     2  
     3  ## Changelog
     4  
     5  * 2019 Aug 31: Initial draft
     6  * 2021 Sep 14: Superseded by ADR-045
     7  
     8  ## Status
     9  
    10  SUPERSEDED by ADR-045
    11  
    12  ## Context
    13  
    14  The current AnteHandler design allows users to either use the default AnteHandler provided in `x/auth` or to build their own AnteHandler from scratch. Ideally AnteHandler functionality is split into multiple, modular functions that can be chained together along with custom ante-functions so that users do not have to rewrite common antehandler logic when they want to implement custom behavior.
    15  
    16  For example, let's say a user wants to implement some custom signature verification logic. In the current codebase, the user would have to write their own Antehandler from scratch largely reimplementing much of the same code and then set their own custom, monolithic antehandler in the baseapp. Instead, we would like to allow users to specify custom behavior when necessary and combine them with default ante-handler functionality in a way that is as modular and flexible as possible.
    17  
    18  ## Proposals
    19  
    20  ### Per-Module AnteHandler
    21  
    22  One approach is to use the [ModuleManager](https://pkg.go.dev/github.com/cosmos/cosmos-sdk/types/module) and have each module implement its own antehandler if it requires custom antehandler logic. The ModuleManager can then be passed in an AnteHandler order in the same way it has an order for BeginBlockers and EndBlockers. The ModuleManager returns a single AnteHandler function that will take in a tx and run each module's `AnteHandle` in the specified order. The module manager's AnteHandler is set as the baseapp's AnteHandler.
    23  
    24  Pros:
    25  
    26  1. Simple to implement
    27  2. Utilizes the existing ModuleManager architecture
    28  
    29  Cons:
    30  
    31  1. Improves granularity but still cannot get more granular than a per-module basis. e.g. If auth's `AnteHandle` function is in charge of validating memo and signatures, users cannot swap the signature-checking functionality while keeping the rest of auth's `AnteHandle` functionality.
    32  2. Module AnteHandler are run one after the other. There is no way for one AnteHandler to wrap or "decorate" another.
    33  
    34  ### Decorator Pattern
    35  
    36  The [weave project](https://github.com/iov-one/weave) achieves AnteHandler modularity through the use of a decorator pattern. The interface is designed as follows:
    37  
    38  ```go
    39  // Decorator wraps a Handler to provide common functionality
    40  // like authentication, or fee-handling, to many Handlers
    41  type Decorator interface {
    42  	Check(ctx Context, store KVStore, tx Tx, next Checker) (*CheckResult, error)
    43  	Deliver(ctx Context, store KVStore, tx Tx, next Deliverer) (*DeliverResult, error)
    44  }
    45  ```
    46  
    47  Each decorator works like a modularized Cosmos SDK antehandler function, but it can take in a `next` argument that may be another decorator or a Handler (which does not take in a next argument). These decorators can be chained together, one decorator being passed in as the `next` argument of the previous decorator in the chain. The chain ends in a Router which can take a tx and route to the appropriate msg handler.
    48  
    49  A key benefit of this approach is that one Decorator can wrap its internal logic around the next Checker/Deliverer. A weave Decorator may do the following:
    50  
    51  ```go
    52  // Example Decorator's Deliver function
    53  func (example Decorator) Deliver(ctx Context, store KVStore, tx Tx, next Deliverer) {
    54      // Do some pre-processing logic
    55  
    56      res, err := next.Deliver(ctx, store, tx)
    57  
    58      // Do some post-processing logic given the result and error
    59  }
    60  ```
    61  
    62  Pros:
    63  
    64  1. Weave Decorators can wrap over the next decorator/handler in the chain. The ability to both pre-process and post-process may be useful in certain settings.
    65  2. Provides a nested modular structure that isn't possible in the solution above, while also allowing for a linear one-after-the-other structure like the solution above.
    66  
    67  Cons:
    68  
    69  1. It is hard to understand at first glance the state updates that would occur after a Decorator runs given the `ctx`, `store`, and `tx`. A Decorator can have an arbitrary number of nested Decorators being called within its function body, each possibly doing some pre- and post-processing before calling the next decorator on the chain. Thus to understand what a Decorator is doing, one must also understand what every other decorator further along the chain is also doing. This can get quite complicated to understand. A linear, one-after-the-other approach while less powerful, may be much easier to reason about.
    70  
    71  ### Chained Micro-Functions
    72  
    73  The benefit of Weave's approach is that the Decorators can be very concise, which when chained together allows for maximum customizability. However, the nested structure can get quite complex and thus hard to reason about.
    74  
    75  Another approach is to split the AnteHandler functionality into tightly scoped "micro-functions", while preserving the one-after-the-other ordering that would come from the ModuleManager approach.
    76  
    77  We can then have a way to chain these micro-functions so that they run one after the other. Modules may define multiple ante micro-functions and then also provide a default per-module AnteHandler that implements a default, suggested order for these micro-functions.
    78  
    79  Users can order the AnteHandlers easily by simply using the ModuleManager. The ModuleManager will take in a list of AnteHandlers and return a single AnteHandler that runs each AnteHandler in the order of the list provided. If the user is comfortable with the default ordering of each module, this is as simple as providing a list with each module's antehandler (exactly the same as BeginBlocker and EndBlocker).
    80  
    81  If however, users wish to change the order or add, modify, or delete ante micro-functions in anyway; they can always define their own ante micro-functions and add them explicitly to the list that gets passed into module manager.
    82  
    83  #### Default Workflow
    84  
    85  This is an example of a user's AnteHandler if they choose not to make any custom micro-functions.
    86  
    87  ##### Cosmos SDK code
    88  
    89  ```go
    90  // Chains together a list of AnteHandler micro-functions that get run one after the other.
    91  // Returned AnteHandler will abort on first error.
    92  func Chainer(order []AnteHandler) AnteHandler {
    93      return func(ctx Context, tx Tx, simulate bool) (newCtx Context, err error) {
    94          for _, ante := range order {
    95              ctx, err := ante(ctx, tx, simulate)
    96              if err != nil {
    97                  return ctx, err
    98              }
    99          }
   100          return ctx, err
   101      }
   102  }
   103  ```
   104  
   105  ```go
   106  // AnteHandler micro-function to verify signatures
   107  func VerifySignatures(ctx Context, tx Tx, simulate bool) (newCtx Context, err error) {
   108      // verify signatures
   109      // Returns InvalidSignature Result and abort=true if sigs invalid
   110      // Return OK result and abort=false if sigs are valid
   111  }
   112  
   113  // AnteHandler micro-function to validate memo
   114  func ValidateMemo(ctx Context, tx Tx, simulate bool) (newCtx Context, err error) {
   115      // validate memo
   116  }
   117  
   118  // Auth defines its own default ante-handler by chaining its micro-functions in a recommended order
   119  AuthModuleAnteHandler := Chainer([]AnteHandler{VerifySignatures, ValidateMemo})
   120  ```
   121  
   122  ```go
   123  // Distribution micro-function to deduct fees from tx
   124  func DeductFees(ctx Context, tx Tx, simulate bool) (newCtx Context, err error) {
   125      // Deduct fees from tx
   126      // Abort if insufficient funds in account to pay for fees
   127  }
   128  
   129  // Distribution micro-function to check if fees > mempool parameter
   130  func CheckMempoolFees(ctx Context, tx Tx, simulate bool) (newCtx Context, err error) {
   131      // If CheckTx: Abort if the fees are less than the mempool's minFee parameter
   132  }
   133  
   134  // Distribution defines its own default ante-handler by chaining its micro-functions in a recommended order
   135  DistrModuleAnteHandler := Chainer([]AnteHandler{CheckMempoolFees, DeductFees})
   136  ```
   137  
   138  ```go
   139  type ModuleManager struct {
   140      // other fields
   141      AnteHandlerOrder []AnteHandler
   142  }
   143  
   144  func (mm ModuleManager) GetAnteHandler() AnteHandler {
   145      retun Chainer(mm.AnteHandlerOrder)
   146  }
   147  ```
   148  
   149  ##### User Code
   150  
   151  ```go
   152  // Note: Since user is not making any custom modifications, we can just SetAnteHandlerOrder with the default AnteHandlers provided by each module in our preferred order
   153  moduleManager.SetAnteHandlerOrder([]AnteHandler(AuthModuleAnteHandler, DistrModuleAnteHandler))
   154  
   155  app.SetAnteHandler(mm.GetAnteHandler())
   156  ```
   157  
   158  #### Custom Workflow
   159  
   160  This is an example workflow for a user that wants to implement custom antehandler logic. In this example, the user wants to implement custom signature verification and change the order of antehandler so that validate memo runs before signature verification.
   161  
   162  ##### User Code
   163  
   164  ```go
   165  // User can implement their own custom signature verification antehandler micro-function
   166  func CustomSigVerify(ctx Context, tx Tx, simulate bool) (newCtx Context, err error) {
   167      // do some custom signature verification logic
   168  }
   169  ```
   170  
   171  ```go
   172  // Micro-functions allow users to change order of when they get executed, and swap out default ante-functionality with their own custom logic.
   173  // Note that users can still chain the default distribution module handler, and auth micro-function along with their custom ante function
   174  moduleManager.SetAnteHandlerOrder([]AnteHandler(ValidateMemo, CustomSigVerify, DistrModuleAnteHandler))
   175  ```
   176  
   177  Pros:
   178  
   179  1. Allows for ante functionality to be as modular as possible.
   180  2. For users that do not need custom ante-functionality, there is little difference between how antehandlers work and how BeginBlock and EndBlock work in ModuleManager.
   181  3. Still easy to understand
   182  
   183  Cons:
   184  
   185  1. Cannot wrap antehandlers with decorators like you can with Weave.
   186  
   187  ### Simple Decorators
   188  
   189  This approach takes inspiration from Weave's decorator design while trying to minimize the number of breaking changes to the Cosmos SDK and maximizing simplicity. Like Weave decorators, this approach allows one `AnteDecorator` to wrap the next AnteHandler to do pre- and post-processing on the result. This is useful since decorators can do defer/cleanups after an AnteHandler returns as well as perform some setup beforehand. Unlike Weave decorators, these `AnteDecorator` functions can only wrap over the AnteHandler rather than the entire handler execution path. This is deliberate as we want decorators from different modules to perform authentication/validation on a `tx`. However, we do not want decorators being capable of wrapping and modifying the results of a `MsgHandler`.
   190  
   191  In addition, this approach will not break any core Cosmos SDK API's. Since we preserve the notion of an AnteHandler and still set a single AnteHandler in baseapp, the decorator is simply an additional approach available for users that desire more customization. The API of modules (namely `x/auth`) may break with this approach, but the core API remains untouched.
   192  
   193  Allow Decorator interface that can be chained together to create a Cosmos SDK AnteHandler.
   194  
   195  This allows users to choose between implementing an AnteHandler by themselves and setting it in the baseapp, or use the decorator pattern to chain their custom decorators with the Cosmos SDK provided decorators in the order they wish.
   196  
   197  ```go
   198  // An AnteDecorator wraps an AnteHandler, and can do pre- and post-processing on the next AnteHandler
   199  type AnteDecorator interface {
   200      AnteHandle(ctx Context, tx Tx, simulate bool, next AnteHandler) (newCtx Context, err error)
   201  }
   202  ```
   203  
   204  ```go
   205  // ChainAnteDecorators will recursively link all of the AnteDecorators in the chain and return a final AnteHandler function
   206  // This is done to preserve the ability to set a single AnteHandler function in the baseapp.
   207  func ChainAnteDecorators(chain ...AnteDecorator) AnteHandler {
   208      if len(chain) == 1 {
   209          return func(ctx Context, tx Tx, simulate bool) {
   210              chain[0].AnteHandle(ctx, tx, simulate, nil)
   211          }
   212      }
   213      return func(ctx Context, tx Tx, simulate bool) {
   214          chain[0].AnteHandle(ctx, tx, simulate, ChainAnteDecorators(chain[1:]))
   215      }
   216  }
   217  ```
   218  
   219  #### Example Code
   220  
   221  Define AnteDecorator functions
   222  
   223  ```go
   224  // Setup GasMeter, catch OutOfGasPanic and handle appropriately
   225  type SetUpContextDecorator struct{}
   226  
   227  func (sud SetUpContextDecorator) AnteHandle(ctx Context, tx Tx, simulate bool, next AnteHandler) (newCtx Context, err error) {
   228      ctx.GasMeter = NewGasMeter(tx.Gas)
   229  
   230      defer func() {
   231          // recover from OutOfGas panic and handle appropriately
   232      }
   233  
   234      return next(ctx, tx, simulate)
   235  }
   236  
   237  // Signature Verification decorator. Verify Signatures and move on
   238  type SigVerifyDecorator struct{}
   239  
   240  func (svd SigVerifyDecorator) AnteHandle(ctx Context, tx Tx, simulate bool, next AnteHandler) (newCtx Context, err error) {
   241      // verify sigs. Return error if invalid
   242  
   243      // call next antehandler if sigs ok
   244      return next(ctx, tx, simulate)
   245  }
   246  
   247  // User-defined Decorator. Can choose to pre- and post-process on AnteHandler
   248  type UserDefinedDecorator struct{
   249      // custom fields
   250  }
   251  
   252  func (udd UserDefinedDecorator) AnteHandle(ctx Context, tx Tx, simulate bool, next AnteHandler) (newCtx Context, err error) {
   253      // pre-processing logic
   254  
   255      ctx, err = next(ctx, tx, simulate)
   256  
   257      // post-processing logic
   258  }
   259  ```
   260  
   261  Link AnteDecorators to create a final AnteHandler. Set this AnteHandler in baseapp.
   262  
   263  ```go
   264  // Create final antehandler by chaining the decorators together
   265  antehandler := ChainAnteDecorators(NewSetUpContextDecorator(), NewSigVerifyDecorator(), NewUserDefinedDecorator())
   266  
   267  // Set chained Antehandler in the baseapp
   268  bapp.SetAnteHandler(antehandler)
   269  ```
   270  
   271  Pros:
   272  
   273  1. Allows one decorator to pre- and post-process the next AnteHandler, similar to the Weave design.
   274  2. Do not need to break baseapp API. Users can still set a single AnteHandler if they choose.
   275  
   276  Cons:
   277  
   278  1. Decorator pattern may have a deeply nested structure that is hard to understand, this is mitigated by having the decorator order explicitly listed in the `ChainAnteDecorators` function.
   279  2. Does not make use of the ModuleManager design. Since this is already being used for BeginBlocker/EndBlocker, this proposal seems unaligned with that design pattern.
   280  
   281  ## Consequences
   282  
   283  Since pros and cons are written for each approach, it is omitted from this section
   284  
   285  ## References
   286  
   287  * [#4572](https://github.com/cosmos/cosmos-sdk/issues/4572):  Modular AnteHandler Issue
   288  * [#4582](https://github.com/cosmos/cosmos-sdk/pull/4583): Initial Implementation of Per-Module AnteHandler Approach
   289  * [Weave Decorator Code](https://github.com/iov-one/weave/blob/master/handler.go#L35)
   290  * [Weave Design Videos](https://vimeo.com/showcase/6189877)