通用支付通道适用于其它在 Layer 1 上的资产和 DApps,这种可组合性让我们可以创造出一些有趣的使用范例,例如将稳定币进行通道化。

原文标题:《一种通用的支付通道结构及其可组合性》
撰文:谢晗剑(Jan Xie),Nervos 首席架构师,秘猿科技首席执行官,前以太坊核心研发团队成员
翻译:史迪仔,Williams

通道网络(Channel Network)可能是 Layer 2 舞台上最耀眼的角色。一个广泛部署的通道网络可以最大化交易吞吐量(无上限!),可以最小化交易处理延迟(可以和连接各方的网络一样快),可以增强交易的隐私性,甚至可以为区块链提供一定的互操作性。简而言之,如果运用得当,通道网络将会是解决问题的灵丹妙药。

不同于基于零知识证明的协议或者其他 Layer 2 链,通道网络的基本思想非常简单:使用直接连接双方的网络进行链下的交易互换,并确保这些交易在区块链上都是可执行的,然后通过这种点对点的连接共同组合成一个网络,在这个网络中的任意两个点之间都有一条或者多条路径连接着它们。尽管通道网络自身也存在一些问题,比如流动性效率和路径问题,但这些问题都是可以解决的,或者说是有近似的最优解的。

相比于 Layer 1,Layer 2 协议在部署之后相对是比较容易升级的,因此实现通道网络的最佳方案就是先启动它然后再逐步发展改进它。闪电网络就是一个这样的典型案例。本文将介绍一种简单的、可组合性强的基于 CKB 网络的通用支付通道结构,希望能够引起更多的研究和关注,引发更多关于通道网络的讨论,促进 CKB 上通道网络的建设和发展。

介绍

支付通道网络(Payment Channel Network 缩写:PCN)是一个去中心化的支付网络,用来支持全球支付。通道网络的设计主要包含两个方面,第一:通道协议,它规定了两个用户在一个直接相连的通道内的链下信息传递、链上合约和链上链下交互;第二:网络协议,它规定了如何将多个直接相连的通道组合成一个较长的通道,如何在两个用户之间找到一条路径等等。本文主要讨论一种在 CKB 上的通用支付通道结构。它是通用的,因为它基本适用于 CKB 上的任意一种资产。

通道结构最主要的问题在于,如何确保只有最新的通道状态才会在链上被认为是有效的。闪电网络使用的 Poon-Dryja 结构 [1] 是基于撤销惩罚实现的,这给双方带来了复杂的密钥管理和状态管理,进而增加了正确实现协议的难度和节点操作的风险。Decker-Wattenhofer 结构 [2] 提供了一种基于时间戳的状态更新机制,缺点是只允许有限数量的状态更新,这意味着 Layer 1 和用户都需要付出更高的成本,因为通道必须定时关闭和开启。Decker-Russell-Osuntokun 结构的 eltoo[3] 是一个优雅的基于版本(Replace by Version[4])的解决方案,但是它需要在比特币中引入一个新的操作码 SIGHASH_NOINPUT。以太坊提供一个有状态的图灵完备的智能合约模型,这使得构造基于版本的解决方案更加容易,如 Sprites[5],ForceMove[6] 以及 Perun[7]。

译者注,Replace by Version 缩写 RbV,每个状态有一个表示状态版本的单调递增的计数器。在发生争议时,经过授权的具有最高版本的状态被认定为最新状态,如果另一个新的状态具有较大的版本号,那么它将替换先前的状态。

结构

这里描述的通用支付通道结构主要是指一个基于 CKB 的 eltoo 端口。然而,我们将看到这样一个几乎相同的想法在 CKB 上不仅更容易实现,而且还展现出比特币 / 以太坊的支付通道方案中不存在的可组合性等优点,只是在设计上稍微有一点点谨慎。通道的可组合性是指通道结构可以适用于其他在 Layer1 上的资产和 dapps。通过通道的可组合性,我们可以为所有的 Layer 1 的资产构建一个通用的支付通道网络。

