本文探讨了在以太坊弱无状态性背景下,如何保持抗审查性。提出了Validity-Only Partial Statelessness (VOPS)方案,该方案通过节点存储验证待处理交易所需的最小账户数据(地址、nonce、余额和代码标志),在实现25倍存储缩减的同时,保持以太坊的抗审查性。
\
ChatGPT Image Apr 22, 2025, 11_26_48 AM1024×1536 148 KB
感谢 Julian Ma, Ignacio Hagopian, Carlos Perez, Guillaume Ballet, Justin Drake, Francesco D’Amato 和 Caspar Schwarz‑Schilling 的想法,讨论,反馈以及对提案的贡献。
以太坊长期以来一直追求 无状态验证 的目标:使参与者无需存储整个链的状态即可验证区块。无状态性的目标是降低硬件要求,促进验证者节点之间更大的去中心化,并通过允许构建和验证更大的区块来释放可扩展性,而无需所有节点复制完整状态。
实现这一愿景的一个主要提案是 弱无状态性,其中只有区块生产者保留完整状态,而其他节点使用小的状态证明来验证区块。虽然弱无状态性因其简单性和效率而具有吸引力,但它提出了一个关键挑战:在大多数节点无法独立验证交易的世界中,以太坊如何保持其抗审查性(CR)属性?
在这篇文章中,我们将探讨为什么仅靠弱无状态性会破坏以太坊的抗审查性保证,并提出了一个务实的解决方案:仅验证部分无状态性(VOPS)。通过要求节点存储足够的账户数据来验证待处理的交易,VOPS 提供了 25 倍的存储减少,同时保留了以太坊的抗审查性。
我们认为:
- 仅靠弱无状态性不能保证强大的抗审查性。
- 未来的设计必须重新审视 强无状态性,并解决实际问题,例如 谁生成这些证明,哪种类型的证明最有效,以及带宽和证明成本如何影响节点要求。
与此同时,仅验证部分无状态性(VOPS) 提供了一个简单有效的桥梁:在保留功能性的,抗审查的公共 mempool 的同时将本地存储减少 25 倍。
AA-VOPS 扩展了 VOPS 以支持完整的原生账户抽象,通过最小化本地缓存和增量更新带来的见证开销,提供了一条通往强无状态性的道路。
我们将首先解释一下,如果我们希望通过 FOCIL 等机制为所有交易提供强大的抗审查性保证,为什么弱无状态性(即仅依赖区块生产者来保持状态)不能很好地工作。在深入研究之前,这里快速回顾一下 FOCIL 在有状态世界中工作所需的主要要素。我们将展示 FOCIL 当前的设计如何依赖于 mempool 可以在删除无效交易的同时保留有效交易的假设:
用户 发送交易,如果有效(即通过 nonce 和余额检查),则会在 节点 间广播,并保留在公共 mempool 中。
注意:在本文中,我们使用术语“节点”通过其执行层客户端来指代 mempool 维护者。
包含者(Includers): 每个Slot,16 个包含者观察待处理的 mempool 交易,将它们添加到包含列表(ILs)中,并在 CL P2P 网络上广播这些 IL。
区块生产者 必须在其区块中包含来自所有 IL 的有效交易的并集。只有当 IL 交易无效或区块已满时,才能将其从区块中排除(即条件属性)。
证明者(Attesters) 验证所有 IL 交易是否包含在区块中。如果是,则证明者为区块投票。否则,他们会评估区块是否已满或缺失的交易是否有效,以确定排除是否合理或区块是否正在审查。
现在,假设协议只期望 区块生产者 持有状态。这将意味着维护 mempool 的 用户,节点,包含者 和 证明者 无法自行确定交易是否针对 preStateRoot
有效。实际上,他们将缺乏访问关键验证检查所需的完整,最新的状态信息,包括确认发件人具有足够的 balance
并且交易的 nonce
是正确的。请注意,任何智能合约条件或依赖于状态的逻辑(例如,Uniswap 池的当前状态)都由今天的 dApp 提供,以帮助 用户 避免回滚。
因此,在弱无状态性世界中:
preStateRoot
都是有效的。换句话说,他们证明了区块执行所提供的数据存在于先前的区块状态根中。重要的是,区块生产者 还必须为他们排除的任何 IL 交易附加见证(可以使用与 IL 一起提交的见证或重建它们),以便证明者可以重新执行该区块,并验证每次遗漏是否合理。preStateRoot
有效,因为他们无法执行通常的 nonce
和 balance
检查来确定他们是否应该重新广播交易或从其本地 mempool 中删除它们:
nonce
和 balance
检查会创建一个新的 DoS 向量:它允许 任何人 向 mempool 或 IL 提交无效交易,从而降低 mempool 的质量,并使有效交易更难以出现。这会产生与 包含者是对抗性的 并用垃圾填充 IL 时发生的相同类型的破坏。关键的区别在于,今天,只有验证者(拥有 32 ETH)才能成为包含者,而如果没有基本的过滤,任何参与者 都可以降低 mempool 质量并干扰包含列表的有效性 - 降低攻击的门槛并在实践中削弱抗审查性。在以下部分中,我们将探讨克服弱无状态性方法带来的挑战的潜在选择。
一种看似简单的方法是要求用户交易与针对 preStateRoot
的完整状态访问(例如,Merkle 或 Verkle 证明)捆绑在一起,通常称为强无状态性。我们甚至可以通过要求每个先前的 区块生产者 将状态见证附加到每个交易来放宽强无状态性的严格定义,以便 节点 可以按需获取它们。从理论上讲,这将使任何 节点,包括 包含者 和 证明者 能够独立验证交易的状态访问是否与 preStateRoot
一致,而无需访问完整的当前状态。这种机制对于有效地管理 mempool 很有用:节点 可以保留不受后续区块影响的交易,并删除 nonce 或余额已更改的交易,而不是在每个Slot排除和包含交易。
但在实践中,它需要节点和当前区块生产者之间的实时交付渠道,从而引入了强大的信任假设和一个新的审查向量:区块生产者可以延迟或有选择地扣留交易级别的证明,以使目标交易远离每个 mempool 和包含列表,从而在静默地排除它们的同时,仍然产生一个表面上有效的区块。此外,对于新交易,仅依赖上一个区块的状态是不够的。用户需要访问更新的,实时的状态见证,其中包括交易实际涉及的帐户和存储槽,以及重建到这些条目的路径所需的任何其他状态树的部分 - 这是由于树结构如何将状态互连。持续获取这些证明不仅会增加带宽使用和延迟 - 从而降低 UX,而且还会引发一个关键问题:谁应该充当 证明服务节点?钱包?dApp?Portal 网络?超级节点(即,验证器赌注 2048 ETH
)?以上全部?
虽然如果我们希望证明者和包含者节点完全无状态并在智能手表上运行,那么强无状态性可能是最终目标,但进一步的研究对于回答这个问题至关重要,并确定哪些参与者最适合履行此职责,方法是评估 (1) 存储完整状态和 (2) 生成和广播与用户交易相关的见证和证明所需的成本和硬件要求。请注意,这种方法只需要 N 分之一的诚实假设 - 从理论上讲,单个能够生成有效证明的诚实参与者就足够了。但是,在实践中,仅依赖一个或很少的参与者可能会导致诸如租金提取(例如,承诺攻击,审查和垄断定价)之类的问题。
一种务实的短期方法是依赖部分无状态性,并且仅存储验证交易有效性所需的最小数据。在 VOPS 下,每个节点仅维护每个 EOA 四个字段 - address
(20 字元组), nonce
(8 字元组), balance
(12 字元组)和一个一位 codeFlag
- 而不是完整状态。
当交易到达时,节点检查 codeFlag
:
codeFlag = 0
(纯 EOA,无委托指示符 - 表示该账户无法将执行委托给自定义代码):
codeFlag = 1
(任何能够使用 23 字节 EIP‑7702 委派指示符运行代码的地址):
在每个新区块上,节点使用所有修改后的四元组更新其表,删除任何因陈旧的 nonce 或不足的余额而无效的交易,每当帐户的 codeFlag
从 0 翻转到 1 时,删除除最高优先级待处理交易以外的所有其他交易,并在标志从 1 翻转回 0 时,提升现在与其 nonce 匹配的任何排队 EOA 交易。
因为每个帐户条目仅为 20 + 8 + 12 + 0.125 ≈ 40.125 bytes
,所以维护 ~2.41 亿个帐户 需要:
241\,\text{million} \times 40.125\,\text{bytes} \approx 8.4\,\text{GiB}
与今天的 ~233 GiB
完整状态大小相比,这减少了 25 倍以上(h/t Guillaume),但仍然让 VOPS 节点有效地维护 mempool。请注意,8.4 GiB
数字未压缩,因此这是 VOPS 可以提供的存储节省的悲观估计。
为了使 VOPS 的想法具体化,我们将首先将该提案锚定在 Verkle 设置中。
区块标头字段
字段 | 目的 |
---|---|
preStateRoot |
执行区块之前的状态根。 |
postStateRoot |
执行后的状态根。 |
区块级 witness (区块主体中的 IPA 多重证明) |
证明在执行期间读取的每个状态元素对于 preStateRoot 都是有效的。 |
transactions |
完整交易列表。 |
让我们回顾一下在这种情况下谁做什么:
address
,nonce
,balance
和 codeFlag
- 仅足以决定每个挂起的交易是应该保留还是删除。preStateRoot
的有效性检查(例如,nonce
,balance
和 codeFlag
)是作为 mempool 维护的一部分执行的。但是,如果将来包含者被分离为独立的角色(例如,轻型“智能手表”包含者),他们将需要在将交易包含在 IL 中之前独立执行交易有效性检查。在 VOPS for Verkle 世界中,区块生产者负责生成和提交以下内容:
postStateRoot
。这用作执行的输出,并且必须由证明者进行验证。这已经是区块生产者今天所做的事情,而不是 VOPS 世界中的新要求。
验证区块级别的见证:确认通过 IPA 多重证明证明的所有提供的状态对于
preStateRoot
都是有效的。
在本地重新执行该区块:使用提供的交易,证明者从预状态开始独立地重新执行该区块(从见证中重建)以重新计算
postStateRoot
。
检查
postStateRoot
:确保本地重新计算的 postStateRoot
与区块中提交的 postStateRoot
相匹配。
验证 IL 条件
如果所有检查都通过 - 状态访问的有效性,执行的正确性和 IL 条件的满足 - 证明者会为区块投票。通过在 第 2 步 中重新执行区块并在 第 3 步 中同时更新其本地四元组表,VOPS 节点可以使其部分状态完全同步,而无需存储整个 Verkle 树。
以 Verkle 风格的 VOPS 为基础,我们可以使用零知识虚拟机(zkVM) 替换区块级状态确认和本地重新执行。每个区块都会附带一个 SNARK,使每个验证者都可以通过运行一次毫秒级的验证来检查整个转换以及所有 IL 条件。
preStateRoot
。postStateRoot
。preStateRoot
会产生 postStateRoot
,并且该差异与嵌入在标头中的差异相匹配。区块标头字段
字段 | 目的 |
---|---|
preStateRoot |
执行之前的状态根。 |
postStateRoot |
执行后的状态根。 |
stateDiff |
每个修改后的帐户叶和存储槽的完整列表的 Merkle 根。 |
execProof |
将 transactions + stateDiff 绑定到转换 preStateRoot → postStateRoot 的 SNARK。 |
两点说明:
- 在 VOPS 和 AA‑VOPS 下,节点依赖于接收每个区块的完整
stateDiff
来修补其本地状态。EIP-7928 区块级别访问列表 (BALs) 将准确地提供此功能:可验证,强制发布所有修改后的帐户,存储密钥,余额和 nonce。- IL 合规性由 VOPS 节点使用其更新的四元组表和收到的
stateDiff
在本地进行检查 - 此处没有额外的证明义务(有关每个帐户证明与包含列表规则的联系,请参见 AA-VOPS)。
VOPS 节点的逐块例程
execProof
。 确认 stateDiff
中,提取每个修改后的帐户的 (address, nonce, balance, codeFlag)
并更新本地表。资源概况
方面 | 节点 | 区块生产者 |
---|---|---|
磁盘 | ≈ 8.4 GiB 用于四元组表(≈ 比完整 MPT 状态小 25 倍) | 未更改 |
带宽 | stateDiff 仅添加几十 KB - 相对于当前区块限制可以忽略不计 |
几乎不变,但用于见证的上载带宽略有增加 |
CPU | 每个区块一次快速 SNARK 验证(在笔记本电脑上为毫秒级) | 繁重 的证明工作 - 仍然比构建 Merkle/Verkle 见证要昂贵得多(多个 GPU),但正在 迅速改善。 |
证明大小 | 恒定大小,验证者始终下载相同的几百个字节 | 恒定大小,理想情况下每个证明的目标值为 128-256 KiB |
结论。
凭借 ≈ 8.4 GiB 的本地状态,不变的 mempool 规则和每个区块的单个简洁证明,zkVM VOPS 在将验证器硬件要求保持在消费级限制内的同时,保持了以太坊的抗审查性。
让我们从一个重要的说明开始。在今天的 Verkle 和 Binary tree 提案中,帐户和存储槽交错在一起,这与帐户形成不同的子树的 Merkle Patricia 树不同。VOPS 节点无法可靠地区分帐户和存储槽,并且无法简单地下载快照以重建其本地帐户表。可以过滤掉大多数孤立的槽(> 80%),但攻击者可以创建伪造的类似帐户的槽以减慢同步。从创世同步并不能解决此问题,因为当前的见证格式不指示值是帐户还是存储槽。解决此问题将需要更丰富的见证格式,例如为区块级别访问列表提出的那些格式,或对树结构进行小的更改。
如今,帐户树大约占总状态大小的 1/6
。假设采用 snap 同步方法,仅下载帐户树将使同步速度比下载完整状态快约五到六倍(考虑了修复阶段)。修复阶段仍将花费与今天相同的时间,并且下载的大部分数据最终将被丢弃,但是同步可以像今天一样从任何完整节点发生。
引导 VOPS 节点需要 仅帐户状态(没有存储尝试或代码 Blob)以及通常的区块头:
execProof
+ 紧凑型 stateDiff
侧车(address, nonce, balance, codeFlag)
。stateDiff
侧车列表。codeFlag
从 0→1 翻转(仅保留该地址的最高优先级挂起 tx)。原生帐户抽象(AA,请参阅 EIP-7701) 引入了主要的范例转变:帐户不再是具有固定验证规则的简单对象,而是可以运行任意代码的可编程实体。这种灵活性打破了仅检查 nonce,余额和 codeFlag
足以验证交易的假设。因此,VOPS 需要升级:AA-VOPS。
AA-VOPS 扩展了 VOPS 以支持原生 AA,同时通过避免完全全局状态复制来保持节点轻量级。每个节点仅跟踪它主动关心的帐户(对于它自己的 EOA 或与其交互的帐户),而不是要求每个节点跟踪每个帐户,从而维护一个小的本地缓存,该缓存会随着时间的推移而增量更新。
虽然原生 AA 释放了强大的新功能,但它也迫使生态系统更接近强无状态性设计,其中交易必须携带显式见证。当我们扩展 VOPS 以支持 AA-VOPS 时,我们应仔细权衡完全原生 AA 的好处是否证明了这种增加的复杂性,或者坚持更简单的 VOPS 模型是否更好地保留了去中心化和效率。
强无状态性 希望用户在每个交易中附加完整的状态见证,涵盖所有触及的帐户和存储槽。
相反,AA-VOPS 允许节点仅维护与自己的 EOA 相关的特定帐户的最新证明。除非帐户更改,否则这些证明在多个区块中保持有效,并使用每个区块包含的轻量级 stateDiffs
进行刷新。
这样可以避免在每个交易中出现笨重的见证,从而使带宽和存储需求保持在最低水平,同时保持抗审查性和交易有效性。
本地缓存和见证维护
节点(或代表其行事的钱包或 dApp 后端)保留:
nonce
,balance
,storageRoot
和 codeHash
。stateRoot
验证这些字段。在后续的区块修改任何覆盖的字段之前,见证保持有效。
引导见证:
eth_getProof
获取初始叶和路径(在 Reth 中,你现在可以 通过单个 RPC 调用获得所有见证)。增量更新:
每个区块提供
stateDiff
(如带有 zkVM 的 VOPS 中)。transactions
,stateDiff
和 IL 合规性规则绑定在一起。当区块到达时,节点:
stateDiff
正确编码了从 preStateRoot
到 postStateRoot
的转换。(可选优化:区块生产者可以为每个排除的 IL 交易附加一个“失败提示”,指示执行过程中失败的索引,从而减少证明者的工作量。)
VOPS 节点(或跟踪其自己 EOA 的钱包/dapp)检查其任何帐户是否出现在 stateDiff
中。
stateRoot
有效。如果节点保持在线并处理每个区块,则在引导后不再需要其他存档查询。如果它落后,它可以重播丢失的差异或通过 eth_getProof
重新种子其见证。
提交交易时:
VALIDATE
逻辑执行。如果未来的 AA 标准允许 VALIDATE
在帐户外部读取,则发送者可以扩大附加的见证以覆盖那些额外的存储槽。该模型自然地扩展。
Mempool 准入
接收节点使用其缓存的 stateDiffs
针对引用的 stateRoot
验证见证或证明,以检查更新:
实际上,节点还可以维护最近的 stateDiffs
的滑动窗口 - 例如,最后约 N 个区块 - 以允许在没有重新查询的情况下进行交易验证。
没有全局复制:
AA 兼容性:
今天可以使用 EIP-7701,如果验证逻辑稍后读取外部存储,则可以适应。
eth_getProof
并使用差异保持其新鲜。更高的 P2P 带宽:
用户端证明或获取:
节点必须通过跟踪差异或查询完整节点来保持证明的最新状态,这容易受到中心化向量的影响。
eth_getProof
重新种子,如果很少有提供程序占主导地位,则可能会带来中心化风险。仅验证部分无状态性(VOPS)将节点的本地存储需求减少到大约 8.4 GiB 未压缩 - 只有 (address, nonce, balance, codeFlag)
四个字段的四元组 - 同时保持了以太坊的抗审查属性。这种方法利用了帐户和存储槽增长之间的不对称性:只要存储继续主导新的状态条目,节省额仍然很大;如果帐户创建速度超过存储增长速度,则相对优势将会相应降低。
原始版本的 VOPS 有两个主要优点:第一个是没有见证 mempool:因为每个节点都存储每个 EOA 的 (address, nonce, balance, codeFlag)
四元组,所以 mempool 永远不需要每个交易的 Merkle 或 Verkle 证明。区块级别的证明(例如,zkEVM 中的 SNARK)仍然可以保证完全正确,同时仅向每个区块的传播添加几百个字节。第二个是真正的对等同步:由于每个节点都为所有 EOA 维护该最小状态,因此你可以从任何对等体引导或恢复,而无需额外的证明或单个帐户数据,从而消除了对完整节点或存档节点的依赖。
但是,支持原生 AA 会将我们推向一组不同的权衡。AA-VOPS 通过将小的,最新的证明附加到每个交易,将本地存储减少到仅几千字节,但缺点是 P2P 有效负载更高,并且每当你开始跟踪新帐户,引导节点或在离线下线后返回时偶尔会调用完整节点或专用服务。随着证明技术和 SNARK 的不断改进,AA-VOPS 可能会成为通往完全无状态性的长期,面向未来的途径。相比之下,原始的 VOPS 作为一个务实的短期解决方案脱颖而出,通过避免交易级别的证人来保留无缝的 UX。
- 原文链接: ethresear.ch/t/a-pragmat...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!