区块链就是何交易打交道,我们今天就介绍下,交易处理过程中的一个重要组成部分:txpool。这篇文章主要从功能角度介绍,通过这篇文章会了解:
txpool 的在交易中的位置和作用。
txpool 的功能,核心组成部分 queued 和 pending。
txpool 如何实现它的功能。
txpool 源码的重要关注点。
以太坊内部有个重要的内部功能是 txpool,从字面意思就能看出来,交易池就是存放交易的池子。它在以太坊中的位置如下图,只要有新交易,无论是本节点创建的,还是其他 peer 节点广播来的,都会先加入到交易池里,在打包区块的时候,就从这个池子里提取,区块产生之后,共识区块,交易上链。
txpool 有 4 个功能:
作为存放交易的缓冲区,大量交易到来时,先存起来
为打包区块服务,合适交易会被打包到区块
清理交易
当交易的数量多于缓冲区大小时,过滤 / 惩罚发送大量交易的账户(攻击者)
我们来一张稍微详细点的模块交互图,看 txpool 怎么实现上面 4 个功能的。
缓存功能的设计
txpool 中的交易分为 queued 和 pending 2 种,其中 queued 存放未来的、当前无法执行的交易。以太坊使用 nonce 值决定某个账户的交易顺序,多条交易值 nonce 值必须连续,如果和过去的交易不连续,则无法执行,我们不妨使用 nonce 值,标记交易的号码,nonce 为 10 的交易,称为第 10 号交易。举个例子,当前账户的 nonce 是 10,txpool 中有该账户的第 100 号交易,但 txpool 中没有第 11~99 号交易,这些交易的缺失,造成第 100 号交易无法执行,所以第 100 号交易就是未来的交易、不可执行的交易,存放在 queue 中。
pending 存放可执行的交易。比如我们把上面的 11~99 号交易补全了,那么 11~100 号交易都可以进入到 pending,因为这些交易都是连续的,都可以打包进区块。
当节点收到交易(本地节点发起的或 peer 广播来的)时,会先存放到 queued,txpool 在某些情况下,把 queued 中可执行的交易,转移到 pending 中。
为区块打包服务
这是 txpool 最核心的功能,worker 在打包区块的时候,会获取所有的 pending 交易,但这些交易还存在 txpool 中,worker 只是读取出来,至于 txpool 何时删除交易,稍后从 txpool 清理交易的角度单独在看。
清理交易
txpool 清理交易有以下几种条件,符合任意以下 1 条的,都是无效交易,会被从 pending 或者 queued 中移除:
交易的 nonce 值已经低于账户在当前高度上的 nonce 值,代表交易已过期,交易已经上链就属于这种情况
交易的 GasLimit 大于区块的 GasLimit,区块容不下交易
账户的余额已不足以支持该交易要消耗的费用
交易的数量超过了 queued 和 pending 的缓冲区大小,需要进行清理
交易清理主要有 3 个场景:
txpool 订阅了 ChainHeadEvent 事件,该事件代表主链上有新区块产生,txpool 会根据最新的区块,检查每个账号的交易,有些无效的会被删除,有些由于区块回滚会从 pending 移动到 queued,然后把 queued 中可执行的交易移动到 pending,为下一轮区块打包组号准备。
queued 交易移动到 pending 被称为“提升”(promote),这个过程中,同样会检查交易,当交易不符合以上条件时,就会被直接从 queued 中删除。
删除停留在 queued 中超过 3 小时的交易,3 小时这个超时时间是可以通过 geth 的启动参数调整的。txpool 记录了某个账户交易进入 pending 的时间,如果这个时间超过了 3 小时,代表该账号的交易迟迟不能被主链打包,既然无法被主链接受,就删除掉在 queued 中本来就无法执行的交易。
惩罚恶意账号
这也是 txpool 很重要的一个属性,可以防止恶意账户以发起大量垃圾交易。防止恶意用户造成:
占用 txpool 空间
浪费节点大量内存和 CPU
降低打包性能
只有当交易的总数量超过缓冲区大小时,txpool 才会认为有恶意账户发起大量交易。pending 和 queued 缓冲区大小不同,但处理策略类似:
pending 的缓冲区容量是 4096,当 pending 的交易数量多于此时,就会运行检查,每个账号的交易数量是否多于 16,把这些账号搜集出来,进行循环依次清理,什么意思呢?就是每轮只删除(移动到 queued)这些账号的每个账号 1 条交易,然后看数量是否降下来了,不满足再进行下一轮,直到满足。
queued 的缓冲区容量是 1024,超过之后清理策略和 pending 差不多,但这里可是真删除了。
该部分功能未抽象成单独的函数,而是在 promoteExecutables() 中,就是在每次把 queued 交易转移到 pending 后执行的。
本地交易的特权,txpool 虽然对交易有诸多限制,但如果交易是本节点的账号发起的,以上数量限制等都对他无效。所以,如果你用本节点账号不停的发送交易,并不会被认为是攻击者,你用 txpool.status 命令,可以查看到交易的数量,肯定可以大于 4096,我曾达到过 60000+。
重点关注的源码
txpool 的主要设计上面就讲完了,如果你想把 txpool 的代码阅读一番,我建议你重点关注一下这些函数和变量,按图索骥能就完全掌握 txpool 的实现。
TxPoolConfig:txpool 的配置参数
chainHeadCh:txpool 订阅了新区块事件
pending:pending 的交易,每个账号都有一个交易列表
queue:queued 的交易,每个账号都有一个交易列表
loop:txpool 的事件处理函数
addTx:添加 1 条交易的源头,你能找到类似的函数
promoteExecutables:queued 交易移动到 pending
reset:根据当前区块的最新高度,重置 txpool 中的交易
仔细阅读一遍,你会发现 txpool 会涉及多个锁(TxPool.mu, TxPool.all, TxPool.priced.all),所以当 txpool 中的交易很多时,它的性能是很低的,这也会影响到区块的打包。
如果这篇文章对你有帮助,请点个赞 / 喜欢,鼓励我持续分享,感谢。
我的文章列表,点此可查看
如果喜欢本文,随意转载,但请保留此原文链接。