链下交易梳理之一 闪电网络

|谢骏毅 2018-05-19 09:56:08 927 来源:码农学习区块链
摘要:闪电网络是大家都耳熟能详的老牌项目了,也基本能大概说出来闪电网络是干嘛的。本文会更加深入地整理下该项目具体是怎么实现它的目标和功能的,可能会偏技术性些,看不大懂的也请努力看吧。

之前的《跨链梳理之哈希锁定 及 IOV简评》一文中在讲哈希锁定的时候稍微涉及到了些闪电网络,闪电网络是大家都耳熟能详的老牌项目了,也基本能大概说出来闪电网络是干嘛的。本文会更加深入地整理下该项目具体是怎么实现它的目标和功能的,可能会偏技术性些,看不大懂的也请努力看吧。


由于闪电网络和BTC是强关联,所以要弄清楚整个机制是如何运作的话就不得不预备一些BTC的原理和基础知识。


1. 基础知识


1.1 UTXO

UTXO全称是未花费的交易输出(Unspent Transaction Output)。其实就是某个地址内可支配使用的余额。所谓“输出”是针对交易来说的,如果把交易本身看作一个黑盒,那么交易的“输入(Input)”就是这笔交易中付款方打过来的币,“输出(Output)”就是交易完成后收款人能接收到的币,而且该“输出”还没被花过,就被称为“未花费的输出(UTXO)”。可想而知,如果上述交易中的接收方将这收款打给另外一个人,那么该“输出”就变成了新交易中的“输入”,类似于接水管一样。


绝大多数的交易都有相应的“输入”和“输出”,除了挖矿交易(Coinbase)是类似于“无中生有”,只有“输出”,即把出块所得的币记到相应的矿工地址上。


而且在具体实现上,每个UTXO都相当于拥有不同固定面额的支票一样,是不能掰开使用的。比如,Alice有100个BTC(即存在一个UTXO,面额是100个BTC,并且只能由Alice使用),当她打算打款30个BTC给Bob时,该转帐交易的“输入”就是Alice的这100个BTC的UTXO_a,“输出”则是两个全新的UTXO,一个是Bob可以支配的面额为30个BTC的UTXO_b,另一个是找零给Alice的70个BTC的UTXO_a'。交易完成后,作为“输入”的UTXO_a则被正式花掉了,并且也没有人能再花一次(否则就成“双花”了)。当然一个交易的“输入”也可以包含多个UTXO,假如这些UTXO的面额加起来才能凑够支付金额的话。此外,一个地址中余额是将它能支配的所有UTXO的面额加在一起算出来的。


UTXO的设计是比较奇特的,和传统的基于账户的复式记账完全不同,也不需要传统关系数据库的ACID属性支持。 其主要的好处是:

  • 各个交易通过UTXO都像接水管一样的被串了起来,形成了一个类似DAG的拓扑结构,从而可以确定UTXO之间的使用顺序以及交易之间的依赖关系,可以使得并行化地进行交易处理成为可能,提高出块速度,加大了潜在的可扩展性。而且分片技术中的跨区交易也离不开它,OmniLedger爸爸和它的儿子们里的很多的创新也是基于UTXO。

  • 还有个好处就是隐私,因为一个账户的余额可以是多个独立的UTXO拼出来的,于是可以使用不同的地址来接收UTXO(每个地址都可以由同一个用户的主种子(master seed)生成)。


1.2 再深一点点

UTXO中指定了可花费的面额以及支配者(即所有者)。前者就是一个数值类型的字段,是很容易理解。那么后者呢,后者并不是简单使用了支配者的地址,而是通过一个“锁定脚本(ScriptPubKey)”来实现的,这段脚本存在“输出”的一个字符串类型的字段里。当UTXO 被花费用作交易的“输入”的时候,需要提供一段“解锁脚本(ScriptSig)”,将“解锁脚本”和“锁定脚本”结合在一起运行,一旦验证成功返回true,则UTXO中的金额就被允许在交易中使用,来生成新的“输出”。


假设Alice打了30个BTC给Bob了, 那么这个转账交易中“输出”里就会设定的如下的“锁定脚本”:

