EOF:复杂性超过必要性

这篇博文深入探讨了以太坊对象格式(EOF)提案的复杂性和成本,认为其带来的好处主要是附加的,而不是必要的改进。文章详细分析了EOF的设计架构、优缺点以及其对以太坊协议复杂性的影响,强调可以通过更简单的改进方案替代大量新变化,并警示可能引入的共识错误。

这篇博客文章是由 MattMoodyRamana 共同撰写,并得到了 Charles 的额外反馈。这些贡献者代表了整个 EVM 堆栈,从虚拟机、形式规范和编译器维护者到应用程序和库开发者。

你可以在 这里 的讨论线程中分享你对这篇博客文章的反馈。

这篇博客文章探讨了以太坊对象格式(EOF)提案——其声称的好处、潜在的复杂性,以及其成本是否真正值得实施。我们首先对 EOF 的设计架构进行了高层次的概述,探索其声称的好处,并最终表明这些好处代表的是“可有可无的”优点,而非对协议至关重要的改进。此外,这些改进可以通过更小、更简单的更改单独引入。相反,这些更改的具体绑定方式来伴随的是带来协议复杂性 显著增加 的真实成本,从而提高了共识错误的风险。

它还引入了“未知的未知”,正如在其开发周期的四年内尚未发现的重入性问题所证实的那样。

理解 EOF 的设计

EOF 为 EVM 代码引入了一种结构化格式,最新的规范在 这里 可用。设计围绕“容器”展开,这些容器将代码与数据分开,同时允许未来容器类型的灵活性。EOF 通过新操作码——CALLFRETF 和(暂定)JUMPF——引入了子程序功能,这些操作码在格式中结合容器进行建模。

总体而言,EOF 的设计目标包括多个方面:

  • 通过用静态定义的控制流替代动态跳转来改善静态分析,
  • 向 EVM 添加子程序,以及
  • 通过添加或移除操作码来促进 EVM 升级。

在开发过程中,还增加了额外的设计目标:禁止代码和 gas 反照,以便在 EOF 版本之间促进可升级性。因此,EOF 引入了新的 *CALL 和创建模式,同时移除了现有的一些模式。它还移除了 *CODECOPY*CODESIZE 指令。EOFCREATE 引用当前容器内的现有“子容器”以进行代码部署。

EOF 的声称好处在 pcaversaccio 的以太坊魔法师帖子中概述:以太坊正变成一个不必要复杂性的迷宫,让我们重新考虑 EOF

让我们将这些好处分为几个子群体并加以扩展。

编译器复杂性减少

EIP-663 操作码使得能更深入地访问数据堆栈,可能允许 Solidity 解决或消除著名的“堆栈太深”问题。然而,这实际上代表的是 编译器设计挑战——具体来说是与 Solidity 语言和编译器架构相关的问题,而不是虚拟机设计问题。例如,Vyper 就不遇到这些问题。

关于这点,Danno Ferrin 写道

编译器确实可以通过寄存器分配解决著名的“堆栈太深”问题,但它们不能保证最佳解决方案。但是即使他们能做到,堆栈/寄存器溢出在没有可靠的廉价存储器访问的情况下是非常不具成本效益的。EVM 的非线性 gas 成本使得更大变量池的访问比典型硅处理器中的分页存储模型更昂贵和脆弱。

这一点很好,EVM 内存定价确实昂贵且复杂!因此,一个影响更小且可以说更有效的解决方案是直接 改革 EVM 内存定价模型,仅需 Gas 计划更改,而无需操作码或格式修改(详见 这里这里)。

此外,EIP-663 另一个缺点是它可能对解释型 EVM 环境中的性能产生不利影响,因为它要求热访问额外的内存,并可能对已编译的 EVM 产生负面影响。

由于寄存器分配是 NP 完全(在实际中是多项式算法),因此热线堆栈项的增加可能使 EVM 到本地编译器性能进入超线性区域,这与 EOF 的目标之一 发生了冲突。

字节码大小改进

