内存池 Mempool
内存池是一个内存缓冲区,用于保存等待执行的交易。
概要
准入控制(AC)模块将交易发送到内存池(Mempool)。 在共识模块提交之前,内存池将会把交易保留一段时间。 当有新交易添加时,内存池会与系统中的其他验证程序(验证器节点)共享此交易。 内存池是一个“共享的内存池”,因为这些内存池之间的交易与其他验证器共享。 这有助于维护一个伪全局排序(pseudoglobal ordering)。
当验证器从另一个内存池接收到一个交易时,该交易在被添加到接收者验证器有些队列时,同样会排序。 为了减少共享内存池中的网络消耗,每个验证器负责处理自己的交易。 我们不会重复广播来自对等验证器的交易。
我们只广播有可能包含在下一个块中的交易。 这意味着要么交易的序列号(sequence number)是发起人帐户的下一个序列号,或者是顺延的。 例如,如果帐户的当前序列号为2且本地内存池中包含序列号为2,3,4,7,8的交易,则仅广播交易2,3和4。
共识模块从内存池获取交易,内存池不会将交易推向共识。 这样在交易达成共识前,还可以:
- 内存池可以继续基于gas对交易排序;
- 共识模块可以允许交易在内存池中建立。
这允许将交易分组为单个共识区块,并按gas价格划分优先级。
内存池不会跟踪发送到共识模块的交易。 在每个get_block请求上(去内存池中提取一个交易块),共识模块发送一组从内存池中提取但未提交(committed)的交易。 这让内存池对不同的共识模块提议分支保持不可知。
当交易完全执行并写入存储时,共识模块通知内存池。 然后,内存池将此交易从其内部状态中删除。
实现细节
在内部,内存池被建模为 HashMap<AccountAddress, AccountTransactions>
并在其上构建了各种索引。
主索引 - PriorityIndex 是一个有序的交易队列,它“准备好”被包含在下一个块中(即,它们的序列号与帐户当前序列号是顺序的)。 这个队列按gas价格排序,这样如果客户愿意为每个执行单位支付更多(相比其他客户多),那么他们可以更早进入共识。
请注意,即使全部订单是按gas价格维护的,对于单个帐户交易按序号排序。 未准备好包含在下一个块中的所有交易都是单独的 ParkingLotIndex 索引的一部分。 一旦某个事件让交易解除阻塞,它们就会被移动到有序队列中。
下面是一个示例:内存池中有一个序列号为4的交易,而该帐户的当前序列号为3.此交易被视为“未就绪”。来自共识的回调通知交易已提交(即,交易3已提交到一个不同的节点,因此已经在链上提交)。 则“解除阻塞”本地交易,交易#4 被移动到 OrderedQueue。
内存池只保留有限数量的交易,以避免系统被滥用和攻击。 内存池中的交易有两种类型的过期: systemTTL 和客户端指定的过期。 达到其中任何一个时,将从内存池中删除该交易。
在后台会定期检查SystemTTL,同时在每个共识提交请求时检查客户端指定的到期时间。 我们使用单独的System TTL来确保交易不会永远停留在内存池中,即使共识未取得进展。
这个模块是怎样组织的?
mempool/src
├── core_mempool # main in memory data structure
├── proto # protobuf definitions for interactions with mempool
├── lib.rs
├── mempool_service.rs # gRPC service
├── runtime.rs # bundle of shared mempool and gRPC service
└── shared_mempool.rs # shared mempool