Replace by fee
问题
如果一个交易成功发送到交易池,但可能出现因为费用较低而一直得不到处理。之前 CKB 没有其他措施来处理这种情况。
例如 Dotbit 4 位域名注册拥堵 这个事故发生过程中,CKB
的应用方无法使用任何方式来尽快让自己的交易被打包,这就是引入 Replace-by-fee(RBF)
的原因,我们需要一个机制来提高已经在交易池里交易的费用,替换掉旧的交易,让新的交易尽快被打包。
在新的 multi_index_map
重构后,交易在 pending
阶段也会按照交易的 score
来优先处理 (通常费用高的交易 score
也会高),这会避免高费用的交易被阻塞住,所以理论上述需要手动提高费用的情况会减少,但我们还是需要 RBF 来手动提高交易的费用,应对意外的情况。
另外,RBF 可能将多个老的交易替换出去,因此也是将两个或多个支付合并为一的方法,例如下图所示,如果满足条件 tx-a
, tx-b
, tx-c
, tx-d
都会被 tx-e
这个交易替换掉:
参考
- bips/bip-0125.mediawiki
- Bitcoin Core :: Opt-in RBF FAQ
- RBF in CKB(draft 2023.01.05)
- Bitcoin Optech Newsletter #191 | Bitcoin Optech
中本聪最初的 Bitcoin 版本中就有引入一个 nSequence
的字段,如果相同交易的 nSequence
更高,就可以替换之前老的交易,这个实现的问题是没有支付额外的 fee,miner 没用动力去替换交易,另外因为没有 rate-limiting 从而导致可能被滥用,所以 Bitcoin 在 0.3.12 版本中禁止了这个功能。后来 Bitcoin 重新引入了新的 RBF 改进,主要包括需要支付额外的费用来替换老交易,另外为 RBF 指定了更多的限制条件。
在 CKB 上我们之前做过两次 RBF 的相关调研,因为之前 Pending
是一个 FIFO 的数据结构,所以处理替换不是很方便,在 RBF in CKB(draft 2023.01.05) 尝试引入一个 high priority queue
来实现 inject-replace
。交易池改造之后,整个交易池可当作一个优先队列,所以应对 RBF
会简单很多。
新增 RBF
的流程
实现细节
pre-check
为 entry 加入到 tx-pool 之前必须要做的检查,之前只是做双花的检查,新增 RBF 后如果双花检查失败(这里意味着冲突),继续做 RBF 的相关检查,如果 RBF 检查成功则也返回成功,否则直接返回错误。这里默认直接做 resolve_tx 的检查,如果成功则走正常流程,目的是不给正常流程增加额外成本。所以这就是pre-check
修改后的主要逻辑 。
RBF 的检查规则参考 Bitcoin 的六条,check_rbf 初步实现
实现细节:(Bitcoin Core 0.12.0)
~~1. 交易需要声明为可替换交易~~
2. 新替换交易没有包含新的、未确认的 inputs
3. 新替换交易的交易费用比待替换交易费用高
4. 新替换交易费用必须比节点的 min relay fee 高
5. 待替换交易的子交易数量不可超过 100 条(即使用了该交易的任意 outputs,该交易替换后它们将被从内存池中移出)
6. 因为 ckb 是做了两步提交,我们新增规则:被替换的交易只能是 Pending 或者 Gap 阶段的。
我们不给交易加新的字段表示是否可以被替换,而是通过节点是否配置了 min_rbf_rate
来决定是否能做替换,因此 规则 1
不做对应考虑。
替换和提交
修改 tx-pool
的 submit_entry
函数,传入 conflicts
,在新增 entry 之前把所有冲突的交易删除 放入 rejected
记录,另外确保所有检查完成了之后才做删除和写操作:submit_entry 逻辑。
最终实现在这个 PR 里Tx pool Replace-by-fee。
并发的 Bug
在最初的实现版本中,隐藏了一个并发的 bug 后来在测试发现了。RBF 的检查如果放在 pre-check
中,如果多个线程中的多个交易发生了冲突,input resolve 可能会出问题。Fix concurrency issue for RBF 这个 PR 修复了这个问题,把 RBF 的冲突检查移动了 submit entry 之前,因为在这个函数里面会持有 write 锁。
cycling attack
后来我们在做闪电网络的时候又发现 RBF 可能会引入 cycling attack 的风险,这个攻击通过构造巧妙的新交易,让支付路径上的中间节点的 commitment tx 不能按时上链,Lightning Replacement Cycling Attack Explained这篇文章有更详细的描述。
所以我们后来又做了这么一个改进:Recover possible transaction in conflicted cache when RBF 来规避这个问题。