EOF 支持者 声称 其改善字节码大小和性能。我们在 Solidity 0.8.29(支持实验性 EOF 的最新版本)上的编译测试显示,启用 EOF 的输出仅 424 字节,其中 350 字节的改进源于去除了 JUMPDEST。这种改善似乎值得怀疑,因为没有什么可以阻止我们直接从 EVM 中移除 JUMPDEST——跳转目标不必是 JUMPDEST 指令。

JUMPDEST 目前为链下符号工具提供了便利,使得搜索空间受到限制。然而,符号工具可以实施用于分析潜在跳转目标的启发式(并且无论如何需要这样做)。与具有软实时要求的客户端实现不同,后者因 DoS 漏洞担忧而无法依赖启发式,因此符号执行中的启发式,至多会导致本地用户应用程序的超时,而不是客户端 DoS 或链分裂。

请注意,移除 JUMPDEST额外惠及JUMPDEST 分析”性能,减少分析循环中的特殊案例处理。这可以通过完全向后兼容的更改将分析速度提高到 2 倍。

此外,EOF 实际上引入了字节码大小的权衡,而不是严格改善大小——每个子程序声明的段头需要多个额外字节,这对拥有众多小子程序的合约造成了惩罚。由于在 EOF 中有效补充函数跳转不可用,编译器需要发出额外的函数以符合验证限制,进一步增加了代码大小。

EVM 性能提升[1]

在 EOF 中,一个主要的声称好处是堆栈和代码验证仅在部署期间执行一次,EVM 实现可以消除许多运行时检查,更高效地执行代码。然而,虚拟机指令的执行并不是以太坊的瓶颈。瓶颈在于输入/输出

这表明我们在优化错误的事物——对 EVM 来说,一个侵入性更小的优化是提升状态根的效率。目前的几个提案包括 延迟状态根计算 和通过 区块级访问列表 预取状态。

这只是计算资源错误分配的另一个例子。如前所述,移除 JUMPDEST 也将提升 EVM 性能,改善 EVM 内存和算术操作的 Gas 定价模型也会带来同样的效益。

促进 EVM 升级

EOF 的支持者认为它简化了 EVM 升级,例如添加或移除操作码。一例为 地址空间扩展 (ASE)——现有操作码当前将地址的顶部字节清零;未来的 EVM 迭代可能希望扩展地址以使用更多可用的 32 个字节。EOF 的版本和代码验证声称的好处是将方便这些更改。然而,任何 EVM 版本方案都会简化升级。(值得注意的是 EIP-6800(Verkle 状态树)也引入了账户版本控制)。事实上,我们可以在现有 EVM 内实现版本控制和/或验证规则。合约字节码可以在创建时进行分析,如果某些操作码在特定的 fork_blocknum 之后被依赖,则禁止合约的部署——或者改变其语义。

一个问题是,现有合约可能包含具有任意内容的“数据节”。这是已知问题,关于 BEGINDATA 操作码的提议在之前的提案中(在 EIP-615EIP-2327 中提出)将自己和随后的字节标记为不可执行将优雅地解决这一问题,而无需格式更改。进行此更改后,确定合约是否仅包含根据分叉规则定义的有效操作码将需要一次性分析。

同时,是否真正允许在 EOF 中实施 ASE 似乎存在争议,因为 EOF 和非 EOF 仍然需要在可预见的未来相互交互。

启用具有立即值的操作码

这是一般 EVM 升级问题的后续。关于即时值操作码的当前关切是,如果它们在合约中已经存在(目前是无效操作码),通过将它们启用为具有立即值的操作码,合约语义可能会被改变(例如,通过覆盖 PUSH 指令的初始字节)。但是,我们只需上述 BEGINDATA 操作码即可启用针对未定义操作码的验证,这为今后安全地引入具有立即值的操作码创建了路径。

此外,唯一提出的立即值操作码是 EIP-663 指令(前面讨论过)和 EOF 特定指令。在实践中没有 强烈需要 列入具有立即值的操作码。

消除 gas 反照

EOF 的目标之一是消除 gas 反照。然而,它仅部分实现了这一目标。从 EIP-7069

与原始 CALL 系列指令相比,一个重要变化是呼叫者无法控制作为调用一部分所传入的 gas 量。这样一个特征至关重要的情况可能更好地通过直接协议集成解决。