OP_DUP OP_HASH160 <Bob公钥哈希值> OP_EQUALVERIFY OP_CHECKSIG

意思就是谁能证明自己是Bob就可以花这30个BTC。此外,这里的变量<Bob公钥哈希值>是Bob的公钥经过两次哈希运算后(先SHA256再RIPEMD160,可以防止其中一个哈希算法被攻破的风险)的值。也可以从平时看到的1开头或3开头的钱包地址经过Base58Check解码得来。


当Bob要花掉这笔得自于Alice的钱时,比如转给Carol,需要构建新的交易:

  1. 第一步是通过BTC命令行中的listUnspent命令(罗列未花费的输出)找到上述从Alice那得来的UTXO。

  2. 然后通过createRawTransaction命令(创建原始交易)创建一个新的交易,需要传入的参数包括:上述UTXO的交易ID,以及提供Carol的地址和合理的转账金额。于是,新的交易就会有一个“输入”,其ID就是UTXO的交易ID(即本交易的“输入”源自于上次交易中的“输出”)。这个时候,该交易的“输出”已经被产生了,而且其中针对Carol的“锁定脚本”也已经就绪,但是新交易的“输入”中还没有设定“解锁脚本”。

  3. 通过signRawTransaction命令对新交易做数字签名。这一步会先找到交易中的“输入”,将其所对应的UTXO的支配者(Bob)的公钥放入“解锁脚本”中的右半边。然后根据传入的签名哈希类型对当前交易数据进行哈希运算,得到哈希值后,使用UTXO对应的支配者(Bob)的私钥对刚算出的交易哈希值做数字签名,然后将这个签名放入“解锁脚本”中的左半边。

  4. 通过sendRawTransaction命令广播该交易,该交易进入内存池,等待被矿工校验和打包到区块里。


完成第三步后,“输入”里的“解锁脚本”则应该是:

<Bob数字签名> <Bob公钥>

如上所述,这里的是签名是Bob用自己的私钥对当前新的交易做的数字签名。这里所谓的签名哈希类型(SignHash)有好几种,可以选择交易中的不同内容做哈希以及数字签名,常用的是SIGHASH_ALL,也就是算上当前交易中的所有“输入”和“输出”一起做哈希后再签名。


在第四步中,任何矿工都可以验证该交易中的“输入”是否合法,具体做法就是取出“输入”中的“解锁脚本,以及该“输入”所指向的UTXO中的“锁定脚本”,然后拼在一起运行。


将这个例子中的两个脚本拼在一起就变成了这样:

<Bob签名> <Bob私钥> OP_DUP OP_HASH160 <Bob公钥哈希值> OP_EQUALVERIFY OP_CHECKSIG

绿色是解锁脚本,红色是锁定脚本。BTC的脚本被设计成以栈(Stack,先入后出)来运行的虚拟机指令,只有有限的几种指令(OP_XXX),并且故意被设计成很原始,没有循环,没有条件跳转,非图灵完备,以防黑客攻击和滥用(所谓:复杂度才是安全性最大的敌人)。上面合成的指令中,尖括号代表变量,脚本运行从左到右,遇到变量则压栈,遇到OP开头的指令则从栈顶取出需要使用的变量,作为输入参数传给指令并执行,得出运算结果后再将结果压栈。上面这个命令的执行步骤以及栈的状态如下:

  1. [] // 初始状态:空栈(左边是栈底,右边是栈顶)

  2. [签名] // <Bob签名>:签名入栈

  3. [签名, 公钥] // <Bob私钥>:公钥入栈

  4. [签名, 公钥, 公钥] // DUP指令:制栈顶的公钥(DUP:Duplication)

  5. [签名, 公钥, 公钥哈希] // HASH160指令:取出栈顶的公钥,进行哈希运算(先SHA256再RIPEMD160),并把结果压入栈中

  6. [签名, 公钥, 公钥哈希, 公钥哈希] // <Bob公钥哈希值>:入栈支配者公钥哈希,即Bob的

  7. [签名, 公钥] // EQUALVERIFY指令:取出栈顶两个公钥哈希,确认它们相等则继续,否则验证失败推出执行

  8. [TRUE] // CHECKSIG指令:取出栈顶的公钥和签名,结合当前交易进行验证签名验证,将验证结果入栈,如果是true,则该笔交易合法。


