EIP-8142:区块内嵌Blob(BiB)——原型报告
本文详细介绍了EIP-8142(Block-in-Blobs,简称BiB)的原型实现。
作者:Péter Garamvölgyi ( @thegaram33)
最后更新:2026-06-26
简介
我在 ethrex 和 Lighthouse 上为 EIP-8142: Block-in-Blobs(简称 BiB)构建了一个原型。本文档概述了这项工作。其目标是帮助讨论此 EIP,并引导核心开发者将其纳入。具体来说,我们需要梳理开放的规范和实现挑战。
摘要 · BiB · 原型 · 发现 · 代码 · 背景 · 资源
摘要
在 ethrex、Lighthouse 和 Kurtosis 开发网络的基础上,使用 Claude 实现了一个端到端原型。
范围内的代码变更
- EIP 更新:EIPs#11763
- EL 原型:Thegaram:ethrex:eip-8142-block-in-blobs(基于 v18.0.0-rc.1)
- 通过 Engine API 的原生流程
- ethrex EIP-8025 guest 程序变更(完整证明器集成推迟)
- CL 原型:Thegaram:lighthouse:eip-8142-block-in-blobs(基于 v8.1.3)
- Kurtosis 开发网络:Thegaram:ethereum-package:eip-8142-block-in-blobs
不在本原型范围内的变更
- 将 EIP 移植到 execution-specs、consensus-specs、EELS/EEST
- 完整的 EIP-8025 zkAttester 集成
- 集成到 ere-guest、zkboost、Proof Engine 等
- 不下载负载即可证明
- 使用真实证明器的端到端开发网络
- CL 负载 blob 托管和负载重建
- 外部区块构建器
- Dora 浏览器变更
重点见解(详情请跳转至 发现):
- 概念变更很小,主要是 EL 侧,CL 变更极少。
- BiB 与现有的 DA 逻辑以及 EIP-8025 都很好地组合。
- BiB 将 KZG 置于区块生产过程的 热路径 上。
- 后 BiB 的 blob 费用市场仍然是一个开放的研究问题。
- 估算 BAL 大小和负载 blob 数量是实现的最大障碍。
建议的下一步:
- 对 BiB 对 Engine API 延迟的影响进行更详细的基准测试。
- 重新设计 BiB 编码,或考虑将 BAL 排除在 BiB 之外。(如果没有 BAL,负载 blob 的大小更可预测,并且可以增量构建。)
- 实现
verify_blob_kzg_proof和verify_blob_kzg_proof_batch的 Rust 原型。 - 端到端证明测试,集成 ere-guest 和 zkboost。
- 与提议的 pq-DA 方案进行初步 BiB 集成。
- 最终确定 BiB blob 费用市场设计。
- 与 CL 客户端开发者 讨论 CL 负载 blob 托管问题。
- 收集反馈(ethrex 团队、zkEVM 团队、EL/CL 客户端开发者等)。
BiB 概述
动机
BiB 提议使用以太坊现有的 blob 机制(如 EIP-4844 和 EIP-7594 所定义)来分发执行负载。这一变更的动机是向 zkEVM(EIP-8025: 可选执行证明)的过渡。动机有两个方面:
-
执行负载可用性。zkAttesters 通过验证针对区块头的有效性证明来验证执行负载,而不是重新执行它。这意味着他们可以在不下载完整负载的情况下进行证明,因此今天负载可用性的隐含保证不再成立。将负载发布在 blobs 中保证了任何人都可以下载并重新执行它。请参阅 ethresear.ch/t/blocks-are-dead-long-live-blobs。
-
带宽扩展。过渡到 zkEVM 可以扩展执行,因为 zkAttesters 可以在亚线性时间内验证区块。但带宽仍然会随区块大小线性扩展。通过使用 blobs,验证者将采样数据而不是下载完整负载。请参阅 ethresear.ch/t/a-native-zkevm-scales-bandwidth-not-just-execution。
在 BiB 下,区块提议者(EL 客户端)必须将执行负载编码到一个或多个 blobs 中,然后将这些 blobs 传递给 CL 客户端。CL 客户端随后像处理任何其他 blobs 一样进行数据分发和数据可用性采样。证明者通过重新编码负载并比较 blob 版本化哈希来验证此编码。zkAttesters 将其作为有效性证明的一部分进行验证。
术语
Payload blob。我们引入术语 payload blob 来指代包含执行负载数据的 blobs。相比之下,user blobs(又名 type-3 blobs)由用户通过 mempool 提交。Payload blobs 不附加到任何交易,并且无法从 EVM 内部访问。Payload blobs 在 blob 包中始终位于 user blobs 之前。
这种 blob 类型的区分是概念性的:对于 DA 逻辑,所有 blobs 看起来相同。
分叉激活和依赖项
BiB 依赖于 BALs(EIP-7928: 区块级访问列表)。为简单起见,原型构建在 ePBS(EIP-7732: 内建提议者-构建者分离)之前的版本上。根据 strawmap,BiB 可能会与后量子 DA 一起推出,但此原型仍使用 KZG。
规范亮点
如需完整上下文,请先阅读 EIP-8142。
数据类型变更:在负载头部添加了一个新字段 payload_blob_count。没有这个新字段,就无法区分 payload 和 user blobs。
Payload blob 编码:Payload blobs 包含交易和 BAL,以 RLP 编码。交易是唯一无法从执行负载头部推导出的数据。BAL 可以通过重新执行区块来推导,因此将它们包含在 payload blob 中使此构造更有利于证明器。

