作者:josh

来源:https://delvingbitcoin.org/t/input-triggered-transaction-expiry/2667

长话短说

“以输入触发的交易超时” 是一种有趣的原语,值得进一步的研究。它可以用一种极简的共识变更来实现:

nSequence 执行一种基于区块高度的相对时间锁 R,当第其 21 位置 1 时,如果 R < 100、或 nLockTime 是基于钟表时间的、或 BIP68 最小高度超过了 nLockTime ,则脚本执行失败。

这一变更是非常强大的。它能够带来无需观察交易池的 HTLC 转发,以及可用于 LN-Symmetry(对称式闪电通道)的(准)合约层面的相对时间锁。

这些应用,在传统的超时提议(比如 OP_EXPIRE)下也能实现,但以输入触发的超时不会遭遇 “免费转发” 问题。

背景

直接的交易超时是一个得到充分研究的话题。最全面的似乎是 Peter Todd 的 OP_EXPIRE 提议,它会在一个特定的区块高度之后让一笔交易失效。

该提议要求多项共识变更:

  1. 一个 nVersion 标签,在所有输出上强制执行一个长达 100 个区块的成熟规则(译者注:即所有交易的输出都在交易确认的 100 个区块后才能花费)。
  2. 一种新的 OP_EXPIRE 操作码。
  3. 使用 taproot annex(交易附言)显式表达超时高度。

OP_EXPIRE 的主要动机是解决 HTLC 转发中的 “替代交易循环攻击”。通过让(闪电通道的)“HTLC timeout 交易” 的原像花费路径超时(无法使用),我们就能阻止这种攻击,并且不必再监控本地交易池(以寻找揭晓原像的交易),可以代之以链状态监控。

该提议的主要困难在于,可能出现免费转发问题。如果一笔有效交易稍后会变成无效的,那么攻击者就能以少量预期成本来轰炸 P2P 网络。Todd 提出了一种转发策略,要求手续费足以在下一个区块中得到确认,但社区发现这是不充分的(大概是因为依然可能出现概率性的免费转发)。

以输入触发的交易超时

以输入触发的交易超时方法没有那么强大,但能够免疫免费转发攻击,而且天然适合现有的交易语境。主要的想法是:如果一个输入太晚得到确认,就让这笔交易超时。

只需以下一项共识变更,就能强制执行这种要求:

nSequence 执行一种基于区块高度的相对时间锁 R,当第其 21 位置 1 时,如果 R < 100、或 nLockTime 是基于钟表时间的、或 BIP68 最小高度超过了 nLockTime ,则脚本执行失败。

比如说,为了强制执行在高度 H 超时,交易将在 nSequence 字段的第 21 比特置 1,从而强制执行一个基于高度的相对时间锁 R=100,同时,需要将 nLockTime 设为 H 。如果这个输入(所花费的输出)在 H − 100 高度之后从挖出,这笔交易就会超时(无法确认)。

这个 100 个区块的最小相对时间锁保证了,一笔有效的交易不会作废,除非出现了长达 100 个区块的重组,保证输入具有跟 coinbase 交易输出相同的成熟度,并且防止免费转发问题。

这个提议的最明显的缺点是,它要搭配基于区块高度(值为 H)的绝对时间锁,因此,即使输入提早得到确认,目标交易也无法提早发布。虽然并不理想,在一些应用场景下它依然是可取的(比如下文讲到的 “应用场景 2”),并且,可以通过使用交易内省来避免这个缺点(详见这个帖子)(中文译本)。

应用场景 1:无需观察交易池的 HTLC 转发

OP_EXPIRE 相似,以输入触发的交易超时可以带来无需在本地交易池监控原像花费交易的 HTLC 转发。详细的构造可见这个帖子中文译本)。

应用场景 2:(准)合约层面的相对时间锁

背景

在传统的 LN-Symmetry 提议中,每一笔 “更新交易” 都要重设相对时间锁。在 2 方的通道中,这会给 “HTLC timeout” 交易施加 2 倍长的时延(在 N 方通道中,时延会变成 N 倍),也即会锁定流动性。

最高效的解决方案是一种合约层面的相对时间锁,它基于 “kickoff 交易” 的确认高度,在 “结算交易” 上强制执行一个相对时间锁。不幸的是,这种想法似乎要求一种紧凑的祖先交易证明,或者一种传递 kickoff 交易确认高度的办法。没有任何一种是今天能够实现的,并且,不论想要实现哪一种,都需要重大的共识变更。

总结

一种(准)合约层面的相对时间锁可以使用本方法构造出来,代价是增加交互、存储和周期性刷新的要求。使用 “向量承诺”,这些额外的开销可以使用链外计算消除掉。

构造