1.3 多重签名

上面例子中的交易又被称作P2PKH交易(Pay To PubKey Hash,支付到公钥哈希),是BTC中定义的标准交易类型中的一种,用一个私钥就可以解锁相应的UTXO,平时看到的1开头的地址都是此类交易中使用的地址。


多重签名交易则允许一个UTXO同时靠多个私钥才能被解锁,假设一共有N个公钥(不是公钥哈希,是原始公钥)被用来锁定(最多允许15个),并且最少需要其中M个私钥生成的数字签名才可以解锁的话,被称为M-of-N多重签名。


比如2-of-3情况下的锁定脚本就是这样的:

2 <公钥1> <公钥2> <公钥3> 3 OP_CHECKMULTISIG


假设我们只用私钥1和私钥3,那么解锁脚本则是:

0 <签名1> <签名3>

这里最左边补了个0是没什么意义的,之所以存在是因为OP_CHECKMULTISIG指令中的bug,从栈顶多取了一次,一直遗留至今...(这个bug真是吊爆了..)


拼在一起就是:

0 <签名1> <签名3> 2 <公钥1> <公钥2> <公钥3> 3 OP_CHECKMULTISIG

具体脚本运行就很简单了,全靠OP_CHECKMULTISIG指令,该指令读取了2个签名和三个公钥,以及M和N值,然后按顺序使用公钥1,2,3来按序分别试着验证签名1,3:公钥1最先成功匹配到签名1,计数器加一,然后接着用公钥2来验证签名3,必然失败,然后用公钥3验证签名3,成功,计数器再加一,等于了M,运行成功。


OP_CHECKMULTISIG中的算法是比较高效的,就是因为是单向的处理流,没有回溯(backtracking),不过如此一来,解锁脚本中提供的签名的顺序就一定要匹配锁定脚本中提供公钥的顺序,否则GG。


上述的这个多重签名的解决方案当然是可行的,不过有几个问题,

  • 第一,UTXO中的锁定脚本和实际打款地址无关,一般使用者会觉得匪夷所思,非常不直观。而且锁定脚本和所有的公钥绑定,需要特别定制,使用门槛略高。

  • 第二,就是锁定脚本中占据的数据量太大,想象一下15个公钥排成行,画面太美...而且前面说到了锁定脚本是保存在于付款人打钱到受多重签名保护的收款人的这个交易里的“输出”中的,这意味着付款人因为臃肿的交易需要承担更高的交易费,这显然是不合理的,因为多重签名保护的是收款人们的利益,对于付款人可能没什么好处。


所以,如今更常用的实现多重签名的做法是P2SH(Pay to Script Hash,支付给“脚本哈希”),这里“脚本哈希”就是某个脚本的哈希值。思路是想办法将N个公钥信息从锁定脚本里挪到解锁脚本中,如此一来转变成了被多重签名保护的BTC拥有者们需要在花销这些余额的时候提供这些公钥信息,并且为此付出相应的交易费代价。


具体的实现方法是引入一个赎回脚本(RedeemScript)的新概念,P2SH里说的脚本也就是指这个赎回脚本,脚本哈希也就是这个脚本的哈希值。理论上可以是任何合法的脚本。在多重签名这个特定的场景中,赎回脚本里存放的就是之前说的多重签名中使用的锁定脚本内容,surprise~。所以下面就是“赎回脚本”的原文:

2 <公钥1> <公钥2> <公钥3> 3 OP_CHECKMULTISIG


而新方案中的锁定脚本变得很简洁:

OP_HASH160 <赎回脚本的哈希> OP_EQUAL

这里的哈希值只有160位,即20个字节,比之前说的N个公钥短多了。这里涉及到的OP指令之前也都介绍过了。


相应的解锁脚本则是:

0 签名1 签名2 <赎回脚本>

最左边的0依旧存在,稍后就能知道为什么了。


交易验证的话运行以下合成指令:

0 签名1 签名2 <赎回脚本> OP_HASH160 <赎回脚本的哈希> OP_EQUAL