重用现有 blob 逻辑:BiB 在 CL 侧需要最小的更改,它旨在重用现有的 blob 分发和数据可用性采样逻辑。对于大多数目的,CL 客户端将 payload 和 user blobs 同等对待。EL 客户端或 guest 程序验证 payload blobs 是否正确构造,但数据可用性是通过 CL 中现有的 PeerDAS 机制实现的。
无需更改 EVM、mempool:EL 的更改主要封装在 Engine API 中。由于 payload blobs 不暴露给 EVM,因此状态转换函数没有更改。需要澄清的是:当 blob 交易(type-3)触发 EVM 执行时,其 blob 版本化哈希可以从 EVM 内部访问。相比之下,payload blobs 永远不会注入到 EVM 上下文中。
限制和费用:协议定义的 blob 计数限制(MAX_BLOBS_PER_BLOCK)适用于 payload 和 user blobs 的总和,不能超过。然而,payload blobs 可能会覆盖节点操作员在其自身验证节点上配置的用户定义 blob 上限。Payload blobs 也参与费用市场计算。

不变量:在 BiB 之前,有一个不变量:“每个 blob 承诺都有一个对应的 blob 交易”。这不再成立。另一方面,MAX_BLOBS_PER_BLOCK 不变量仍然有效。
BiB 原型讨论
我们概述了原型实现,涵盖原生和 zk 流程(见下图)。我们首先介绍主要收获,然后详细说明代码变更。
原生与 zk 的等价性似乎是明显的(假设 KZG 是安全的且错误概率可忽略),但应该更正式地论证。

发现
1. KZG 操作进入 Engine-API 热路径。
在 BiB 之前,blob 承诺和 cell 证明始终由用户在发送交易之前计算,而不是由节点计算。有了 BiB,payload blob 承诺和 cell 证明现在由 EL 客户端计算。这是一个计算量大的操作(大约每个 blob 100 毫秒),处于 engine_getPayload 的关键路径上。
下面的开发网络截图显示 BiB 激活后延迟增加:

无法流水线化:BiB 编码为 header ‖ BAL ‖ RLP(txs)。header 和 BAL 在区块最终确定之前是未知的,因此 engine_getPayload 中的计算(KZG 承诺和 cell 证明)无法流水线化。然而,它可以对所有 payload blobs 并行化。下面有更多关于此的内容。
对负载验证的影响:在 engine_newPayload 中,BiB 步骤是非阻塞的:我们可以并行进行 blob 验证和区块重新执行。主要风险是资源争用,例如,8 个内核并行进行 payload blob 验证可能会占用并行交易执行和状态根计算所需的资源。

