FOCIL 与 原生账户抽象 - Magicians

本文探讨了EIP-7805 (FOCIL) 如何处理以太坊 Inclusion List (IL) 中被省略的交易,特别是针对EIP-8141 (Frame Transactions) 引入的原生账户抽象(AA)。

Thomas Thiery

鸣谢 Jihoon SongJulian MaToni Wahrstätter Vitalik Buterin 提供的反馈和评论。

TL;DR: FOCIL 针对后状态(post-state)使用廉价的 nonce/余额代理检查被省略的包含列表(IL)交易。这适用于 EOA,但不适用于原生 AA,因为交易有效性需要执行 VERIFY 帧。本篇文档定义了一个公共内存池合格的 FrameTx 子集,具有更严格的省略检查:针对后状态的有限 VERIFY 执行,以及每个 IL 的 VERIFY-gas 预算,以限制 attester 的工作量。

背景

在执行区块后,FOCIL (EIP-7805) 检查每个未包含的 IL 交易:如果它符合剩余 Gas 限制并通过针对后状态的 nonce/余额检查,则该区块不符合 IL 条件,并且 attester 不会为其投票。

该代理成本低廉(每个被排除的交易只需一次账户查找,无需执行),并且适用于 EOA 和 EIP-7702,其中省略冲突通过发送者 nonce 和余额浮现。

本提案不改变 FOCIL 对 EOA 或 EIP-7702 交易的行为。以下所有内容仅适用于接下来定义的合格子集中的 Frame Transactions (EIP-8141)。

合格的 FrameTx

本说明明确区分了两类 FrameTx。

合格的 FrameTx 是同时被认为对公共内存池有效且可由 FOCIL 执行的子集。合格子集是根据设计从公共内存池合格集中抽取的:任何不适合广泛传播的 FrameTx 也不适合 FOCIL 执行,但反之则不成立。所有其他 FrameTx 仍可在可选的替代内存池或私人订单流通道中广播;其省略总是被允许的。

这反映了更广泛的 AA 生态系统。ERC-4337 使用替代内存池;ERC-7562 定义了更严格的共享内存池规则以及单独的替代内存池规则。同样的原则也适用于此:FOCIL 执行仅涵盖已获准广泛公共传播的 FrameTx。

为何原生 AA 会破坏代理

与 EOA 不同,Frame Transaction 的有效性不能仅凭发送方状态来确定。VERIFY 帧必须针对状态执行并调用 APPROVE:支付方只能在此执行期间被发现,并且可能与发送方不同。这意味着 nonce 和余额检查可以通过,而 VERIFY 失败。没有廉价的代理:attester 必须重放验证前缀以确定是否应包含被排除的合格 FrameTx。

通过共享基础设施产生的冲突

AA 还引入了仅从交易字节无法看到的冲突。考虑 TX_A 和 TX_B 都针对同一赞助商进行验证:

// 伪代码;APPROVE 是一个 EVM 操作码(根据 EIP-8141 为 0xaa)
function validateSponsorship() {
    require(address(this).balance >= MIN_RESERVE)
    APPROVE(payment)
}

如果 TX_A 被包含并收取费用,赞助商的余额可能会低于 MIN_RESERVE,从而使 TX_B 在后状态下无效。这在后状态规则下自然解决:任何在最终后状态下无效的内容都被允许。构建者还可以通过耗尽赞助商的资金来故意发动审查攻击,一次性允许许多省略;这会消耗区块资源,并且是任何后状态机制固有的。下面的有限状态访问规则(约束 5)限制了这种攻击面。

更广泛地说,共享合约存储可能允许单个包含的交易使许多被排除的 FrameTx 在后状态下无效,从而创建系统的批量省略理由;约束 5 排除了这种情况。

合格性约束

合格的 FrameTx 是满足以下所有约束 1-5 的 FrameTx。约束 1-4 是在第一层(Tier 1)评估的静态检查;约束 5 是在第三层(Tier 3)执行期间评估的运行时检查。一个 FrameTx 只有在通过所有五个约束时才合格。

约束分为两种:

静态检查可以从帧列表检查,无需任何状态访问或执行。它们在第一层强制执行。

运行时检查不能静态评估。它们在第三层 VERIFY 执行期间作为不合格条件强制执行。命中不合格条件的交易被允许。

VERIFY Gas 权重

verify_gas(tx) = Σ frame.gas_limit  for all frames with frame.mode == VERIFY

