本文讨论了以太坊协议设计中的封装复杂性与系统复杂性之间的权衡。作者阐述了这两种复杂性的定义,并通过多个密码学和经济学的例子(如BLS签名与Schnorr签名、ZK-SNARKs与欺诈证明)探讨了如何在协议设计中做出选择。设计复杂性的减少并不总是单一的解决方案,而是在面对不同复杂性的权衡时需要灵活判断的问题。
协议设计中的封装复杂性与系统复杂性
Ethereum 协议设计的主要目标之一是最小化复杂性:使协议尽可能简单,同时仍然让区块链能够完成有效的区块链所需的功能。Ethereum 协议在这方面远非完美,尤其是在2014-2016年期间设计的许多部分,那时我们的理解远不如现在,但我们仍然积极努力在可能的情况下减少复杂性。
然而,达成这一目标的一个挑战是,复杂性很难定义,有时你必须在两种引入不同类型复杂性并具有不同成本的选择之间权衡。我们该如何比较呢?
一个强有力的智力工具,可以让我们对复杂性进行更细致的思考,就是区分我们所称的 封装复杂性 和 系统复杂性。
封装复杂性发生在存在一个具有内部复杂子系统的系统时,但对外部呈现出一个简单的“接口”。而系统复杂性则发生在系统的不同部分甚至无法被清晰分离,并且彼此之间有复杂的交互。
以下是一些例子。
BLS 签名 和 Schnorr 签名 是两种流行的基于椭圆曲线的密码签名方案。
BLS 签名在数学上看起来非常简单:
签名:σ=H(m)∗k
验证:e([1],σ)=?e(H(m),K)
H 是一个哈希函数,m 是消息,k 和 K 是私钥和公钥。到目前为止,这非常简单。然而,真正的复杂性隐藏在 e 函数的定义中:椭圆曲线配对是密码学中最难理解的数学之一。
现在,考虑 Schnorr 签名。Schnorr 签名仅依赖于基本的 椭圆曲线。但签名与验证逻辑则稍显复杂:
那么...哪种类型的签名是“更简单”的呢?这取决于你关心什么!BLS 签名具有大量的技术复杂性,但这种复杂性全部埋藏在 e 函数的定义中。如果你把 e 函数当作一个黑盒,那么 BLS 签名其实是相当简单的。另一方面,Schnorr 签名的总复杂性较低,但它有更多可能与外部世界以复杂方式交互的部分。
例如:
总的来说,椭圆曲线配对是一个强大的“复杂性海绵”,因为它们包含大量的封装复杂性,但能启用系统性复杂性更小的解决方案。在多项式承诺领域也是如此:比较 KZG 证明的简单性(需要配对)与 内积 аргументов 的更复杂的内部逻辑(不需要配对)。
许多区块链设计中的一个重要设计选择是密码学与密码经济学之间的选择。通常(例如,在 Rollups 中),这表现为在 欺诈证明 和 有效性证明(即 ZK-SNARKs)之间的选择。
ZK-SNARKs 是复杂的技术。尽管 基本原理 可以在一篇文章中解释,但实际实现 ZK-SNARK 来验证某些计算涉及比计算本身多得多的复杂性(这也是为什么 ZK-SNARKs 用于 EVM 仍在 开发中,而 EVM 的欺诈证明 已经在测试阶段)。有效地实现 ZK-SNARK涉及电路设计与特定目的优化,使用不熟悉的编程语言以及其他许多挑战。另一方面,欺诈证明本质上是简单的:如果有人提出挑战,你只需直接在链上运行计算。为了提高效率,有时会增加一个二分搜索方案,但即使这样也不会增加太多复杂性。
但是,尽管 ZK-SNARKs 复杂,它们是 封装复杂性。而欺诈证明相对较轻的复杂性则是 系统复杂性。下面是欺诈证明引入的一些系统复杂性的例子:
由于这些原因,即使从复杂性的角度来看,基于 ZK-SNARKs 的纯密码解决方案在长期内可能更安全:ZK-SNARKs 有更多复杂的部分, 某些 人需要考虑,但它们有更少的悬而未决的问题需要 每个人 考虑。
通常,封装复杂性较少的选择也是系统复杂性较低的选择,因此有一个显然简单的选择。但有时,你必须在一种复杂性和另一种复杂性之间做出艰难选择。此时应该明确的是,封装的复杂性较少则风险较小。系统复杂性的风险并不是规范文档长度的简单函数;一个小的10行规范在与每一个其他部分交互时所增加的复杂性,可能会比一个100行的函数(如果按黑盒处理的话)所带来的复杂性要大得多。
但是,这种偏向封装复杂性的方法也有其限制。软件漏洞可以发生在任何代码片段中,随着代码的增大,出现漏洞的概率会接近1。有时,当你需要以一种意外且新的方式与子系统进行交互时,最初封装的复杂性可能会变得系统性化。
一个后者的例子是以太坊目前的双层状态树,其中包含一个账户对象树,每个账户对象又拥有自己的存储树。
这个树结构是复杂的,但在开始时,复杂性似乎封装得很好:协议的其他部分以键/值存储方式与树进行交互,你可以读取和写入,因此我们不必担心树的结构。
然而,后来复杂性被发现对系统产生了影响:账户的任意大小存储树的能力意味着无法可靠预测状态的特定切片(例如,"所有以0x1234开头的账户")的大小。这使得将状态拆分成多个部分变得更困难,复杂化了同步协议的设计以及尝试 分布存储过程 的尝试。为什么封装的复杂性变得系统性?因为接口改变了。 修复办法?当前 迁移到 Verkle 树的提案 也包括向树的良好平衡的单层设计迁移。
归根结底,在任何给定情况中要偏好哪种复杂性是一个没有简单答案的问题。我们最好的做法是采取适度偏向封装复杂性的态度,但不要过度,同时在每个特定情况下行使我们的判断。有时,为了实现巨大的封装复杂性减少,牺牲一点系统复杂性确实是最好的做法。而其他时候,你甚至可能误判什么是封装的,什么不是。每种情况都是不同的。
- 原文链接: vitalik.eth.limo/general...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!