尖峰导致停滞:在本地开发网络中,我们观察到 Engine API 延迟偶尔出现尖峰,导致区块生产停滞——这需要进一步调查。
| 每个 payload blob 的操作 | getPayload(生产者) |
newPayload(验证者) |
|---|---|---|
承诺 MSM(blob_to_kzg_commitment) |
✓ | ✓ |
cell 证明(compute_cells_and_kzg_proofs) |
✓ — 主要成本(每个 blob 约几百毫秒) | — |
打开证明(compute_blob_kzg_proof) |
✓(仅 zk 流程) | — |
| 相对 KZG 成本 | 高昂 | 仅承诺 → 约轻 10 倍 |
2. 负载构建期间难以执行 blob 数量限制。
在构建负载时,我们必须强制执行不变量:payload_blob_count + user_blob_count <= MAX_BLOBS_PER_BLOCK。payload_blob_count 取决于 RLP 编码的交易和 BAL。后者 难以估计。
在原型中,当我们添加新交易时,我们计算添加的 BAL 大小的快速但不准确的上限。这可能导致负载构建器过早密封区块。类似的问题在原始 EIP PR 中 被指出。

这种启发式方法可能非常不准确,具体取决于区块的状态访问模式(开发网络使用了具有重叠状态访问的交易):

3. KZG blob 和批量验证在某些证明后端上不可用。
以前,运行 ethrex 节点不需要 c-kzg(功能标志)。现在它需要,因为它在验证(engine_newPayload)和区块生产(engine_getPayload)中都被使用。
Deneb 规范 定义了各种 KZG 函数。verify_kzg_proof 在任何地方都能工作(它用于点评估预编译),但 verify_blob_kzg_proof 在 OpenVM/Zisk 上缺失,并且没有后端提供纯 Rust 批量验证(verify_blob_kzg_proof_batch)。在证明器 guest 程序中,拥有 verify_blob_kzg_proof_batch 对于高效验证 payload blob 承诺至关重要。
由于点评估在任何地方都已可用,每个后端都已经拥有 BLS12-381 配对和 G1 操作,只是它们没有被组装成缺失的规范函数。

4. Blob 费用市场的影响尚不清楚。
Payload blobs 与 user blobs 共享 blob 预算和费用市场。它们如何重新定价数据可用性尚未解决。
5. CL blob 托管无法针对 payload blobs。
EIP 设想 CL 客户端从其 blobs 重建负载。但托管要么是所有 cell(超级节点),要么是 PeerDAS 列子集。没有像“仅 payload blobs”(或特定 rollup 的 blobs)这样的配置。换句话说,托管是按列的,而针对 blobs 将需要按行的托管。这可以作为单独的 ERC 添加到 CL 客户端。
这对于任何希望在不运行超级节点的情况下从 blobs 下载负载的人来说都很重要。

一个相关的问题是历史数据的可用性。目前这是一个隐含的保证,节点愿意为同步对等方提供服务(出于利他主义,或者说默认配置)。CL 客户端仅保留 blob 数据几周,之后默认删除。
6. 当前的 EIP 文本有些不准确和不完整。
一些修复在 EIPs#11763 中。
编码。最初 EIP 使用了错误的(小端)blob 域元素编码;已更新为将大端 MSB 设置为 0x00。
空负载。BAL 永远不会为空,即使区块是空的(考虑到系统合约调用)。因此,payload blob 数量始终严格非零。
创世配置和激活。尚未指定。原型混合使用了 Amsterdam 和 Gloas:EL 在 Amsterdam 后激活,CL 在 Fulu 和 Gloas 之间激活。
费用市场变更尚待评估。
7. 没有激励高 payload blob 利用率。
负载不太可能恰好填满 N 个 blobs。最后一个 blob 可能已满或几乎为空。没有激励来填满它。
另外,域元素编码(将 MSB 保留为 0x00)可能不是最有效的编码。从技术上讲,我们可以额外利用 MSB 的 6 位,同时仍然适合 BLS 域,以编码简单性换取效率。
以前有一些关于对 BiB 数据应用某种压缩的讨论。

