github.com/KYVENetwork/cometbft/v38@v38.0.3/docs/architecture/adr-111-nop-mempool.md (about)

     1  # ADR 111: `nop` Mempool
     2  
     3  ## Changelog
     4  
     5  - 2023-11-07: First version (@sergio-mena)
     6  - 2023-11-15: Addressed PR comments (@sergio-mena)
     7  - 2023-11-17: Renamed `nil` to `nop` (@melekes)
     8  - 2023-11-20: Mentioned that the app could reuse p2p network in the future (@melekes)
     9  - 2023-11-22: Adapt ADR to implementation (@melekes)
    10  
    11  ## Status
    12  
    13  Accepted
    14  
    15  [Tracking issue](https://github.com/KYVENetwork/cometbft/v38/issues/1666)
    16  
    17  ## Context
    18  
    19  ### Summary
    20  
    21  The current mempool built into CometBFT implements a robust yet somewhat inefficient transaction gossip mechanism.
    22  While the CometBFT team is currently working on more efficient general-purpose transaction gossiping mechanisms,
    23  some users have expressed their desire to manage both the mempool and the transaction dissemination mechanism
    24  outside CometBFT (typically at the application level).
    25  
    26  This ADR proposes a fairly simple way for CometBFT to fulfill this use case without moving away from our current architecture.
    27  
    28  ### In the Beginning...
    29  
    30  It is well understood that a dissemination mechanism
    31  (sometimes using _Reliable Broadcast_ [\[HT94\]][HT94] but not necessarily),
    32  is needed in a distributed system implementing State-Machine Replication (SMR).
    33  This is also the case in blockchains.
    34  Early designs such as Bitcoin or Ethereum include an _internal_ component,
    35  responsible for dissemination, called mempool.
    36  Tendermint Core chose to follow the same design given the success
    37  of those early blockchains and, since inception, Tendermint Core and later CometBFT have featured a mempool as an internal piece of its architecture.
    38  
    39  
    40  However, the design of ABCI clearly dividing the application logic (i.e., the appchain)
    41  and the consensus logic that provides SMR semantics to the app is a unique innovation in Cosmos
    42  that sets it apart from Bitcoin, Ethereum, and many others.
    43  This clear separation of concerns entailed many consequences, mostly positive:
    44  it allows CometBFT to be used underneath (currently) tens of different appchains in production
    45  in the Cosmos ecosystem and elsewhere.
    46  But there are other implications for having an internal mempool
    47  in CometBFT: the interaction between the mempool, the application, and the network
    48  becomes more indirect, and thus more complex and hard to understand and operate.
    49  
    50  ### ABCI++ Improvements and Remaining Shortcomings
    51  
    52  Before the release of ABCI++, `CheckTx` was the main mechanism the app had at its disposal to influence
    53  what transactions made it to the mempool, and very indirectly what transactions got ultimately proposed in a block.
    54  Since ABCI 1.0 (the first part of ABCI++, shipped in `v0.37.x`), the application has
    55  a more direct say in what is proposed through `PrepareProposal` and `ProcessProposal`.
    56  
    57  This has greatly improved the ability for appchains to influence the contents of the proposed block.
    58  Further, ABCI++ has enabled many new use cases for appchains. However some issues remain with
    59  the current model:
    60  
    61  * We are using the same P2P network for disseminating transactions and consensus-related messages.
    62  * Many mempool parameters are configured on a per-node basis by node operators,
    63    allowing the possibility of inconsistent mempool configuration across the network
    64    with potentially serious scalability effects
    65    (even causing unacceptable performance degradation in some extreme cases).
    66  * The current mempool implementation uses a basic (robust but sub-optimal) flood algorithm
    67    * the CometBFT team is working on improving it as one of our current priorities,
    68      but any improvement we come up with must address the needs of a vast spectrum of applications,
    69      as well as be heavily scaled-tested in various scenarios
    70      (in an attempt to cover the applications' wide spectrum)
    71    * a mempool designed specifically for one particular application
    72      would reduce the search space as its designers can devise it with just their application's
    73      needs in mind.
    74  * The interaction with the application is still somewhat convoluted:
    75    * the application has to decide what logic to implement in `CheckTx`,
    76      what to do with the transaction list coming in `RequestPrepareProposal`,
    77      whether it wants to maintain an app-side mempool (more on this below), and whether or not
    78      to combine the transactions in the app-side mempool with those coming in `RequestPrepareProposal`
    79    * all those combinations are hard to fully understand, as the semantics and guarantees are
    80      often not clear
    81    * when using exclusively an app-mempool (the approach taken in the Cosmos SDK `v0.47.x`)
    82      for populating proposed blocks, with the aim of simplifying the app developers' life,
    83      we still have a suboptimal model where we need to continue using CometBFT's mempool
    84      in order to disseminate the transactions. So, we end up using twice as much memory,
    85      as in-transit transactions need to be kept in both mempools.
    86  
    87  The approach presented in this ADR builds on the app-mempool design released in `v0.47.x`
    88  of the Cosmos SDK,
    89  and briefly discussed in the last bullet point above (see [SDK app-mempool][sdk-app-mempool] for further details of this model).
    90  
    91  In the app-mempool design in Cosmos SDK `v0.47.x`
    92  an unconfirmed transaction must be both in CometBFT's mempool for dissemination and
    93  in the app's mempool so the application can decide how to manage the mempool.
    94  There is no doubt that this approach has numerous advantages. However, it also has some implications that need to be considered:
    95  
    96  * Having every transaction both in CometBFT and in the application is suboptimal in terms of memory.
    97    Additionally, the app developer has to be careful
    98    that the contents of both mempools do not diverge over time
    99    (hence the crucial role `re-CheckTx` plays post-ABCI++).
   100  * The main reason for a transaction needing to be in CometBFT's mempool is
   101    because the design in Cosmos SDK `v0.47.x` does not consider an application
   102    that has its own means of disseminating transactions.
   103    It reuses the peer to peer network underneath CometBFT reactors.
   104  * There is no point in having transactions in CometBFT's mempool if an application implements an ad-hoc design for disseminating transactions.
   105  
   106  This proposal targets this kind of applications:
   107  those that have an ad-hoc mechanism for transaction dissemination that better meets the application requirements.
   108  
   109  The ABCI application could reuse the P2P network once this is exposed via ABCI.
   110  But this will take some time as it needs to be implemented, and has a dependency
   111  on bi-directional ABCI, which is also quite substantial. See
   112  [1](https://github.com/KYVENetwork/cometbft/v38/discussions/1112) and
   113  [2](https://github.com/KYVENetwork/cometbft/v38/discussions/494) discussions.
   114  
   115  We propose to introduce a `nop` (short for no operation) mempool which will effectively act as a stubbed object
   116  internally:
   117  
   118  * it will reject any transaction being locally submitted or gossipped by a peer
   119  * when a _reap_ (as it is currently called) is executed in the mempool, an empty answer will always be returned
   120  * the application running on the proposer validator will add transactions it received
   121    using the appchains's own mechanism via `PrepareProposal`.
   122  
   123  ## Alternative Approaches
   124  
   125  These are the alternatives known to date:
   126  
   127  1. Keep the current model. Useful for basic apps, but clearly suboptimal for applications
   128     with their own mechanism to disseminate transactions and particular performance requirements.
   129  2. Provide more efficient general-purpose mempool implementations.
   130     This is an ongoing effort (e.g., [CAT mempool][cat-mempool]), but will take some time, and R&D effort, to come up with
   131     advanced mechanisms -- likely highly configurable and thus complex -- which then will have to be thoroughly tested.
   132  3. A similar approach to this one ([ADR110][adr-110]) whereby the application-specific
   133     mechanism directly interacts with CometBFT via a newly defined gRPC interface.
   134  4. Partially adopting this ADR. There are several possibilities:
   135      * Use the current mempool, disable transaction broadcast in `config.toml`, and accept transactions from users via `BroadcastTX*` RPC methods.
   136        Positive: avoids transaction gossiping; app can reuse the mempool existing in ComeBFT.
   137        Negative: requires clients to know the validators' RPC endpoints (potential security issues).
   138      * Transaction broadcast is disabled in `config.toml`, and have the application always reject transactions in `CheckTx`.
   139        Positive: effectively disables the mempool; does not require modifications to Comet (may be used in `v0.37.x` and `v0.38.x`).
   140        Negative: requires apps to disseminate txs themselves; the setup for this is less straightforward than this ADR's proposal.
   141  
   142  ## Decision
   143  
   144  TBD
   145  
   146  ## Detailed Design
   147  
   148  What this ADR proposes can already be achieved with an unmodified CometBFT since
   149  `v0.37.x`, albeit with a complex, poor UX (see the last alternative in section
   150  [Alternative Approaches](#alternative-approaches)). The core of this proposal
   151  is to make some internal changes so it is clear an simple for app developers,
   152  thus improving the UX.
   153  
   154  #### `nop` Mempool
   155  
   156  We propose a new mempool implementation, called `nop` Mempool, that effectively disables all mempool functionality
   157  within CometBFT.
   158  The `nop` Mempool implements the `Mempool` interface in a very simple manner:
   159  
   160  *	`CheckTx(tx types.Tx) (*abcicli.ReqRes, error)`: returns `nil, ErrNotAllowed`
   161  *	`RemoveTxByKey(txKey types.TxKey) error`: returns `ErrNotAllowed`
   162  * `ReapMaxBytesMaxGas(maxBytes, maxGas int64) types.Txs`: returns `nil`
   163  * `ReapMaxTxs(max int) types.Txs`: returns `nil`
   164  *	`Lock()`: does nothing
   165  * `Unlock()`: does nothing
   166  *	`Update(...) error`: returns `nil`
   167  * `FlushAppConn() error`: returns `nil`
   168  *	`Flush()`: does nothing
   169  * `TxsAvailable() <-chan struct{}`: returns `nil`
   170  * `EnableTxsAvailable()`: does nothing
   171  * `SetTxRemovedCallback(cb func(types.TxKey))`: does nothing
   172  * `Size() int` returns 0
   173  * `SizeBytes() int64` returns 0
   174  
   175  Upon startup, the `nop` mempool reactor will advertise no channels to the peer-to-peer layer.
   176  
   177  ### Configuration
   178  
   179  We propose the following changes to the `config.toml` file:
   180  
   181  ```toml
   182  [mempool]
   183  # The type of mempool for this CometBFT node to use.
   184  #
   185  # Valid types of mempools supported by CometBFT:
   186  # - "flood" : clist mempool with flooding gossip protocol (default)
   187  # - "nop"   : nop-mempool (app has implemented an alternative tx dissemination mechanism)
   188  type = "nop"
   189  ```
   190  
   191  The config validation logic will be modified to add a new rule that rejects a configuration file
   192  if all of these conditions are met:
   193  
   194  * the mempool is set to `nop`
   195  * `create_empty_blocks`, in `consensus` section, is set to `false`.
   196  
   197  The reason for this extra validity rule is that the `nop`-mempool, as proposed here,
   198  does not support the "do not create empty blocks" functionality.
   199  Here are some considerations on this:
   200  
   201  * The "do not create empty blocks" functionality
   202    * entangles the consensus and mempool reactors
   203    * is hardly used in production by real appchains (to the best of CometBFT team's knowledge)
   204    * its current implementation for the built-in mempool has undesired side-effects
   205      * app hashes currently refer to the previous block,
   206      * and thus it interferes with query provability.
   207  * If needed in the future, this can be supported by extending ABCI,
   208    but we will first need to see a real need for this before committing to changing ABCI
   209    (which has other, higher-impact changes waiting to be prioritized).
   210  
   211  ### RPC Calls
   212  
   213  There are no changes needed in the code dealing with RPC. Those RPC paths that call methods of the `Mempool` interface,
   214  will simply be calling the new implementation.
   215  
   216  ### Impacted Workflows
   217  
   218  * *Submitting a transaction*. Users are not to submit transactions via CometBFT's RPC.
   219    `BroadcastTx*` RPC methods will fail with a reasonable error and the 501 status code.
   220    The application running on a full node must offer an interface for users to submit new transactions.
   221    It could also be a distinct node (or set of nodes) in the network.
   222    These considerations are exclusively the application's concern in this approach.
   223  * *Time to propose a block*. The consensus reactor will call `ReapMaxBytesMaxGas` which will return a `nil` slice.
   224    `RequestPrepareProposal` will thus contain no transactions.
   225  * *Consensus waiting for transactions to become available*. `TxsAvailable()` returns `nil`.
   226    `cs.handleTxsAvailable()` won't ever be executed.
   227    At any rate, a configuration with the `nop` mempool and `create_empty_blocks` set to `false`
   228    will be rejected in the first place.
   229  * *A new block is decided*.
   230    * When `Update` is called, nothing is done (no decided transaction is removed).
   231    * Locking and unlocking the mempool has no effect.
   232  * *ABCI mempool's connection*
   233    CometBFT will still open a "mempool" connection, even though it won't be used.
   234    This is to avoid doing lots of breaking changes.
   235  
   236  ### Impact on Current Release Plans
   237  
   238  The changes needed for this approach, are fairly simple, and the logic is clear.
   239  This might allow us to even deliver it as part of CometBFT `v1` (our next release)
   240  even without a noticeable impact on `v1`'s delivery schedule.
   241  
   242  The CometBFT team (learning from past dramatic events) usually takes a conservative approach
   243  for backporting changes to release branches that have already undergone a full QA cycle
   244  (and thus are in code-freeze mode).
   245  For this reason, although the limited impact of these changes would limit the risks
   246  of backporting to `v0.38.x` and `v0.37.x`, a careful risk/benefit evaluation will
   247  have to be carried out.
   248  
   249  Backporting to `v0.34.x` does not make sense as this version predates the release of `ABCI 1.0`,
   250  so using the `nop` mempool renders CometBFT's operation useless.
   251  
   252  ### Config parameter _vs._ application-enforced parameter
   253  
   254  In the current proposal, the parameter selecting the mempool is in `config.toml`.
   255  However, it is not a clear-cut decision. These are the alternatives we see:
   256  
   257  * *Mempool selected in `config.toml` (our current design)*.
   258    This is the way the mempool has always been selected in Tendermint Core and CometBFT,
   259    in those versions where there were more than one mempool to choose from.
   260    As the configuration is in `config.toml`, it is up to the node operators to configure their
   261    nodes consistently, via social consensus. However this cannot be guaranteed.
   262    A network with an inconsistent choice of mempool at different nodes might
   263    result in undesirable side effects, such as peers disconnecting from nodes
   264    that sent them messages via the mempool channel.
   265  * *Mempool selected as a network-wide parameter*.
   266    A way to prevent any inconsistency when selecting the mempool is to move the configuration out of `config.toml`
   267    and have it as a network-wide application-enforced parameter, implemented in the same way as Consensus Params.
   268    The Cosmos community may not be ready for such a rigid, radical change,
   269    even if it eliminates the risk of operators shooting themselves in the foot.
   270    Hence we went currently favor the previous alternative.
   271  * *Mempool selected as a network-wide parameter, but allowing override*.
   272    A third option, half way between the previous two, is to have the mempool selection
   273    as a network-wide parameter, but with a special value called _local-config_ that still
   274    allows an appchain to decide to leave it up to operators to configure it in `config.toml`.
   275  
   276  Ultimately, the "config parameter _vs._ application-enforced parameter" discussion
   277  is a more general one that is applicable to other parameters not related to mempool selection.
   278  In that sense, it is out of the scope of this ADR.
   279  
   280  ## Consequences
   281  
   282  ### Positive
   283  
   284  - Applications can now find mempool mechanisms that fit better their particular needs:
   285    - Ad-hoc ways to add, remove, merge, reorder, modify, prioritize transactions according
   286      to application needs.
   287    - A way to disseminate transactions (gossip-based or other) to get the submitted transactions
   288      to proposers. The application developers can devise simpler, efficient mechanisms tailored
   289      to their application.
   290    - Back-pressure mechanisms to prevent malicious users from abusing the transaction
   291      dissemination mechanism.
   292  - In this approach, CometBFT's peer-to-peer layer is relieved from managing transaction gossip, freeing up its resources for other reactors such as consensus, evidence, block-sync, or state-sync.
   293  - There is no risk for the operators of a network to provide inconsistent configurations
   294    for some mempool-related parameters. Some of those misconfigurations are known to have caused
   295    serious performance issues in CometBFT's peer to peer network.
   296    Unless, of course, the application-defined transaction dissemination mechanism ends up
   297    allowing similar configuration inconsistencies.
   298  - The interaction between the application and CometBFT at `PrepareProposal` time
   299    is simplified. No transactions are ever provided by CometBFT,
   300    and no transactions can ever be left in the mempool when CometBFT calls `PrepareProposal`:
   301    the application trivially has all the information.
   302  - UX is improved compared to how this can be done prior to this ADR.
   303  
   304  ### Negative
   305  
   306  - With the `nop` mempool, it is up to the application to provide users with a way
   307    to submit transactions and deliver those transactions to validators.
   308    This is a considerable endeavor, and more basic appchains may consider it is not worth the hassle.
   309  - There is a risk of wasting resources by those nodes that have a misconfigured
   310    mempool (bandwidth, CPU, memory, etc). If there are TXs submitted (incorrectly)
   311    via CometBFT's RPC, but those TXs are never submitted (correctly via an
   312    app-specific interface) to the App. As those TXs risk being there until the node
   313    is stopped. Moreover, those TXs will be replied & proposed every single block.
   314    App developers will need to keep this in mind and panic on `CheckTx` or
   315    `PrepareProposal` with non-empty list of transactions.
   316  - Optimizing block proposals by only including transaction IDs (e.g. TX hashes) is more difficult.
   317    The ABCI app could do it by submitting TX hashes (rather than TXs themselves)
   318    in `PrepareProposal`, and then having a mechanism for pulling TXs from the
   319    network upon `FinalizeBlock`.
   320  
   321  [sdk-app-mempool]: https://docs.cosmos.network/v0.47/build/building-apps/app-mempool
   322  [adr-110]: https://github.com/KYVENetwork/cometbft/v38/pull/1565
   323  [HT94]: https://dl.acm.org/doi/book/10.5555/866693
   324  [cat-mempool]: https://github.com/KYVENetwork/cometbft/v38/pull/1472