通用支付通道结构由一个通用支付通道 lock 和三种不同类型的交易组成。

通用支付通道 lock (GPC lock)

GPC lock 是通用支付通道的关键组成部分,它是通道内的 cells 使用的 lock script[8]。它有 5 个参数:

  1. state:锁状态,一个 OPEN 和 CLOSING
  2. timeout:关闭周期长度
  3. pubkey_a:Alice 的公钥
  4. pubkey_b:Bob 的公钥
  5. nonce:一个单调递增的无符号整数

一个通用支付通道可以被建模为一个简单的有限状态机,包含了三种状态:OPEN (打开), CLOSING (关闭)和 SETTLED (结束)。OPEN 是初始状态, OPEN 和 CLOSING 分别用 GPC lock 的 OPEN 和 CLOSING 状态进行表示,而没有 GPC lock 就表示为 SETTLED 。

谢晗剑:简述 Nervos 通用支付通道原理与可组合性

当 GPC lock 被解锁时,状态将会发生转变。当 GPC lock 的状态为 OPEN 时,它可以在两种情况下被解锁:双向关闭和单向关闭。(关于双向关闭和单向关闭的具体流程,请参见原文,此处不影响阅读)

交易类型

出资交易—— 一笔出资交易就是打开了一个新的通用支付通道。它有一个 funding output ,该 output 的 lock 就是一个状态为 OPEN 的 GPC lock。

关闭交易—— 一笔关闭交易会消费 funding output 或者另一笔关闭交易中的 output。它会有一个 closing output,该 output 的 lock 就是一个状态为 CLOSING 的 GPC lock。它总是会有一笔匹配的结算交易。

关闭交易的独特之处在于它们并不会固定于一个先前的 outpoint。当一笔关闭交易被签名时,它的输入被排除在哈希过程之外,因此签名只会包括 outputs。这个特殊的哈希过程被称为无输入哈希(no-input hash[9])。例如,当用户想要签署一笔关闭交易时,他 / 她会序列化输出并对其进行哈希,然后在哈希结果上进行签名。这样在结束交易中的输入不会影响签名交换。正因为一笔关闭交易可以附加到一笔出资交易或者另一笔关闭交易,并且可以在关闭交易签名之后的一段时间后再确认连接。这样的操作可以在 CKB 上直接实现,无需添加新的操作码,因为一笔 CKB 交易的哈希和签名是完全可定制的。

GPC lock 情况 1 (单向关闭),它要求关闭交易的 output 0 必须与其 input 0 一致,除了它们 lock 中的 nonce 和 state,这意味着它的 input 0 只能连接到先前的使用类似 GPC lock 的 output 作为它自己的 output 0 (timeout,pubkey_a 和 pubkey_b 需要一致)。

结算交易—— 结算交易消费了一个 closing output 并关闭了一个通用支付通道。它有两个输出,分别将资金给到 Alice 和 Bob。它总是会有一笔匹配的关闭交易。

示例流程

这里我会给出一个交互流程的示例。它只是一个用于解释的草拟协议,而不是一个包含确切步骤的真正的协议。草拟和真正协议的关键区别在于在每次状态转换(即每个关闭 / 结算交易对)上是如何达成一致的。考虑到并发性和公平交换 [10] 等问题,两方协议的问题要比听起来复杂的多。当然也是存在解决方案的,但是它已经超出了本文的范畴,并且和通用支付通道结构毫不相关。我只是假设在必要时会使用这样一种合适的安全协议,例如在交换签名的时候。

后续涉及具体流程,包括:开启通道、发送交易、关闭通道等操作,建议前往译文或者原文,以获得更佳的阅读体验。

基本原理

在出资交易被签名之前,任何一方如果不想再继续,都可以在没有任何损失的情况下停止。如果出资交易已经被签名,但是通道的最初开启人还没有广播它,则另一方依旧总是可以发送另一笔交易,消费和等待被广播的出资交易相同的输入,从而使之无效。