可从帧列表计算,无需任何执行。

参数(初始,可调)

  • MAX_VERIFY_GAS_PER_FRAMETX (例如,100,000)
  • MAX_VERIFY_GAS_PER_INCLUSION_LIST (例如,250,000)

注意:在这些参数下,PQ 签名验证 (Falcon, Dilithium) 将超过 MAX_VERIFY_GAS_PER_FRAMETX,并且不符合 FOCIL 的资格。PQ FrameTx 仍可首先通过替代通道传播,并且其省略总是被允许的。

静态检查(Tier 1)

1. 验证前缀排序。 VERIFY 帧必须在所有 DEFAULT 和 SENDER 帧之前。唯一的例外是合约创建帧(to == null),它们可以出现在 VERIFY 帧之前以部署正在验证的合约。这确保了 attester 只需执行创建和 VERIFY 帧来确定有效性,而无需执行常规执行帧。

2. 每个交易的有限 VERIFY Gas。verify_gas(tx) ≤ MAX_VERIFY_GAS_PER_FRAMETX。超出此上限的交易不受限制;省略它们是被允许的。

3. 每个包含列表的 VERIFY 预算。 对于每个 IL,按声明顺序扫描交易并构建一个预算子集:仅当添加候选 FrameTx 后,其运行的 verify_gas 总和保持在 MAX_VERIFY_GAS_PER_INCLUSION_LIST 或以下时,才包含它。不在任何 IL 的预算子集中的合格 FrameTx 被允许。

同一个 FrameTx 可能出现在多个 IL 中,位置不同,并且可能被某些 IL 预算,但不被其他 IL 预算。Attester 必须评估所有 IL,并且如果至少一个 IL 对其进行预算,则将该 FrameTx 视为已预算。这提供了一个抗审查特性:一个诚实的委员会成员将一个 FrameTx 列在预算内就足以使其可执行,无论其他成员如何排序。

4. 基本交易健全性。 链 ID 匹配,max_priority_fee_per_gas ≤ max_fee_per_gasmax_fee_per_gas ≥ block.base_fee,解析有效性,无 blobs(blob_versioned_hashes 为空,max_fee_per_blob_gas 为零)。

运行时检查(Tier 3 不合格条件)

5. 有限状态访问。 VERIFY 只能读取发送者和支付方的账户状态(余额、nonce、代码)及其前 N 个存储槽,其中 N 在 2-4 之间(具体值待基准测试)。超出此范围的读取将使交易不合格。

内存池节点在准入时重放 VERIFY,而不执行完整的区块。将读取限制为账户状态加上少量固定数量的存储槽意味着每个节点都确切知道要获取什么:一个小的、确定性的集合,而不会按需暴露于任意存储查找。该约束位于合格层,以便内存池准入和 FOCIL 执行在同一合格集上保持同步。

N 槽边界的选择是为了与 AA-VOPS 对齐,AA-VOPS 通过让节点缓存每个账户的少量存储槽来扩展 VOPS。大多数智能钱包将其 nonce、所有者和守护者存储在槽 0-3 中,因此 N 个槽足以覆盖常见的验证模式。N 的确切值将在 AA-VOPS 完全指定时确定。

推荐的内存池策略(非共识)

每个发送者的待处理上限: 每个发送者最多一个待处理的受限 FrameTx(或深度为 D 的有限 nonce 链),与 EIP-7702 类似。

每个支付方的风险跟踪: 跟踪每个支付方的累积最坏情况费用风险(tx_gas_limit × max_fee_per_gas),当其超过 state[payer].balance × α 时,驱逐最低优先级的交易,其中 α < 1 是某个安全系数。

确定性验证环境: 拒绝其 VERIFY 访问不稳定区块环境操作码(TIMESTAMPNUMBERCOINBASEBLOCKHASH 等)的 FrameTx。这是一个内存池准入规则,而不是 FOCIL 合格性约束:attester 针对这些操作码确定化的固定后状态重放 VERIFY。内存池节点通过在准入时跟踪 VERIFY 来强制执行此规则,遵循 EIP-7562 的方法。

构建

对于合格的 FrameTx,nonce/余额代理被替换为针对后状态 S 的 EIP-8141 验证前缀的有限重放。如果一个被排除的合格 FrameTx 在 S 处有效并符合 gas_left,则该区块不满足 IL 条件。