基于栈的特点,该指令运行会先对<赎回脚本>做哈希运算,然后和<赎回脚本的哈希>进行比较,如果一样,则运行原始脚本,即

0 <签名1> <签名3> 2 <公钥1> <公钥2> <公钥3> 3 OP_CHECKMULTISIG

熟不熟悉,意不意外?我们又回到了之前多重签名的指令了,0依旧用来补位来弥补OP_CHECKMULTISIG指令中的逻辑bug。


此外,还可以将赎回脚本进行编码后产生出新的3开头的P2SH地址,换句话说,N个公钥可以被用来产生出一个多重签名的P2SH地址。如此一切就变得和最一开始介绍的P2PKH那么自然,付款方只需要知道收款方的地址,就可以将P2SH解码后得到锁定脚本中需要的<赎回脚本的哈希>,这也就是为什么P2SH叫做“支付给赎回脚本哈希”了,因为“赎回脚本哈希”和地址是等价的。


除了支持多重签名之外,自由弹性的P2SH设计模式也使其可以适用于其他各式各样的锁定和解锁场景。


1.4 时间锁

BTC允许在交易中使用nLockTime字段来指定某笔交易在未来某一个区块高度或时间戳时才进行支付。


另外,还有一种叫做检查锁定时间验证(Check Lock Time Verify, CLTV) 的基于UTXO的时间锁,就是通过P2SH实现的。


有了上述基础知识后(额,怎么突然有种这一篇文章已经可以完结撒花的感觉...),总算可以开始介绍闪电网络的东西。


2. 闪电网络


闪电网络本身的主要思路十分简单直白——将大量交易放到BTC区块链之外进行,只把关键环节放到链上进行确认。


状态通道是一个久已有之的离链(off-chain)策略,闪电网络和雷电网络都用到了它。相当于两个或两个以上的实体之间更新状态的协议:在用户之间,或是用户和服务之间建立双向通信管道,消息以交易的形式只针对通道内参与者广播,而非全网广播。这样一来,很多琐碎的,来来回回的状态更新和交易就不会影响整个公链主网从而导致网络堵塞,并且还能增加主网的并发能力,而交易只消耗超低的手续费。等通道内的参与者达成一致并签名确认后,最终将交易结果上链,共识之后结果不可篡改,广播全网,拥有最高可靠性。


闪电网络主要通过引入智能合约的思想来完善链下的交易渠道。核心是多重签名地址,加上两种新的合约类型是:可撤销的按序到期合约(Revocable Sequence Maturity Contract,RSMC)和 HTLC(不记得这是什么的话就再回个炉吧《跨链梳理之哈希锁定 及 IOV简评》)。


2.1 注资

假设Alice和Bob私下里有很多业务来往,Alice天天从Bob那里买鸡蛋饼,Bob从Alice那儿买茶叶蛋,那么他们可以选择两人都预存一部分钱到一个共同账号,每次发生任何小额交易的话,就不用像之前那样直接互相给钱了,而是直接在共同账号里划账就行了。等到有朝一日谁想把钱取出来,那么钱按照正确的分配比例打还给双方。


这里的第一步就是要建立一个共同账户:让两个人可以分别往里预存些钱,但是要把钱拿出来必须要两人都同意才行。由此,才能作出各种文章来。


这里的核心部件就是之前提到的多重签名账户,3开头的那个。Alice和Bob拿出分别属于自己的公钥(N=2),然后设定需要提供2个数字签名才能账户中花钱(M=2) ,于是创建出一个2-of-2多重签名地址了。


然后,Alice和Bob分别打币(比如每人都打0.5BTC)作为“输入”转到多重签名账户,于是这个多签名账户内应该有两个UXTO,每个都是0.5BTC,都有同样的锁定脚本。如果从账户里花钱,换句话说就是把UXTO作为输入用在新的交易中,则需要Alice和Bob分别用私钥对新交易进行数字签名来解锁UXTO。


2.2 RSMC

简单来说RSMC类似于一个资金暗池,支付交易的本质就是双方重新确认资金池中资金的分配方案(也被称作通道的状态)。每次发生频繁的小额交易时,需要对交易后产生资金分配结果共同进行确认,任何一方需要提现时将他手里经过双方签署好的交易结果写到区块链网络中,从而被确认。所以,提现之前的各种中间交易都可以通过RSMC发生在链下,所谓用对资产所有权的承诺来代替所有权本身。


