闪电网络——区块链领域基于 Golang 的核心技术之一

在 2019年第五届 Gopher China 大会上,讲师方圆进行了主题为《闪电网络——BTC小额支付解决方案》的演讲,以下为演讲实录。

00

前言


今天我分享的主要内容包括以下两部分:首先简单介绍 BTC 的基本原理和交易流程;然后介绍闪电网络的基本原理和Go语言的实现版本LND。鉴于大家基本没有接触过闪电网络,我也会补充说明 LND 的使用过程。

在正式话题开始之前,我们先简单回顾下 Go 语言在区块链领域两个著名的项目。第一个是以太坊,以太坊相比于比特币的主要特点在于支持智能合约。智能合约是运行在区块链上的一段代码,保证输入数据相同的情况下,所有节点计算都会得到相同的结果。很多人质疑我们到底需是否需要这个智能合约,我认为是需要的。比如入职的时候老板承诺给你两个月的薪水作为年终奖,到年终的时候老板要取消掉这个年终奖,如果使用智能合约来签订合同的话,你可以通过执行该智能合约,拿走属于你的年终奖。第二个有名的项目就是今天要介绍的闪电网络。闪电网络是构建于BTC之上的微支付网络,我们今天主要介绍其核心原理,LND的使用和实现部分只做简单介绍。

01

BTC 简介


比特币历史

闪电网络——区块链领域基于 Golang 的核心技术之一

比特币基本架构
如下图。P2P 网络是比特币的基础。比特币新节点通过连接种子节点加入P2P网络之后,从其他节点获得临近节点信息。所有节点接入P2P网络进行通信,节点发现等相关功能和普通P2P网络节点类似。

右边是RPC 相关的组件。BTC链之外的世界如果要和比特币的区块链打交道,就需要通过比特币的RPC接口来实现。比特币区块链通过P2P网络接收或者广播交易,并且把交易放到交易池,这些交易可能来自于RPC也可能来自于其他比特币节点广播的数据。挖矿节点从交易池中获取一批交易并打包为区块,再通过P2P网络广播出去,然后继续下一个区块的挖矿竞争。

闪电网络——区块链领域基于 Golang 的核心技术之一

比特币地址
正常使用比特币的时候要生成公钥私钥,公钥hash过后就是所谓的比特币地址。你可以拥有很多的地址,花币的时候就需要使用当前地址对应的私钥进行签名,私钥丢了钱就没了。你可以随意生成公钥和私钥。

交易
对于BTC来说,简化的交易结构如下所示。Transaction hash就是对整个的 transaction 进行Hash运算的结果。vin_size和vout_size是对应交易内部有多少个输入(input)和输入(output)。Output是表示输出,具体来说就是需要支付给谁,因此内部包含对方的地址(address)和需要支付多少钱(value),script是输出的锁定脚本,关于这部分内容,后文会有介绍。Input 对应输入,具体来说收钱方花钱的时候需要指定来源,Input内的 Hash ,实际指向前一个 transaction,script_sig是对应于output上的锁定脚本,我们把它叫做解锁脚本,这部分内容也会在后面介绍,index是对应的输出在该交易中的位置,因为同一个交易可以有多个输出。

闪电网络——区块链领域基于 Golang 的核心技术之一

在内存的结构用go语言表达是这样的,MsgTx是交易,TxIn是输入,TxOut输出,如果MsgTx Locktime 就是交易在验证过后在交易池中等待一段时间再打包进区块,比如说这个是等待两天,就等两天之后再打包。TxOut中的 Pk 就是对方地址。TxIn中的Output也已经关联到对应的交易输出。需要注意的是我们这里列出的代码只是为了说明原理,省略了很多细节,对这些细节有兴趣的可以详细研究BTC源代码。

闪电网络——区块链领域基于 Golang 的核心技术之一