移除 gas 可选择性还引入了一个有价值的属性,未来对 gas 计划的修订将受益于此:你始终可以通过在交易中发送更多 gas 来克服 "Out of Gas" (OOG) 错误(受区块 gas 限制限制)。之前在提高存储费用时( EIP-1884),某些仅向其调用发送有限 gas 的合约被新的费用模型破坏。

因此,一些合约有一个返回到下一次调用的 gas 上限,这永久限制了它们可以花费的 gas 量。额外的 gas 无法解决这个问题,因为调用会限制发送的量。该规范保留了一个补贴底线。此底线可以独立于智能合约进行更改,并仍然保留 OOG 停止可以通过在交易中发送更多 gas 来解决的特性。

根据 63/64 规则,简单地“作为交易的一部分发送更多 gas”并不能完全解决问题,因为 执行仍然依赖于 gas。63/64 规则意味着子调用的成功或失败取决于提供给当前调用上下文的 gas,影响当前上下文的结果。换句话说,增加可用的 gas 可能会修改程序语义,而不仅仅是二元的“是否 OOG”。

为了真正消除 gas 反照,必须超越移除 GAS 操作码。63/64 规则也必须被消除,还包括它保护的 1024 深度调用栈。

请注意,在实践中,PAY 操作码( EIP-5920,本身是 EIP-5065 的后续方案)解决了移除 gas 反照应解决的问题。使用有限补贴调用合约的最常见原因是确保在价值传递过程中非重入性。PAY 提供了价值转移,而没有重入风险。(根据早先描述的版本控制方案,仍然可以通过引入 EIP-7069 操作码并逐步淘汰遗留的 *CALL 操作码来减少 gas 反照。)

消除代码反照

Vitalik Buterin 在以太坊魔法师论坛帖子中提出了这个概念,建议 EVM 的可升级性可以通过转译实现。然而,目前仍不清楚这种方法是否明智,或者 EOF 是否真的能实现代码转译。根据 Martin Swende 的说法,转译 EOF 合约所需的工作量与 Verkle 比较复杂。

Danno Ferrin 表示

它把合约的表示与协议层关于其执行和结果的共识分开,允许代码转译到其他格式,例如 RISC-V。允许代码进入或离开系统内存会将合约的一个特定表示锁定在共识作为一部分。

这只有在假设非 EOF EVM 最终将在某一点被弃用时才有用——这种情况不太可能。此外,转译到 RISC-V 将牺牲 EOF 的好处,使得其目的变得不明确。

同样通过移除代码反照和添加新的创建操作码,可以在不破坏格式的情况下进行。代码反照可以在没有 EOF 的情况下消除,方法是向非 EOF EVM 添加验证规则,移除相关操作码并后来引入新的创建操作码。这些更改不需要与其他修改同时部署。

静态分析改进

EOF 设计的核心在于通过引入新的控制流操作码来使跳转静态化:

  • RJUMPRJUMPIRJUMPV
  • CALLFJUMPFRETF

它还禁止使用 JUMPJUMPI 操作码。

这一旨在提供以下好处:

  1. 有可能为 EVM 到本地编译器提供线性时间遍历合约控制流的能力,以及
  2. 允许链下工具在没有启发式或“路径空间爆炸”的情况下解决控制流。

对于现有智能合约编译器而言,似乎存在疑问,因为它们已经可以很好地产生 EVM 代码。实际上,这也是比赛中 Solidity 团队在 2021 年反对 EIP-2315 的主要原因——因为它似乎不必要,与当前的做法相比有些多余(见 这里这里)。

我们发现值得注意的是,Solidity 团队用来驳斥 EIP-2315 的例子声称的低气体收益 在 EOF 中的成本是相同的(!!!)。

为什么 EOF——一种格式限制更大的形式,同时具有相同的操作码成本[2]——能让 EVM 编译器获益更多呢,而不是 EIP-2315? Solidity 团队尚未澄清他们反对 EIP-2315 的原因,但支持 EOF 除了当前的 EOF 迭代包括 EIP-663。如果 EIP-663 从 EOF 中移除,他们会继续支持吗?

