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