本文作者: yaning-u2
闪电网络主要解决了比特币的可扩展性问题,闪电网络可以分为两部分:双向支付通道和通道间支付,本文主要介绍如何在 CKB 上实现双向支付通道。
双向通道的主要分为以下几个流程:通道建立、余额更新、通道关闭。
其中交易包括以下几种:
- Funding Transaction,用于通道建立。
- Commit Transaction,用于更新余额。
- Exercise Settlement Transaction,用于关闭交易通道。
通道建立
通道建立是通过在链上广播 Funding Transaction 完成,Funding Transaction 是一个 2-2 的签名交易,为了方便我们将参与双方命名为 Alice 和 Bob。通道建立的步骤如下:
- 新建 Funding Transaction
- 新建 Commit Transaction
- 各自对 Commit Transaction 签名
- 交换 Commit Transaction 签名
- 各自对 Funding Transaction 签名
- 交换 Funding Transaction 签名
- 到链上广播 Funding Transaction
这里为什么要首先构造出 Commit Transaction 呢?因为如果 Alice 和 Bob 都对 Funding Transaction 签名后,任何一方都可以将交易广播到链上,但是花费 Funding Transaction 的 Output 需要双方的签名,如果任何一方不合作则会导致资金永远锁在 Funding Transaction 中,所以这里首先先构造出可以消费 Funding Transaction 的 First commitment Transaction 再对 Funding Transaction 签名,这样任何一方都可以从Funding Transaction中取出自己的资金,而不需要对手方去进行配合。
举例,Alice 提供一个 300 ckb 的 Input,Bob 提供一个 300 ckb 的 Input,然后 Output 为600 ckb,锁定脚本为 2-2 的多签脚本,需要 Alice 和 Bob 的签名才能花费 Output。
(闪电网络白皮书中的一张图,将就看)
由于 Funding Transaction 的 Output 是一个 2-2 的多签脚本锁定,所以我们可以先部署一个 2-2 的多签合约。
合约代码如下:
https://github.com/u2/lightni...
我们可以看到 CKB 是可以用 C 语言写合约的。在部署 Funding Transaction 之前,我们可以先部署 2-2 的锁定脚本,这样 Funding Transaction 直接引用 2-2 多签合约即可。这样如果在 CKB 中有很多双向支付通道,可以共享一个多签脚本,而不必每个支付通道单独在合约中包含多签合约,减少合约的交易手续费,降低 CKB 的存储空间的占用。
闪电网络实现的一个前提是,必须要解决交易延展性问题,否则交易的Hash是不确定的,可能导致 Funding Transaction 的资金无法解锁。比如 Alice 和 Bob 构造完成 First commitment Transaction(1a, 1b)并广播 Signed Funding Transaction,但是这个时候矿工修改了 Signed Funding Transaction 中 Input 的签名脚本,Funding Transaction 最终上链,但是交易 Hash和原来不一致。由于 First commitment Transaction 通过交易 Hash 来引用 Funding Transaction,此时找不到合法的 Funding Transaction,First commitment Transaction 将无法被消费,如果 Alice 或者 Bob 其中的任何一方采取不合作的方式,另一方将无法取出自己的资金。
比特币通过实现隔离见证解决了交易延展性问题,CKB 也实现了自己的隔离见证。在 CKB 中,用户只需要选择将签名等解锁参数放入交易中的witnesses中即可:
https://github.com/nervosnetw...
由于交易hash不包括用户解锁参数,
https://github.com/nervosnetw...
这样就可以是交易hash只包含Inputs和Outputs等交易处理的相关信息而不包含解锁脚本,解决交易延展性问题。
更新余额
Funding Transaction 被广播到链上之后,双向支付通道就成功建立了,后续 Alice 和 Bob 就可以构造离线的 Commitment Transaction 来更新双方的金额了。闪电网络的每一笔 Commitment Transaction 交易都是建立在 Funding Transaction 之上的。
在构造 Commitment Transaction 时,需要解决以下两个问题:
- 追责问题。因为每笔 Commitment Transaction 都是合法的,而且都是建立在 Funding Transaction 之上。Alice 和 Bob 都各有一份不同 commitment transaction,如果其中任何一方将历史 commitment transaction 发送到链上,可以对其进行罚没。
- 罚没机制。如果任何一方不遵循双向通道的机制,将历史交易发送到链上,则需要能将其资金进行罚没。
追责问题。解决追责问题,需要Alice和Bob构造两个不同的交易,我们可以将这两个交易分别命名为 2a,2b。假设最新的余额为 Alice 400,Bob 200,commitment transaction 具体的流程如下:
- 对于 Alice,其创建新的 Commitment Transaction 结构为,Output-0 为 400,花费条件为 Alice 需要在 10 个高度可以花费或者拥有 Alice2(Alice2 的新私钥)+ Bob 的签名可以立即花费。Output-1为 200,需要 Bob 的签名可以立刻花费。
- 双方交换签名。
通过这种机制,可以构造一种交易,Alice 如果广播其交易 a1 交易,则 Alice 可以在 10 个高度之后花费 Output-0,Bob 可以立刻花费其 Output-1。
在介绍罚没机制之前,我们先介绍下比特币中的时间锁机制。在比特币中使用 nLocktime 来对交易进行时间限制,使交易必须在某个绝对高度或者时间之后才可以被记录到链上,CHECKLOCKTIMEVERIFY 指令可以配合 nLockTime 一起使用。交易 Inputs 中的 nSequence 可以用来对相对时间进行限制,是某个 Input 必须在 Output 上链一定时间后,其所在交易才可以被打包,CHECKSEQUENCEVERIFY 可以配合 nSequence 来使用。
在 CKB 中使用 Transaction Valid Since 机制,即可以表示相对时间或者区块,也可以表示绝对值,所以可以起到 Bitcoin 中的 nSequence 和 nLocktime 作用,设计上更简洁。由于 CKB VM 可以通过 syscall 获取到交易 Input 中的 since 值:
https://github.com/nervosnetw...
所以可以在不增加任何指令的情况下实现相对时间锁的作用。
例如代码
https://github.com/u2/lightni...
表示取出 Input 中的 since,并判断其是否是使用相对块高度,且必须在相对块高度大于 10 是才验证通过。这里保证了交易 Input 在当前条件下,只有在 10 个高度以后,才能被消费。
我们现在回到罚没机制上来,在上面的基础上,又加入了 Breach Remedy Transaction 机制,用来防止 Alice 或者 Bob 发送历史交易到链上。这样从建立通道到更新余额的完整流程为:
- Alice & Bob 构造完成各自的First commitment Transaction(1a,1b),结构如上所示
- 广播 Funding Transaction 到链上。
- Alice & Bob 更新余额,构造各自新的 commitment transaction (2a, 2b),并交换签名
- Alice & Bob 分别向对方揭示 Alice2,Bob2 私钥(上一个 commitment transaction 中所用解锁output的私钥)。
在这种情况下,因为 Alice 拥有 Bob 的 Bob2 私钥,而 Bob 拥有 Alice 的 Alice2 私钥。如果 Alice 广播历史交易 a1,其交易 Output-0 可以被 Alice2 + Bob 的签名理解花费,或者可以被 Alice 在 10 个高度后消费。在这种情况下,因为 Bob 已经拥有 Alice2 的私钥,所以 Alice 没有动机去广播历史交易 a1。同理对于 Bob 也适用。我们称这种 commitment transaction 为可以撤销的 commitment transaction(Revocable commitment transaction)。其 commitment transaction 的锁定脚本可以称为 revocable maturity script,代码:https://github.com/u2/lightni...
Alice 和 Bob 每次更新余额,都会构造一个新的可以撤销的 commitment transaction,并向对方揭示自己上一个 commitment transaction 的 output-0 的私钥,这样自己的历史 commitment transaction 不需要保存,只需要保存对方揭示的私钥即可。双方都有检测链上对方是否有广播历史交易,如果对方有广播历史交易,可以使用自己的私钥和对方揭示的私钥构造 Breach Remedy Transaction 立即花费对方的 Output,这样可以将罚没对方的资金。
(闪电网络白皮书中的图,可以将就看)
关闭通道
Alice 和 Bob 可以选择合作构造 Exercise Settlement Transaction(ES transaction)来选择关闭通道。ES transaction 是没有任何时间锁的交易,这样 Alice 和 Bob 可以选择直接广播交易来花费 Funding transaciton。
总结
在作者第一次实现的时候,和以上方案略有不同,并没有 revocable maturity 脚本,而是在构造每个 commitment transaction 时,双方各自事先构造好其上一个交易的 Breach Remedy Transaction 并互相交换签名。这种情况下,Alice 和 Bob 不需要生成很多私钥,但是缺点是 Alice 和 Bob 需要保留历史的 Breach Remedy Transaction,因为交易比私钥更占空间,相对而言这种方案并不够合理。
在 CKB 中,实现了隔离见证以及灵活的时间锁机制,且用户可以通过提前部署闪电网络的相关合约,用更低成本来建立双向支付通道。对实现细节感兴趣的读者可以阅读下面项目中的代码。另外 CKB 中可以通过定义 Type Script 而实现 User defined Token 的闪电网络,这里不再赘述。
项目地址:https://github.com/u2/lightning
相关文章:《漫谈闪电网络》
比特币时间锁机制相关 Bips:
- https://github.com/bitcoin/bi...
- https://github.com/bitcoin/bi...
- https://github.com/bitcoin/bi...
CKB 的时间锁机制:https://github.com/nervosnetw...
闪电网络白皮书:https://lightning.network/lig...