如果用图来表示就是这个样子,transaction1 包含了一个输入一个输出,就是选择transaction 0的OUTPUT 0作为交易的INPUT。transaction1的OUTPUT 0 又被transaction 3的input 0引用。这样所有的交易的BTC都可以找到来源,我们可以从当前交易一直追溯到最初的来源。

闪电网络——区块链领域基于 Golang 的核心技术之一

闪电网络——区块链领域基于 Golang 的核心技术之一

了解了 INPUT 和 OUTPUT 之后。我们再来看锁定脚本和解锁脚本。由于BTC是去中心化的交易网络,下个交易需要花费一个OUTPUT的时候,需要证明该Output确实是属于当前账户的。锁定脚本相当于在OUTPUT上增加了verify函数,该函数的输入参数是公钥和签名,假如一个交易input的解锁脚本里的公钥和签名使得verify函数返回true,那么这个input就可以通过BTC节点验证,也就意味着该input是合理的。

闪电网络——区块链领域基于 Golang 的核心技术之一

整个交易流程实际是这样的,UTXO 是本地钱包有多少没有使用的 OUTPUT,用户想要花钱的时候,找到满足条件的一个或者多个 OUTPUT ,把它们作为transaction的输入,然后再通过对方的地址等信息构建一个或者多个 OUTPUT,形成一个transaction,然后把这个 transaction 广播出去,矿工收到 transaction 之后,会对它进行验证,验证之后打包进相应的区块,最后把整个区块广播出去。

闪电网络——区块链领域基于 Golang 的核心技术之一

区块链

讲了这么多,还在讲 transaction。那么什么是区块呢?

区块就是打包在一起的很多个 transaction 的结构。多个交易被打包在一起,并且添加上元数据就形成一个区块,每一个区块的头部都包含了这些元数据,并且包含上一个区块的hash,这样很多个区块就会形成一个链式结构,这个就是区块链。

闪电网络——区块链领域基于 Golang 的核心技术之一

存在的问题
刚才讲了比特币交易的大概流程,第一个问题是占用很大的存储空间,现在在本地起一个BTC节点,把所有区块链同步下来,我需要 200G 到 300G的存储空间,这个对于一般用户来说确实有点大了,主要解决方案是轻钱包,大家有兴趣可以去看相关的内容,这里不做详细介绍。第二个问题是,TPS太小了,大概是小于 7 的。为什么小于 7 呢,因为区块大小是固定的,比特币区块就是 1 兆大小,平均一个区块可以打进去 4000 个左右的 transaction,大概每10分钟一个区块,所以平均一秒算有 6 到 7 个交易,这个数字对于大规模的交易系统来说显然太少了。另外为了避免双花问题,通常需要在交易被打包进区块后等待6个块的确认时间,也就意味着交易的最终确认有一小时的延迟。

如何解决问题
为了解决交易TPS的问题,一部分人认为现在一个区块只有一兆,一兆只能打包那么多的 transaction,所以交易TPS太小的原因是什么呢?就是区块太小了。假设一个区块扩容到 10 兆,那么TPS可以扩到70。确实有人这么实践的,于是BCH由此而生。大区块也可以解决一部分交易TPS的问题,然而无法解决需要等待6个区块确认时间的问题。

为了解决这个问题,我们得探究为什么区块链这么慢呢?因为需要 全球50% 以上的节点达成共识。但是很多时候我们不需要这么多的节点达成共识,比如说我在我家楼下买酱油,我并不需要全球人都知道,只需要跟小卖部老板之间达成共识就可以了。所以有人提出,借鉴网络分层的理念,实现分层的区块链。第一层比特币区块链或者以太坊区块链处理跟全球共识相关的功能,如果不需要全球共识就不需要在一层做你的业务,可以在区块链二层处理两个人之间或者多个人之间的共识问题。

对于我们刚才的例子来说,比特币上的小额支付方案就是支付通道。支付通道有点类似于显示生活中打白条(二层交易),白条积累多了再一次付款(一层交易)。这样显著减少区块链一层交易的数量。对于用户来说相当于也基本上不需要等待交易最终确认了(只有最终上链的时候等待交易确认)。比特币上现在最成功的支付通道,就是我们马上要介绍的闪电网络了。