一旦出资交易上链并且通道已经打开,通道的任何一方都可以发起单向关闭,如「关闭通道:糟糕的」中所述,由他 / 她自己关闭所有通道,如果对手发起单向关闭,一方可以等待关闭超时或更新 GPC lock,前提是他 / 她需要有一个 nonce 值更大的余额状态。关闭期为任何一方提供了一个可以提交其最新状态的窗口,在结束时通道将以 nonce 值最大的状态进行结算。

可组合性

通用支付通道结构可以适用于任意一种 CKB 上发行的 token,无论它是原生的 CKByte 还是 UDT。此结构只涉及 cell 的 lock,cell 的 data 和 type 字段将留给智能合约开发者们去进行处理。这个小小的接口使得它可以兼容任何只使用了 data 和 type 字段的合约。sUDT[11] 就是这样的一种合约,未来的 UDT 标准也将是如此。一个 UDT 的开发者将不需要再去考量如何将 UDTs 通道化,因为它随时都可以在不需要做任何改动的情况下被通道化。这代表 UDTs 生来就是可通道化的,并且通道化和扩容 UDTs 的开发成本是「0」。所以 UDTs 不只是 Layer1 上的一等公民,也是 Layer2 上的一等公民,因为它可以在不做任何改动的情况下就在 Layer1 和 Layer2 之间自由的流入流出。这也给予了支付通道网络最大的流动性,因为所有 Layer1 上的资产都可以在无摩擦的情况下加入 Layer2 的池子中。

这和比特币和以太坊上面的支付通道完全不同。比特币上的支付通道只有比特币可以运作,因为比特币是唯一的资产,并且这个区块链的能力是有限的。至于以太坊上虽然有非常多种的资产,但是它的 ERC 20 代币和通道的可组合性却是受限的。支付通道必须要和 token 通过预先已经定义好的接口进行交互,但这些接口对于不同的 token 而言可能是不同的。举例来说,Celer 才刚以硬编码的方式支持 ETH 和 ERC20 代币并且需要依赖它的 approve/transferFrom 的机制 [12]。即使 ERC token 遵循同一个标准接口来实现通道化,可能仍然是不安全的,因为接口的语意可能是不一样的,近期的 Uniswap + ERC777 事件 [13] 已经说明了这一问题。相反的,CKB 上的 GPC lock,根本不需要去管 UDT 具体是如何实现的。

通用支付通道结构也能够在 anyone-can-pay[14] 的 lock 中运行,也就是说当关闭交易的时候可以用任意的 lock 作为输出。通用支付通道和 UDTs 的可组合性,对于用户而言是更便于使用的。例如,他们在打开通道前不需要用「Approve」方法来进行授权。他们的资金可以像往常一样安全地放在自己的钱包里面,在任何需要的时候通过单笔签名交易将资金发送到通道中即可。

这个结构的可组合性让我们可以在 CKB 上创造出一些有趣的使用范例,例如将稳定币进行通道化。不论你是算法稳定币还是基于信任的稳定币,它从第一天起就是支付通道网络上的一等公民。稳定币的支付通道网络可以带给用户超越 Visa,Paypal 以及支付宝的顺滑体验,因为它是快速的,可扩展,无需许可的并且有未来的 —— 我说的是基于 APIs 和 IoT 的流支付!

双方共同出资

以太坊上的支付 / 状态通道当通道被打开时也面临了原子性的问题,因为以太坊上的交易只有一个发送方,但通道的资金却可能是来自双方的。为了允许双方共同出资, 以太坊的通道可以使用两个独立的出资交易,这也会给交易的对手方带来风险,因为一方可以在另一方已经承诺出资之后终止通道,或通过使用更多的链上交易来模拟原子双方共同出资 [15],从而增加未来打开新通道的成本,同时这又会取决于 ERC20 特定的接口。CKB 上的通用支付通道的出资交易可以拥有多个输入,自然地就可以支持双方共同出资。

