摘要:以太坊扩容问题一直被大家所关注,这篇文章来谈一谈扩容问题的一个重要方向:Layer2 扩展(链下扩容)。
前言:自区块链技术诞生以来,对其 “性能” 的诟病就从来没有停止过。虽然从技术上说,一个基于分布式对等网络架构的系统,与成熟的中心化技术相比,其 “性能” 方面有着天然的劣势,但业内人士对区块链 “扩容” 的研究和努力也从没有停止过。近两年,所谓的 “区块链 Layer2 扩展” 的提法已经逐渐在业内达成共识,并出现了一些有潜力的项目。本文就将为大家介绍一些与区块链 “扩容” 和 “Layer2 扩展” 相关的基础概念。
本文可以看作是对区块链的 Layer2 扩展的扫盲性介绍,不会涉及过多的技术细节;但我会假设读者已经知道比特币、以太坊是什么,区块链大概是什么,我们会基于这些最基础的知识来讨论扩容的问题。
希望本文能给区块链开发者或爱好者一些有价值的参考。
如果我们现在来问一个区块链爱好者或者从业者:你认为目前比较成熟的公链,比如比特币和以太坊在技术上面临的最大的问题是什么?我想大多数人的回答应该都是类似的:交易确认时间长(一个交易从发出到最终确认所经过的时间)、网络拥堵严重(如果同一个时间产生的交易太多,有些交易无法被马上处理)等等。这也就是通常意义上讲的所谓 “性能” 问题。
对于目前基于区块链架构的公链平台的所谓 “性能” 的评估,应该考虑两个方面。
被讨论最多的就是所谓的 TPS(Transactions Per Second 每秒交易量),这个维度衡量的是区块链在单位时间内所能处理的交易数量;我们近几年最常提到的所谓 “扩容” 指的就是这个维度。
如果把以太坊比做 “世界计算机”,那么目前,它只能用单核(单线程)来进行计算(同一时间只能有一个矿工来记账,或者说只有一个矿工记的账会被接受);而所谓 “扩容” 可以想象为把这个 “世界计算机” 扩展为多核(多线程),使它在单位时间内可以同时运行多个任务(同时有多个矿工在记账,他们记的账都可以被接受),最终反映为 TPS 的提高。这也就是所谓的 Layer1 扩容。在以太坊里,指的就是现在已经合二为一的 Casper + Sharding(我之前曾发过一篇 技术翻译稿 - 以太坊的分片 来讲解 分片 Sharding 的原理,有兴趣的读者可以自行参考,这里不再展开了)。
但是在实际应用中还有一个衡量性能的维度是不能忽视的,那就是 “平均处理时间”。基于刚刚的比喻,在以太坊中,这个维度就相当于这台 “世界计算机” 的单核(单线程)处理能力。
我们假设某个基于以太坊智能合约的业务流程需要 5 个步骤(交易)才能完成,也就是说,我大概有个智能合约,这个合约会有 6 个状态:初始状态,状态 1,…,状态 4,最终状态。那么要完成整个流程,就至少需要 5 个区块时间(从初始状态变为状态 1,需要交易 1 来完成,以次类推,则至少需要 5 个交易才能把状态变为最终状态)。
很显然,在这个例子里,区块链性能的瓶颈就变成了 “区块时间”。(这是因为智能合约本质上就是一个可定制的状态机,如果它有 6 个单向变化的状态,那么必须经过 5 次变化才能达到最终状态,所以 5 个交易是必须的。)而区块时间是由公链协议所规定的,比如在比特币里是 10 分钟,在以太坊里现在大概是 16 秒,这是无法简单缩减的;整个流程的 5 个区块时间是最乐观的估计,也就是性能上限。那么我们如何缩短这个流程的执行时间、降低 “平均处理时间” 呢?
这就是所谓的区块链 Layer2 扩展要解决的问题。而答案就是 —— off-chain(这个词的译法大概还没有共识,我这里姑且译为 “脱链”,也就是不在主链上处理的意思)。
编者在 2019 年更新本文时,业界大多称为链下扩容。
这种 off-chain
解决方案的思路是:我们可以把计算、交易等业务处理拿到主链之外来执行,只在主链上反映最终的结果,中间过程不在主链做记录。
这样,在上边例子里,我们要在主链上保存的状态就是初始状态和最终状态,中间过程的 4 个状态变动我们可以不关心,那对应的 4 个交易就可以拿到 “链外” 去执行;因为 off-chain 方案通常处理性能会非常高(后文中我会具体解释技术方案的原理),很有可能在主链的一个区块时间内就处理完这 4 个交易,并将结果发送回主链(即达到最终状态);于是从结果来看,整个处理过程只经过了一个区块时间(也就是最终状态的确认交易)就完成了。
很明显,如果采用这样的方案,越复杂的流程得到的性能提升越大;比如一些有高交互性能需求的应用 —— 游戏。另外对于支付的场景,因为相对高昂的交易手续费,那些高频的小额交易从经济上讲也显然成本过高。所以无论是支付还是合约的应用场景中,都有对 Layer2 扩展的强烈需求。
Off-chain 链下扩容方案的总体思路是类似的:首先需要把主链上的部分 “状态” 拿到链外来,可以本地存储(基于某种客户端)或者临时存储;然后在链外做具体的操作,比如转账或者其他会影响 “状态” 的处理;当处理完成或者到达需要同步 “状态” 的时间点时,再把最终状态传回主链保存。
目前已经成体系的 off-chain 链下扩容技术方案大概可以分为两大类:
我们首先来看看 “状态通道”。
状态通道是一个临时的点对点(交易的两个参与者间)价值转移通道:在开启时,通常会在主链上分别锁定一定的余额,并设定一个有效时间,并可以由任意参与方主动关闭,也就是参与方之间会基于特定的技术协议进行数据交互、价值转移(数字资产转移);然后当可以接入网络、到达某个约定的时间点或者某方主动向主链同步数据时,会将最终结果提交到主链。
推荐 Solidity 文档中的一篇文章:如何在以太坊上实现一个微支付通道
状态通道主要解决的是前边提到的高频、小额支付这样的场景中手续费过高的问题,但它的局限也很明显:
首先,它是一个临时的通路,数据并不是永久存储的,而是由参与双方自己本地保存;如果某个参与者使用的设备出现故障,损失基本上无法避免(虽然绝对的经济损失大概并不会太高)。
其次,一个状态通道仅能支持两个用户之间的价值转移;当系统中同时存在大量用户间的状态通道时,实际上就构成一个通道网络:网络中的两个用户有交易需求的时候,并不是简单地在他们两点间创建新的通道,而是通过特定的路由(routing)算法来查找是否有可用路径,而后再决定如何创建他们之间的数据通道;但这本身也增加了实现的难度和相应的技术风险。
上图是一个状态通道网络示意图。我们可以看到,如果 A 要向 C 进行转账,可以通过 A -> B -> C 的路径完成的(通过 A -> B -> E -> D -> C 的路径也可以完成,但这通常会需要更多的网络传输,所以并不是首选);而如果 D 要向 F 转账,则可以通过 D -> E -> B -> A -> F 或 D -> C -> B -> A -> F 的路径完成。所以理论上说,如果某个节点与状态通道网络中的任意一个节点之间有通道,那么就不需要再创建新的通道,而可以通过路由算法找到对应的路径完成价值转移。
当然,状态通道本身就是用来处理小额支付场景的,所以这些局限是可以接受的;即使出现不可恢复的故障,实际经济损失也不会过大。但这种技术本身显然限制了扩展的通用性和数据容量。
所以,可以进行永久存储、可以容纳更多交易、可以拥有独立的地址空间的所谓 “侧链(side-chain)” 方案就应运而生了。
侧链可以认为是主链的分支,是可以独立记账、独立增长的子区块链,所以其中同样会有记账人(矿工)、有永久存储机制和共识算法(因为参与侧链记账的通常会是实现了侧链协议的多个节点)。
下面我将基于以太坊的 Plasma 协议的思路来简单介绍侧链的实现方案。
对于侧链来讲,我们可以把它与主链的交互抽象为若干的 “状态迁移(State Transition)”:在侧链产生时,需要把若干 “状态” 转移到侧链的 “创世区块” 中,作为侧链的 “初始状态”;在侧链自己演进的过程中,需要定期把侧链的状态变动在主链进行记录,以便在发生争议或者有用户想 “退出” 侧链时可以恢复相应的状态。
从应用角度看,侧链要解决的主要技术问题就是用户如何 “进入” 侧链以及如何 “退出” 侧链。
由于侧链本身就是个区块链,所以侧链也可以拥有自己的地址空间;当主链用户 “进入” 侧链时就可以通过简单的 “地址映射”,将主链用户的 “状态”—— 比如账户余额或者持有的数字资产(ERC20 或者 ERC721 Token)—— 全部或者部分转移到侧链地址上。
复杂的,当然是 “退出” 机制。
当一个用户 A 想从侧链 “退出” 的时候,他应该要提出一个申请,将自己在侧链中的 “状态” 变动映射回主链。但因为用户在侧链中的状态变动必然是因为与其他用户进行了交互(交易)才会发生的,所以这也将会影响其他用户的 “状态”。因而,这需要一个争议期,在这个期间内,如果侧链的其他用户对用户 A 的退出状态有异议,他们可以发起一个 “争议(dispute)“,提交他们自己所留存的 “状态” 数据,并提交 “技术证明”(或者请求侧链上的无利益冲突的第三方证明人提供 “技术证明”,比如某个矿工或全节点提供的数据状态证明);主链上的所谓 “仲裁合约” 就可以根据 “技术证明” 来自动化地判断谁的状态变动才是 “合法” 的,从而进行最终在主链上的状态更改。
这里只是一个极简的描述,实际的技术方案比较复杂,限于文章篇幅,就不再展开了。
有兴趣的读者可以自行阅读参考资料: Plasma 论文。
Plasma 协议定义了一套子链(侧链)的实现协议,其中包括 5 个核心组件:
显然,因为侧链本身是一个有永久性存储的子区块链,里边同样需要矿工来记账,所以与普通公链类似的经济激励机制、共识算法以及数据存储结构设计就都是必然要考虑的东西。在侧链中,通常为了达到更高的处理性能,会采用 PoS、DPoS 或者其他改进算法,而不会采用 PoW 算法。同时还会在侧链自己的经济模型中考虑对有欺诈行为的矿工的惩罚机制。
此外,因为侧链本身也是一个区块链,所以在侧链之上再创建侧链,理论上也是可行的。这就相当于提供了一种多层的、几乎无限的扩展方案。
看起来这是种 “完美” 的方案;但事实上并没有 “完美” 的方案,Plasma 中也还有很多需要解决的问题。(可以在参考 Plasma 论文 的第 11 章找到相关论述,这里也不再展开了。)这也是社区和相关项目在努力研究的方向。
既然侧链可以提供很高的 “性能”,那么在侧链上运行智能合约自然就是一件极具吸引力的事了。
这里必须要提一个项目 ——Loom。Loom 是一个参考了 Minimal Viable Plasma 构建的侧链开发框架,已经在 2018 年 6 月发布了其 SDK,使用它的 SDK 我们可以快速地创建自己的侧链作为我们自己的 Dapp 的后端支撑。尽管这个框架目前来讲功能还比较弱,但已经是一个可用的选择了。因为它也是开源的,所以对侧链的具体实现也有很高的参考价值 (编者注: Loom SDK 在 2019 年 2 月发布了 2.0 版,功能已经强大的多)。
此外就是 Celer 项目,这是一个通用的区块链 Layer2 扩展框架,有非常宏大的愿景;在状态通道网络的实现方案上也有自己的创新。不过我个人比较关心的还是它对侧链的支持,这也还需要等待后续的工程进展。
在 Plasma 中,为了简化 “状态转换” 的验证,侧链的数据模型使用了 UTXO 模型,而对账户余额变动的验证则很自然地采用了所谓的 “Merklized Proof”。但这样的设计也对侧链上的智能合约执行框架提出了挑战。
我们知道,智能合约本质上是一个 “状态机”,所以,必须有永久性的存储来保存其状态数据,也就是类似于以太坊中的 “存储树(Storage Trie)” 这样的设计。所以如果在侧链上运行智能合约,也就同样需要某种用来保存合约状态的机制。
以太坊当初选择账户模型而不是 UTXO 模型的主要原因就是实现状态机的难度问题;显然,基于账户模型的状态机更容易实现,范式也更清晰。所以如何在基于 UTXO 模型的侧链上实现智能合约运行环境就有了很多可以研究和讨论的东西。我们可以基于 UTXO 模型构建状态验证机制,问题是这个账户状态(余额)变动如果不是通过交易直接产生的,而是通过合约代码产生的,那么如何证明这个改动是 “合法” 的就成了侧链在与主链间进行状态转移时的验证机制的关键。
我们当然希望有更多的项目能在这方面拿出可行的、可验证的方案,因为这将对侧链技术的继续发展产生深远的影响。
本文浅尝辄止地介绍了区块链的 Layer2 扩展的相关概念,仅仅希望作为区块链开发者或者爱好者进入这一领域的简单向导,更深入的学习理解自然也需要读者投入更多的时间和精力。比如你可以从精读下边列出的参考资料开始。
[1] Lightning Network: https://lightning.network/lightning-network-paper.pdf
[2] Raiden Network: https://raiden.network/
[3] Plasma: https://plasma.io/plasma.pdf
[4] Minimal Viable Plasma: https://ethresear.ch/t/minimal-viable-plasma/426