用于 Proposer Commitments 的 BALs —— 执行层研究

以太坊中文 发布于 2025-09-25 阅读 146

文章探讨了如何利用 EIP-7928 提出的块级访问列表(BAL)来优化以太坊的执行前确认(Execution Preconfs)机制。通过将 BAL 作为规范的状态变更记录,可以解决当前证明机制在碎片化和粒度上的不足。作者还提出了改进 BAL 编码结构的建议(如引入 MPT 树),旨在提高 EVM 层面验证 broken commitments 的效率并简化协议设计。

感谢 ToniLadislaus 的讨论与反馈。

EIP-7928 引入了块级访问列表 (Block-Level Access Lists, BALs),以改进 L1 上的并行执行。除了扩展性之外,BALs 还为提议者承诺协议(尤其是 执行预确认 (execution preconfs))解锁了一个更简单的基础。BALs 不再依赖于分布在多个 Merkle Patricia Tries 中的碎片化证明,而是提供了一个所有交易后数值的规范记录,承诺可以与之绑定。

即便如此,目前的 EIP-7928 规范并不直接适用于 高效 的 EVM 级证明。本文旨在说明将 BALs 用于预确认的动机,强调为了使其对证明友好而提议的规范更改,并概述 Discord 讨论 中的复杂性权衡。

动机 (Motivation)

执行预确认 (execution preconfs) 让提议者(或代理人)在交易上链之前,就交易结果向用户提供早期承诺。这将预确认时间缩短为预确认者与用户之间的一次“ping”,提供的 UX 比共识协议所能实现的更快。用户无需等待区块确认,在收到 可信的 执行预确认那一刻起就可以放心地进行操作。

如今,可信承诺要求能够在承诺被违反时进行去信任的证明,以便对有抵押的预确认者执行链上罚没条件(例如,使用 URC)。这些证明散布在多个 Trie 中:用于包含/排序的交易 Trie、用于执行成功的收据 Trie、用于 nonce 或余额变化的账户 Trie,以及用于存储的状态 Trie。这种方式虽然可行,但存在碎片化问题,需要定制的证明逻辑,且在粒度上受到限制。

  1. 碎片化 (fragmentation):完整的覆盖需要对每个被修改的状态片段在多个 Trie 中提交数值。

  2. 逻辑 (logic):每个承诺做出不同的保证并影响不同的状态,需要自定义逻辑。

  3. 粒度 (granularity):这些 Trie 仅记录应用所有交易后的状态,这意味着中间状态更新(例如,第四笔交易后 Alice 的余额)是无法直接证明的。

一些提议的设计通过在执行后状态与用户预期不符时回滚交易来规避 1) 和 3),例如 “以至少 0.98 ETH 的价格出售 1000 USDC”。虽然这种做法很务实,但这将负担转移到了合约开发者身上,他们必须手动编码这些条件。在实践中,这类似于意图 (intent) 风格的编程,每个动作都带有定制的有效性条件。

通过提供交易级状态差异的规范记录,BALs 为构建提议者承诺协议提供了一个更简单的原语。执行预确认可以表达为对 BAL 子集的承诺,而被违反的承诺可以针对规范的 BAL 进行证明。这使得承诺更容易理解、执行和标准化,降低了协议设计者和用户的摩擦。因此,一个能够有效利用 BALs 的通用提议者承诺协议可以涵盖当今所有的预确认用例。

现状下的 BALs (BALs Today)

BAL 是一个 RLP 编码的 List[AccountChanges],其中 AccountChanges 对象记录了每个交易对地址的 nonce、余额 (balance)、存储 (storage) 和/或代码 (code) 的状态更改。通过迭代应用所有 AccountChanges,客户端可以确定性地且并行地重建区块中每个受影响账户的演变过程。

这足以解决我们在执行预确认方面面临的挑战:

  1. 碎片化:与多个 Trie 相比,仅需要 BAL 即可做出承诺。

  2. 逻辑:一个通用的协议就足以捕捉当今所有的预确认用例,而无需意图风格的编程。

  3. 粒度AccountChanges 对象允许对每笔交易的状态差异进行承诺。

