主页 > imtoken地址查询 > 什么是无状态以太坊

什么是无状态以太坊

imtoken地址查询 2023-07-16 05:10:52

描述

在我们关于以太坊 1.x 的上一篇文章中,我们简要回顾了 Eth 1.x 研究的起源、利害关系和一些可能的解决方案。 在上一篇文章的最后,我们提到了“无状态”以太坊的概念,而在这篇文章中我们将进一步详细解释无状态客户端。

Stateless 是 Eth 1.x 研究的一个新方向,所以我们会进行比较深入的分析,了解未来可能面临的挑战和可能性。 如果读者有兴趣了解更多,我会尽量提供相关资源的链接。

什么是“状态”?

要解释无状态的以太坊,我们首先需要了解“状态”的概念。 当我们提到“状态”时,通常指的是“事态”。

以太坊的完整“状态”描述了所有账户和余额的当前状态以太坊正在打包,以及在 EVM 中部署和运行的所有智能合约的集体历史。 链上每个最终确定的区块都有一个且只有一个状态,由网络中的所有参与者共同确认。 每次将新块添加到链中时,状态都会相应地更改和更新。

在 Eth 1.x 研究上下文中,我们不仅需要知道状态是什么,还需要知道它在协议(如黄皮书所定义)和大多数客户端实现(如 geth、parity、trinity、besu等)如何表现。

什么是尝试?

以太坊使用的数据结构称为 Merkle Patricia Trie。 有趣的是,“Trie”最初取自“retrieval”一词,但大多数人会发音为“try”以区别于“tree”。 回到正题,关于MPT数据结构,我们需要了解:

在 trie 的一端,是描述状态(值节点)的所有特定数据片段。 数据可以是特定账户的余额,也可以是存储在智能合约中的变量(例如某个 ERC-20 代币的总供应量)。 Trie的中间是分支节点,所有的值通过哈希运算拼接在一起。 分支节点是一个包含其子节点散列的数组,然后每个分支节点再次散列到其父节点的数组中。 这个哈希链最终到达 trie 树另一端的状态根节点。

以太坊

在上面的简化图示中,我们可以看到一些值以及获取它们的路径。 例如,要得到V-2,我们要经过1、3、3、4的路径。同理,可以通过3、2、3、3的路径得到V-3。注意本例中的路径长度为始终为 4 个字符,并且只有一个路径可用于获取值。

以太坊为什么叫以太坊_以太坊正在打包_sitehqz.com 以太坊和以太坊贸易的关系

这种结构具有确定性和密码可验证的重要特性:生成状态根的唯一方法是计算状态的每个单独数据段,因此通过将根哈希与预序哈希(Merkle 证明)进行比较,它是很容易证明这两个状态是相同的。 反过来,我们也不能用相同的根哈希创建两个不同的状态,任何尝试用不同的值修改状态都会导致不同的状态根哈希。

以太坊通过引入新的节点类型、扩展节点和叶子节点来提高效率并优化 trie 结构。 通过将部分路径编码为节点,trie 变得更加紧凑。

以太坊

在这个优化的 MPT 结构中,每个节点需要在路径的压缩部分或由多个后续节点共享的值(如果需要,路径的其他部分加上前缀)之间进行选择。 其实都是一样的数据和组织,只不过这个trie结构只需要9个节点,而不是18个节点。 它可能看起来更有效率,但事后看来,它实际上是次优的。 我们将在下一节讨论原因。

要获取状态的特定部分(例如账户的当前 ETH 余额),需要从状态根开始,沿着 trie 的路径从一个节点到另一个节点,直到达到所需的值。 在每个节点,路径中的字符用于确定下一个目标节点,就像用于导航哈希数据结构的探棒。

在以太坊实际使用的版本中,路径是 64 个字符(256 位)的地址哈希,值是 RLP 编码的数据 [1]。 分支节点是 17 个元素的数组(其中 16 个是每个可能的十六进制字符,剩下的一个是值),而叶节点和扩展节点包含 2 个元素(一个是部分路径,另一个是值或散列)下一个孩子)。 有关详细信息,您可以浏览以太坊 wiki 页面 [2],或者,如果您更喜欢自己挖掘,这篇文章提供了一个很棒的 Python DIY trie 练习 [3](不幸的是,这篇文章已经关闭。过时了).

在数据库中使用 Tries

读到这里我们应该提醒自己,trie 结构只是一个抽象的概念。 这是一种将整个以太坊状态打包成统一结构的方法。 这种结构需要在客户端的代码中实现并存储在磁盘上(或者分布在全球数千个磁盘中)。 这意味着采用多维 trie 结构并将其嵌入到仅理解 [key, value] 对的普通数据库中。

在大多数以太坊客户端(turbo-geth 除外)中,MPT 是通过为每个节点创建不同的 [key, value] 对来实现的,其中 value 是节点本身,key 是该节点的哈希。

以太坊

sitehqz.com 以太坊和以太坊贸易的关系_以太坊为什么叫以太坊_以太坊正在打包

