在本文中,我们历数了以太坊在扩展 DeFi 容量时的局限性。如你所见,这是一个复杂的问题。
如果你最近用过 DeFi ,最近高涨的交易费会令你瞠目结舌。如今,就以太坊交易而言,数十乃至数百美元的交易费实属稀松平常之事。在如此高昂的手续费下,只有巨鲸才能有利可图,更别提什么 “让没有银行账户的人也能享受银行服务”、“让免许可型金融基础设施惠及所有人” 等宏图伟业了。以太坊正在成为富人的聚集地。 <center>- 如果是小额交易,手续费可能会超过交易金额的 10% -</center>
高昂的手续费表象之下的核心是 区块链可扩展性问题。这个问题太过臭名昭著,以至于有了自己的维基百科页面。这是当前区块链最主要的局限性,此外还有终局性时滞太大、抢跑交易、跨链互操作性等局限性。
我们想要创建一个能让所有价值自由流动的代币化世界,区块链的局限性会阻碍我们实现这一愿景。这就是为什么 0x Labs 组建了一个专门的研究工程师团队针对这些局限性开发解决方案。在本文中,我们将探索以太坊的局限性及其对 DeFi 用户的影响。我们还将简要介绍下一代区块链。在后续文章中,我们将探索另一类解决方案(Layer 2),并介绍我们自己解决 DeFi 需求的策略。
首先,要知道以太坊交易的大小是以 Gas 消耗量来衡量的。交易会打包到区块中,大约每 13 秒出一个块。每个区块只能容纳有限的交易,因为有所谓的 “区块 Gas 上限”。目前,每个区块的 Gas 上限是 1200 万。一个普通的 ERC20 代币交易需要消耗大约 5 万 Gas 。这就意味着,一个区块最多可容纳 240 笔代币转账操作,即,每秒可完成大约 18 笔交易。DeFi 交易通常涉及多笔代币转账,以及其它记账操作,这会导致成本翻倍,并进一步限制吞吐量。Gas 容量和出块时间意味着有源源不断的 Gas 可供交易使用。
我们先来看看以太坊诞生以来,Gas 的供应量和使用量增长情况。
每天,以太坊上都会新增大约 6000 个区块,容纳数十亿 Gas 的交易。这个数量一直在变化,并随着时间流逝而增长,主要是因为区块 Gas 上限增加。与此同时,随着以太坊上交易的数量和规模在不断增加,交易所消耗的 Gas 总量也在不断增加。
回顾整个以太坊的发展史,Gas 的供应量(灰色部分)和消耗量(黑色部分)如下图所示:
<center>-(你可以说,以太坊是企业用的,因为 Gas 使用量在工作日更高)-</center>
如图所示,在拜占庭(Byzantium)、君士坦丁堡(Constantinople)和缪尔冰川(Muir Glacier)硬分叉发生前,Gas 供应量都出现了锯齿状的骤降。这些都是以太坊难度炸弹,又称冰河世纪(ice age),所产生的影响。在冰河期,出块时间会呈指数型增加,导致每天挖出的块数量减少,每天的 Gas 供应总量也随之减少。当然了,这种情况是非常糟糕的,因此以太坊网络需要用硬分叉来恢复网络。难度炸弹的用意正在于此:引入改进措施,以免出现创新停滞的情况。由于君士坦丁堡硬分叉忘了重新设置难度炸弹,以太坊紧接着又引入了缪尔冰川硬分叉。之后的柏林硬分叉正在考虑改变这一机制(EIP 2515)。
通过图中黑色部分的 Gas 使用量,我们可以看出 2017 年 ICO 热潮以来,以太坊的 Gas 使用量占 Gas 上限的 60% 以上。自此以后,Gas 上限经过几次跃升,增长了四倍。每次跃升后,Gas 使用量也出现相应增长。过去几个月来,以太坊的 Gas 使用量似乎卡在了 Gas 上限的 95% 上。
要想知道以太坊的 Gas 使用率为何无法超过 95% ,我们需要先了解空块和叔块。
纵观以太坊的发展史,即使是在供不应求的情况下,以太坊的 Gas 使用量也没有超过 Gas 上限的 95% 。令人惊讶的是,剩余 5% 的 gas 都是以空块的形式被浪费了。空块是有规律的,大约每挖出 20 个区块就会有一个空块。在有付费交易等待打包的情况下,为什么还会有人挖空块?我们来看一下数据:
空块率随时间的流逝而稳定增长,目前在 5% 左右。所有矿池都有挖空块,因此不是恶意行为。真正的问题是,出块速度过快。如果挖出一个区块的时间少于 6 秒,这个块是空块的概率会成倍增加(译者注:作者这个断言应该是根据时间戳统计数据得出的,因为没有使用虚拟语气)。
有一种解释是,矿工在收到一个新区块的区块头、但还没处理完整个区块时就开始挖下一个区块。这在比特币中叫作 “简单支付验证(SPV)” 挖矿,可以让矿工立即开始挖下一个块,但是只能挖空块。一旦新的区块处理完成,矿工就可以开始打包并挖出下一个区块。可以证实这一解释的另一个证据是,如果同一个矿工连续挖出了两个区块,空块率就会降低 25% 。
除了挖空块之外,还有一种方法是在处理新区块的同时继续接着上一个区块挖矿(而不是挖更新的可挖位点)。这就有可能导致多个新区块被挖出。如果这种情况发生在以太坊上,网络就会选出一个合法区块,并将其它区块视为叔块。挖出叔块的矿工会获得同样的区块奖励。叔块率非常稳定:
2018 年,Gas 使用量达到顶峰时,叔块率也达到了顶峰(现已降至稳定的 5% )。那段时间,空块率也有增加,可能是因为矿工改变了策略。
虽然叔块率对以太坊可扩展性的影响不是很明显,但确实存在。拜占庭硬分叉中的 EIP-100 调整了区块难度评估公式,将叔块计算在内。因此,叔块率越高意味着在矿工挖出的区块中,作为叔块被浪费的区块越多,作为合法区块被添加到最长链上的区块越少。具体表现为出块时间增加,每日 Gas 供应量减少。(导致出块时间增加的另一大原因是冰河期。)
无论是叔块还是空块,都是以太坊上重要的网络健康标志。二者中任意一者增加都意味着每日 Gas 供应量减少。对叔块率的分析是 EIP 2028 和 1559 背后主要的研究内容之一(参见 1、2、3)。令人惊讶的是,这两个 EIP 都没有提到空块率,而且研究上存在方法上的缺陷。最好能采用恰当的统计方法(如逻辑回归)来进行更严格的分析,并将叔块率和空块率都纳入考虑范围。
有一些方法可以用来降低空块率和叔块率。假设导致空块率和叔块率较高的根本原因是,由于网络和区块处理延迟,矿池无法获得最新状态。一种简单(但是不可取)的方法是,让矿池变得更加中心化,将最新状态集中在一处。去中心化更高的解决方案是,使用 bloxroute 在矿池之间创建专属的互联通道。还有一种解决方案的灵感来自 “间谍挖矿(spy mining)”,就是让矿池预先共享它们正在挖的区块。其它矿池就能针对每一个在挖区块准备好下一个区块,一旦某个矿池成功挖出了区块,其它矿池就知道下一个该挖哪个区块,并且马上切换过去。从更高层次来看,改进节点通信协议和处理算法也会带来帮助,或许也能带来一些收益。但是,正如我们所见,降低叔块率和空块率最多可以将每日 gas 供应量提高 5% 左右。
由此看来,gas 上限是 gas 供应量的 95% 。那么,如果人们对 gas 的需求超过这一上限,会发生什么情况呢?
当以太坊用户对 Gas 的需求接近上限时,会发生什么情况?矿工可以随心所欲地选择交易进行打包(详见下文)。但是实际上,矿工会优先打包 Gas 价格高的交易,因为这会让他们的收益最大化,最终引发针对 Gas 的最高价格拍卖。
Gas 价格已经成为完美的无弹性供给范例。 随着以太坊网络对 Gas 的利用率超过 80% ,向 95% 的上限直线靠拢,Gas 价格就会出现大幅上涨。需求量增加只会推动 Gas 价格上涨,直至用户负担不起为止。让 Gas 价格下跌的唯一方法是,提高 Gas 供应量或降低用户对 Gas 的需求量。近期 Gas 上限的增加并不足以有效压低 Gas 价格。
乍看之下,人们对以太坊的兴趣增加,只会导致 Gas 价格上涨,不会导致以太坊的使用量增加。实际上,Gas 价格高的交易会淘汰 Gas 价格低的交易。那些小额 NFT(非同质化代币)交易会减少,大额的 DeFi 交易会增加。
EIP 1559 旨在让 Gas 供应量在较短的时间范围内更具弹性。在需求量高峰时期,会创建出较大的区块(高达 2000 万 Gas)。这有助于平抑 Gas 价格,让交易更早被打包进区块。但是,这并不会改变长期中的无弹性供应问题。EIP 1559 实施后仍然会有一个长期不变的 Gas 上限。这就意味着,Gas 价格将持续上涨,直至需求量足够小为止(与现状相同)。此外,EIP 1559 实施后仍有用来支付给矿工、作为打包交易的报酬的激励机制(假设矿池会继续按照这个激励的大小为交易排序、创建区块)。这就意味着,抢跑交易、Gas 竞价之战和矿工抽取价值的问题依然存在。
图中数据代表被打包进下一个区块所需支付的最低价格。如果你愿意等久一点,Gas 价格会低得多。从近期的历史数据来看,如果你愿意等待两分钟及以上,Gas 价格就会下降。EIP 1559 有助于在降低溢价的同时加快交易的处理速度。
因此,达到 Gas 上限时,Gas 价格就会上涨。那么,我们如何提高 Gas 上限?
矿池决定着区块的 Gas 上限。我们先来快速回顾下矿工和矿池的运营模式:几乎所有矿工都会选择将自己的算力起源与其他人的汇集在一起。他们会将资源集中到一起,然后获得稳定的收益流,而不是冒着长期没有收益的风险、单打独斗地挖矿。因此,矿池应运而生,负责验证每位矿工的贡献,并分配下一个区块的挖矿任务。大多数区块都是由大矿池挖出的。下图显示了以太坊诞生以来,矿池的出块占比情况:
由上图可见,星火矿池、Ethermine 和鱼池的出块占比遥遥领先。
除了参与硬分叉之外,矿池运营者还承担一项重要的治理职责:设置以太坊的 Gas 上限。不同于出块时间和 Gas 价格等特性,每个区块的 Gas 上限都需要主动设置。新区块的 Gas 上限与上一个区块之间的差距必须在 0.1% 以内,因此每个区块的大小只能轻微上下浮动(参见黄皮书:公式 47)。如果矿池运营者达成共识,可以大幅调整 Gas 上限:在 2.5 个小时内,Gas 上限可以加倍或减半。如果没有达成共识,Gas 上限就是矿池规模的加权中位数。
目前,由于缺乏关于矿工真实行为的详细信息,我们采取了一种比较简单的方法:投票系统。希望我们将来能通过软分叉将这一方法转变成更精确的算法。
—— 以太坊的基本设计原理 (2015 年 3 月)
从以太坊发展初期开始,由矿工设置 Gas 上限就是一个权宜之计。由于其它方案都不够好,这个权宜之计就一直延续了下来。EIP 1559 提出了一个不同的机制,目前社区正在讨论是否将其加入柏林硬分叉。在那之前,矿池运营者可以像 OPEC(石油输出国组织)管理石油开采那样管理 Gas 供应。
<center>- Ethermine 矿池运营者 -</center>
最近,全球最大的两个以太坊矿池做出了一个有争议的决定,将每日 Gas 产量提高 25% 。此举的目的是通过增加 Gas 供应量来抑制过高的交易费。到目前为止,我们发现,再怎么提高 Gas 上限,也跟不上用户对交易需求的增长速度。这就意味着,Gas 价格在经历短暂下降后,最终还是会再度回升。
提高 Gas 上限会给以太坊带来很高的隐形安全成本。正如我们所见,提高 Gas 上限会提高叔块率和空块率。在交易负载量正常的情况下,这一影响不大。但是出于安全性考虑,我们只对极端情况感兴趣,并不关心正常情况。 根据 Perez 和 Livshits(2019)的研究,在最糟糕的情况下,如果生成一批处理效率极低的交易(在 Gas 成本相同的情况下,处理这类交易的速度比普通交易慢 100 倍),并将其打包到区块内,节点需要花 90 秒的时间来处理这个区块,从而导致节点落后,矿池挖出大量空块和叔块。自这项研究发布以来,已经出现了一些缓解措施,但是问题并未得到彻底解决。因此,两位主要的节点开发者 Péter Szilágyi 和 Alexey Akhunov 批评了关于提高 Gas 上限的决定。
因此,达到 Gas 上限时,Gas 价格就会上涨,而且我们似乎不应该进一步提高 Gas 上限。那么我们还能采取什么措施?我们能够降低交易所需的 Gas 成本吗?
交易的 Gas 消耗量基本上就是其 EVM 操作的消耗量。一笔交易是由许多 EVM 基本操作组成的,而每一个操作的 Gas 耗用量都通过 EIP 和硬分叉来治理的。在过去的硬分叉中,主要操作的 Gas 消耗量有升(EIPs 150、160、1884)有降(EIPs 1108、2028、2200)。计划中的 “柏林” 硬分叉也在考虑引入几个 Gas 消耗量变更(EIPs 1380、1559、2046、2565、2537)。
所有这些变更的目的都是为了让手续费能更精确地反映操作的真实成本。因为计算操作的成本会随着计算机性能和算法效率的提升而变得更低。但存储操作的成本则完全不同。存储和查找操作的成本会跟随区块链状态规模的变动而变动,而以太坊的状态规模是一直在变大的。这种增长不会因为存储设备的升级或者数据库技术的改变而改变。
这就意味着存储仍然会是 DeFi 成本中的一大块。创建一个新的余额需要 2 万 Gas,而修改一个已有的余额要耗费 5 千。一笔转账至少要改变两个月,一次交换则要改变至少 4 个,更复杂的 DeFi 交易会包含更多昂贵的状态操作。看起来实在不容易降低相关存储的数量和成本,这个趋势九头牛都拉不回来。但好消息是,Layer-2 扩展方案会尝试做成存储粗放且计算密集的,看起来会更讨喜一些。
最后,提高区块的 Gas Limit 也同样有安全性方面的隐忧:提高 Gas Limit 是有效果的办法中最差的。单纯根据当前真实操作的平均成本来改变 Gas 消耗量也是非常危险的(Naively optimizing the Gas cost for the current true average cost of operations is very dangerous.)。
到此为止,为什么扩展以太坊是一个棘手的问题,已经很显然了。在我们讨论大家提出的解决方案以前,我们希望再探讨一个当前以太坊可能伤害 DeFi 用户的缺点。
区块生产者虽然受到共识规则的限制,但共识规则也为他们提供了一些重要的自由(freedom),比如交易选择和排序。对于一般的代币转账来说,生产者的自由并不会造成很大影响,但对于 DeFi 交易比如交易所交易来说,抢跑(front-running,指发送 Gas Price 更高的交易来抢先使交易上链)可能收获巨大的经济价值。更复杂的问题是,一笔目标交易被夹在两笔交易中间,像夹三明治一样。Daian et al. (2019) 将能够从抢跑交易中获得的价值称为 矿工可抽取价值(miner extractable value)。
没有证据表明现在的矿池在恶意利用他们的交易排序自由,但如果他们做过,也必然是有获利的。矿池一般都使用 Geth 客户端,该客户端会根据 Gas Price 来排序交易(见 1,2)。这导致了 Gas 的价格竞拍,对 Gas 出价最高的交易能最先打包上链。这一机制的负面效果是,任何人都能通过提高出价来抢跑一笔交易。相互竞争的交易者会持续提高出价,直至交易的利润被 Gas 费完全耗尽。到了这个时候,交易可获得的所有价值都会变成 Gas 费,进到矿工的口袋(译者注:这是一个理论上的结果,也是 “矿工可抽取价值” 概念的由来;实际上,高 Gas 费的交易可能很快就会打包上链,而不会存在相互竞争的交易者像拍卖那样安逸地相互出价;所以矿工可抽取价值也没有想象中那么高,毕竟矿池之间有竞争)。
另一方面,让自己的交易紧跟在另一笔交易之后上链,可能也是有价值的,比如在价格信息传输机制更新信息后第一个清算仓位(liquidate a position)。这个叫做 back-running,最后也会变成矿工的收益。
矿工可抽取价值最终是从普通的 DeFi 用户处得来的,表现是更大的价差、更差的价格、更高的手续费,以及更多失败的交易。要想获得更好的 DeFi 体验,就应该解决这个问题。解决办法之一是限制交易排序自由,比如要求一个区块里的交易按 Gas Price 由低到高排序。
现在我们已经对以太坊的局限性及其对 DeFi 的影响有了全面的了解。当然,所有天王级的团队都在开发解决方案,对吧?
现在有很多厉害的团队在开发不同的可扩展性解决方案。解决方案主要有两个方向,“Layer-1” 和 “Layer-2”。Layer-1 的方案致力于打造一个更可扩展的区块链来取代当前的以太坊,Layer-2 方案则是尝试在当前以太坊的基础上接入更可扩展的基础设施。我们这里就谈 Layer-1,Layer-2 方案留给下一篇文章。
我们从最明显的一个方案开始:提高当前以太坊区块链的性能。也就是 “Eth1.x” 同仁在做的事。单论以太坊客户端的性能提升,就有很多工作可以做。不过,Eth1.x 还没有得到他们应得的支持力度,所以进展缓慢。想要对这个方向的性能提升可达到的程度有个基本概念,只需了解一下 Solana 就好,Solana 实现了以太坊的 1000 倍吞吐量,而且还有空间可以提高;缺点在于需要很高级的硬件才能运行全节点。
大部分其他解决方案都有三点共同之处:(1)使用 WebAssembly 作为虚拟机;(2)最小化状态的架构;以及最重要的,(3)分片。当前的以太坊是串行执行所有交易的。事实上,让交易能排成一个队列可以说是区块链的全部意义所在。但这种模式的缺点就在于,难以并行执行交易,所以我们无法通过投入直接更多资源来解决扩展问题。那么把区块链分割成多个松散互联的领域,一个线程就叫一个 “分片”,就能实现并行化处理。在一个分片内部,事务仍然是串行发生的,但不同分片的事务是移步发生的。这就使得所有分片能并行运行,以分片数量为倍数扩大网络的吞吐量。我们用来分割网络的领域也不需要与分片一一对应,可以把多个领域分配给同一个分片(线程),甚至可以移动它们做负载均衡。看 Near Protocol 的 “夜影” 协议白皮书可更深入地了解分片的大体。
具体如何将区块链分割成多个领域,下一代的区块链各有想法。可以认为,这是一条从细粒度(许多细小领域)到粗粒度(少数几个大型领域)的光谱。
fine parallelization <-------------------> coarse parallelization
DFinity Eth 2.0 Polkadot
actors (TBD. Contracts?) chains
两个项目分别代表这条光谱的两端。DFinity 在细粒度这一端,每一个 “actor” 都有自己的细小领域,而且每个 actor 之间的交互都是异步的。粒度稍大的的 Near Protocol,其中每个合约都有自己的领域。粗粒度一端的是 PolkaDot,一个领域就是一整个分片,更具体一点应该叫做一条 “平行链”。从一个 dApp 开发者的视角来看,断言 Ethereum 2.0 位于哪个位置还为时尚早。ETH1 EE(执行环境)应该是粗粒度型的,其边界恰好与一个分片的边界一致,就是当前的以太坊所化成的一个分片(也就是 Eth1 在 Eth2 实现后的未来)。一个专门的 Eth2EE 可能会选择一个更细粒度的方案。细粒度方案的长处在于它们是透明的;可以将所有的跨合约调用一视同仁,无论这些调用是否跨越了分片边界。这就能反过来允许我们通过在分片间移动合约来实现负载均衡。
缺点在于,跨越领域的事务就不再是原子化的了,它们会变成 并行的,甚至是部分 不可逆 的。在 DFinity 和 Near 中,这一点表现为跨合约的交易变成 async
状态,并返回一个承诺:你需要 await
(等待)。在一场 await
期间,所有到时已经发生的交易都会被提交到链上。然后其他人的交易可以堆积在上面。到这时你就无法回滚已经发生的事情了。当 await
最终解决的时候,它会从合约调用中返回 “成功” 或者 “失败”。有许多提案尝试避免这一点、找回某种形式的跨分片原子性,但都有自己的缺点。看起来,拥抱非原子性会是自然结果。
但对 DeFi 应用来说,一个异步的 transferFrom
调用会带来相当大的挑战。设想一个两方之间的简单交换,Alice 和 Bob 希望交换 Eth 和 Dai。基本合约看起来会像这样:
但现在我们需要能处理错误。如果第一笔交易失败,我们可以简单地终止交易。但如果第一步交易成功而第二笔交易失败,我们要能返还给 Alice 一个 ETH。问题在于,Bob 可能已经在我们处理错误之前就花掉了这笔钱。解决这个问题的一个办法是使用一个 escrow:
很好,现在就不会有人丢币了。但现在 Bob 就对 Alice 的出价有了一个免费的排他性期权(free exclusive option)。虽然现在 Alice 的钱保管在托管方手里,但这些钱既不能接受其他人的交易请求,也不能保证跟 Bob 的交易一定会成功。你可以用惩罚恶意行为来解决这个问题,但是你很难确定合理的惩罚力度,因为不同 DeFi 交易的价值差别非常大。你也可以要求所有市场参与者从一开始就把钱都放在同一个储蓄合约(托管方)里,但这样就重新使得状态集中化、取消了分片的意义。
另一个需要注意的事情是,这些并发问题可能非常棘手。在现实的交易所中,订单的填充状态(fill-state)需要不断更新,这就使得写一遍的更加复杂。如果说困扰以太坊 1.0 的可重入漏洞(reentrancy bug)是蝴蝶,可能引起巨大的后果,那并发问题就是床上的臭虫(bug),无孔不入。并发漏洞是不确定的,而且在测试中可能根本看不出来。就像我们在上面的简单的交换的例子中看到的,开发者需要从头重新思考整个架构,就像床上的臭虫,清除它们的唯一办法就是把一切都推倒,从头重建。
交易所是 DeFi 世界其余部分的基石,而且显然是个川流不息的连续过程。我们已经看过了订单簿类型的交易所面临怎样的挑战。自动化做市商(Automated market maker)交易所还简单点,因为参与者已经将资金都存储在托管方(合约)处,但在这种情境下,储备起来的余额本身就阻碍了并行化处理。即使在更快的传统交易所里,结算活动最终也是在一个撮合引擎里面按顺序处理的,不会用到并发,(但比较好的是传统交易所还可以有冗余)。想深入了解传统交易所是如何运作的,可观看 Brian Nigito 的演讲。
上面说的不代表这些问题是不可能解决的。最简单的解决方案就是要求所有这些协议在每一个分片上都部署一个独立的实例,然后让套利者来保证不同实例之间没有价差。又或者,我们可以让一个同步的分片拥有足够强大的性能,强大到可以包含所有的 DeFi 交易,然后就不用烦恼并发问题了。
在本文中,我们历数了以太坊在扩展 DeFi 容量时的局限性。如你所见,这是一个复杂的问题,没有显而易见的解决办法。在后续的文章中,我们会深入了解具体的一类解决方案,“Layer-2 方案”,并阐述 0x 自己的想法。
(完)
原文链接: https://blog.0xproject.com/scaling-defi-layer-one-7eeb24aca4f0 作者: Remco Bloemen 翻译&校对: 闵敏 & 阿剑
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!