另见来自本地开发网络的数据:

8. 潜在的证明开销。
证明器为 payload blobs 执行 KZG 批量评估和 RLP 编码。开销尚未在真实证明环境中评估。
9. 原生 rollups 重用 BiB 更改的 L1 STF。
BiB 将 payload-blob 构建和验证添加到高级状态转换函数中。原生 rollups 旨在按原样重用 L1 的 STF,可能不应继承这一点。
Ethrex 变更
完整差异:Thegaram:ethrex:eip-8142-block-in-blobs(基于 v18.0.0-rc.1)
EL 变更广泛,涉及 Engine API、负载构建、KZG 库以及 EIP-8025 guest 程序。
分叉激活:types/genesis.rs
在 Amsterdam 之后激活 EIP-8142,因为它依赖于 BALs。
Payload blob 编码:types/eip8142.rs
在 payload blobs 和字节之间进行转换,遵循规范。
头部和 blobs 包变更:types/block.rs,networking/rpc/types/payload.rs,types/blobs_bundle.rs
- 向 区块头部 和负载添加新字段
payload_blob_count,在 EIP-8142 激活之前省略。 - Blob 包 继续使用 payload blobs 和 user blobs 的 cell 证明,并将这些传递给 CL 客户端(
create_from_blobs)。 - 可选地,我们还为 payload blobs 计算 KZG 打开证明,以便在 guest 程序中验证(
compute_payload_kzg_proofs)。 - 我们 强制执行不变量,即 payload blobs 必须位于 user blobs 之前(
from_sections)。 - 并行化 payload blob 包的承诺和证明计算,因为这位于
engine_getPayload的热路径上(create_from_blobs)。
KZG/加密:crypto/provider.rs,crypto/kzg.rs
- 添加先前未实现的
verify_blob_kzg_proof_batch方法。 - 当
c-kzg可用时(原生/主机、RISC0),我们直接使用其批量验证。 - 否则(例如 SP1/kzg-rs),我们目前循环进行单 blob 验证作为临时解决方法。
新的 Engine API:networking/rpc/engine/mod.rs,networking/rpc/engine/payload.rs
- 定义新的 API
engine_newPayloadV6(以及engine_newPayloadWithWitnessV6)。在verify_eip8142_payload中实现 payload-blob 验证。 - 定义新的 API
engine_getPayloadV7。 - 定义新的 API
engine_getPayloadWithKzgProofsV7,未暴露的面向证明器的 API。对应 EIPget_payload_zk。附加 KZG payload blob 证明。 - 注意:BiB 中新添加的大部分验证都是在 Engine API 中完成的。ethrex 的区块指标(显示在日志中)不会包含这些,因为这些指标是在不同的 crate 中实现的。
负载构建:blockchain/payload.rs,common/validation.rs
- 在区块密封期间(
finalize_payload),我们调用add_eip8142_payload_blobs来构造并预置 payload blobs。 - 我们必须强制执行不变量:
payload_blob_count + user_blob_count <= MAX_BLOBS_PER_BLOCK。我们 保持一个快速的运行估计 关于 BAL 大小,并用它来估计payload_blob_count。这在 block_access_list.rs 中实现。 - 此限制在
verify_blob_gas_usage中 验证。
证明器 guest 程序:types/eip8025_ssz.rs,guest-program/src/l1/input.rs,guest-program/src/l1/program.rs
- 定义 BiB guest 程序公共 输入
NewPayloadRequestBib(以及ExecutionPayloadV5)。与规范 EIP-8025 输入的唯一区别是添加了新字段payload_blob_count。 - 定义
BibStatelessInput,它添加了payload_kzg_commitments和payload_kzg_proofs作为额外的私有输入。此输入类型当前 定义为版本0x02。 - 新的 guest 程序入口点:
execute_bib_stateless_input_decoded。对应 EIPnew_payload_zk。 - 在
validate_eip8025_bib_execution/verify_payload_blobs内部,我们执行 EIP 中定义的 payload blob 验证步骤。