所以遍历trie的过程和前面讲的理论过程差不多。 为了找到账户余额,我们将从根哈希开始,在数据库中查找它的值以获得第一个分支节点; 使用散列地址的第一个字符,可以找到第一个节点的散列; 在数据库中查找hash,得到第二个节点; 使用散列地址的下一个字符,我们可以找到第三个节点的散列。 如果幸运的话,我们可能会一路碰到一个扩展节点或叶节点,而我们不需要检查所有 64 个半字节。 无论如何,我们最终找到了所需的帐户并从数据库中检索了它的余额。

这个过程在很大程度上类似于计算每个新块的哈希值,但是相反:从所有边缘节点(账户)开始,通过连续的哈希值构建trie,直到最后一个新的根哈希值,并将其与最新确认的块进行比较连锁,链条。

这就是state trie明显的效率优势可以发挥的地方:在磁盘上重构整个trie的强度非常高,以太坊使用的优化版MPT结构通过牺牲执行效率来提高协议效率。

这些额外的节点类型(叶和扩展)理论上节省了存储 trie 所需的内存,但它们使用于修改常规数据库中的状态的算法复杂化。 但是,功能强大的计算机设备可以非常快速地执行该过程。 然而,纯粹的处理能力只能到此为止。

节点同步

到目前为止,我们的讨论仅限于运行以太坊客户端(如 geth)的个人计算机的范围。 而以太坊是一个网络,而这一切的关键是要在全球成千上万的计算机和不同的客户端之间达成一个统一的状态共识。

#Defi 的不断洗牌、来自 cryptokitty 拍卖或 cheeze wizard 战争的代币,以及常规的 ETH 交易都发生了冲突,为以太坊客户创造了一个极其快速变化的状态。 以太坊客户端需要保持状态同步。 随着以太坊的应用越来越广泛,同步状态会越来越困难,状态树的结构也会越来越深。

“Turbo-geth 是 geth 的一个实现:它扁平化了 trie 数据库并使用节点的路径(而不是它们的哈希值)作为 [键,值] 对。这有效地使检索操作与树深度无关,并带来各种在运行完整节点时可以提高性能并减少磁盘负载的很酷的功能。”

以太坊的状态非常大,随着每个区块的变化而变化。 那么state和change有多大呢? 我们可以粗略定位整个以太坊目前的状态在state trie中大概有4亿个节点。 其中,大约每15秒需要添加或修改约3000个(甚至高达6000个)节点。 与以太坊区块链保持同步本质上意味着持续高效地构建新的状态树。

“状态特里数据库操作的这种多步骤过程是以太坊客户端占用如此大量的磁盘 I/O 和内存的原因,即使是“快速同步”(fast sync)模式也可能需要长达 6+ 小时。并且要运行以太坊全节点,快速的 SSD(而不是便宜但可靠的 HDD)是必不可少的,因为处理状态变化对磁盘读/写的要求非常高。”

以太坊正在打包_以太坊为什么叫以太坊_sitehqz.com 以太坊和以太坊贸易的关系

这里要注意的关键点是,设置一个新节点进行同步与保持现有节点同步是非常不同的。 当我们实现无状态以太坊时,它们之间的区别将变得模糊(希望如此)。

同步节点最直接的方式是使用“全同步”方式:从创世块开始,将每个块中的每笔交易还原成一个列表,构建状态树。 一旦生成后续区块,状态树将被修改,随着区块链完整历史的再现,节点将被添加或修改。 从创世块下载并执行每个块的状态更改需要整整一周的时间。 如果您在同步时不急于进行新交易,那只是时间问题。

另一种同步方法“快速同步”名副其实,速度更快但更复杂:新客户端可以从最近的可信“检查点”块请求状态条目,而不再需要从创世块开始. 这样需要下载的信息少了很多,但是仍然有很多信息需要处理。 Quick Sync 目前不受带宽限制,而是受磁盘性能限制。

本质上,进行快速同步的节点正在与链的末端竞争。 在状态变得陈旧并且全节点不再提供该状态之前,节点需要从“检查点”获取所有状态(如果发生这种情况,节点可以翻转到新的检查点)。 一旦快速同步节点克服了这个障碍并使其状态与检查点完全同步,它就可以切换到完全同步,从每个块中包含的事务生成和更新自己的状态副本。

什么是区块见证?

此时,我们可以开始探索无状态以太坊的概念。 无状态以太坊的主要目标之一是让新节点的同步过程不那么痛苦。 考虑到每个区块只有 0.1% 的状态发生变化,似乎应该有一种方法可以减少在切换到完全同步之前需要下载的所有额外信息。

但这是以太坊采用加密安全数据结构的挑战之一:在 trie 中,仅更改一个值会导致完全不同的根哈希。 这是一个功能,而不是错误。 这使得每个人都可以确保他们与网络中的其他节点处于相同的状态。

为了走捷径,我们需要一条关于状态的新信息:区块见证。 假设最近这个trie结构中只有一个值发生变化(下图中绿色部分):