我们想强调的是,包含子程序和静态跳转的 EVM 提案已有更少的干扰。例如,将 EIP-2315EIP-2327BEGINDATA)与 EIP-4200RJUMP*)相结合,可以实现静态分析的目标,而无需引入新的代码和堆栈验证规则(以及伴随的共识错误潜在风险)。

Charles Cooper 指出 EIP-2315 实际上在编译器实现方面要简单 1-2 个数量级(10-20 行与 1k loc)并不复杂,因为它只影响调用约定的操作码,而无需遵守新格式或验证规则。

此外,具有可静态分析代码的价值本身对于 EVM 也值得怀疑。虽然这可能简化一些链下工具的实现,但并不具备新的使用案例。如前所述,缺乏静态可解决跳转对链下工具的影响是潜在的超时发生——显然比部署共识错误要好得多。

静态跳转能够使 EVM 到本地编译在时间复杂性上得到线性时间的提升,这个想法听起来不错,但实际上还有很多其他因素影响编译时间(例如,上述寄存器分配问题,而 EIP-663 实际上加重了这一问题!)。因此,目前尚不清楚静态可解析跳转是否会在实际应用中显著改善 EVM 到本地编译器的情况。

消除代码大小限制

EIP-7830 的动机部分称:

合约大小限制是作为针对 DoS 攻击的防范措施引入的。对于遗留合约,执行 JUMPDEST 分析是必需的,而且执行这一分析的许多算法不是线性的,或者存在未知的未知。这就是限制提高的原因之一。

这根本是一种不真实的恐惧宣传。JUMPDEST 分析显然是线性的。即使有人对此意见相左( 与基准结果相矛盾),EOF 中的代码和堆栈验证算法实际上是 复杂的,而不是更简单的,但却宣传成线性时间。这代表了一个逻辑矛盾。

此外,提升 EIP-170 限制的问题(早在 2016 年就在 EIP 文本中指出!)包括,除了执行 JUMPDEST 分析外:见证大小膨胀以及从磁盘加载的线性费用。因此,EIP-7830 中的动机只涉及 JUMPDEST 分析,而忽视了其他问题!

通过对 gas 计量采取替代提案来增加代码大小限制的问题,同时包括:

值得注意的是,这些提案完全不依赖于 EOF 或部署时代码验证。

ZK 友好性

EOF 被称为更友好的 ZK。根据 Succinct,EOF 提测的以太坊合约在证明上提供了性能改进。基准测试是一个循环密集的算术基准,以基准 Fibonacci 函数进行测试,这并不一定代表典型链工作负载,因此不清楚这在实践中是否意味着真正的改进。

此外,我们没有看到令人信服的论点说明这是否构成硬性要求,或者为何同样的好处无法通过较小的更改获得。

EOF 的缺点

迄今为止,我们已论证了所有 EOF 的好处都可以通过更渐进、更不具干扰的 EVM 更新引入。

与此同时,即使承认其好处,EOF 的复杂性也无法忽视(有关概述这些复杂性的其他资源,请参见 Marius van der Wijden 2024 年的博客文章 这里)。前一部分单独研究了 EOF 的声称优势;本部分列出了其缺点,这些是直接的成本,而没有相应的好处(超出前述的)。

不是真正的升级

根本的问题在于必须一直支持非 EOF EVM,很可能是无限期的!这需要 EVM 团队在 永续 的时间中同时维护 EOF 和非 EOF 格式。客户端团队至少需要支持 EVM 的两种语义,甚至是多种语义!

此外,工具必须支持 EOF,这需要多个团队之间的协调。编译器、应用程序开发者、规范开发者、调试者、符号工具、框架维护者和测试编写者现在需要在两种格式之间重复工作。

这与上述 EIP-2315EIP-615EIP-2327 有着截然不同的对比,这些提案保持 EVM 字节码格式和设计。支持新操作码的实现是维护的事,而不需要交付完全分开的代码格式。

生态系统中的鸿沟

EOF 创建了 EVM 生态系统中的鸿沟,因为 EOF 和非 EOF 合约可以互相交互。这是现实情况,因为 EOF 与非 EOF 合约并存,并不妨碍它们的存在。

