摘要 本节介绍如何使用和替换应用侧 mempool。
自 v0.47 起,应用程序拥有了自己的 mempool,从而能够比此前版本更细粒度地构建区块。该变更由 ABCI 1.0 启用。尤其是,它引入了 ABCI++ 的 PrepareProposal 和 ProcessProposal 步骤。
前置阅读

概述

应用程序 mempool 是应用内部进程中的交易存储,用于让开发者控制交易的排序方式以及如何选择交易纳入区块。与在网络层处理交易接收与 gossip 的 CometBFT mempool 不同,SDK 的应用 mempool 在区块提案阶段运行,用于决定区块内交易的排列顺序。 当一笔交易被提交到节点时,CometBFT 会先接收它。CometBFT 会调用应用的 CheckTx 来验证交易,然后将其存入自己的 mempool(默认是 flood mempool),并将其 gossip 给对等节点。 当需要构建区块时,CometBFT 会调用应用的 PrepareProposal。这正是 SDK 应用 mempool 发挥作用的地方:应用会从自己的内部 mempool 中取出交易,并决定将哪些交易纳入区块,以及它们的顺序应该如何安排,例如按优先级、nonce、手续费,或开发者自定义的任意逻辑。 因此,CometBFT 决定哪些交易能够被网络接受;SDK 应用 mempool 决定这些已被接受的交易在区块内如何排序。

Mempool

应用开发者可以为 mempool 设计出无数种方案,SDK 选择仅提供一些简单的 mempool 实现。 具体来说,SDK 提供以下 mempool: 默认情况下,SDK 使用的是空操作 Mempool,但应用开发者可以在 app.go 中将其替换:
nonceMempool := mempool.NewSenderNonceMempool()
    mempoolOpt   := baseapp.SetMempool(nonceMempool)

baseAppOptions = append(baseAppOptions, mempoolOpt)

空操作 Mempool

空操作 mempool 是一种 mempool:当 BaseApp 与其交互时,交易会被完全丢弃并忽略。 使用这种 mempool 时,默认假定应用会依赖 CometBFT 在 RequestPrepareProposal 中定义的交易顺序,该顺序默认是 FIFO。
注意:如果使用 NoOp mempool,PrepareProposal 和 ProcessProposal 都应意识到这一点, 因为 PrepareProposal 可能会包含一些在 ProcessProposal 中校验失败的交易。

发送者 Nonce Mempool

nonce mempool 会按 nonce 排序保存交易,以避免 nonce 相关问题。 它会将交易存入一个按交易 nonce 排序的列表。当提议者请求将交易纳入区块时,它会随机选择一个发送者,并取出该列表中的第一笔交易。这个过程会不断重复,直到 mempool 为空或区块已满。 它支持以下配置参数:

MaxTxs

这是一个整数值,可将 mempool 设为以下三种模式之一:有界、无界 或 禁用。
  • negative:禁用,mempool 不会插入新交易并会提前返回。
  • zero:无界,mempool 没有交易数量限制,也永远不会因为 ErrMempoolTxMaxCapacity 而失败。
  • positive:有界,当 maxTx 的值与 CountTx() 相同时,会因为 ErrMempoolTxMaxCapacity 而失败。

Seed

设置随机数生成器的种子,该生成器用于从 mempool 中选择交易。

优先级 Nonce Mempool

优先级 nonce mempool 是一种 mempool 实现,它会按两个维度组成的偏序集合来存储交易:
  • priority
  • sender-nonce(序列号)
其内部使用一个按优先级排序的 skip list,以及为每个发送者维护一个按 sender-nonce(序列号)排序的 skip list。当同一发送者存在多笔交易时,这些交易相对于其他发送者的交易并不总是能仅按优先级进行比较,因此必须同时按 sender-nonce 和 priority 进行偏序处理。 它支持以下配置参数:

MaxTxs

这是一个整数值,可将 mempool 设为以下三种模式之一:有界、无界 或 禁用。
  • negative:禁用,mempool 不会插入新交易并会提前返回。
  • zero:无界,mempool 没有交易数量限制,也永远不会因为 ErrMempoolTxMaxCapacity 而失败。
  • positive:有界,当 maxTx 的值与 CountTx() 相同时,会因为 ErrMempoolTxMaxCapacity 而失败。

Callback

