原文链接:https://developers.libra.org/docs/crates/mempool
译者:humyna
日期:2019.09.25
版权及转载声明:本文采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可。
内存池是一个内存缓冲区,用于保存等待执行的交易。
准入控制(AC)模块将交易发送到内存池。 在共识组件交易提交之前,内存池将会把交易保留一段时间。 当一笔新的交易被添加进来,内存池会与系统中的其他验证器(验证器节点)共享此交易。 内存池是一个“共享的内存池”,因为这些内存池之间的交易与其他验证器共享。 这有助于维护一个伪全局排序。
当验证器从另一个内存池接收到一个交易时,该交易在被添加到接收者验证器有序队列会被排序。 为了减少共享内存池网络消耗,每个验证器负责处理自己的交易。 我们不会重复广播来自对等验证器的交易。
我们只广播有可能包含在下一个块中的交易。 这意味着要么交易的序列号是发起人帐户的下一个序列号,要么它之后一组连续的序列化。 例如,如果帐户的当前序列号为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。
内存池只保留有限数量的交易,以避免系统压垮整个系统,阻止滥用和攻击。 内存池中的交易有两种过期类型: 系统TTL 和客户端指定过期时间。 达到其中任何一个条件,交易将被从内存池中移除。
在后台会定期检查系统TTL,同时在每个共识组件提交请求时检查客户端指定的到期时间。 我们使用单独的系统 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