EIP-8141 Frame 交易消除隐私之路的三道门

文章探讨了如何利用 EIP-8141(Frame 交易)消除隐私协议中的中继器,并分析了隐私交易在公共内存池准入、FOCIL 包含列表强制执行以及无状态节点验证这“三重门”中面临的挑战。作者提出了建立规范隐私池模式及优化 FOCIL 执行机制等建议,以增强隐私交易的抗审查性。

fallout_comic_strong_compress

这篇文章将 EIP-8141 视为既定前提,而不是争论其优劣。目标是展示 Frame 交易如何改进隐私协议,以及公共内存池(mempool)规则和包含列表(inclusion list)强制执行需要做哪些改变才能使其真正发挥作用。

非常感谢 Carlos, Ben, Milos, 和 Matt 的反馈!

Frame 交易(EIP-8141)有望从隐私协议中消除中继者(relayers)。但是,管理公共内存池准入、FOCIL 强制执行以及无状态路线图的规则,各自对其支持的交易划定了不同的界限。本文分析了这些界限在哪里交汇,以及在哪里发生冲突,特别是对于保护隐私的交易而言。

Frame 交易如何消除中继者

Tornado CashRailgun 这样的隐私协议使用 zk-SNARKs 打破了存款人和提款人之间的链上联系。提款过程证明了对 Merkle 树中有效承诺的了解,而无需透露是哪一个。问题在于:提款者的地址是全新的,没有用于 Gas 的 ETH。如今,中继者(中心化的、可审查的第三方,通常在需要时离线)通过赞助提款交易来弥补这一差距。

EIP-8141 改变了经济模式。一个 Frame 交易的 VERIFY frame 以 STATICCALL 形式运行:只读,没有状态变更。如果 VERIFY 在付款被批准之前回滚(revert),则该交易在协议级别是无效的,永远不会进入区块,并且不会向任何人收取 Gas。一个隐私提款变为:

  • VERIFY frame(带有 tx.sender = pool,因此该 frame 针对矿池自身存储):从 SENDER frame 的 calldata 中读取 publicInputs,根据矿池存储的历史记录(SLOAD)验证 Merkle 根,确认 nullifier 尚未被使用(SLOAD),并针对这些 publicInputs 执行 Groth16 配对检查。如果一切通过,调用 APPROVE
  • SENDER frame:将 nullifier 标记为已使用,将净金额转移给接收者,并在矿池内部将费用记入赞助商的贷方。

至关重要的是,费用不再需要来自外部赞助商。提款本身可以支付执行费用:SENDER frame 可以划拨一部分提款资金来覆盖 Gas,从而消除了对预注资发送者或第三方中继者的需求。赞助商的分成留在矿池中作为内部信用,稍后可以领取,因此提款只会发出一次出站转账而不是两次。

[0] 流程阶段

  • 模式: VERIFY
  • 子类: only_verify
  • 目标: pool (nulltx.sender)
  • 调用者: ENTRY_POINT
  • 标志: APPROVE_EXECUTION
  • 数据: SNARK 证明
  • 角色/逻辑: 从 Frame 2 calldata 读取 publicInputs(root, nullifier, recipient, amount, sponsor, fee),执行 SLOAD acceptedRoots[root]!nullifierHashes[nullifier],验证证明,APPROVE(APPROVE_EXECUTION)

[1] 流程阶段

  • 模式: VERIFY
  • 子类: pay
  • 目标: 赞助商
  • 调用者: ENTRY_POINT
  • 标志: APPROVE_PAYMENT
  • 数据: (空 / 赞助商策略数据)
  • 角色/逻辑: 内省 Frame 2:target = pool,selector = withdraw,编码的 sponsor = self,编码的 feeMIN_FEEAPPROVE(APPROVE_PAYMENT) — 在此处扣除赞助商的 ETH

[2] 流程阶段

  • 模式: SENDER
  • 子类: user_op
  • 目标: pool (nulltx.sender)
  • 调用者: pool (tx.sender)
  • 标志: APPROVE_SCOPE_NONE
  • 数据: withdraw(publicInputs)
  • 角色/逻辑: 标记 nullifierHashes[nullifier] = trueERC20.transfer(recipient, amount − fee)sponsorCredits[sponsor][token] += fee

对于无效证明,赞助商的风险为零(VERIFY 回滚,交易丢弃);对于重放证明,赞助商的风险也为零(nullifier 已被标记,VERIFY 回滚)。不需要信任,不需要中继者基础设施,也没有额外的审查层面。

准入的三道关卡

一个以隐私为重点的 Frame 交易的抗审查之路要经过三个独立的关卡,每个关卡都有其自身的约束条件:

关卡 1:公共内存池准入