在一种修改过的 LN-Symmetry(假设 CSFSTEMPLATEHASH 可用)中考虑以下强制关闭路径:

  1. 一笔 kickoff 交易(使用 TRUC)(称为 “Tx1”),将通道资金移动到一个 kickoff 输出中(在启动阶段,使用 TEMPLATEHASH 来承诺这笔交易),并配有一个临时锚点输出。
  2. 一笔预先签名的窗口交易(也使用 TRUC,称为 “Tx2”),将资金移动到一个 update 输出中,这个输出由一个专属于该窗口的公钥 P_H 控制,不带临时锚点。这笔交易设定 nLockTimeH ,并将 nSequence 的第 21 比特置 1 ,也就是强制执行 100 个区块的相对锁定时间。
  3. 一笔或多笔 update 交易发布,最后是一笔最新的结算交易。
  4. 这笔结算交易承诺了一个非最终的 nSequence,并将 nLockTime 设为 H + C,其中 C 就是我们想要的挑战窗口。

关键思路:因为 nSequence 的第 21 比特得到强制执行,如果 Tx1 没有在 H − 100 区块高度之前确认, Tx2 就会过期。这就会让所有用 P_H 签名的更新交易过期,从而在结算交易上强制执行一种准合约层的锁定时间。

这种功能的代价是额外的交互和存储。每一次更新状态,通道的参与者要签名 N 笔更新交易(为从接下来 N 个区块开始的窗口)。如果没有为高度 H 预先签名的 Tx2 ,则这些交易也要签名。

通道参与者们要周期性地刷新最新状态的,做法是签名新的交易,承诺相同的状态输出。这保证总是可以使用一个强制关闭路径。

优化

一种启用向量承诺的操作码(即 PAIRCOMMITCAT,等等)可以启用默克尔证据验证,从而消除额外的交互和存储负担,并且无需刷新。这包括四项更新:

  1. 所有更新输出都由同一个公钥 P 控制。
  2. 窗口高度 H 在更新输出脚本中承诺。
  3. 参与者们签名一个许可接下来 N 个窗口的默克尔根,每一个叶子都承诺一笔更新交易的模板哈希值以及窗口高度 H
  4. 更新输出脚本通过默克尔证据来验证一个模板哈希值以及窗口得到了签名。

使用这种优化,通道可以轻松支持几百万个窗口,消除在实践中刷新的需要。

强制关闭流程

假设 Alice 和 Bob 之间有一条通道,Alice 现在想要强制关闭它。

  1. 首先,Alice 发布 kickoff 交易(Tx1),通知 Bob 自己要强制关闭。

  2. 假设 Tx1 在区块高度 K 得到确认。这会让所有其 nLockTime 值小于 K + 100 的窗口交易(Tx2)无效,这反过来,又让每一笔承诺了 nLockTime 值小于 K + 100 + C 的结算交易的更新交易失效(这里的 C 是双方同意的挑战期)。

  3. 在 100 个区块以后, Alice 选择任何一笔 Tx2 来发布,只要其 nLockTime 数值小于或等于当前的区块高度。

  4. 如果 Alice 是诚实的,那么她会发布带有最小有效锁定时间的那一个 Tx2 。如果 Alice 是恶意的:

    a)她会扣住 Tx2 、迫使 Bob 来发布。(Bob 必须发布在最早的有效窗口结束之前发布一个 Tx2 ,这样他才有机会阻止过时的承诺。)

    b)她将发布带有更高有效锁定时间的 Tx2,也即推迟结算。

  5. Alice 发布一笔更新交易,该交易承诺了一笔结算交易。

  6. 如果一笔更新交易是老旧的,Bob 发布表示最新状态的更新交易。

  7. Alice 在绝对时间锁解锁后发布结算交易。

安全性

这种构造继承了使用理想的 CLRT(合约级相对时间锁) LN-Symmetry 的绝大部分安全属性,但如果有向量承诺,它会更吸引人,因为向量承诺可以消除额外的刷新动作和交互需要。

而显著的局限性在于它会给强制关闭路径添加一个长达 100 个区块的时延。这回提高 HTLC 的最小存续时间,从而减少最大支付跳数。在实际场景中,绝大部分支付都将不受影响,因为总的 CLTV 预算是足够高的;不过,如有需要,总的 CLTV 预算也可以提高 100 。

结语

可以安全转发的可过期交易,似乎是一种非常有用的原语,而我发现的惊喜是,似乎只需一个很小的领域的共识变更,就能实现它 —— 将关注点转移到以输入来触发过期。

前述应用场景主要是为了举例说明,但它们似乎已经足够有趣,值得进一步研究。具体到 CLRT ,还有许多可能的构造,但这里描述的构造令人惊喜。CSFS + TEMPLATEHASH + nSequence 第 21 比特 + 向量承诺操作码,是梦幻组合。

我很乐于听到各位对这个想法的意见。如果能以可靠的方式实现、无需担心免费转发问题,交易过期方法是否值得重视?

快速回顾

“输入超时” 似乎是对这种原语的最好描述:

  • nLockTime 指明交易可以确认的区块高度。
  • nSequence 指明多少个区块以前输入就超时了。

这样描述,会更符合直觉。

这也能表明,它跟纯粹的 “交易超时” 是不同维度。它能带来类似的应用,但机制完全不同。

(完)