测试:tests/eip8142_native.rs,tests/eip8142_guest.rs
- 原生引擎往返(getPayloadV7 -> newPayloadV6)测试。
- Guest 程序证明器/拒绝测试。
Lighthouse 变更
完整差异:Thegaram:lighthouse:eip-8142-block-in-blobs(基于 v8.1.3)
CL 变更最小且主要是机械性的。差异的主体是分叉激活样板代码,添加了一个新的 BiB 分叉变体。概念性的 CL 变更很小:一个新字段 payload_blob_count 和对 blob 版本化哈希验证的调整。
分叉激活:fork_name.rs,chain_spec.rs,upgrade/bib.rs。
- 添加一个新的
ForkName::Bib变体,排序在 Fulu 和 Gloas 之间——在此处激活(而不是像 EL 那样在 Amsterdam 之后),以避免不完整的 Gloas/ePBS 变更的干扰。 - 将
bib_fork_version/bib_fork_epoch添加到ChainSpec,并添加is_bib_scheduled()。 upgrade_to_bib()Fulu→Bib 状态升级(仅执行负载头部类型更改),连接到per_slot_processing。- 剩余的样板代码(preset、eth_spec、config_and_preset)是分叉激活管道,非 BiB 特有。
Payload 头部数据结构变更:execution_payload_header.rs,execution_payload.rs,beacon_block_body.rs。
- 执行负载和头部的新
Bibsuperstruct 变体,添加了payload_blob_count字段;头部升级 默认为 0。 - 新的
BeaconBlockBodyBib/BeaconBlock/BeaconState变体贯穿 BiB payload 类型(例如状态的latest_execution_payload_header)。 - 这些是机械性的 superstruct 添加;
payload_blob_count是唯一新的 BiB 语义字段。
连接新的 Engine API:engine_newPayloadV6 / engine_getPayloadV7:engine_api/http.rs,json_structures.rs,new_payload_request.rs。
- 新的方法常量
engine_newPayloadV6/engine_getPayloadV7/engine_forkchoiceUpdatedV4,添加到声明的功能列表中。 JsonExecutionPayloadBib包含block_access_list/slot_number/payload_blob_count及其 (反)序列化。NewPayloadRequestBib请求变体,以及用于 BiB 区块 /payload_blob_count的新 EL 指标。- 注意:
engine_getPayloadBodies(全有或全无)和fetch_and_process_blobs_v2在原型中暂时损坏。
Blob 验证:versioned_hashes.rs。
verify_versioned_hashes现在剥离前导的payload_blob_count哈希(payload blobs),并仅针对负载的交易版本化哈希验证 type-3 user-blob 后缀。- User blobs 可以在没有 EL 状态的情况下检查;payload blobs 无法廉价验证,因此此 CL 侧检查(在乐观同步期间使用)将它们推迟到 EL(
engine_newPayloadV6),类似于状态根。
测试:bib.rs。
beacon_node/beacon_chain/tests/bib.rs中的端到端 BiB 区块生产/验证测试。
本地开发网络
完整差异:Thegaram:ethereum-package:eip-8142-block-in-blobs
适用于本地测试的原型开发网络,使用本地构建的 ethrex/lighthouse 镜像。这个 2×2 Lighthouse-ethrex Kurtosis 开发网络运行 原生 BiB 路径:创世为 Fulu/Osaka,BiB 在 epoch 1 激活,之后开发网络在 spamoor 负载下生产和导入带有 payload blobs 和 type-3 blobs 的 BiB 区块。

运行开发网络:
docker build -t ethrex:bib .
docker build -t lighthouse:bib .
kurtosis run --enclave bib . \
--args-file ./bib_network_params.yaml \
--image-download missing
更多开发网络截图。
以下是一些来自 Grafana 的示例截图,这些数据来自本地开发网络运行。我们有一个 spamoor 实例首先运行 EOA、ERC20 和 blob 交易,然后运行“大区块”交易(eoatx 带 data: "random:25000")。
在 EIP-8142 激活时,我们开始看到 payload blobs,并且我们可以看到数据分解(交易 vs BAL)。