以太坊

同步状态(包括此事务)的完整节点将以传统方式进行:获取所有状态片段并将它们全部散列以创建新的根散列。 这使得验证您自己的状态与其他人的状态相同变得容易(因为节点具有相同的哈希值和相同的交易历史)。

以太坊正在打包_sitehqz.com 以太坊和以太坊贸易的关系_以太坊为什么叫以太坊

新添加的节点呢? 一个新节点至少在其观察期内验证其观察与其他节点一致所需的最少信息量是多少?

新节点需要较早的完整节点提供证据,证明观察到的交易对应于目前的状态。

以太坊

抽象地说,区块见证证明提供了状态树中所有缺失的哈希值,并结合了一些位置“结构”信息,指示这些哈希值在状态树中的位置。 这使新节点能够在其状态中包含新事务并在本地计算新的根哈希,而无需下载状态 trie 的完整副本。

简而言之,这就是波束同步(beam sync)背后的原理[4]。 Beam同步方案不再收集checkpoint trie中的每个节点,而是在交易发生时开始监听并尝试执行,向全节点请求每个区块的见证内容,以获取未掌握的信息。 随着越来越多的状态受到新交易的影响,信息逐渐通过波束同步填充以太坊正在打包,直到最终切换到完全同步,客户端可以更加信任自己的状态副本。

不同程度的“无国籍”

随着区块见证人的引入,“完全无状态”概念的定义逐渐清晰。 同时,这也是我们开始遇到没有明显可行解决方案的瓶颈和问题的地方。

与beam同步方案不同的是,真正的无状态客户端不会自始至终保留一份状态副本,而只是获取与区块见证人(witness)的最新交易,其中只需要包含执行该交易所需的所有信息即可下一个街区。

所以我们几乎可以预见,如果整个网络是无状态的,其实是可以达到永动机状态的。 新区块的witness是从前一个block产生的,然后依次通过witness! 至少直到最后确认的“交易状态”,以及该状态产生的第一个见证。 这对以太坊来说将是一个巨大的翻天覆地的变化,因此不太可能获得广泛支持。

有一种不太激进的方法:适应不同程度的“无国籍”。 在这样的网络中,一些节点保留了状态的完整副本,也可以为所有其他节点提供最新的见证。

sitehqz.com 以太坊和以太坊贸易的关系_以太坊正在打包_以太坊为什么叫以太坊

全状态节点会照常工作,但需要计算一个额外的见证人(witness)并将其添加到新区块中,或者通过辅助网络子协议进行广播;

部分状态节点只能在少数出块周期内保持完整的状态,或者只“监控”自己相关的状态部分,然后从见证人那里获取验证区块所需的其余数据。 这对于需要运行基础设施的 DApp 开发者来说有很大的好处;

· 根据定义,零状态节点希望在运行完全依赖块见证来验证新块的客户端时尽可能轻量级。

为了启用此方案,可能需要类似于 bittorrent 采用的分块和蜂群行为,其中根据需要广播见证片段并建立最佳连接。 或者,这可能需要开发状态特里的替代实现,使其更适合见证生成。 这都是我们需要研究和试验的东西!

有关有状态节点和无状态节点之间权衡的更深入分析,请参阅 Alexey Akhunov 的“The shades of statefulness”[5]。

这种半无状态方法的一个重要特征是这些更改不一定必须诉诸硬分叉。 在小而可测试的增量步骤中,以太坊的无状态组件可以构建成一个辅助子协议,或者分成一系列无争议的 EIP,而无需进行大的“信仰飞跃”升级。

无状态客户端路线图

我们在研究中遇到的一个明显问题是区块见证人的规模。 一个普通的区块包含区块头和交易列表,其大小约为 100 kB。 此大小允许相对于网络延迟和 15 秒块时间的快速块广播。

然而,见证人需要包含状态特里的边缘和深度节点的哈希值。 这意味着它的大小要大得多:早期数据显示大约 1 MB。 因此,与网络延迟和阻塞时间相比,同步见证要慢得多,这可能是一个障碍。

这种困境类似于电影下载和流媒体之间的区别:如果网络太慢而流媒体无法顺利加载,下载完整电影是唯一可行的选择。 如果网速快,电影播放会很流畅。 如果实际情况是网速没有增加,那么我们就需要更多的数据来做决定。 当需求过高时,低于这些标准的互联网服务提供商将认识到低速网络的局限性。

在很大程度上,这是 Eth 1.x 工作组正在处理的特定问题。 目前,我们对假设的见证网络的了解还不够多,无法确保它能正常或理想地工作,棘手的部分是细节(和数据)。

一种方法是考虑如何通过改变 trie 本身的结构(例如二进制 trie)来压缩和减少 witness 的数量,以使其在实现中更加高效。 另一种方法是对网络原语进行原型设计(例如 bittorrent 的 swarming),以便可以在网络中的不同节点之间有效地传递见证。 这两种方案都可以从正式的见证规范(目前不存在)中受益。