外汇天眼APP讯 : DeFi 应用中锁定的资金量已超过 15 亿美元;几乎每天都有新的 dApp 官宣。照这个速度下去,以太坊可能会死于太过成功。就基础设施层而言,状态无限增长是一大难题。
几天之前,Alexey 在 EthResearch 论坛上发布了一篇文章,提出了一个有趣的设想,并将其命名为 ReGenesis 。
这个设想比较简单,乍看之下可能会让人觉得很激进:定期(例如,每 100 万个区块)删除存储在本地的所有状态,保留最新区块的根哈希,然后重新开始构建状态,再过 100 万个区块之后,进行下一次状态清零。
-ReGenesis :每 N 个区块重设状态。随着状态增长,需要的证明越少,直到下一个 ReGenesis 启动-
要想理解这个设想为什么可行且值得研究,你需要先了解一些背景知识和讲解。
这就是本文的主题。
目前情况
1. 状态存储集中化
在以太坊上发送交易非常容易,你自己不需要用到什么状态数据。交易发送者没有保存任何状态的动力。
如果你(矿工或交易所)正在运行一个基础设施节点,你需要确保自己拥有验证及广播交易所需的一切状态。如果你是矿工,你还需要更多状态。
因此,当前状态存储都集中于大型实体(Infura、大矿工、交易所等)运行的基础设施节点。
2. 状态继续无限增长
截至发稿时(6 月 29 日),Turbo-Geth 客户端中的 “当前状态” 大约 40 GB ,而且还在继续增长。
这里的问题并不在于状态大小本身,而在于状态增长不受限制。当然了,我们也可以祈祷固态硬盘容量的增长速度超过区块链状态。
到目前为止,我们还算幸运,不过单凭运气绝非长久之计。
3. 无状态以太坊很难对 Gas 重新定价
对于无状态以太坊计划来说,对区块见证(witness)定价是非常困难的。这些见证是由矿工生成的,交易发送方在发送交易时很难预测区块见证的大小。
因此,我们需要在不影响现有智能合约的情况下,找到一种能够通过智能合约行为推测见证价格的方法。
目前已经有了一些提案,如,Oil ,不过这确实是个棘手的问题。
ReGenesis 后如何运行交易
好了,现在我们都知道了,要将所有当前状态清零,只保留根哈希。数据量从 40 GB 减少到 32 字节。
太棒了,硬盘空间省下了,但是我该如何发送交易呢?
这时,无状态以太坊就派上了用场。为了能够运行交易,你需要提供见证。见证包含所用账户、代码和合约存储内容,以及能够用来验证根哈希的默克尔证明(点击此处,了解更多关于见证和无状态以太坊的信息)。
然而,ReGenesis 和 Stateless Ethereum 之间有一个区别。在解释这个区别之前,我想先介绍两个术语:显式状态(explicit state)和隐式状态(implicit state)。
假设你在运行一个 geth 节点。无论你的对等节点何时向你发送了新的区块 N ,该节点都假定你已经拥有了验证该区块内所有交易所必需的状态数据,并且已经同步了区块 N-1 。
- 现行的以太坊:假设两个节点拥有一切必需状态 -
这就是 100% 隐式状态。对等节点假定你已经拥有了状态,因此没有在区块中添加任何状态。
在无状态以太坊中,当你运行一个节点时,对等节点向你发送了一个新的区块 N,并且认为你在任何情况下都没有隐式状态。它们会将你运行区块 N 所需的一切都打包进区块 N 。这就是区块见证(block witness)。
- 无状态以太坊:我们提供显式状态 -
你的对等节点向你发送显式状态。
ReGenesis 则位于两者中间。
假设 ReGenesis 在区块 10.000.000 启动。目前,位于区块链顶端的是区块 10.001.000 。我们可以认为任何 ReGenesis 节点都拥有区块 10.000.000 和区块 10.001.000 之间所有数据的隐式状态。这些区块中用到的每个账户、每个存储条目和每个合约已经存储在每个节点上,因此不需要区块见证。这样就可以大幅削减见证的大小,正如我们在准-无状态同步实验中看到的那样。
- ReGenesis:区块 1 提供节点 1 所缺少的信息 -
- ReGenesis:将来自区块 1 的状态合并到节点 1 的隐式状态中 -
如果你(作为交易发送方)需要将新的交易发送到当前区块高度为 10.001.000 的网络,你需要执行以下步骤:
创建你想要的交易;
查看自上一次 ReGenesis 启动以来生成的隐式状态;
为不包含在隐式状态内的条目创建显式状态,将其打包为交易见证(下文会作详细讨论);
将带有见证的交易发送至网络;
等一下,这是不是就意味着,交易发送方必须拥有一些 ReGensis 启动前状态?!
没错。
如果你想在 ReGenesis 启动后发送交易,你可能需要拥有一些 ReGenesis 启动前的状态信息,才能生成见证。
然而,对于大多数 dApp 来说,它们需要存储实际使用的一小组合约和账户的状态(当然还有默克尔证明)。
这种优化就不会太过激进。从好的方面来看,这种做法有助于推动数据存储的去中心化。
交易见证
你可能已经注意到了,我在上文提到了交易见证这个词。
交易见证 vs 区块见证
- 组合式区块见证 -
我们没有为整个区块生成见证(区块见证),而是为每个交易生成见证。
- 交易见证 -
交易见证包含交易中使用的所有账户、存储条目和代码的显式状态,以及用于验证状态的默克尔证明。
- 区块见证是由矿工生成的。我们需要通过复杂的 Gas 定价机制来补偿他们 -
交易见证有一个很重要的优点。它们是由交易发送方生成的,与交易一起发送。因此,我们可以立即知道交易见证的大小以及如何为其定价。我们不需要通过 EVM 来重新定价。
- 交易见证是由交易发送方生成的,补偿起来容易得多 -
使用交易见证的一个潜在缺陷是数据复 制。假设一个区块中的所有交易都由两个相同的账户达成,交易见证内将包含重复的数据。
另一个缺陷是,使用交易见证的算法更复杂一点。
区块见证由矿工生成。矿工知道区块中交易的确切顺序,因此区块见证总是包含最新数据。
交易见证来自交易发送方。因此,应该有一个智能合并机制,将交易见证与区块中较早的交易所生成的隐式状态合并起来。
但是,整个 ReGenesis 设想需要用到交易见证,所有这没什么大不了的。
为什么我们现在还无法使用交易见证?
简单来说,是因为动态状态访问(DSA)和恶意参与者抢跑交易的潜在风险。
具体来说,在无状态以太坊中,你需要提供这个交易的完整状态,我们假定接收方没有任何状态。如果你的交易使用 DSA ,那么你的代码读取哪些存储部分取决于代码其他部分的值。
从理论上来说,这就有可能招致 DoS 攻击。
假设 Alice 的智能合约基于存储地址 K 读取状态条目 A 或 B 。Bob 在 Alice 的交易被执行之前抢先让自己的交易被执行(这就是 “抢跑交易”),更改 K 值导致 Alice 的交易失败。
如果 Alice 提供了一个包含 A 的证明,Bob 可以在 Alice 的交易被打包之前更改 K ,导致 Alice 交易失败。如果 Alice 提供了一个包含 B 的证明,Bob 可以故技重施。
当然了,Alice 可以提供包含 A 和 B 的证明,但是如果存储地址是由 uint64 决定的,Alice 就要在证明中包含完整的状态来防止被攻击,但要包含完整的状态是根本不可行的。
当然了,这种攻击是理论层面上的。不过,还有可能出现更多类似的攻击。鉴于 dApp 持有大量资金,我们需要非常谨慎,不能破坏任何东西。
ReGenesis 是如何缓解这个问题的?
ReGenesis 的确定性可以帮助我们确定节点拥有多少状态。对于我们确定节点拥有的那些状态,我们不需要相关证明。
为了缓解这个问题,无论交易是成功还是因状态不足失败,我们都要确保我们所提供的证明被包含在状态中。
我们接着上文的例子来看。
假设 Alice 发送了一笔交易,带有路径 A 所需的证明,但是 Bob 更改了 K ,让 Alice 的合约只能选择路径 B 。
虽然 Alice 的交易失败,但是该交易将路径 A 所需的一切证明都添加到了节点的隐式状态中。
现在,Alice 可以重新发送这笔交易,提供路径 B 所需的证明。Bob 无法再通过更改 K 来阻止 Alice 的交易,即使他通过更改 K 来让交易选择路径 A ,那也太迟了,路径 A 所需的证明已经包含在了节点的隐式状态中,因此交易无需任何证明即可发送至节点。如果交易被导向路径 B ,那么它正好拥有 B 所需的证明。
结论
最后,让我们来快速回顾一下 ReGenesis 的设想和要点:
每生成 N 个区块,我们就会将所有状态清零,只保留根哈希;
ReGenesis 启动的频率不能太高,可以设定为每 100 万个区块、每 1000 万个区块等;
交易发送者需要提供显式状态(作为交易见证);
交易发送者基于交易见证的大小为其支付 gas 费;
为此,交易发送者必须保留其感兴趣的 合约/账户 的 ReGenesis 启动前状态;
如果交易因为显式状态不足而失败,我们会将该交易提供的证明添加到隐式状态中,这样我们在下一次发送交易时就不需要提供同样的证明了;
通过智能合并机制将来自交易证明的显式状态与区块中较早运行的交易所生成的隐式状态合并起来。
从宏观角度来看,ReGenesis 方案有以下几个优点:
改变交易发送者和基础设施节点之间的激励平衡,从而提高状态存储的去中心化程度;
通过 ReGenesis 来限制状态增长;
允许使用交易见证并简化每个交易见证的 gas 定价。
当然了,首先,我们还应该对很多东西进行测试、检验和证明。我认为这是一个很有趣也很有前景的研究领域,能够带来很多潜在好处。