我们可以看到最后一个 blob 的利用率随机波动。

Engine API 的 getPayload 方法显示出显著升高的延迟。

当前的负载构建器 BAL 估计可能非常不准确,具体取决于交易负载。

在启用大型交易垃圾邮件发送器后,我们现在看到每个区块最多有 8 个 payload blobs。

创世和解析器连接:el_cl_genesis_generator.star,input_parser.star,sanity_check.star。
- 通过输入解析器和健全性检查传递
bib_fork_epoch,并教导 EL/CL 创世生成器发出 BiB 分叉计划(创世保持 Fulu;BiB 是一次upgrade_to_bib转换)。 - 避免 BiB 在创世时构建:两层都从生成器处理的 Fulu/Osaka 开始,并在 epoch 1 转换,这也测试了分叉边界的版本化哈希处理。
开发网络配置:bib_network_params.yaml。
- 2×2 ethrex/lighthouse 验证者;spamoor 配置为高 blob/交易可变性,以驱动 payload blobs 和 type-3 user blobs 的混合。
Grafana 仪表板:eip8142-dashboard.json。
- BiB 可观察性:payload-blob 数量、payload BAL/txs 字节大小和 BiB 区块计数器(由新的 EL 仪表
eip8142_payload_bal_bytes/eip8142_payload_txs_bytes和 CLEXECUTION_LAYER_BIB_BLOCKS计数器支持)。
安全考虑
依赖 KZG。BiB 使用现有的 DA 机制,包括 KZG 承诺和 blob 证明。这不能抵御量子攻击,并且将来必须迁移到不同的 DA 方案。为了高效证明,我们依赖批量承诺验证,新方案也应支持。
数据扣留。构建者可以设置 payload_blob_count 并发布承诺,同时扣留底层 payload-blob 数据。类似于扣留 user blobs,这样的区块将被验证者拒绝。
格式错误/无效的 payload blobs。构建者可以发布无法解码为有效负载的 blobs,或者与 blobs 不匹配的承诺。两个验证路径都会拒绝:原生 newPayloadV6 重新计算承诺(MSM)并比较版本化哈希,然后解码;zk guest 验证打开(verify_blob_kzg_proof_batch)并解码。编码是严格的——每个域元素 MSB 为零、零填充以及 [BAL-len][txs-len] 头部都经过检查——因此格式错误的 payload blob 会失败验证,区块被拒绝。
Blob 预算争用。Payload blobs 和 user blobs 共享一个 MAX_BLOBS_PER_BLOCK 限制和一个费用市场。大区块需要更多 payload blobs,这可能会挤出 user blobs 并改变 blob 费用。组合限制限制了总 DA 负载,并在每个路径上强制执行,但 经济 交互(payload blobs 如何重新定价 blob 市场)是主要的开放研究问题,而不是已确定的属性。
验证延迟/资源成本。Cell 证明生成(~每个 blob 几百毫秒)现在位于 engine_getPayload 热路径上(engine_newPayload 仅重新计算承诺,轻得多)。生产者承担构建成本;验证者承担验证成本,通过批量验证和并行化减轻。这提高了节点资源需求(并使 c-kzg 成为必需),但受现有区块大小/blob 限制的约束,而不是无界 DoS 向量。
证明可靠性。zk 路径的可靠性依赖于 KZG 绑定加上 Fiat–Shamir 批量组合器;有缺陷的组合器会让证明者将不同的负载当作已承诺的负载传递。新添加的 KZG 实现应仔细验证和审计。
乐观同步。在乐观同步期间,CL 跳过 payload-blob 验证,仅检查 user-blob 版本化哈希,将 payload blobs 推迟到 EL(newPayloadV6),类似于状态根。Payload blobs 在没有 EL 状态的情况下无法廉价验证,并且这仅是对尚未完全验证的区块的健全性检查,因此推迟是安全的。
附录:背景
Engine API
Engine API 是以太坊节点的共识层(CL)和执行层(EL)客户端之间的认证 JSON-RPC 接口。CL 驱动共识、分叉选择以及区块/blob 的 gossip。EL 处理交易执行和状态。它们专门通过此接口进行通信。
engine_getPayload:CL 要求 EL 为其提议的区块构建一个执行负载。EL 返回负载以及其中包含的任何 blobs 的 blobs 包(blobs 及其 KZG 承诺和 cell 证明)。engine_newPayload:CL 将从网络接收到的负载交给 EL 执行和验证,EL 回复有效/无效。
由于 BiB payload blobs 由 EL 生成和验证,且从不暴露给 EVM,因此更改几乎完全位于此 Engine API 边界内,作为这些方法的新版本实现:engine_getPayloadV7 和 engine_newPayloadV6。
区块访问列表(BALs)
区块访问列表(EIP-7928)是一种附加到区块的结构,记录区块接触的每个状态位置:账户、存储槽及其执行后的值。BAL 的主要目的是 性能优化:它允许节点并行化磁盘读取、交易验证和状态根计算,并在同步期间重建状态而无需重新执行交易(“无执行状态更新”)。
BAL 不是 无状态或证明机制;它不携带 Merkle 证明,也不允许节点在没有状态的情况下验证区块。该角色属于 执行见证(状态值 + 证明),它与 BAL 正交,不会被其取代。
BAL 格式(简化):
AccountChanges = [\
address, # bytes20\
[ SlotChanges, ... ], # storage_changes (写入,包括将槽清零)\
[ uint256, ... ], # storage_reads (读取但从未写入的槽)\
[ BalanceChange, ... ], # post-tx 余额 (仅当余额实际改变时)\
[ NonceChange, ... ], # post-tx nonce\
[ CodeChange, ... ], # post-tx 运行时字节码 / 7702 委托\
]
## 每个接触的地址一个条目
BlockAccessList = [ AccountChanges, ... ]
规范 RLP 编码:由于头部绑定了 keccak256(rlp(bal)),字节必须是规范/确定性的。EIP 指定了 RLP 编码、最小大端整数、强制键排序等。
为什么 BAL 不是增量可构建的:rlp([tx_1, …, tx_n]) 可以增量构建,因为每个交易是自包含的,可以简单地追加到字节流中(为简单起见忽略 RLP 长度前缀)。BAL 不同:它不是一个扁平列表,而是一棵树,由一个全局值(地址)键控,并在整个区块中聚合。任何交易都可以更新任意条目中的值;我们直到整个区块执行完毕才知道最终的数据结构(和编码字节)。
这对 BiB 有一个重要的影响:在负载构建期间,payload blobs 只能在区块准备好时才能知道。这使得负载构建更加复杂(更难遵守 MAX_BLOBS_PER_BLOCK 限制)且成本更高(增加了 KZG cell 证明生成延迟)。
KZG
BiB 严重依赖 KZG 承诺,因此值得回顾主要的 KZG 原语(另请参阅 Deneb 规范):
- 承诺。一个 blob 是 BLS12-381 标量域中的 4096 个域元素,解释为次数 <4096 的多项式
p在固定点(4096 次单位根)上的求值。KZG 承诺是一个单一的曲线点,它绑定该多项式。blob 的版本化哈希只是此承诺的哈希。 - 点评估证明。一个简短的证明,证明
p(z) = y在单点z处,通过单个配对(verify_kzg_proof)检查。这是所有其他内容构建的基础原语。这在 EVM 内直接作为点评估预编译公开,其中所有输入由调用者提供。 - Blob 证明。
verify_blob_kzg_proof(blob, commitment, proof)证明一个完整的 128 KiB blob 与承诺匹配。它简化为点评估证明:推导一个挑战z = compute_challenge(blob, commitment)(Fiat–Shamir),评估y = p(z)(重心坐标),然后运行verify_kzg_proof。非正式地,Schwartz–Zippel 引理表明,当且仅当此随机检查通过时,承诺与 blob 匹配的概率非常高。 - Cell 证明。EIP-7594/PeerDAS blob 包携带 cell 证明,用于数据可用性采样而不是整个 blob 验证。
- 批量验证。批量形式
verify_blob_kzg_proof_batch通过规范定义的 Fiat–Shamir 组合器,使用大约一个多配对同时验证许多 blobs。