EIP-8141 的内存池规则借鉴了 ERC-7562 但完全剥离了质押和声誉,定义了什么可以通多公共 P2P 网络传播:

  • 验证前缀必须匹配一个公认的模式self_verifyonly_verify + pay,可选地在前面加上 deploy
  • SLOAD 仅限于 tx.sender 存储
  • VERIFY Gas 上限为 MAX_VERIFY_GAS (100,000)
  • 禁用操作码:TIMESTAMP, NUMBER, BLOCKHASH, BALANCE, SELFBALANCE, SSTORE, TLOAD, TSTORE
  • 通过确切的运行时代码哈希识别规范支付者(Canonical paymaster),具有时间锁定的提现和节点端待处理余额跟踪
  • 非规范支付者被限制为每个只有 MAX_PENDING_TXS_USING_NON_CANONICAL_PAYMASTER (1) 个待处理交易

任何违反这些规则的内容都会被公共内存池拒绝,但仍可以通过替代内存池(alt-mempools)或私有通道到达构建者(builders)。

关卡 2:FOCIL 强制执行

FOCIL 保证交易包含:每个插槽(slot)有 16 个包含列表(IL)委员会成员根据他们看到的待处理交易构建包含列表。证明者(attesters)仅投票给包含 IL 交易的区块(或证明其在后置状态下无效)。

对于 EOA,FOCIL 的遗漏检查是对后置状态进行廉价的 nonce/balance 查询。对于 FrameTxs,遗漏检查需要重新运行 VERIFY 前缀。没有廉价的代理指标,因为交易有效性取决于执行,而不仅仅是发送者状态。FOCIL-frame-txs 提案通过五个约束条件定义了资格:

  1. 验证前缀排序VERIFY frames 必须先于 DEFAULT/SENDER frames
  2. 每笔交易 VERIFY Gas 有界verify_gas(tx) <= MAX_VERIFY_GAS_PER_FRAMETX (100,000)
  3. 每个 IL 的 VERIFY 预算:跨 IL 的累积 VERIFY Gas 上限为 MAX_VERIFY_GAS_PER_INCLUSION_LIST (250,000)
  4. 基础交易健全性chain_id,费用,无 blobs
  5. 有界状态访问VERIFY 只能读取 tx.sender 和付款人账户状态(balance, nonce, code)以及它们的前 N 个存储槽(N = 2-4),与 AA-VOPS 缓存保持一致。从任何其他合约进行的存储读取都将使交易失去资格。

违反任何约束条件的 FrameTxs 都会从 FOCIL 强制执行中豁免:它们的遗漏不能作为针对构建者的理由。

关卡 3:节点验证能力

术语:

PS = 部分无状态(Partial Statelessness):持有部分状态,而非全部

VOPS = 仅有效性无状态(Validity-Only PS):持有足够的状态来验证来自 EOA 的交易

AA-VOPS = VOPS + 每个账户几个存储槽

在 ZKEVM 之后,节点不需要持有完整状态。例如,VOPS 节点仅存储约 8.4 GB(每个账户的 nonce, balance, codeHash)。AA-VOPS 通过在 trie 中维护每个账户的前 N 个存储槽来扩展这一点(因此某些 tx.sender 和付款人槽可用于本地 VERIFY 重播)。部分无状态(PS)节点还会额外跟踪选定合约的存储。节点类型决定了它可以本地重播哪些 VERIFY frames,从而决定了它可以准入哪些交易类别到其内存池并将其源引入类似 FOCIL 的包含列表中:

能力 全节点 PS 节点 AA-VOPS VOPS
EOA nonce/balance 检查
AA 钱包 VERIFY (tx.sender 存储) 如果被跟踪 N 个槽子集
规范支付者准入
规范支付者 VERIFY 追踪重放 如果被跟踪
隐私池存储 (roots, nullifiers) 如果被跟踪

无法验证某种交易类型的节点无法在其内存池中维护该交易,也不应将其包含在 IL 中。该类别的抗审查性随着有能力的验证者比例的减少而降低。

为什么隐私交易无法通过所有三道关卡

在默认参数下,隐私提款无法通过所有三道关卡:

关卡 1(公共内存池):当 tx.sender = pool 时,VERIFY frame 对矿池 Merkle 根历史和 nullifierHashes 映射的 SLOAD 满足了仅限 tx.sender 存储的限制,但 Groth16 配对检查超过了 100k MAX_VERIFY_GAS 的上限。被拒绝。

关卡 2(FOCIL 资格):Groth16 配对检查超过了每笔交易 100kVERIFY Gas 预算。即使 tx.sender = pool 使矿池存储符合有界状态访问规则,单凭 Gas 上限就使交易失去资格。从强制执行中豁免。

关卡 3(节点能力)VOPSAA-VOPS 节点不持有矿池合约存储。只有跟踪矿池的 PS 节点或全节点才能验证。

交易类型 公共内存池 符合 FOCIL 资格 VOPS AA-VOPS PS (跟踪矿池)
EOA frame tx (ECDSA/P256)
智能钱包 (tx.sender 存储)
规范支付者赞助 如果被跟踪
隐私提款 如果被跟踪

因此,隐私交易被排除在抗审查的每一条默认路径之外。最需要抗审查的交易正是当前设计无法保护的交易。

FOCIL 强制执行:两种方法

追加循环法(Append-Loop Approach)

