框架交易与隐私的三重门
本文以EIP-8141为前提,而非对其进行论证。其目的是展示帧事务如何改善隐私协议,以及为了真正实现这一目标,公共记忆体池规则和包含清单的执行需要做出哪些变更。
非常感谢Carlos 、 Ben 、 Milos和Matt的回馈!
帧事务( EIP-8141 )旨在消除隐私协定中的中继。然而,公共记忆体池准入规则、 FOCIL执行规则以及无状态性路线图各自对所支援事务的边界设定了不同的界限。本文将分析这些边界的交集和冲突之处,特别关注隐私保护事务。
帧交易如何消除中继器
Tornado Cash和Railgun等隐私协议利用 zk-SNARKs 打破了存款人和提款人之间的链上连结。提款过程证明了取款人知晓 Merkle 树中的有效承诺,但并不透露具体是哪个承诺。问题在于:提款人的地址是新地址,并且没有 ETH 用于支付 gas 费用。目前,中继器(中心化的、可审查的第三方,通常在需要时离线)会透过赞助提款交易来弥补这一缺陷。
EIP-8141改变了经济机制。帧交易的VERIFY帧以STATICCALL方式运作:唯读,不进行状态修改。如果在付款获得批准之前VERIFY发生回滚,则该交易在协议层面无效,永远不会被打包进区块,也不会向任何人收取 gas 费用。隐私提现变为:
-
VERIFY帧(tx.sender = pool,因此该帧指向池自身的存储):从SENDER帧的调用资料中读取 publicInputs,根据池存储的历史记录验证 Merkle 根(SLOAD),确认空值未花费(SLOAD),并针对这些 publicInputs 执行 Groth16 配对检查。如果一切通过,则呼叫APPROVE。 -
SENDER帧:将无效化项标记为已花费,将净额转移给接收方,并将费用记入资金池内的赞助商。
至关重要的是,手续费不再需要来自外部赞助商。提现本身即可支付执行费用: SENDER帧可以将一部分提现资金用于支付 gas 费用,从而无需预先注资的发送方或第三方中继。赞助商的份额将作为内部信用留在资金池中,稍后可领取,因此提现只需进行一次出站转账,而不是两次。
| # | 模式 | 子类别 | 目标 | 呼叫者 | 旗帜 | 数据 | 角色 |
|---|---|---|---|---|---|---|---|
| 0 | VERIFY | only_verify | pool ( null → tx.sender ) | ENTRY_POINT | APPROVE_EXECUTION | SNARK 证明 | 从帧 2 呼叫资料读取publicInputs (root, nullifier, recipient, amount, sponsor, fee) , SLOAD acceptedRoots[root]和!nullifierHashes[nullifier] ,验证证明, APPROVE(APPROVE_EXECUTION) |
| 1 | VERIFY | pay | 赞助 | ENTRY_POINT | APPROVE_PAYMENT | (空白/赞助商政策数据) | 自省帧 2: target = 池,选择器 = withdraw ,编码sponsor = 自身,编码fee ≥ MIN_FEE APPROVE(APPROVE_PAYMENT) — 赞助商的 ETH 在此扣除 |
| 2 | SENDER | user_op | pool ( null → tx.sender ) | pool ( tx.sender ) | APPROVE_SCOPE_NONE | withdraw(publicInputs) | Mark nullifierHashes[nullifier] = true , ERC20.transfer(recipient, amount − fee) , sponsorCredits[sponsor][token] += fee |
对于无效证明( VERIFY回滚,交易丢弃)和重播证明(无效标记已标记, VERIFY回滚),赞助商的风险为零。无需信任,无需中继基础设施,也无需额外的审查。
包容的三道门
以隐私为中心的框架交易要实现抗审查性,需要经过三个独立的关卡,每个关卡都有其自身的限制:
1号门:公共会员池入口
EIP-8141 的记忆体池规则借鉴了ERC-7562 ,但完全剥离了质押和声誉机制,定义了哪些内容可以透过公共 P2P 网路传播:
- 验证前缀必须与已识别的模式相符(
self_verify或only_verify+pay,可选地以deploy开头)。 -
SLOAD仅限于tx.sender存储 VERIFY气体总量上限为MAX_VERIFY_GAS(100,000)- 停用的操作码:
TIMESTAMP、NUMBER、BLOCKHASH、BALANCE、SELFBALANCE、SSTORE、TLOAD、TSTORE等。 - 透过精确的运行时代码杂凑值识别规范的支付主程序,并进行时间锁定提款和节点端待处理余额追踪。
- 非规范付款人限制为
MAX_PENDING_TXS_USING_NON_CANONICAL_PAYMASTER(1) 个待处理交易
任何违反这些规则的内容都会被公共记忆体池拒绝,但仍然可以透过备用记忆体池或私人频道到达开发者。
闸门 2:焦点执行
FOCIL保证交易被纳入:每个区块16名IL委员会成员根据他们对待处理交易的理解构建包含清单。验证者只对包含IL交易的区块进行投票(或在区块后状态中证明其无效)。
对于 EOA,FOCIL 的遗漏检查是透过post-state进行低成本的nonce / balance查找来实现的。对于FrameTxs ,遗漏检查需要重播VERIFY前缀。由于交易有效性取决于执行情况,而不仅仅是发送方状态,因此不存在低成本的代理。 FOCIL -frame-txs 提案透过五个约束定义了资格:
- 验证前缀顺序:
VERIFY帧必须位于DEFAULT/SENDER帧之前 - 每笔交易
VERIFYgas :verify_gas(tx) <= MAX_VERIFY_GAS_PER_FRAMETX(100,000) - 每个 IL 的
VERIFY预算:整个 IL 的累积VERIFYgas 上限为MAX_VERIFY_GAS_PER_INCLUSION_LIST(250,000) - 基本交易完整性:
chain_id、手续费、无资料区块 - 受限状态存取:
VERIFY仅可读取tx.sender和 payer 帐户状态(balance、nonce、代码)及其前N储存槽位(N= 2-4),与AA-VOPS快取机制一致。任何其他合约的储存读取操作都会导致该交易无效。
违反任何约束的FrameTxs可免于 FOCIL 强制执行:不能因此怪罪建构者。
门3:节点验证能力
术语:
PS = 部分无状态:持有部分状态,而非全部状态
VOPS = 仅验证 PS:持有足够的状态以验证来自 EOA 的交易
AA-VOPS = VOPS + 每个帐户若干储存空间
ZKEVM 之后,节点无需储存完整状态。例如, VOPS节点仅储存约8.4 GB (每个帐户的nonce 、 balance和codeHash )。 AA -VOPS在此基础上扩展,在 trie 树中为每个帐户维护前N储存槽位(因此部分tx.sender和 payer 插槽可用于本地VERIFY重播)。部分状态 (PS) 节点也会追踪特定合约的储存。节点类型决定了它可以本地重播哪些VERIFY帧,进而决定了它可以将哪些交易类别新增到其记忆体池中,并将其来源新增至类似 FOCIL 的包含清单中:
| 能力 | 完整节点 | PS节点 | AA-VOPS | 语音操作员 |
|---|---|---|---|---|
EOA nonce / balance检查 | 是的 | 是的 | 是的 | 是的 |
AA钱包VERIFY ( tx.sender储存) | 是的 | 如果被追踪 | N槽子集 | 不 |
| 规范支付主准入(代码哈希匹配 + 余额预留) | 是的 | 是的 | 是的 | 是的 |
| Canonical 付款人验证追踪重播 | 是的 | 如果被追踪 | 不 | 不 |
| 隐私池储存(根、无效化器) | 是的 | 如果被追踪 | 不 | 不 |
无法验证某种交易类型的节点不能将其保存在其记忆体池中,也不应将其包含在完整性日志(IL)中。该类交易的抗审查能力会随著具备验证能力的节点比例的降低而下降。
有关哪些节点类型可以支援哪些EIP-8141记忆体池策略的详细分析,请参阅「透过无状态视角进行帧事务处理」 。
为什么隐私权交易无法通过所有三个关卡的考验
在预设参数下,隐私权提现操作无法通过所有三个验证关卡:
门 1(公共记忆体池) :当tx.sender = pool时, VERIFY帧对池的 Merkle 根历史记录和nullifierHashes映射的SLOAD操作满足tx.sender仅限存储的限制,但 Groth16 配对检查超过了EIP-8141 验证跟踪规则中定义的100k MAX_VERIFY_GAS 。已拒绝。
门禁 2(FOCIL 资格) :Groth16 配对检查超过了每笔交易100k的VERIFY gas 预算。即使tx.sender = pool将池的储存与有界状态存取规则对齐,仅 gas 上限就足以使该交易不符合资格。因此,该交易不予强制执行。
闸控 3(节点能力) : VOPS和AA-VOPS节点不持有矿池合约存放。只有追踪矿池的 PS 节点或全节点才能进行验证。
| 交易类型 | 公共游泳池 | 符合FOCIL资格 | 语音操作员 | AA-VOPS | PS(追踪池) |
|---|---|---|---|---|---|
| EOA帧tx(ECDSA/P256) | 是的 | 是的 | 是的 | 是的 | 是的 |
智慧钱包( tx.sender存储,<= N插槽) | 是的 | 是的 | 不 | 是的 | 是的 |
| Canonical 付款人赞助 | 是的 | 是的 | 不 | 不 | 如果被追踪 |
| 隐私撤回 | 不 | 不 | 不 | 不 | 如果被追踪 |
因此,隐私权交易被排除在所有预设的抗审查路径之外。而最需要抗审查的交易,恰恰是目前设计无法保护的。
这并非不可避免。差距源自于 FOCIL 如何强制执行FrameTxs的纳入,而FOCIL-frame-txs 提案则提供了两种方法,对可强制执行的内容有著截然不同的影响。选择哪种方法决定了与隐私相关的FrameTxs是否能够实现抗审查。
焦点强化:两种方法
这两种方法都解决了同一个问题:建筑商如何证明他们没有审查符合条件的 IL FrameTx ,以及证明人如何验证这些声明。
追加循环方法
预设方法是让建构器迭代地将排除的符合条件的 IL FrameTxs加入区块中,直到最终post-state中所有被排除的FrameTx都有效为止。其成本为O(k²) ,即排除的FrameTxs的数量。这种二次方建构器成本迫使使用保守的参数: MAX_VERIFY_GAS_PER_FRAMETX为100_000 , MAX_VERIFY_GAS_PER_INCLUSION_LIST为250_000 。
假设恶意 IL 委员会占比 25%(16 名成员中的 4 名,每月大约进行一次,权益份额为 1%),且交易大小约为 100 字节:每个 IL 可以在MAX_BYTES_PER_INCLUSION_LIST ( 8 KiB )的范围内容纳约 81 笔交易,但 IL VERIFY预算仅允许 21 笔的 21 月 21 笔笔FrameTxs的MAX_VERIFY_GAS_PER_FRAMETX gas 验证( 2 * 100k = 200k <= 250k )。 4 个恶意 IL 会产生k=8无效FrameTxs 。追加循环运行k(k+1)/2 = 36 VERIFY重播: 3.6M gas(约占区块的 6%)。测试器工作量: 4 * 250k = 1M gas(约占 1.7%)。虽然开销不大,但这些参数使得 FOCIL 对FrameTxs几乎形同虚设: MAX_VERIFY_GAS_PER_FRAMETX设定为100k 250k ,会排除 Groth16 证明、PQ 签章以及任何非平凡的验证FrameTxs ;而MAX_VERIFY_GAS_PER_INCLUSION_LIST一个只能支援 2 个可执行FrameTxs IL 几乎无法起到任何帐户抽象的作用。
验证指数方法
后续操作消除了追加循环。建构者透过发布一个(tx_hash, claimed_index)对来声明每个被排除的交易,该对声明了交易失效的区块索引。验证者使用区块存取清单 ( EIP-7928 ) 重建claimed_index处的状态,并重播VERIFY前缀。如果VERIFY成功,则建构者撒谎,该区块不符合 IL 条件。建置成本从O(k²)降至O(k) 。但代价是增加了协议的复杂性:索引声明是额外的网路元件, engine_newPayload需要将其与 IL 交易一起接受,并且 EL 需要相应地验证它们。
由于建造成本不再是瓶颈, MAX_VERIFY_GAS_PER_INCLUSION_LIST可以提升至2^20 (约 100 万 gas),并且由于每个被排除的交易只需在其claimed_index处验证一次,而非迭代验证,因此每笔交易的 gas 上限不再那么关键。 2 2^20的每 IL 值足以使隐私协议中的交易受益于 FOCIL 的抗审查保证。
同样是 25% 的对抗场景:每个 IL 现在最多可以预算 10 个FrameTxs每个 FrameTx 消耗100k gas( 10 * 100k = 1M <= 2^20 )。四个恶意 IL * 10 = 40 个FrameTxs将被排除。建构器成本是线性的: 40 * 100k = 4M gas(约占区块成本的 6.7%)。测试器成本: 4 * 2^20 ≈ 4.2M gas(约占 7%)。所有 16 个 IL 的最坏情况: 16 * 2^20 = 2^24 ≈ 16.8M gas(约占 28%)。每个 IL 预算增加约 4 倍,彻底消除了建造器成本的二次方级成长。
节点能力是限制因素
索引方法将公共记忆体池规则与 FOCIL 执行机制分开。公共记忆体池规则必须严格,因为交易可能需要在每个区块之后重新验证,所以状态依赖性必须很小且具有确定性。在验证索引方法下,FOCIL 会在固定的claimed_index处重播一次VERIFY 。由于没有持续的维护成本,因此它可以支援更广泛的状态存取权限和更高的 gas 预算。另一方面:FOCIL 验证位于测试器的关键路径上(在t=4s验证截止时间内),而记忆体池验证是异步运行的。较高的MAX_VERIFY_GAS_PER_INCLUSION_LIST会直接占用测试器的时间预算。
这意味著 IL 委员会成员可以从替代记忆体池(包括注重隐私的记忆体池)获取交易,并将其纳入自己的 IL 中,即使预设的公共记忆体池不包含这些交易。但要让隐私交易也能正常运作,需要放宽有界状态存取规则(读取权限仅限于tx.sender和 payer 帐户)。此外,强制执行包含规则的节点需要相应的状态来验证这些交易。
AA-VOPS无法弥合这一差距。它为每个帐户快取N储存槽位,足以满足简单的钱包验证(所有者金钥、nonce),但不足以支援隐私协定。与隐私交易相关的VERIFY帧读取的是池合约储存(根环缓冲区、无效化映射),而不是发送方储存。无论N的值如何,都无法解决这个问题:读取操作的目标完全不同。即使 AA-VOPS 为每个合约快取N插槽,也无济于事:无效化映射使用杂凑值作为键,因此存取的插槽是不可预测的,并且分散在整个储存树中。
规范隐私池
最实用的解决方案是规范池模式,类似规范支付者。一个由代码杂凑识别的规范合约可以作为多个隐私协议的共享注册表:以统一且可审计的布局储存每个池的仅追加 Merkle 根和无效化器集。如果设计得当,这样的池对于公共记忆体池和 FOCIL 强制执行都是安全的:针对此合约的VERIFY帧恰好读取两个储存槽: acceptedRoots[R]和nullifierHashes[h] ,这两个储存槽都使用可从呼叫资料派生的键。存取模式是有界的、可预测的且单调的:VERIFY 读取的每个储存槽都始终从false → true 。规范注册表无需节点追踪 N 个独立的池合约,而是将问题简化为一个具有已知储存布局的已知位址,从而以最小的成本实现隐私的部分状态性。
单调状态是该设计能够抵御大规模失效攻击的关键。目前的隐私权协议使用滚动环形缓冲区来储存最新的默克尔根,每次存款都会移除最旧的条目。根据 EIP-8141 协议,这种移除操作构成了一种大规模失效攻击途径:一次存款(或攻击者的垃圾邮件)就可以轮换移除数十个待处理的赞助提现所依赖的根,从而同时将它们全部从内存池中移除。用仅追加的acceptedRoots映射取代环形缓冲区可以完全消除这种攻击途径。针对任何历史根生成的提现证明将永远有效;无效化器集已经可以防止双重支付,因此接受旧根在密码学上是安全的。唯一可以使待处理提现失效的状态变更是写入其特定的nullifierHashes[h]槽,这需要持有该票据的秘密。这种操作是针对每笔交易的,而且是单独进行的,绝对不会是大规模的。
一个实作细节:目前隐私协定中常用的默克尔根环形缓冲区必须替换为仅追加映射,以避免大规模失效。就大小而言,这不成问题:例如,最常用的 Tornado Cash 合约(1 ETH 池)有 81,881 笔存款。这大约相当于 10.5 MB 的原始键值资料(
acceptedRoots和nullifierHashes各 81,881 笔记录,每笔记录约 64 位元组),或储存在状态 trie 树中后约为 25-40 MB。追踪此池的 PS 节点提交的额外状态远低于 50 MB,与约 8.4 GB 的 VOPS 基准相比可以忽略不计。
每个槽位有 16 个 IL 委员会成员, FOCIL只需 1/16 的诚实成员即可保证包含。但这里的「诚实」也意味著「有能力」:如果一个成员想要包含一个隐私交易,但他运行的是VOPS或AA-VOPS节点,那么他就无法验证该交易。 1/16 的诚实假设就变成了 1/16 的能力假设。隐私池节点解决了这个问题。它们不需要完整的状态,只需要它们关心的合约。一个追踪一个规范隐私池的隐私池节点最多只会增加几 MB 的空间。基础设施成本可以忽略不计;问题在于是否有足够的验证者选择这样做。
另一种替代规范化隐私池的方法是允许交易发送者附加所需的储存状态以及针对最新状态根的梅克尔证明。这种方法有许多不足,其中最重要的是,这些证明在每个区块产生后都会失效,必须不断重新计算。
拟议变更
- 将规范合约例外扩展到隐私池。 EIP -8141 已经允许透过执行时间程式码杂凑匹配来指定规范支付者,并使其免受严格的公共记忆体池规则的约束,因为它们的状态依赖性是已知的安全机制。
- 提高规范合约帧的单笔交易
VERIFYgas 上限。 MAX_VERIFY_GASMAX_VERIFY_GAS = 100_000对于 Groth16(约 250k)来说不够。规范合约具有固定的验证路径,因此其最坏情况下的VERIFY成本是程式码杂凑的静态属性。无效证明消耗的 gas 与有效证明相同,并会被丢弃,不会进行任何放大。通用帧保持 100k 的上限;规范合约帧的上限允许为例如约 400k,留出足够的空间,并限制其在记忆体池中的数量。 - 采用 验证索引 FOCIL 强制执行,接受增加的协定复杂性(
(tx_hash, claimed_index)映射),以消除二次建构器开销,从而避免FrameTx的 gas 预算过于保守。 - 将
MAX_VERIFY_GAS_PER_INCLUSION_LIST提高到2^20(=1M),使具有更高VERIFYgas 的单一交易(Groth16、PQ 签章)能够适应每个 IL 的预算。 - 放宽规范隐私池合约的有界状态存取规则,允许 IL 委员会成员从替代记忆体池获取这些交易,并使运行追踪这些合约的 PS 节点的证明者能够强制执行这些合约的纳入。
这些措施共同为隐私FrameTxs提供了一条可行的抗审查途径:在规范合约例外下进行公共记忆体池传播,由具备 PS 能力的 IL 成员纳入,并透过验证索引方法强制执行 FOCIL,而不会削弱非规范FrameTxs的 DoS 抵抗能力。
类似的论点也适用于后量子交易。后量子签名规模庞大,验证成本高昂,10万
VERIFYgas 不足以支付。与隐私协议类似,后量子交易也需要豁免于 gas 上限,可以透过规范化验证合约或添加规范化的预编译程序来实现。
目前针对FrameTxs 、VOPS 和 AA-VOPS 的公共记忆体池规则对于隐私协定而言过于僵化。规范化隐私池可以改变这一现状。透过程式码杂凑识别隐私池,公共记忆体池可以安全地验证其VERIFY帧,而追踪少量高价值合约(隐私池、规范支付者)的 PS 节点可以为它们建立包含列表。这样做可以为最需要抗审查性的交易提供保障。
附录
为什么备用记忆体池会失败
- 碎片化并非复合的根源:每个需要更严格验证(隐私、PQ 等)的交易都可能需要创建自己的备用记忆体池。节点业者会挑选出优胜者,支援这些优胜者的节点数量会逐渐减少,而最弱的备用记忆体池则成为所有依赖它的服务的审查载体。由于缺乏内建激励机制,备用记忆体池只能透过请求志工无私地运作来扩展,这甚至可能要求志工购买更强大、更昂贵的硬体。
- 匿名集崩溃:匿名集从“所有以太坊节点”降级为“支援隐私记忆体池的节点”,并且对等连接在网路层泄漏。
- 在网路层很容易被审查:用於单一用途备用记忆体池的引导节点、DNS 记录和对等清单都是关键节点,ISP 或国家可以透过一条规则将其移除。公共记忆体池难以审查,因为它承载著整个网路的负载;而备用记忆体池则不然。隐私流量最终会流经比其所取代的中继器更容易被离线的基础设施。
- focil 的 1/16 假设不成立:只有当至少有一位 IL 委员会成员与备用记忆体池建立对等连接并能验证交易时,才能保证及时包含。 “1/16 诚实”的假设变成了“1/16 诚实、已订阅且有能力”,这远不如EIP-7805所承诺的保证。如果 IL 建构者被宣传为低硬体节点,这将尤其成问题。
- 中继节点不会消失,它只是移动了位置:Alt-mempool 交易仍然需要到达建置节点。跨越边界转送的桥接节点,正是框架交易原本旨在消除的信任和审查机制,只不过这次是在堆叠的下一跳。
帧 tx <> 公共记忆体池决策树:
规范隐私池范例
有关规范隐私协议实现的范例(使用伪代码),请查看此处。