在当今的以太坊中,完整的 blob 验证(blob-承诺绑定检查)在 mempool 和 CL 客户端中完成,但从未在 EL STF 中完成。在 BiB 下,节点(和 guest 程序)必须验证实际的 payload blob 内容。
证明后端问题。KZG 支持在各后端之间不均匀,只有 blob 级操作是问题所在:
- 点评估在每个后端上都有效(主机/RISC0 上的 c-kzg,SP1 上的 kzg-rs,openvm-kzg,以及 Zisk 上的硬件加速)。
verify_blob_kzg_proof在 OpenVM 和 Zisk 上缺失(未实现),并且没有后端提供纯 Rust 批量验证。- 计划中的修复(在原型中推迟以保持差异最小)是通用归约:在 ethrex 自己的代码中执行
compute_challenge+ 重心评估,并仅调用普遍可用的verify_kzg_proof,绕过每个后端的 blob 级差距,批量验证稍后作为优化添加。

Blob 验证:
- BiB 之前:用户计算 blob 交易 sidecar(承诺和 cell 证明)。EL 在 mempool 中验证它(
verify_blob_kzg_proof),然后构建一个区块并将其作为 blobs 包的一部分传递给 CL。CL 验证这些,分发和采样 blobs。EL STF 永远不需要 blob 内容,只需要点评估预编译的 blob 版本化哈希(0x0a)。 - BiB 之后:EL 客户端(和 guest 程序)必须验证 payload blobs 与负载匹配。
EIP-8025
EIP-8025: 可选执行证明是以太坊向 zkEVM 过渡的第一步。zkAttesters 不是重新执行每个区块,而是验证针对区块头的有效性证明。
- Guest 程序。EL 状态转换函数被编译为在 zkVM 内运行。证明区块意味着在区块的输入(无状态执行)上执行此 guest 程序,并产生一个正确执行的简洁证明。
- 公共与私有输入。公共输入是验证者看到并绑定的:规范 SSZ
NewPayloadRequest。私有输入是证明者使用但验证者永远看不到的见证(状态、交易等)。 - 加密后端。相同的 guest 逻辑在几个证明后端上运行,每个后端都有自己的加密实现(例如主机和 RISC0 上的 c-kzg,SP1 上的 kzg-rs),由 guest 入口点选择。
附录:规范符合性检查清单
由 Claude 生成。
| 领域 | 状态 |
|---|---|
Blob 编码/解码(31 字节块,MSB=0,[BAL-len][txs-len] 头部,填充和长度检查,往返) |
✅ |
payload_blob_count 头部字段(存在当且仅当 EIP-8142 激活,RLP 尾部可选,serde) |
✅ |
engine_getPayloadV7 — 从 {BAL, transactions} 生成 payload blobs,payload-first 包,cell 证明 |
✅ |
engine_newPayloadV6 — 重新计算承诺(MSM),版本化哈希相等性(payload + type-3) |
✅ |
zk guest(new_payload_zk)— 推导 blobs,verify_blob_kzg_proof_batch,通过 HTR'd 版本化哈希固定承诺 |
✅ |
所有路径上的组合限制 payload + type-3 ≤ MAX_BLOBS_PER_BLOCK(溢出安全) |
✅ |
get_payload_zk(engine_getPayloadWithKzgProofsV7)— 随机点打开证明,仅证明器 |
✅ |
| 批量验证后端覆盖 | ⚠️ 仅 c-kzg + SP1;OpenVM/Zisk 差距 |
附录:资源
规范:EIP-8142 及后续修复 EIPs#11272。另请参见这些 PR 下的评论中的历史背景:EIPs#11212,EIPs#11530
研究讨论:https://ethresear.ch/t/blocks-are-dead-long-live-blobs/24611 和 https://ethresear.ch/t/a-native-zkevm-scales-bandwidth-not-just-execution/25254
- 原文链接: hackmd.io/@peter-scroll/...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~