当前的局限性 (Current Limitation)

显然,BALs 有助于改进预确认协议;然而,挑战在于 BALs 的编码方式。在当前的 EIP-7928 规范下,BALs 是 RLP 编码的,且区块头包含 BAL 哈希。

这使得向 EVM 证明承诺被违反变得十分繁琐。用户需要将整个 BAL 作为 calldata 传递,根据父区块哈希验证其哈希值,然后手动进行 RLP 解码以定位冲突的数值。目前,一个未压缩的 BAL 大约为 100KB,这在 calldata 限制范围内。考虑到违反承诺 应当 是罕见发生的,这并非不合理。

提议的 EIP 更改(较难方案)

随着区块和 BALs 变得越来越大,将 BAL 作为 calldata 传递将变得更加昂贵。提议的更改将使从 EVM 证明 BAL 成员身份变得更加容易。

由于执行客户端不太可能使用 SSZ 来编码 BAL,另一种方法是将 BAL 编码为其自身的 MPT,如 Toni 所提议的那样。区块头将包含 BAL 根而不是哈希,从而允许针对 EIP-2935 父哈希进行高效的 MPT 证明。

预确认的理想编码将具有两级 Trie。外层 Trie 将 bal_index(即交易索引)键映射到 subTrieRoot 值,其中每个 bal_index 代表一个唯一的交易索引。内层 subTrie 包含涵盖所有不同账户更改的唯一键:

  • RLP(["balance_change", address])

  • RLP(["storage_change", address, slot])

  • RLP(["nonce_change", address])

  • RLP([“code_change”, address])

其数值包含 RLP 编码的交易后数据(即 RLP([uint256]))。

这将允许预确认者对交易后状态差异做出简洁的承诺,即对给定 bal_indexsubTrieRoot 进行签名。证明承诺被违反随后简化为:展示该 bal_index 处的实际 subTrieRoot 与所签名的不同。subTrie 中的单个账户更改与承诺协议是抽象分离的。

这比现状要简单得多,在现状中,预确认者需要对交易涉及的每个位置在多个 Trie 中提交交易后状态差异。相反,现在每笔交易都折叠成一个单一的、整洁打包的 subTrieRoot

然而,这种方法需要引入另一个 Trie,这增加了 EIP 的复杂性。

提议的 EIP 更改(较易方案)

假设区块头仍然提交 RLP 编码的 BAL 哈希,对 BAL 布局进行细微的重新结构可以大大简化承诺。该提案是按 bal_index 而不是账户对 BAL 进行索引,这样 BAL 就被编码为 RLP 编码的 List[TransactionDiff]

一个 TransactionDiff 将包含给定交易的所有 nonce、余额、存储和代码更改。这将把承诺从许多 AccountChanges 值折叠为一个 TransactionDiff,其功能就像前一个提案中的 subTrieRoot 一样。

简洁地对 TransactionDiff 做出承诺将使 连续区块构建 (continuous block building) 更有可能成为现实,因为钱包和 PBS 流水线的其余部分可以在应用执行预确认后保持最新的状态。

BALs 的其他益处

  • 主要的 Rollup 已经在尝试通过诸如 shredsflashblocksfrags 等承诺方案来提供更快的确认,以改善 UX。目前,这些方案依赖于第三方实现,且承诺需要完全信任排序器 (sequencer)。

BALs 使这种模式标准化:使用 BALs 的 Rollup 不再需要维护自己定制的承诺方案,用户可以更轻松地从可信承诺中受益。实际上,BALs 将实践中已经开始发生的事情协议化 (enshrine) 了。

  • Vitalik 指出,BAL 作为 MPT 的方法允许部分无状态节点仅验证它们关心的 BAL 部分的证明,例如,我的余额在区块执行过程中是如何变化的。

  • 执行预确认是 payload chunking 等构思的协议外先驱。

  • 原文链接: ethresear.ch/t/bals-for-...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~

相关文章

0 条评论