换句话说,EOF 的任何优势都因非 EOF 合约的存在而被抵消,而且它们不受益于 EOF!例如,移除 gas 反照(正如我们已经展示的,EOF 实际上并不能做到这一点,不过考虑到论点而言这里的结论是成立的)理论上将允许在不损害生产合约的情况下修改 gas 计划。然而,既然非 EOF 合约仍然存在,通过 gas 计划破坏生产合约的可能性依然存在!虽然减少 gas 和代码反照或改善 EVM 合约可分析性是值得追求的目标,但 EOF 被当作是解决这些问题的绝对方案,而实际上却并非如此。

过度的特性耦合

EOF 将多个不同的更改耦合在一起。它的支持者声称这些必须同时交付以应对后勤问题,这形成了令人尴尬的 NM 耦合关系,似乎要获取任何单一的好处必需将所有更改一起发送出去。

这是一系列对 EVM 的升级大杂烩。如果我们不必担心向后兼容性,EOF 包将 一个好主意(尽管这一点本身也是有疑问的——如果从头再设计 EVM,真的会呈现与 EOF 一样的样子吗?)。

我们认为,整体收益并不足以承担发布全新格式所需的向后不兼容风险,特别是因为每项改进都可以在 没有 破坏格式的更改中引入。

这就是为什么我们倡导可以独立交付的更改,而不需要进行耦合。此外,我们也支持对现有功能的微调;例如,优先选择让新用例成为可能的 Gas 调整,而不是新操作码,和新操作码替代全新格式。

由于复杂性带来的风险

最后,或许最重要的是,EOF 代表了一项极为复杂的变化。除了新格式和验证程序外,它还引入了新的创建模式、新交易类型( TXCREATE)和全新的合约交互机制( EIP-7069)。

以意外的交互为例,Solidity 的 send()transfer() 函数现在允许 重入(!)——这是在 EOF 开发进行近四年且奥桑加举行前一个月尚未被识别的问题。EOF 的支持者建议将 PAY 操作码纳入奥萨卡升级作为解决方案。但正如前面所论证的,PAY 实际上解决了EOF限制 gas 反照本旨在解决的问题!我们可以简单地实现 PAY 操作码,而无需创建 EOF/非 EOF 鸿沟,即可获得 80% 的好处。

另一项由 Marius van der Wijden 提出的 担忧 是代码和堆栈验证结果没有存储在任何地方。因此,验证规范错误可能允许部署具有未定义运行时行为的合约。

结论思考

我们必须区分“EOF 启用 X”和“X 需要 EOF”。以太坊确保了超过 $3000 亿 的价值。对 EVM 的更改必须仔细权衡与之代表的风险。

EOF 的优点,逐一来看,确实是令人钦佩的目标。然而,这些目标都可以通过避免侵入策略来实现。在我们的评估中,EOF 过分强调了格式的更改,而不足以解决 EVM 的功能改进。我们为什么要优先考虑象牙塔的改善而非对用户体验与安全性直接影响的改进,比如合约大小限制增加和 PAY?我们应当聚焦于基于现有生态系统进行发展,而非一再从头开始。

此外,这些更改的捆绑方式将 显著增加复杂性,通过可能的设计交互缺陷带来的风险。此外,如果客户端的代码/堆栈验证中出现任何错误,还引入了对 未定义的运行时行为 的新可能性,这将为以太坊客户端带来一个新的错误来源。

或许我们会得出这样的反思:保持 EVM 的稳定性更有价值,还是创新性更有价值? 稳定性或许看起来并不引人入胜,甚至会被认为是危险的(“停滞”),尤其是对虚拟机研究者和开发者来说。然而,稳定性的价值与网络效应结合,不应被低估。以太坊的价值来自于提供的稳定性、鲁棒性和确定性。与此同时,对于应用设计者、工具开发者、客户端开发者和其他 EVM 用户而言,不确定性带来了巨大的且通常是隐形的成本 .

  • 原文链接: hackmd.io/@pcaversaccio/...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
pcaversaccio
pcaversaccio
江湖只有他的大名,没有他的介绍。