RSMC并非指的是某个特定交易,而是由一系列交易所构成的、拥有多个执行路径的结构。下图是一个简单的RMSC结构。具体怎么玩,为什么RSMC的名气这么奇怪等到撸一遍后应该就能明白。

640.webp (4).jpg


图里隐藏了2-of-2多重签名地址的细节,只用了一个注资交易(Funding Tx,绿色)来指代从Alice和Bob将彼此的比特币进行“合体”的过程。注资交易的输入是分别来自两人钱包的0.5BTC;该交易的输出就是2-of-2多重签名地址能支配的总额为1BTC的UXTO。如前文所说,着眼于交易本身和它的输入输出更加本质(接水管),请务必习惯这种看待交易的角度。一旦该交易完成后,总额为1BTC的UTXO就老老实实的躺在多重签名地址内了(这个比方虽然形象,不过其实并没有发生过所谓的“躺”这种行为,只是UXTO到地址的映射而已)。


然后Alice和Bob分别构造承诺交易(Commitment Tx):C1a和C1b。上图中,左边是Alice的视野,其中的C1a是Alice构造了并且能够广播的承诺交易(紫色框表示了所有Alice能广播的交易);右边是Bob的C1b,蓝框。这两个承诺交易的输入都来自于注资交易的输出,所以最后这两个承诺交易中只能广播其中任一一个,一旦确认成功后,钱就从2-of-2多重签名地址转走了,另外一个承诺交易就作废了(否则就变成双花了)。


虽然两个承诺交易的输入来源一致,但是输出看起来就不同了。其实是镜像对称的,并没那么大区别。而通常来说,我们看其中一边就够了。假设从Alice角度来看,那么C1a的输出有两个,

  1. 第一个输出(上图中的output 0)是付给 Alice和Bob共同签名的RSMC地址的0.5BTC。接着,需要创建一个可撤销交付交易(Revocable Delivery Transaction)RD1a来消费RSMC中的BTC。这个RD1a的输入和输出分别是:

    1. RD1a的输入就是C1a的第一个输出:存入多重签名RSMC中的0.5BTC。由此,RD1a需要来自于Alice和Bob的签名才可以解锁这个输入。

    2. RD1a的输出是付给Alice的0.5BTC,但是有额外的条件,就是要等C1a交易被广播并且经过1000个区块确认后,RD1a交易才有资格被加入区块,否则就算RD1a被广播出去了也没有矿工搭理(通过前文提到过“时间锁”实现)。这里的1000是通过nSequence字段提供的,也就是所谓的“按序到期”。

  2. 第二个输出(图中output 1)其实就等于是直接付给Bob的0.5BTC,不过这里还是用了一个所谓的交付交易(Delivery Transcation)D1a表示Bob可以随意消费这0.5BTC,看起来稍微有点啰嗦,其实是为了更加系统性进行说明。看下D1a的情况:

    1. D1a的输入就是C1a的第二个输出:Bob可以支配的0.5BTC。

    2. D1a的输出则是Bob付给Bob的0.5BTC。为了便于理解,可以想成Bob把钱从他的临时账户(其公钥已被用来创建了用以注资的2-of-2多重签名地址)到自己的其他账户。只要C1a被广播确认后,Bob就可以立即广播D1a拿到0.5BTC


由于承诺交易C1a/C1b是从多重签名地址里花钱,每个承诺交易都需要两个签来解锁输入对应的UTXO。Alice在构造完C1a后,会交给Bob去签名,这样以后等到Alice想广播该交易的时候补上自己的签名就可以了。


此外,RD1a也需要两个签名才可以从多重签名RSMC中转钱,所以也需要将该交易拿给Bob得到他的同意和签名。此外RSMC这个多重签名地址是通过新的一组公钥创建出来的,和生成注资池所用到的公钥是不一样的。


对于Bob来说,他也会做相应的事情,构造C1b以及RD1b和D1b,并且需要把C1b和RD1b交给Alice签名。