默认方法是让构建者反复将排除的符合条件的 IL FrameTxs 追加到区块中,直到没有被遗漏的 FrameTx 在最终的后置状态下有效。成本在被排除的 FrameTxs 数量上是 O(k²)。这种二次方构建者成本迫使参数趋于保守:MAX_VERIFY_GAS_PER_FRAMETX100,000MAX_VERIFY_GAS_PER_INCLUSION_LIST250,000

假设有 25% 的对抗性 IL 委员会,每个 IL 可以在 MAX_BYTES_PER_INCLUSION_LIST 内容纳约 81 笔交易,但每个 IL 的 VERIFY Gas 预算仅允许 2 笔具有 MAX_VERIFY_GAS_PER_FRAMETX Gas 的 FrameTxs。开销虽适中,但这些参数使 FOCIL 对 FrameTxs 几乎毫无用处:100kMAX_VERIFY_GAS_PER_FRAMETX 排除了 Groth16 证明和 PQ 签名。

验证索引法(Validation-Index Approach)

后续方案消除了追加循环。取而代之的是,构建者通过发布一个 (tx_hash, claimed_index) 对来豁免每个被排除的交易,声明该交易在哪个区块索引处变得无效。证明者使用区块访问列表(EIP-7928)在 claimed_index 处重建状态并在此处重播 VERIFY 前缀。构建者成本从 O(k²) 降至 O(k)

由于构建者成本不再是瓶颈,MAX_VERIFY_GAS_PER_INCLUSION_LIST 可以提高到 2^20(约 100 万 Gas)。这将高到足以让隐私协议的交易从 FOCIL 的抗审查保证中受益。

节点能力是限制因素

索引法开启了公共内存池规则与 FOCIL 强制执行之间的分离。公共内存池规则必须严格,因为交易可能在每个区块之后都需要重新验证。而在验证索引法下的 FOCIL 仅在一个固定的 claimed_index 处重播一次 VERIFY

这意味着 IL 委员会成员可以从替代内存池中获取交易并将其包含在他们的 IL 中,即使默认的公共内存池不携带这些交易。但对于隐私交易来说,这需要放宽有界状态访问规则。AA-VOPS 无法弥补这一差距,因为隐私交易相关的 VERIFY frames 读取的是矿池合约存储,而不是发送者存储。

一个规范隐私池

最实际的解决方案是规范池(canonical pool)模式,类似于规范支付者。一个通过代码哈希识别的规范合约可以作为多个隐私协议的共享注册表。如果设计得当,这样的池对于公共内存池和 FOCIL 强制执行都是安全的:针对该合约的 VERIFY frames 正好读取两个存储槽:acceptedRoots[R]nullifierHashes[h]

单调状态(Monotone state)是该设计能够抵抗大规模失效(mass-invalidation-resistant)的原因。用只增不减的 acceptedRoots 映射替换环形缓冲区(ring buffer),可以消除单次存款可能换出待处理提款所依赖的根的向量。

每个插槽有 16 个 IL 委员会成员,FOCIL 只需要 1/16 的诚实成员即可保证包含。但“诚实”也意味着“有能力”:一个想要包含隐私交易但运行 VOPS 节点的成员根本无法验证它。PS 节点解决了这个问题。一个跟踪一个规范隐私池的 PS 节点最多只增加几 MB。

建议的更改

  1. 将规范合约异常扩展到隐私池。 通过运行时代码哈希匹配准入规范隐私池,使其免受严格的公共内存池规则限制。
  2. 提高规范合约 frame 的单笔交易 VERIFY Gas 上限。 允许规范合约 frame 使用约 400k Gas 以适应 Groth16(约 250k)。
  3. 采用验证索引 FOCIL 强制执行,以消除二次方的构建者开销。
  4. MAX_VERIFY_GAS_PER_INCLUSION_LIST 提高到 2^20 (1M),让具有较高 VERIFY Gas 的单笔交易能够适应每个 IL 的预算。
  5. 放宽规范隐私池合约的有界状态访问规则,允许 IL 委员会成员从替代内存池中获取这些交易。

综合来看,这些措施为隐私 FrameTxs 提供了一条可行的抗审查路径:在规范合约异常下的公共内存池传播、由具备 PS 能力的 IL 成员包含,以及通过验证索引法进行的 FOCIL 强制执行。

附录

为什么替代内存池会失败

  • 碎片化无法组合:每个需要更重度验证的交易可能都必须生成自己的替代内存池,从而稀释节点计数。
  • 匿名集崩溃:匿名集退化为仅支持隐私内存池的节点。
  • 在网络层容易被审查:单用途的替代内存池是容易被 ISP 或国家掉线的单点。
  • FOCIL 的 1/16 假设破裂:只有在至少有一个 IL 委员会成员与替代内存池对等连接并能验证交易时,包含性才成立。
  • 中继者并未消失,只是转移了:替代内存池的交易仍需通过桥接节点到达构建者,从而产生新的审查层面。

Frame Tx <> 公共内存池决策树

frameflow (6)

规范隐私池示例

有关规范隐私协议实现的示例(使用伪代码),请查看这里

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

0 条评论

请先 登录 后评论
以太坊中文
以太坊中文
以太坊中文, 用中文传播以太坊的最新进展