02

闪电网络介绍


从名字就知道,闪电网络希望交易快如闪电。首先闪电网络是一个双向支付通道,比特币上除了双向支付通道以外,还有有一种单项支付通道。单项支付通道支持一方可以向另一方付款,反之则不行。双向通道支持双方可以互相付款。其中我们需要注意的就是打开通道和关闭通道是链上的操作,是链上的 transaction,其他的交易都是链下的transaction。

基本原理

闪电网络——区块链领域基于 Golang 的核心技术之一

使用闪电网络的第一步是 Open  channel,这个channel 不是 Go 语言的 channel。这个 channel 在比特币意味着一个多签地址,多签地址是什么呢?我们俩共同发起一个地址,并且把钱打过去。这个地址上的钱只有我们两个都同意才能花出来。我们先假设打开单向注资的通道,假设 Alice 需要注资而 Bob 暂时不需要,Alice 就需要注资到他和 Bob 的一个多签地址上。为了防止Alice注资之后,Bob不合作,注资之前需要先生成一个alice/bob都签名的退款交易,这样即便Bob不合作,Alice依然能把钱取回来。其中退款交易有 30 天锁定时间,也就意味着30 天之后才能进入区块。Alice 拿到退款交易后不会广播出去,假如Bob不配合交易,那么Alice可以广播交易,最终取回注资的那笔资金。

闪电网络——区块链领域基于 Golang 的核心技术之一

如果双方都需要注资的话,那么就分别向多签地址注资,并分别生成退款交易,如上图所示。

闪电网络——区块链领域基于 Golang 的核心技术之一

通道建立完成后,Alice要向 Bob 0.1个比特币,那就分别生成两个交易,交易使用多签地址的output作为input,分别支付1.1个比特币给Bob,0.9个比特币给Alice,需要注意的是这里的交易只有29 天的锁定时间。为什么这么做呢?一旦 Alice 向 Bob 付钱之后,Alice 会广播原始的退款交易,拿回自己注资的1个btc,而实际上退款交易应该作废掉。如果Bob发现Alice已经广播了旧的交易,就可以广播这个新的交易,由于新交易只需要等29天就会被打包,因此即使 Alice 广播了旧的退款交易,也会执行失败,无法作恶。实际的闪电网络有更复杂惩罚机制,如果 Alice 一旦把旧交易广播出去,那么Bob可以拿到双方注资的所有2个BTC,惩罚作恶的一方。这个机制比较复杂,就不讲了,感兴趣的可以自己去深入了解一下。

闪电网络——区块链领域基于 Golang 的核心技术之一

假设 Alice 还要向 Bob 支付0.4个比特币,就重复同样的过程生成交易,只不过锁定时间变成 28 天过期。

上面描述的这个过程,可以解决两个地址之间的快速交易,如果要实现多个用户之间相互支付,如果只是实现上面描述的过程,那么所有人两两之间都要开通支付通道,成本就太大了。假设 Alice 向 Bob 有支付通道,Bob和Carol之间有支付通道,Alice能否通过已有的通道就向Carol付钱呢?答案是肯定的。

闪电网络——区块链领域基于 Golang 的核心技术之一

如果 Bob 不是一个诚实的人,在收到alice支付的0.1个比特币后,Bob 可以不向 Carol 付钱,因为没有任何约束。因此需要另外一个机制制约 Bob,保证一定支付给Carol。怎么办呢?

闪电网络——区块链领域基于 Golang 的核心技术之一

我们可以在锁定脚本上做文章。在原来锁定脚本的基础上增加一个新的条件。Carol 生成一个随机值R,并且对 R 进行的 Hash 得到 H,然后把这个 H 发给 Alice,这时候 Alice 发起相应支付。这里的支付是Alice发起对Bob的支付交易,并且把H作为验证条件放在锁定脚本中,如果解锁脚本中的R计算出的Hash等于 H 就验证通过,把币取走。因为Bob没有R,这时候Bob 再向 Carol 发起交易其中H也做为锁定条件,Carol 收到交易后,把Bob的交易解锁,这样Bob就知道R了,以此类推,Bob再解锁 Alice的交易。