这样一通操作的意义何在?答曰:

  1. 承诺交易以及后续RSMC,延迟交付交易和交付交易的构造都只是交易双方的行为,并且签名交换也只在交易双方之间发生。一切都是链下行为,经济实惠。

  2. 承诺交易以及后续交易的混合结构体现了当前资金分配的情况,相当于交易双方之间的暗账。每一方只有在看过对方构造的交易,满意之后,才用私钥签名。而在完成交易构造,签名交换这一切之前,甚至都不需要双方打钱到注资多重签名地址。这样任何一方都无法作恶,遇到作恶(比如对方胡乱构造他自己的承诺协议)也没有损失,更安全。只有在一切签名都做完后,才会将注资交易签字广播。

  3. 将某个承诺交易进行广播,就意味着从资金池里取现,数据正式上链,并且会触发一级级的后续交易的广播结算。在承诺交易被广播之前,一切交易合约的构建都是类似于沙盘推演般的勾心斗角。

  4. 交易双方可以单方面选择终止链下交易,很自由很方便。但是谁先申请取现谁就吃亏:需要多等1000个区块确认才能拿到钱。这样是为了鼓励大家更多地在链下进行交易,推迟取现和数据上链的发生。


除此之外,RSMC更大的一个作用在于资金的链下再分配。如果Alice和Bob之间发生了新交易,比如Alice从Bob那儿买了蛋饼,Alice要付给Bob 0.1BTC,那么资金池里的分配就应该变成Alice:0.4BTC,Bob:0.6BTC。 这里需要做两步,第一是创建新的一套承诺交易,第二是将之前的交易作废。


2.2.1 创建新的承诺交易

下图中,最左边的一栏是将即将被作废的C1a和C1b,以及被图里省略掉的RSMC和各类后续交易;中间一栏是Alice创建的新交易结构:C2a,RD2a, D2a;最右一栏是Bob创建的新交易结构:C2b,RD2b,D2b。


640.webp (5).jpg


再从Alice的视角看下她新创建的C2a。C2a的输入依旧是注资交易的输出。C2a的输出还是两个:

  1. 第一个输出是付给Alice和Bob共同签名的RSMC地址的0.4BTC。接着,创建一个可撤销交付交易RD2a来消费RSMC中的BTC。RD2a的输入和输出分别是:

    1. RD2a的输入就是C2a的第一个输出:存入多重签名RSMC中的0.4BTC。

    2. RD2a的输出是付给Alice的0.4BTC,同样,要等C2a交易经过1000个区块确认后,RD2a交易才开始有资格被加入区块。

  2. 第二个输出其实就等于是直接付给Bob的0.6BTC,以及交付交易D2a表示Bob可以随意花费这0.6BTC。看下D2a的情况:

    1. D2a的输入就是C2a的第二个输出,Bob可以支配的0.6BTC。

    2. D2a的输出则是Bob付给Bob的0.6BTC。只要C2a被广播确认后,Bob就可以立即广播D2a拿到0.6BTC


Bob也会创建相应的C2b,RD2b和D2b。然后双方互换承诺交易和RSMC交易完成签字,至此新交易生效。


2.2.2 作废旧交易

此时,新老交易(C1a/C2a,C1b/C2b)同时存在,也同时有效。我们需要撤销C1a和C1b,而闪电网络在这里采取的办法很巧妙:双方通过私钥互换以及创建“违规补偿交易(Breach Remedy Transaction, BR1a/BR1b)”来取代之前的RSMC可撤销交易(RD1a/RD1b),如下图。


640.webp (6).jpg


再次站在Alice的角度看,此时将被废弃的C1a中的第一个输出依旧还是转给双方共同签名的RSMC地址的0.5BTC,但是这里有两个新的关键步骤:

  1. Alice把用来生成RSMC所用的公钥所对应的私钥给了Bob,等于放弃了改把私钥。

  2. Alice创建了一个新的违规补偿交易(Breach Remedy Transaction, BR1a)。

    1. BR1a的输入是多重签名RSMC中的0.5BTC

    2. BR1a的输出则是付给Bob的0.5BTC