构建完 payload 后,构建者迭代地追加剩余的合格 IL FrameTx:

  1. 尝试追加每个剩余的合格 IL FrameTx。
  2. 如果有任何成功,则从步骤 1 开始重复,更新状态。
  3. 当一轮完整追加后没有任何内容时停止。

这对于被排除的合格 IL FrameTx 数量来说是 O(k²) 的。来自同一发送者的连续 nonce 会自然处理:TX_A (nonce N) 首先被追加,然后 TX_B (nonce N+1) 在后续的轮次中匹配。

该循环保证了一个定点:在最终后状态下没有被省略的合格 FrameTx 是有效的。这是正确的目标;不需要贪婪的最大化打包,并且定点属性足以实现完全的抗审查。

这不是强制性算法。构建者可以使用任何实现相同结果的策略。

Engine API。 EIP-7805 已经提出了 newPayloadforkchoiceUpdated 的 IL 相关更改。通过 FrameTx 感知检查,EL 还需要完整的有序非模棱两可的 IL 视图来运行第一层到第三层的逻辑。这是对现有依赖关系的改进,需要两个 EIP 之间明确的协调。

构建优化(非规范性)。 按 nonce 预排序来自同一发送者的 FrameTx。对共享支付方的 FrameTx 进行分组,并根据该支付方的预算进行选择,从而在典型条件下将循环减少到少量轮次。

验证

在整个验证过程中,两个属性独立跟踪。合格性由约束 1-5 确定:一个未能通过任何约束的 FrameTx 被允许,无论其是否能成功执行。有效性由 EIP-8141 执行语义确定:如果未触发任何无效条件,则 FrameTx 在给定状态下有效。FrameTx 必须在后状态 S 下既合格又有效,并满足第二层检查,才能构成 FOCIL 违规。

每一层都在前一层的输出上操作。第一层从所有 IL 中获取所有交易,并生成一个通过静态检查的被排除 FrameTx 的候选集 C。第二层接收 C 并过滤出满足 nonce 和 Gas 匹配的交易。第三层接收过滤后的集合,应用运行时检查,并识别其省略违反 IL 条件的交易。

在后状态下有效 在后状态下无效
合格 如果省略则为 FOCIL 违规(假设第二层通过)。示例: 格式良好的 VERIFY,调用了 APPROVE,支付方有偿付能力。 允许。示例: VERIFY 执行但未调用 APPROVE,或支付方余额不足。
不合格 允许。示例: VERIFY 读取了超出 N 槽限制(约束 5)的第三方存储槽,但否则会成功。 允许。示例: 违反约束 5 且未调用 APPROVE。

第二层(nonce 和 Gas 匹配)是合格+有效单元格上的进一步条件;在那里的失败也允许。

Attester 扫描所有 IL 交易(不仅是被排除的交易),以确定哪些 FrameTx 满足第一层的静态检查。只有 C 中被排除的 FrameTx 才能进入第二层和第三层。

Tier 1:静态检查(无状态访问,无执行)

对于所有 IL 中的每个交易 T:

  • 如果 T 被包含在区块中:跳过。
  • 如果 T 不是 FrameTx:应用标准 nonce/余额代理并跳过。
  • 如果 T 未通过约束 1-4:允许。
  • 否则:将 T 添加到候选集 C。

C 是第二层的输入。

约束 1-4 回顾:

  • VERIFY 优先排序成立。
  • verify_gas(tx) ≤ MAX_VERIFY_GAS_PER_FRAMETX
  • Tx 位于至少一个 IL 的预算子集中。
  • Tx 健全性通过(包括无 blobs)。

Tier 2:nonce + Gas 匹配(仅限后状态读取)

  • tx.nonce == S[tx.sender].nonce
  • tx_gas_limit ≤ gas_left

其中:

  • gas_left = block_gas_limit − total_gas_used(包括追加的 IL FrameTx)
  • tx_gas_limit = FRAME_TX_INTRINSIC_COST + calldata_cost + Σ frame.gas_limit

任一检查失败:允许。否则进入第三层。

Tier 3:有限验证前缀重放(后状态)

针对后状态 S 执行 EIP-8141 验证前缀(VERIFY 帧的有序序列),重放精确的帧语义,包括 ORIGIN 行为和批准状态。应用运行时检查(约束 5);不合格:允许。

验证前缀完成后,如果识别出支付方,则检查偿付能力:

required_balance = tx_gas_limit × min(tx.max_fee_per_gas, block.base_fee + tx.max_priority_fee_per_gas)

无偿付能力:允许。在后状态下有效并符合 gas_left:区块不满足 IL 条件。

FrameTx 在 Tier 3 的合格性和有效性条件

FrameTx 在给定状态下有效,如果它没有触发无效条件。非 VERIFY 帧回滚不会使交易无效;未调用 APPROVE 而终止的 VERIFY 帧会使交易无效。

  • tx.chain_id == block.chain_id
  • tx.max_priority_fee_per_gas ≤ tx.max_fee_per_gas
  • tx.max_fee_per_gas ≥ block.base_fee
  • tx.nonce == state[tx.sender].nonce
  • 所有 VERIFY 帧都以 APPROVE 终止;回滚、Gas 耗尽或未调用 APPROVE 而终止:无效。
  • 必须先建立 sender_approved 才能设置 payer_approved;所有 VERIFY 帧完成后 payer_approved 必须为真。

不合格条件(约束 5)允许该交易,无论其余有效性条件是否本应满足。

安全考虑

DoS 抵抗

  • 每个交易:verify_gas(tx) ≤ MAX_VERIFY_GAS_PER_FRAMETX 根据设计。
  • 每个 IL: 每个 IL 最多贡献 MAX_VERIFY_GAS_PER_INCLUSION_LIST 的第三层工作量。Attester 通过跨 IL 的交易哈希进行去重,每个唯一的交易执行一次验证前缀。
  • 每个槽: 16 个成员的 IL 委员会最坏情况是 16 × MAX_VERIFY_GAS_PER_INCLUSION_LIST。根据建议参数:每个槽 4,000,000 VERIFY-Gas。控制 m 个槽的攻击者最多强制 m × MAX_VERIFY_GAS_PER_INCLUSION_LIST

这些数字仅限制 attester 的工作量。构建者的重放成本更高;参见“开放问题”中的构建者复杂性。

批量失效

约束 5 排除了基于存储的共享验证依赖关系,这涵盖了主要的批量失效向量。共享的支付方余额仍然可以被耗尽以使多个 FrameTx 失效,但受限于区块资源成本。

抗审查

在后状态 S 下有效并符合 gas_left 的合格 FrameTx 必须被包含。构建者没有机制可以为此类交易声明理由。

替代方案:基于索引的验证

如果追加循环被证明不切实际,构建者可以通过证明交易在声明的索引处无效来允许其排除。构建者提供交易哈希和声称的索引;attester 使用 BAL (EIP-7928) 或类似的区块级访问数据在该索引处重建状态,并重新执行验证前缀。无论如何,VERIFY Gas 限制仍然是必需的。

这消除了 O(k²) 的追加循环,但引入了一个新的网络对象(构建者的索引声明)并将证明负担转移给构建者,构建者完全控制他们声明哪个索引。

开放问题

构建者复杂性。 迭代重试对 EL 客户端在实践中有多大成本,以及它会增加多少槽时间延迟?作为参考:在 16 个 IL 委员会成员中有 4 个是恶意的最坏情况下,attester 面临高达 1,000,000 VERIFY-Gas 的重放工作量,大约是 60M 区块 Gas 限制的 1.7%。但构建者面临的工作量显著更多:在最坏情况下,O(k²) 追加循环运行 k(k+1)/2 次重放。对于 k=16 个唯一的被排除的合格 FrameTx,即 16×17/2 × MAX_VERIFY_GAS_PER_FRAMETX = 13.6M VERIFY-Gas,大约是 60M 区块 Gas 限制的 22.7%。

AA-VOPS 缓存大小。 约束 5 中前 N 个存储槽的限制需要根据 AA-VOPS 缓存设计进行校准。N 的精确值(2-4)将根据实际智能钱包验证模式进行基准测试。

扩展读取的见证。 除了 AA-VOPS 缓存之外,如果受限 FrameTx 可以附加见证(值 + Merkle 证明)以进行额外的验证读取,那么适用哪些新鲜度(freshness)和根规则,以及当头区块改变时谁刷新证明?

参数更改。MAX_VERIFY_GAS_PER_FRAMETXMAX_VERIFY_GAS_PER_INCLUSION_LIST 需要进行基准测试。

Engine API 细节。 将有序 IL 集传递给 EL 的 newPayload / forkchoiceUpdated 的精确更改需要具体规范。

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

0 条评论

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