有状态的锁定模式

通用支付通道的结构只依赖于 lock script。lock script 是有状态的,它模拟了一个简单的状态机,然后在 lock script 中使用 args (而不是 cell 中的 data) 来维持内部的状态。当需要一个适度复杂的状态 lock 时,也可以在其他地方应用这个模式。

对于具备更复杂的状态机的 lock,有两种替代方案可以考虑。第一种是将 data 变成是一个在 lock script 和 type script 之间可以共享的字段,这需要预先定义好数据格式,以便每个脚本可以读 / 写自己的数据,而不与其他脚本发生干扰。第二种方式是使用一个单独的 cell 来维持 lock 的状态,并且将锁定的 cell 和锁定状态的 cell 一起转换。这两种方案是最明显的,但绝对还有其他可能的模式。

未来工作展望

通用支付通道是一个简单的结构,灵感源自 eltoo,然而它不仅在 CKB 上更容易实现,而且还免费获得了额外的优异性质,如上所述。cells 的「状态对象」的本质天然适合通道网络和 Layer 2,我希望关于通道网络的研究可以吸引到的来自社区的关注。这里还有一些有趣的问题:

状态通道—— 状态通道是支付通道的一种泛化,用于支持智能合约的链下执行。GPC lock 不包含支付的特定结构,这会是状态通道建设的一个良好的开端。

通道网络—— 如何连接通用支付通道,进而任何人都可以通过一些中间节点就可以直接或者间接地向通道网络中的任何一个人进行支付?我们是像闪电网络一样使用 HTLC 还是有什么更好的解决方案?

瞭望塔—— 如何通过一种无需信任的方式将纠纷委托给第三方?这样通道的参与者就可以离线去度假了

路径—— 如何找到通道网络中两个点之间的最佳路径?考虑到通道的开启 / 关闭和通道容量的变化,整个网络处于不断变化的状态中,而节点对于整个拓扑网络仅拥有部分信息。「最佳」的定义也因人而异。由于这些限制,开发几种不同的路径策略并将选择权留给用户可能会更加合理。这一领域有很多有趣的论文。

多部分支付—— 一次支付的金额越大,就越难找到一条有足够容量的路径来传递它。多部分支付的概念是将大额支付拆分成数笔小额支付,并通过不同的路径将它们发送到同一个目的地,并确保小额支付们被全部接受或者全部拒绝。

引用资料

[1]https://lightning.network/lightning-network-paper.pdf

[2]https://tik-old.ee.ethz.ch/file/716b955c130e6c703fac336ea17b1670/duplex-micropayment-channels.pdf

[3]https://blockstream.com/eltoo.pdf

[4]https://pdfs.semanticscholar.org/4d5b/9fb1c4205b61060117e3c71b04464c2a1c77.pdf

[5]https://arxiv.org/pdf/1702.05812.pdf

[6]https://magmo.com/force-move-games.pdf

[7]https://eprint.iacr.org/2017/635.pdf

[8]https://docs.nervos.org/technical-concepts/architecture.html#lock-script

[9]https://github.com/bitcoin/bips/blob/master/bip-0118.mediawiki

[10]https://pdfs.semanticscholar.org/208b/22c7a094ada20736593afcc8c759c7d1b79c.pdf

[11]https://talk.nervos.org/t/rfc-simple-udt-draft-spec/4333

[12]https://github.com/celer-network/cChannel-eth/blob/master/contracts/lib/ledgerlib/LedgerOperation.sol#L87-L114

[13]https://defirate.com/imbtc-uniswap-hack/

[14]https://talk.nervos.org/t/rfc-anyone-can-pay-lock/4438/6

[15]https://github.com/celer-network/cChannel-eth/blob/master/contracts/lib/ledgerlib/LedgerOperation.sol#L87-L114

来源链接:talk.nervos.org