如此一来,如果Alice违规将被废弃了的C1a签名并广播出去的话,那么钱就分别转到了多重签名RSMC以及Bob的地址中,各0.5BTC。然后最骚的来了,由于Bob已经拥有了解锁RSMC中0.5BTC所需的两把私钥,一把自己的,一把是Alice主动给的。于是Bob就可以使用秘钥对BR1a做两次签名,将签名放到BR1a的解锁脚本中,并且广播BR1a到全网,于是便能从C1a的RSMC输出中获得0.5BTC。而Alice之前创建的RD1a还要再等1000个区块确认才能生效,是快不过Bob的天降正义一般的BR1a的,这也是“按序到期”的另一个作用了。


再加上通过交付交易D1a天然就付给Bob的另外0.5BTC,等于是Bob获得了资金池里的所有BTC。所以Alice是不会蠢到真的去广播C1a的。于是,这样等于变相作废了C1a。


Bob那里的情况也是镜像对称的,这里不做赘述。RSMC名字中“可撤销”的意思就全部现在这里了。


综上,使用多重签名RSMC地址引出了两个执行路径,一个是让先套现的人延迟获取资金(按序到期),另一个是可以用来撤销旧交易,否则如果将旧交易广播出来,钱被罚给对方所有(可撤销,按序到期)


2.3 HTLC

上述的流程对于一个通道内的交易双方来说已经足够了,但是怎么才能利用多个通道进行类似的多跳链下交易呢。毕竟每个用户两两之间在链上开2-of-2多重签名地址是蛮浪费资源的。


2.3.1 引入HTLC

HTLC主要就被用来实现跨通道的支付功能。HTLC中使用哈希锁,也就是原像(preimage)来保证多跳HTLC交易的执行不需要第三方介入。并且HTLC的实现中用到了之前提过的BTC所支持的nTimeLock字段来实现合理的时间锁。


通过引入HTLC,于是下图进入了这个更加复杂的形态。


640.webp (7).jpg


为了方便理解,我们还是从单个通道来着手。还是Alice付给Bob0.1BTC的情形,然后我们继续以Alice的视角看左半边。


通过引入HTLC,C2a的输入变成了三个。RSMC和之前一样,还是0.4BTC,略过不表,但是原本写成付给Bob的0.6BTC拆成了两部分,一个是付给Bob的0.5BTC,另一个是HTLC输出,即付给多重签名HTLC地址的0.1BTC。这个多重签名HTLC的作用就和多重签名RSMC一样,是用来引导出HTLC多条可能的执行路径的,可以用来做到可撤销。我们接下来主要看下由多重签名HTLC地址中的0.1BTC这一支引发的可能的花费:

  1. HTLC执行交付交易(HTLC Execution Delivery Transaction)HED1a的输入来自于上述的0.1BTC;输出则是付给Bob的0.1BTC。Bob将自己随机产生的原像R的哈希值交给Alice,由Alice使用该哈希值创建该交易,并由Alice签名后交给Bob,然后Bob可以随时使用R“解锁”该交易并且签名广播来完成0.1BTC的转账。

  2. HTLC超时交易(HTLC Timeout Transaction)HT1a的输入和上面HED1a的一样。并且HT1a上设置了时间锁,如果Bob迟迟不完成转账,三天过后,Alice可以广播该交易触发0.1的退款,这款实际退到了一个新的多重签名RSCM地址,和C1a中的第一个输出的目的地不一样(上图中有误,这个新的RSCM应该写成“RSCM Alice3 & Bob3 0.1”以示区分)。当然,Alice创建好这个交易后要交给Bob获取他的签名。


以上两个花费路径中的交易输入都来自于多重签名HTLC支配下的0.1BTC,而这里玩的非常花,用到了P2SH的高级用法。在0.1BTC这个UTOX的锁定脚本中,保存的是以下条件语句(If-Else):

OP_IF 

    OP_HASH160 <R的哈希值> OP_EQUALVERIFY 

    2 <Alice第二套公钥> <Bob第二套公钥> OP_CHECKMULTISIG 

OP_ELSE 

    2 <Alice第一套公钥> <Bob第一套公钥> OP_CHECKMULTISIG 

OP_ENDIF


