作者:Jesse Posner、ZmnSCPxj
来源:https://lists.linuxfoundation.org/pipermail/lightning-dev/2023-January/003810.html
引言
将资金从链上地址转移到闪电网络上需要很长时间,尤其是你想实现信任最小化的话(这会将零确认解决方案排除在外)。
大体而言,为了降低链上交易的信任要求,链上收款方必须确保创建其 UTXO 的链上交易得到确认。
在现实生活中,链上交易至少需要得到 3 个区块确认,因为 2 个区块的重组时常发生。然而,鉴于挖矿的随机性,即使只是 1 个区块确认也需要耗费很长时间。
就手机场景而言,这个问题尤为突出。由于手机依靠电池运行,对于用户未打开的应用程序,手机的操作系统通常会极大地限制 CPU 和其它资源消耗。
现在,假设有一个同时支持比特币区块链和闪电网络的钱包应用,用户接收付款时的经历如下所示:
- 用户想要在比特币区块链上收款。
- 用户通过钱包应用生成一个地址,将其提供给付款方,以便接收付款。
- 用户关闭钱包应用,手机的操作系统会中断该应用的所有线程。
- 付款方通过区块链发送付款时,用户已经入睡。
- 区块链交易得到确认,时间继续流逝。
- 用户醒来查看区块链浏览器,看到自己的钱包地址上已经有资金到账。
- 用户打开自己的钱包应用,决定给自己买杯咖啡,于是通过闪电网络付了钱。
但是,若使用现有解决方案,将资金从区块链转移到闪电网络,上述流程将需要很长时间才能完成,可能要以数十分钟乃至几个小时来计量,因为需要区块确认:
- 通道开启:需要确认。很多节点需要 6 个或更多区块确认。
- 潜水艇互换/peerswap:需要确认。确认后,互换服务才能通过闪电网络发送 HTLC。
- 通道拼接(Splice-in):通道始终处于可操作状态,但是直到拼接交易确认为止,通道都在“双重模式”下运作。在这种情况下,拼接前后的状态均有效,这意味着 splice 前的通道金额和 splice 后的通道金额中的较低者才能被使用。就 splice-in 而言,splice 前通道内的金额更低,因此充值金额只有在 splice 交易确认后才到账。
在此,我们将介绍一种新型协议 “swap-in-potentiam”,能够在上述案例中快速将资金从区块链转移至闪电网络。
优势与局限
为了吊起你的胃口,我们先介绍 swap-in-potentiam 协议的优势:
- 能够即时将已确认收到的链上资金转移至闪电网络。
- 能够将链上资金转移至另一个链上地址(以一般的链上确认规则为准)。如果收款方愿意承担零确认链上交易的风险,就能实现“即时”转账。
- 最大程度降低了信任要求。
为了让你明白这是一项技术而非魔法豆(同时也是为了避免过度吹嘘这项技术,因为比特币的媒体报道经常会过分夸大新技术,却避而不谈它们的缺陷),我们再来看看它的缺陷:
- 需要与一个闪电网络服务提供商(LSP)合作。如果该 LSP 离线或拒绝合作,链上资金会锁定一段时间。此处会设置一个超时时间(如果 LSP 一直不上线,你只需等待超时即可),起始时间是收到的 UTXO 在区块中确认之时。因此,这不会造成资金丢失,只会贻误时机(即,“非自愿长期持有”)。
- 如果你有多个 LSP,当你生成地址时,你必须选择其中一个 LSP。你无法先向多个 LSP 承诺交易、等你的手机开机时再选择其中一个 LSP。这会加剧上一个缺陷,因为你必须选择一个 LSP,并祈祷当你开机时你选择的 LSP 也同时在线并提供配合。
- 链上收到的资金依然需要先得到区块确认。对于很多仅支持区块链的钱包来说通常就是如此,因此这并没有让情况变得更糟,但也没有起到改进的作用。
- 如果即将超时,操作必须在链上执行,而且需要确认。
Swap-in-Potentiam
如上所述,所有链上资金转移都需要确认,资金从链上转移至闪电网络的交易也不例外。
如果钱包提供的链上地址仅受该钱包的控制,凡是需要跟某个闪电网络参与者互动的操作(如,开通通道、互换和 splice),都需要再发起一个承诺给该闪电网络参与者的链上交易。只有当新的链上交易得到确认时,闪电网络参与者才能依赖该交易的输出,而无需信任发起人。
据此,我们可以思考这样一个问题:如果钱包提供的是一个已经向某闪电网络参与者做出承诺的地址呢?
果真如此的话,当钱包在区块链上收到资金时,“确认计时器” 就会立即启动,而不是等到钱包决定将资金从区块链转移到闪电网络的那一刻(才启动)。
对于移动钱包来说这是很大的区别:移动环境不支持移动钱包长时间在线。因此,在用户打开移动钱包应用之前,移动钱包可能没有任何 CPU 资源能够做出将资金从区块链转移到闪电网络的决定。
人们已经普遍接受了一个事实:由于手机环境的限制,支持闪电网络的手机钱包离不开 LSP 的配合。因此,能够在区块链上收款并通过闪电网络付款的移动钱包可以将交易承诺给另一个闪电网络参与者(必须是与此钱包之间存在支付通道的 LSP)。
然后,当移动钱包应用处于前台且可以使用 CPU 资源时,就能发起与其 LSP 之间的单跳互换交易。只要移动钱包发现资金已经得到确认,即可马上与其 LSP 进行单跳互换交易。LSP 可以立刻处理该互换交易,将资金记入通道余额,同时以原子方式确保只有自己能够取走对应的链上 UTXO。
合约
该合约有两个参与者:资金所有者 Alice 及其潜在互换对象 Bob。
一旦承诺了该合约的地址确认已收到 任何 资金,Alice 就是这笔资金的所有者,可以(在 Bob 的配合下)随心所欲地处置它。
资金的来源不需要是 Alice,可以是有义务经由区块链向 Alice 付款的第三方。
该合约只有两个分支:
- 多签名分支:Alice 和 Bob
- 时间锁分支:Alice 加上一个以周为单位计量的相对时间锁(
OP_CSV
)
聪明如你想必已经发现,这实际上就是一个 CLTV 式单向限时通道[1](此通道本身就是 Spilman 式通道的变体)的变体:
- 使用显式操作码简化通道设置(无需提前签署 Alice 和 Bob 之间的超时交易,可以直接将资金发送至地址)。
- 使用一个相对时间锁而非绝对时间锁,以便随时向通道地址充值(即,接收准备在闪电网络上花费的链上资金)。
可支持的应用场景如下:
- 假设 Alice 想要向另一个链上地址付款,同时 Bob 也在线并予以配合, Alice 可以请求 Bob 协助签署 多签名分支,以便通过任意链上方式转移资金。
- 假设 Alice 想要支付 闪电网络发票/keysend,且出站容量不够(但总容量 足够),她可以跟 Bob 互换 链上/链下 资金:提供通过多签名分支花费的交易,实例化一个新的链上 HTLC,由 Bob 通过闪电网络转发。一旦 Alice 提供了交易的签名,Bob 可以立即在通道内向 Alice 提供一个闪电网络内的 HTLC,然后 Alice 可以立即处理(从而将资金发送到闪电网络上)。
- 如果 Bob 离线或不配合,Alice 可以在时间锁分支内的超时时间到达后单方面找回资金。
此处只需要 Alice 信任 Bob 会与之配合,以便 Alice 立即处理资金。如果 Bob 是不可信的,Alice 可以在超时时间到达后通过时间锁分支找回资金。
Bob 没有窃取资金的余地(实际上,比起窃取 swap-in-potentiam 的资金,窃取闪电网络的资金对 Bob 来说更容易)。
在这个场景下,移动钱包方是 Alice,LSP 方则是 Bob。
Bob 的安全性
Bob 必须确保的是,对于每个 UTXO,要么签署任意链上交易(即,上述第一个应用场景),要么得到来自该 UTXO 的链上 HTLC。无论 Alice 要求 Bob 配合上述哪一种情况,Bob 必须确保自己没有签署另一种情况(收到过一种情况的请求之后,Bob 就必须拒绝配合另一种情况)。
此外,Bob 必须确保的是,如果实在“通道”场景中(即,上述第二个应用场景),距离时间锁分支的超时时间还有很长一段时间,长到那时使用多签名分支的花费可能已经确认完毕。
只要 Bob 做到了上述两个保证,就可以确保如果 Alice 通过多签名分支请求互换交易,只有 Bob 能够花费该 UTXO(至少是在超时前)。因此,Bob 无需等待链上确认即可安全地向 Alice 提供闪电网络 HTLC。
在上述第一个应用场景中,Bob 需要知晓 UTXO。因此,实现第一个场景时无法使用盲签名技术。
从根本上来说,收到签名请求时,Bob 必须使用 Alice 提供的数据生成整个 SIGHASH
,这样 Bob 就可以追踪自己签名的 UTXO。
远程互换
虽然 Bob 通常被认为就是移动钱包方 Alice 的“那个”LSP,闪电网络协议实际上并没有要求 Bob 是 Alice 的直接对等节点。
真正的要求只有 Bob 要能通过闪电网络向 Alice 付款,以换取等额的链上资金。
由此可见,既然移动钱包已经依赖于一个或多个 LSP,它很有可能会选择直接 LSP 而非远程节点。
地址派生
swap-in-potentiam 地址可由一个根 公/私钥 派生而来。
我们只需要 Alice 和 Bob 各提供一个密钥对。Alice 可以使用标准密钥派生路径来生成其密钥对。
由于 Bob 打算成为 LSP,我们可以直接使用其闪电网络节点 ID 作为公钥。Bob 需要拥有对应的私钥,才能建立 BOLT 8 加密传输。
由于 LSP 是公共网络的一部分,Alice 可以尝试搜索所有公开支持 swap-in-potentiam 的节点。或者说,如果该钱包有一列固定 LSP 可用,可以直接参考该列表。
因此:
- Alice 使用一个派生的密钥对。
- Bob 使用一个固定的密钥对(其闪电网络节点 ID)。
有了上述两个密钥对,我们就可以从 xprv
或 xpub
根密钥派生出 swap-in-potentiam 地址。
LSP 如何利用 Swap-in-potentiam
虽然在最初的 swap-in-potentiam 设计中移动钱包是 Alice,LSP 是 Bob,但是事实证明 LSP 可以提供特殊服务来帮助作为收款方的移动钱包。
假设 LSP 持续追踪统计数据,知道哪些移动钱包客户端有可能成为净收款方。
净收款方的入账容量通常很低(因为其入账容量已经在前几次闪电网络收款时用尽)。
在链上费用处于低谷期间,LSP 可以查看哪个离线移动钱包客户端的入账容量低,且未来有可能上线收款。在这种情况下,LSP 可以在移动客户端的帮助下将资金转入 swap-in-potentiam 地址:LSP 就是“Alice”,移动客户端则是“Bob”。这至少可以让 LSP 在低费用时期搞定一半的互换交易。
如果向 swap-in-potentiam 地址的转账在移动钱包客户端上线时得到确认,LSP 可以立即发起互换交易,为该移动客户端提供入账容量。这样一来,互换交易可以立即完成,移动钱包客户端也可以立即通过闪电网络收到资金。
尤其值得注意的是,如果 TheBlueMatt 设计的“离线收款”成功实现,LSP 将提前知晓离线移动客户端将获得支付。LSP 可以查看离线移动钱包是否有足够多的入账容量来完成收款。如果没有的话,LSP 就可以在客户端的配合下向 swap-in-potentiam 地址转入资金。然后,当移动钱包客户端上线时,LSP 可以立即发起互换交易。一旦互换交易完成(移动钱包有了足够的入账容量),LSP 即可联系作为付款方的 LSP 完成付款。
需要强调的是,就上述应用场景而言,只要移动钱包客户端在前台运行,并且拥有 CPU 时间,即可实现 即时 收款,无需零确认交易以及任何类型的半托管式信托,即使移动钱包客户端的入账容量不足。支付通道仍需事先创建好(无需是零确认通道,如果不想将资金委托给 LSP 的话)。
实现概述
初步构想是使用 Taproot 和 Schnorr 签名,但是 不 使用 keyspend 路径(至少在初始阶段是这样)。
目前的计划是将 MuSig(A, B)
用作内部公钥,并通过显式方式将分支变成 tapleaf。换言之,有两个 tapleaf 脚本分别对应上述两个分支:
<A> OP_CHECKSIGVERIFY <B> OP_CHECKSIG
<timelock> OP_CHECKSEQUENCEVERIFY OP_DROP <A> OP_CHECKSIG
使用显式 2/2 分支而非 MuSig 可以让协议变得更简单:我们可以让 Alice 在一个半轮中使用 A
发送签名, 无需完成 2 轮 MuSig2 签名流程。
我们之所以考虑使用 Taproot,是因为移动钱包客户端可能需要使用 2/3 或 2/2 签名方案,就像 Blockstream Green 那样。
这样一来,无论是合约中的 Alice 还是 Bob 都可以秘密成为 FROST 2/3 或 MuSig 2/2(或任何 FROST k/n 或 MuSig n/n)。
这也是避免 Alice 和 Bob 之间 2/2 MuSig keyspend 路径的另一个原因,因为(据我们所知)没有公开审查的安全性证明来证明 FROST-in-MuSig 和 MuSig-in-MuSig (或在签名流程中使用了 MuSig2 的相应变体)是否安全。
之后,等到我们对于在 MuSig2 中使用 MuSig2 和 FROST,以及与可能的非受信外部人员一起使用 MuSig2(如果我们在设计 MuSig2 签名协议的实现时出现差池,就有可能给他们留下可乘之机)更具信心时,我们就可以无缝升级 MuSig2 签名协议来使用 keyspend 路径,以节省见证信息的字节。
就链上应用场景(即,Alice 想要将 UTXO 发送至某个链上地址)而言,Alice 和 Bob 之间的协议会如下所示:
request_arbitrary_signature
Alice->Bob:要求 Bob 签署一个使用链上分支花费 swap-in-potentiam 地址资金的 PSBT。response_arbitrary_signature
Bob->Alice:响应上述要求,返回所需签名。reject_arbitrary_signature
Bob->Alice:在 Bob 拒绝合作的情况下作为对request_arbitary_signature
的响应发送给 Alice(例如,在下述通道应用场景中,Bob 已经接受了 Alice 想要花费的 UTXO)
就通道应用场景(即,Alice 想要将 UTXO 发送给闪电网络收款方)而言,我们使用基于以下两个状态的 Spilman 式通道来处理 swap-in-potentiam UTXO:
- HTLC-offering:将金额为
N
的 HTLC 发送给 Bob,剩余金额则发送至 Alice 的找零地址。 - Resolved:直接发送金额
N
给 Bob,剩余金额则发送至 Alice 的找零地址。
初衷是一开始就将通道置于 HTLC-offering 状态。然后 Bob 通过通道向 Alice 提供对应的闪电网络内 HTLC。Alice 完成闪电网络内 HTLC 后,就可以针对 Resolved 状态发送一个新的签名。一旦通道处于 Resolved 状态,Bob 应 签署最后的状态并将其广播到链上,从而关闭 Spilman 式通道。
通道应用场景的协议消息如下所示:
request_swap_in
Alice->Bob:将 UTXO 连同花费 UTXO 的 swap-in-potentiam 地址、往 Alice->Bob 通道方向充值多少数额,转移至哪个通道,以及 Alice 的找零地址(可选)告诉 Bob。reject_swap_in
Bob->Alice:在 Bob 拒绝合作的情况下作为对request_swap_in
的响应发送给 Alice(例如,待花费的 UTXO 之一已经通过response_arbitrary_signature
签署,或者 Bob 无法合法接受一个或多个待花费 UTXO 的资金控制。accept_swap_in
Bob->Alice:作为对request_swap_in
的响应发送给 Alice,包含状态变为 Resolved 后用于接收资金的 Bob 侧地址。swap_in_signed
Alice->Bob:对accept_swap_in
的响应,包含 Alice 侧对 HTLC-offering 状态交易的签名。一旦 Bob 收到该响应,Bob 就可以安全地使用 BOLT1update_offer_htlc
构建一个新的基于闪电网络的 HTLC。swap_in_resolved
Alice->Bob:在 Alice 已经通过对应的基于闪电网络的 HTLC 的update_fulfill_htlc
获得资金后发送,包含 Alice 侧针对 Resolved 状态交易的签名。
Resolved 状态交易将 UTXO 发送至 accept_swap_in
中给出的 Bob 侧地址,并将找零发送至 request_swap_in
中给出的 Alice 侧找零地址。
此处的计划是只保留一个奇数 BOLT1 消息 ID,并将实际的 swap-in-potentiam 消息 ID 作为头两个字节嵌入到 BOLT1 消息中,以减少有限的 BOLT1 消息 ID 空间带来的污染,同时也让 swap-in-potentiam 能够更加灵活地在其消息 ID 空间内扩展新的消息。
为了让协议变得更加丰富,我们会考虑如何将 swap-in 和 splice-in 相结合,以应对通道的当前总容量低于可用链上资金的情况。swap-in 能够将资金即时记入通道余额(但不能超过当前总容量),额外资金可以通过 splice-in 添加至通道(只有当 splice-in 得到确认时才会被记入通道余额)。
我们也可以使用 openv1 实现同样的效果。其中,swap-in 与通道开启相结合,swap-in 即时记入通道余额,通道开启则需要等待确认:
- 目前,Alice 和 Bob 之间有一条或多条通道。Alice 在 swap-in-potentiam 地址上有一个 UTXO,其值超出了现有通道的入账容量。
- Alice->Bob
open_channel
。 - Bob->Alice
accept_channel
。 - Alice->Bob
request_swap_in
,找零地址是通道的充值地址。 - Bob->Alice
accept_swap_in
提供充值交易的 TXID(Alice 知道找零地址和 Bob 的最终 Resolved 地址,因此知道最终 Resolved 状态交易的 TXID)。 - Alice->Bob 带有 TXID 的
funding_created
。 - Bob->Alice
funding_signed
。 - Alice->Bob
swap_in_signed
向 Bob 提供花费 swap-in-potentiam 地址上资金的签名。 - 然后,Bob 基于现有通道构建一个由 Alice 认领的 HTLC,揭示了原像。
- Alice->Bob
swap_in_resolved
. - 然后,Bob 广播 Resolved 状态交易,该交易也是新通道的充值交易。
- Alice 和 Bob 等待交易确认后使用新的通道。Alice 依然可以使用现有通道,现有通道已经通过 swap-in 换取了新的出站容量。
(上述流程并不安全,因为 Bob 可以使用 HTLC-offering 状态交易完成协议。这个问题的解决方案是让 Alice 开启两个通道并往里充值等额资金,一个使用 HTLC-offering 状态交易充值,另一个则使用 Resolved 状态交易充值,然后在未确认交易所对应的通道上“发生错误”。这就留作各位读者的课后练习。但是,请注意在 HTLC-offering 和 Resolved 状态下,Alice 的找零地址必须区分开来,我们可以为协议进行配置。)
(完)