闪电网络——区块链领域基于 Golang 的核心技术之一

通过上面的介绍,我们可以看出闪电网络可以把大量的链上的比特币的交易,转化成链下交易。减少链上交易的数量,加速交易。用户之间的交易只需要通过闪电网络传输很小的数据,并且不用经过链上确认(通过密码学和博弈论的方式保证资金安全,感兴趣的可以去了解一下),无需等待打包的过程,所以可以认为是及时的。

03

LND 介绍


闪电网络——区块链领域基于 Golang 的核心技术之一

我们刚才讲的都是关于闪电网络的基本的原理,基本原理讲了很多,因为这个确实理解起来比较困难。我们现在看一下闪电网络的实现,现在有三个比较稳定的实现,第一个 LND是官方实现的版本,使用Go语言,第二个 Clightning是C语言实现的版本,第三个 Clair是Scala实现的版本。

闪电网络——区块链领域基于 Golang 的核心技术之一

使用LND支付的过程大概是这样,先启动 BTC 或者 LTC,然后启动 LND 。通过命令行连接对方后,就可以Open  channel,然后就可以支付。

04

支付流程


open channel 过程我们不演示了,就先看看支付的过程。

闪电网络——区块链领域基于 Golang 的核心技术之一

bob通过命令行生成invoice之后,将元数据发送给alice,然后alice通过命令行和invoice相关元数据进行支付。

05

问题及改进

闪电网络——区块链领域基于 Golang 的核心技术之一

闪电网络的问题主要是方案问题和实现问题。先说实现问题。最大的问题就是闪电网络现在的实现还是单机版,以现在的数据量来说单机是能够扛住的。但是随着某个节点增长到几十万 channel 的时候,单机LND困难定是不够的。

闪电网络——区块链领域基于 Golang 的核心技术之一

方案问题主要介绍Balance问题,如果出现上面图里的状况,alice在通道上已经没有钱了,那么无论是Alice想自己向Bob支付,还有别人想通过Alice向Bob支付都是不可能的了。为了解决类似问题,现在也有一些好的方案,感兴趣的可以去了解一下,在此不再赘述。

闪电网络——区块链领域基于 Golang 的核心技术之一

现在闪电网络上已经抵押超过 1000 个BTC,有超过 8 千个节点,有大概 3.8 万个支付通道,每一个支付平均是零点零几 BTC。在年初的时候闪电网络在微博做了一次推广,如果你去下载闪电网络客户端,并且把闪电网络客户端的收款二维码发到微博上,有人会给你转520聪 btc,下次再有活动大家可以试一下。

Q & A


提问:老师您好,在到达交易目标之前的时候,前面要付路由费吗?
方圆:需要的,因为路由是由发起方直接付给中间节点的。然而因为路由费也是通过Hash time Lock来锁定,因此如果最终没有支付成功,是不需要支付的。

提问:比如说闪电网络我 A 从 C 转,参与的方越来越多,他们两个没有直接通道,需要终端,一个通道需要多少给周转费用,这样将来周转费用很大,这样意义是不是受到很大限制?
方圆:我看资料千万左右节点总数,那么一次支付中可以将节点控制在 10 个以内,不会特别多。而单个节点的手续费是基于市场博弈的结果,会达到供需平衡,不会特别高或者特别低。

重磅活动预告

Gopher Meetup 杭州站即将开启。来自阿里巴巴、网易、比原链的大咖讲师讲带来 Go 开发领域的一线实践经验分享,尽在11月24日,阿里巴巴西溪园区!

报名请戳:阅读原文

闪电网络——区块链领域基于 Golang 的核心技术之一

Go中国

扫码关注

国内最具规模和生命力的 Go 开发者社区