简单来说,上半部分指令是给HED1a用的,如果Bob提供了R,以及提供了Alice第二套私钥和Bob第二套私钥创建的签名,才可以解锁该UTOX。下半部分则是给HT1a的,无需R,只需要另外一套(即第一套)私钥产生的签名即可解锁。之所以用不同的密钥对,是为了防止HLTC中收款方不提供R直接通过Else分支里的指令解锁,这在单通道内可能还没什么问题,但是在多条支付环境下,这样R作为每一跳间传递的令牌就失去了作用,对依次支付的顺序也没有了约束。


在HT1a的下面,还依附着一个HTLC超时可撤销交付交易(HTLC Timeout Revocable Delivery Transaction)HTRD1a。

  1. HTRD1a的输入来自于使用第三套公钥产生的RSMC地址可支配的0.1BTC。 

  2. HTDR1a的输出则是还给Alice 本人的0.1BTC。

Bob需要预先使用他的第三套私钥对该交易签名并交给Alice, 然后Alice可以选择在HT1a生效并经过1000个区块确认后,对HTRD1a加上自己的签名并广播到全网来获取退款。


2.3.2 可撤销的链下HTLC

当然,很重要的一点就是HTLC也需要具备可被撤销的能力,如同废除C1a/C1ab一般。于是下图展现了终极形态(高能预警)。


640.webp (8).jpg


上图演示了如果新交易被创建(C3a/C3b),该如何撤销C2a/C2b中的一切,包括HTLC们。其实和之前“2.2.2 做废旧交易”一样,还是通过互换私钥和创建违规补偿交易来达成目的。


我们这里只关注HTLC部分,因为其余的都一样。具体从Alice角度来看,Alice在同意C3a/C3b之后,会把用于多重签名HTLC中的第一套秘钥告诉Bob,并且再创建了两个新的违规补偿交易:

  1. HTLC违规补偿交易(HTLC Breach Remedy Transaction)HBR1a。如果Alice将C2a广播出去,C2a中HTLC输出的0.1BTC将为Bob所有,因为Bob可以即刻解锁这笔钱。

  2. HTLC超时违规补偿交易(HTLC Timeout Breach Remedy Transaction)HTBR1a。如果Alice将C2a广播出去后,Bob并没有去广播HBR1a来收没罚金,那么Alice本来还是有机会等到三天后通过广播HTRD1a来拿回0.1BTC的退款,但是这条路还是被HTBR1a给堵死了。饶是Bob没有主动领取罚金,一旦Alice广播HTRD1a,Bob还有机会即刻通过HTBR1a来解锁HTRD1a的输出来取到钱,因为Alice已经将第一套私钥给了Bob,并且HTRD1a要等待1000区块确认。


以上这么多复杂的构造和布置,主要就是希望通过勾心斗角的代码合约来约束交易中某一方人性的邪恶。另外,如果双方都是讲道理好商量的,那么其实可以最终商量一个资产分配方案,然后通过一个特殊的最终交易Exercise Settlement Transaction (ES)将资金池中的金额直接分配给交易双方,这样就不需要设置各种多重签名地址和后续交易了。


此外还有一些关于多跳支付中的各种极端情况都不是特别的要紧,就此打住了。


总的来说,闪电网络想法新颖,心思缜密。但是也存在改进空间,比如:

  1. 每个新的交易都需要创建好几套新的密钥,因为用于之前被废弃的交易中的私钥都要交给对方,以被惩罚时用。密钥的本地管理可能会是个薄弱之处。

  2. 先申请套现者只能延时得到支付的惩罚在有些场景下过于严厉了。


这篇比以往的文章都更偏技术一些,有任何反馈的话可以通过下面的投票或留言告诉我哦。


审核人:

标签: 闪电网络 区块链

觉得不错,给小编个打赏吧

评论
0
0

登录后才可以评论

查看全部(0)

相关阅读

单公子

评论(0

荣格财经名家专栏

展示荣格财经专栏名家个人发布的最新、最热区块链资讯文章。

推荐名家 更多>

最新快评更多>

推荐阅读 更多>

关注微博

关注荣格财经微信公众号

荣格财经读者11群

加入荣格财经技术交流群