优先级 nonce mempool 提供了 mempool 选项,允许应用设置回调函数。
  • OnRead:设置当交易从 mempool 中被读取时调用的回调。
  • TxReplacement:设置当 mempool 插入时检测到重复交易 nonce 时调用的回调。应用可以基于交易优先级或某些交易字段定义交易替换规则。
关于 SDK mempool 实现的更多信息,请参阅 godocs。
Synopsis This section describes how the app-side mempool can be used and replaced.
Since v0.47 the application has its own mempool to allow much more granular block building than previous versions. This change was enabled by ABCI 1.0. Notably it introduces the PrepareProposal and ProcessProposal steps of ABCI++.
Prerequisite Readings

Overview

The application mempool is an in-process transaction store within the application that gives developers control over how transactions are ordered and selected for block inclusion. Unlike the CometBFT mempool, which handles transaction receipt and gossip at the network layer, the SDK’s application mempool operates at block proposal time to determine the ordering of transactions within a block. When a transaction is submitted to a node, CometBFT receives it first. CometBFT calls CheckTx on the application to validate the transaction, then stores it in its own mempool (the flood mempool by default) and gossips it to peers. When it is time to build a block, CometBFT calls PrepareProposal on the application. This is where the SDK app mempool comes in: the application pulls from its own internal mempool and decides which transactions to include and in what order: by priority, nonce, fees, or any custom logic the developer chooses. So CometBFT decides what gets accepted into the network; the SDK app mempool decides how accepted transactions are ordered within a block.

Mempool

There are countless designs that an application developer can write for a mempool, the SDK opted to provide only simple mempool implementations. Namely, the SDK provides the following mempools: By default, the SDK uses the No-op Mempool, but it can be replaced by the application developer in [app.go:
nonceMempool := mempool.NewSenderNonceMempool()
    mempoolOpt   := baseapp.SetMempool(nonceMempool)

baseAppOptions = append(baseAppOptions, mempoolOpt)

No-op Mempool

A no-op mempool is a mempool where transactions are completely discarded and ignored when BaseApp interacts with the mempool. When this mempool is used, it is assumed that an application will rely on CometBFT’s transaction ordering defined in RequestPrepareProposal, which is FIFO-ordered by default.
Note: If a NoOp mempool is used, PrepareProposal and ProcessProposal both should be aware of this as PrepareProposal could include transactions that could fail verification in ProcessProposal.

Sender Nonce Mempool

The nonce mempool is a mempool that keeps transactions from an sorted by nonce in order to avoid the issues with nonces. It works by storing the transaction in a list sorted by the transaction nonce. When the proposer asks for transactions to be included in a block it randomly selects a sender and gets the first transaction in the list. It repeats this until the mempool is empty or the block is full. It is configurable with the following parameters:

MaxTxs

It is an integer value that sets the mempool in one of three modes, bounded, unbounded, or disabled.
  • negative: Disabled, mempool does not insert new transaction and return early.
  • zero: Unbounded mempool has no transaction limit and will never fail with ErrMempoolTxMaxCapacity.
  • positive: Bounded, it fails with ErrMempoolTxMaxCapacity when maxTx value is the same as CountTx()

Seed

Set the seed for the random number generator used to select transactions from the mempool.

Priority Nonce Mempool

The priority nonce mempool is a mempool implementation that stores txs in a partially ordered set by 2 dimensions:
  • priority
  • sender-nonce (sequence number)
Internally it uses one priority ordered skip list and one skip list per sender ordered by sender-nonce (sequence number). When there are multiple txs from the same sender, they are not always comparable by priority to other sender txs and must be partially ordered by both sender-nonce and priority. It is configurable with the following parameters:

MaxTxs

It is an integer value that sets the mempool in one of three modes, bounded, unbounded, or disabled.
  • negative: Disabled, mempool does not insert new transaction and return early.
  • zero: Unbounded mempool has no transaction limit and will never fail with ErrMempoolTxMaxCapacity.
  • positive: Bounded, it fails with ErrMempoolTxMaxCapacity when maxTx value is the same as CountTx()

Callback

The priority nonce mempool provides mempool options allowing the application sets callback(s).
  • OnRead: Set a callback to be called when a transaction is read from the mempool.
  • TxReplacement: Sets a callback to be called when duplicated transaction nonce detected during mempool insert. Application can define a transaction replacement rule based on tx priority or certain transaction fields.
More information on the SDK mempool implementation can be found in the godocs.