EIP-8146 深度解析:将区块访问列表(BAL)作为独立 Sidecar 提前传播,重塑 ePBS 关键路径
发布时间:2026/9/16 13:06:39 锦皓数字建站
作为独立 Sidecar 提前传播,重塑 ePBS 关键路径`)
EIP-8146 深度解析将区块访问列表BAL作为独立 Sidecar 提前传播重塑 ePBS 关键路径【免费下载链接】EIPsThe Ethereum Improvement Proposal repository项目地址: https://gitcode.com/GitHub_Trending/ei/EIPs导读EIP-8146Block Access List SidecarsDraft 状态Standards Track / Core是对 EIP-7928区块级访问列表 BAL与 EIP-7732内嵌式提议者-构建者分离 ePBS的一次架构级重构它把原本内嵌在执行载荷ExecutionPayload中的区块访问列表剥离出来改经一条专属 gossip topic 作为独立 sidecar 提前传播并在ExecutionPayloadBid中仅携带keccak256(rlp(BAL))这一 32 字节承诺。读完本文你将掌握 EIP-8146 的完整规范——包括共识层容器与 fork choice 变更、两类新的 Req/Resp 协议、Builder 与 PTC 成员的新职责以及engine_notifyBlockAccessListV1等 Engine API 方法如何为执行客户端创造至少 1 秒的预热窗口让状态预取与 post-state root 计算先于执行开始。一、背景BAL 从何而来又为何需要独立传播1.1 BAL 是什么EIP-7928 的遗产EIP-7928 引入了区块级访问列表Block Access ListBAL一份记录区块执行期间所有被访问账户、存储槽及其 post-execution 值的完整清单编码为 RLP结构遵循address - field - block_access_index - change的嵌套模式BlockAccessList List[AccountChanges]。它带来的核心收益包括并行磁盘读取与并行交易验证并行 post-state root 计算无需重放交易即可重建状态executionless state updates。关键约束在于BAL 大小与区块本体相当。仓库中的实测分析 assets/eip-7928/bal_size_analysis_60m.md 显示在 60M gas 上限下1000 个区块样本的完整 BAL 平均压缩后约 72.4 KiB而压缩后的平均区块大小约为 71.7 KiB——也就是说BAL 与区块几乎等大其中存储写入占 40.3%、存储读取占 25.8%。早期分析 assets/eip-7928/bal_size_analysis.md 也佐证了这一量级100 区块样本下含 reads 配置平均压缩约 42.7 KB。1.2 现状问题BAL 卡在关键路径上在 EIP-7732 的 ePBS 架构下执行载荷不再随信标区块体传播而是由 Builder 在 slot 进行期间通过SignedExecutionPayloadEnvelope揭示。EIP-7928 把 BAL 作为ExecutionPayload的一个字段因此BAL 与交易绑定在同一个 topic、同一个截止时间下到达——执行客户端只有等载荷整体到达后才能拿到 BAL预热窗口为零。EIP-8146 正是针对这一耦合提出的解耦方案把 BAL 从载荷中移出作为独立 sidecar 提前至少 1 秒由 PTC 计时委员会强制执行到达。它在原文档中列出了四大收益收益说明分离关注点BAL 是状态差异载荷是产生它的交易集合只推进状态的节点如不做重放的同步客户端只需要 diff不需要交易关键路径块体积减半全量 BAL ≈ 72.4 KiB与压缩区块 ≈ 71.7 KiB 相当从信封中移除后对象尺寸减半可用更低的网络上限做 DoS 防护更早的 BAL 投递通过提前的观察截止时间BAL 至少领先载荷 1 秒PTC 强制执行执行客户端可以带声明的状态预取进入执行post-state root 计算先行启动更好的包含列表EIP-7805 的 slot N 包含列表委员会在 slot N 载荷揭示前就冻结列表提前拿到 BAL 的 post-values 后包含列表构建者无需执行载荷即可推进其状态视图二、共识层规范容器、fork choice 与网络协议2.1 新常量名称值说明MAX_BLOCK_ACCESS_LIST_SIZEuint64(2**23) 8 MiB编码后 BAL 的 SSZ 上限MIN_EPOCHS_FOR_BLOCK_ACCESS_LIST_SIDECARS_REQUESTSuint64(3533)sidecar 必须被服务的 epoch 数与 EIP-7928 的 BAL 保留窗口一致BLOCK_ACCESS_LIST_LEAD_TIMEuint64(1)sidecar 需至少提前载荷证明截止时间多少秒才算可用2.2 新类型与容器变更新增 SSZ 类型BlockAccessList ByteList[MAX_BLOCK_ACCESS_LIST_SIZE]——RLP 编码的区块访问列表对共识层而言是不透明字节consensus layer 不做 RLP 解码。ExecutionPayload移除字段EIP-7928 加入的block_access_list字段被移除class ExecutionPayload(Container): # ... fields unchanged from EIP-7732 ... blob_gas_used: uint64 excess_blob_gas: uint64 # [Removed in EIP-8146] # block_access_list: BlockAccessListExecutionPayloadBid新增字段class ExecutionPayloadBid(Container): parent_block_hash: Hash32 parent_block_root: Root block_hash: Hash32 prev_randao: Bytes32 fee_recipient: ExecutionAddress gas_limit: uint64 builder_index: BuilderIndex slot: Slot value: Gwei execution_payment: Gwei blob_kzg_commitments: List[KZGCommitment, MAX_BLOB_COMMITMENTS_PER_BLOCK] # [New in EIP-8146] block_access_list_hash: Bytes32其中block_access_list_hash keccak256(rlp(BlockAccessList))与 EIP-7928 写入执行区块头的block_access_list_hash字段值完全一致。注意 EIP-7928 规定无状态变化时该字段为keccak256(rlp.encode([]))即空 RLP 列表哈希0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347。PayloadAttestationData新增字段class PayloadAttestationData(Container): beacon_block_root: Root slot: Slot payload_present: boolean blob_data_available: boolean # [New in EIP-8146] block_access_list_present: boolean新增BlockAccessListSidecar容器class BlockAccessListSidecar(Container): beacon_block_root: Root slot: Slot block_access_list: BlockAccessList2.3 Fork Choice 变更Store新增两个字段dataclass class Store(object): # ... existing fields ... # [New in EIP-8146] block_access_lists: Dict[Root, BlockAccessList] field(default_factorydict) block_access_list_availability_vote: Dict[Root, List[Optional[boolean]]] field( default_factorydict )on_block在既有ptc_vote初始化旁为新区块根初始化PTC_SIZEEIP-7732 中定义为 512个投票槽位store.block_access_list_availability_vote[block_root] [None] * PTC_SIZEon_payload_attestation_message在记录payload_present的同时记录 BAL 可用性投票notify_ptc_messages在从信标区块中的聚合证明提取PayloadAttestationMessage对象时也会透传block_access_list_presentstore.block_access_list_availability_vote[data.beacon_block_root][ptc_index] ( data.block_access_list_present )on_block_access_list_sidecar对每个通过 gossip 校验的 sidecar 调用。注意其中两个关键设计共识层把 BAL 视为不透明字节、不做 RLP 解码并在载荷到达之前就把 BAL 交付给执行层def on_block_access_list_sidecar(store: Store, sidecar: BlockAccessListSidecar) - None: assert sidecar.beacon_block_root in store.blocks block store.blocks[sidecar.beacon_block_root] assert sidecar.slot block.slot # The consensus layer treats the BAL as opaque bytes; no RLP decoding bid block.body.signed_execution_payload_bid.message assert keccak256(sidecar.block_access_list) bid.block_access_list_hash store.block_access_lists[sidecar.beacon_block_root] sidecar.block_access_list # Deliver to the execution layer without waiting for the payload EXECUTION_ENGINE.notify_block_access_list(sidecar.block_access_list, bid.block_hash)on_execution_payload_envelope载荷不得先于同区块的 BAL 交给执行层若信封先于 sidecar 到达客户端不得丢弃而是缓存起来待on_block_access_list_sidecar存入匹配的 BAL 后重跑该处理器def on_execution_payload_envelope( store: Store, signed_envelope: SignedExecutionPayloadEnvelope ) - None: envelope signed_envelope.message assert envelope.beacon_block_root in store.block_states assert is_data_available(envelope.beacon_block_root) # [New in EIP-8146] The BAL must be available locally assert envelope.beacon_block_root in store.block_access_lists state store.block_states[envelope.beacon_block_root] verify_execution_payload_envelope(state, signed_envelope, EXECUTION_ENGINE) store.payloads[envelope.beacon_block_root] envelopeverify_execution_payload_envelope本身保持不变。2.4 网络层新 Gossip Topic 与两类 Req/Resp 协议Gossip topicblock_access_list_sidecar携带BlockAccessListSidecar对象的全局 topic。转发前必须通过以下校验[IGNORE]sidecar.slot current_slot允许MAXIMUM_GOSSIP_CLOCK_DISPARITY偏差[IGNORE]sidecar.slot compute_start_slot_at_epoch(store.finalized_checkpoint.epoch)[IGNORE]未见过针对sidecar.beacon_block_root的有效 sidecar[IGNORE]根为sidecar.beacon_block_root的信标区块已被看到客户端 MAY 将 sidecar 排队等待区块到达。设block为该信标区块bid为其body.signed_execution_payload_bid.message[REJECT]block通过校验[REJECT]sidecar.slot block.slot[REJECT]keccak256(sidecar.block_access_list) bid.block_access_list_hash。Req/RespBlockAccessListSidecarsByRoot v1协议 ID/eth2/beacon_chain/req/block_access_list_sidecars_by_root/1/RequestResponseList[Root, MAX_REQUEST_PAYLOADS]List[BlockAccessListSidecar, MAX_REQUEST_PAYLOADS]按请求的信标区块根返回对应 sidecar。Req/RespBlockAccessListSidecarsByRange v1协议 ID/eth2/beacon_chain/req/block_access_list_sidecars_by_range/1/RequestResponse(start_slot: Slot, count: uint64)List[BlockAccessListSidecar, MAX_REQUEST_PAYLOADS]返回 slot 区间[start_slot, start_slot count)内的 sidecar按 slot 排序至多MAX_REQUEST_PAYLOADS条。客户端必须通过两种方法服务最近MIN_EPOCHS_FOR_BLOCK_ACCESS_LIST_SIDECARS_REQUESTS3533个 epoch 的 sidecar更早的 MAY 被裁剪。2.5 验证者职责Builder 与 PTC 成员Builder 的四步流程从engine_getPayloadV6获取载荷与 BAL在ExecutionPayloadBid中设置block_access_list_hash keccak256(blockAccessList)携带 bid 的信标区块发布后在block_access_list_sidecartopic 上广播BlockAccessListSidecar广播不再携带 BAL 的SignedExecutionPayloadEnvelope。Builder 应尽可能早地广播 sidecar且MUST NOT把步骤 3 推迟到步骤 4 的载荷信封揭示之后。PTC 成员仅当某个区块的 sidecar 在本地通过 gossip 校验、且至少早于载荷证明截止时间BLOCK_ACCESS_LIST_LEAD_TIME1 秒时才将block_access_list_present置为True。payload_present与blob_data_available仍按 EIP-7732 独立设置与block_access_list_present互不影响。三、Engine API 变更专用的 BAL 交付通道3.1engine_getPayloadV6响应结构变化blockAccessListRLP 编码的 BAL成为响应顶层字段而非载荷结构内的字段。3.2engine_notifyBlockAccessListV1将 BAL 独立于载荷交付给执行层。参数blockAccessListRLP 编码的 BAL 字节blockHash32 字节将 BAL 与某个载荷绑定。执行层收到后在blockHash下存储该 BAL开始预取 BAL 声明的账户与存储槽MAY 开始利用 BAL 的 post-values 计算 post-state root。注意该调用不是有效性检查——BAL 是在执行载荷时对照区块头校验的此方法仅确认收到。共识客户端必须在on_block_access_list_sidecar验证完 sidecar 后立即调用此方法不得等待载荷信封。对一个给定的blockHash共识层 MUST 先调用engine_notifyBlockAccessListV1再调用engine_newPayloadV5。3.3engine_newPayloadV5EIP-7928 为 engine API 载荷结构添加的blockAccessList字段被移除其余行为不变。执行层将载荷与先前为同一blockHash交付的 BAL 配对并按 EIP-7928 校验。对照 EIP-7928 的原始设计可以更清楚这次改动的含义EIP-7928 中ExecutionPayloadV4扩展了blockAccessList字段engine_newPayloadV5校验计算出的访问列表与提供的blockAccessList一致、不一致则返回INVALIDEIP-8146 只是把这份数据的送达路径从载荷内改成了独立方法BAL 的构造、校验与block_access_list_hash头部字段语义均原样保留。四、设计理由为什么这样设计4.1 头部承诺复用杜绝 Builder 双花equivocateEIP-7928 已经在执行区块头里用keccak256(rlp(BAL))承诺了 BAL。在 bid 中携带这相同的 32 字节共识层就有了认证 sidecar 的全部依据——无需第二套哈希方案也无需在执行层之外做 RLP 解码。Builder 无法双花bid.block_hash传递性地承诺了区块头因此若bid.block_access_list_hash与揭示的载荷不一致要么payload.block_hash ! bid.block_hash导致信封被拒要么执行层重算出的 BAL 与自身头部不符、按 EIP-7928 拒绝载荷。此外把 BAL 移出ExecutionPayload容器也消除了 EIP-7928 为修剪后的 BAL 描述的 hash tree root 替换SSZ 哈希树根替换需求——sidecar 保留策略不再影响载荷的 Merkle 承诺。4.2 不给 sidecar 单独签名Builder 已经签署了 bid验证即keccak256(sidecar.block_access_list) bid.block_access_list_hash。再给 sidecar 加 BLS 签名只会增加验证成本、不增加任何约束力。这与 blob 数据用 bid 中携带的承诺来验证的方式如出一辙。4.3 独立的 PTC 字段block_access_list_present沿用了blob_data_available的模式payload_present信号信封是否及时、blob_data_available信号 blob 可用性、block_access_list_present信号 BAL 可用性——缺失的对象可以归因到具体环节。该投票仅是信号本 EIP 不附加惩罚可用性在本地强制执行因为on_execution_payload_envelope无论 PTC 结果如何都以 BAL 存在为前提。4.4 发布时机为什么 BAL 可以提前、载荷不能在 ePBS 中Builder 会把执行载荷信封推迟到接近证明截止时间才发布——过早揭示会让交易暴露给同 slot unbundling恶意提议者插入对抗性 bundle 来剥削 Builder。但 BAL不暴露这个风险它只携带访问列表和 post-state 值不携带已签名交易。提前收到 BAL 的提议者虽然能知道哪些合约被触碰、哪些余额变动但要窃取 MEV 就必须冒被罚没slash的风险。BLOCK_ACCESS_LIST_LEAD_TIME覆盖了发布较晚的 Builder若 BAL 与载荷同时或更晚到达就会失去可用性投票这也保证了至少 1 秒的预取与 post-state root 计算窗口——没有它这个提前量就完全取决于 Builder 的行为。4.5 专用 Engine API 方法创造时间差分两次调用交付 BAL 与载荷正是提前量的来源t0 sidecar received --engine_notifyBlockAccessListV1-- EL prefetches state, MAY start post-state root t1 envelope received --engine_newPayloadV5------------- EL executes against warm state t2 --------- {status: VALID}--------t1 - t0是本 EIP 创造的头启动窗口若 BAL 仍在信封内这一窗口为零。排序是共识层的责任先到的信封会被缓存直到 sidecar 到达因此执行层绝不会在没有 BAL 的情况下收到载荷也就不需要回退路径。4.6 Sidecar 保留期对齐弱主观性EIP-7928 要求执行层在弱主观性周期 3533 epochs内保留 BAL以便离线短于该周期的节点可通过重执行同步。MIN_EPOCHS_FOR_BLOCK_ACCESS_LIST_SIDECARS_REQUESTS使用相同窗口于是弱主观性周期内同步的节点可以从任一层取得 BAL超出该窗口后BAL 可通过执行区块重新生成。五、向后兼容性本提案修改了 EIP-7732 的ExecutionPayload、ExecutionPayloadBid、PayloadAttestationData容器以及 EIP-7928 的 engine API 方法。这些变更不向后兼容需要一次硬分叉。分叉前区块的 BAL 仍保留在执行载荷内部、按 EIP-7928 的方式可取回sidecar topic 与两类 Req/Resp 方法自分叉起生效。六、安全考量6.1 扣留WithholdingBuilder 可以扣留 BAL sidecar。此时 PTC 成员投票block_access_list_present False让失败全网可见由于on_execution_payload_envelope以本地 BAL 可用为前提任何节点都无法验证该载荷无法验证的载荷不会被继续构建——扣留的代价是 Builder 丢掉自己的区块。EIP-7732 的支付属性不变。6.2 早期 BAL 暴露提前的 sidecar 会在交易之前暴露状态变化。但没有独立签名的对象被暴露因此没有任何东西可以被 unbundling。泄露的是粗粒度的 slot 内活动而且这些信息也会在数秒后随载荷泄露给观察同一变化的各方。6.3 网络开销每 slot 的字节总数不变——原本在信封内传输的 BAL 现在走自己的 topic。负载只是从载荷揭示与证明截止时间之间的集中窗口重新分配到了不同 topic、摊开到更宽的时间窗。6.4 验证成本sidecar 验证只需一次对至多MAX_BLOCK_ACCESS_LIST_SIZE8 MiB字节的keccak256相对载荷验证可忽略。共识客户端因此新增一个 keccak 依赖可由久经考验的现成库满足。6.5 无匹配的 BAL执行层可能持有永远不会到达的载荷所对应的 BAL。实现 SHOULD 限制该缓存例如按 slot 距离或在 finalization 时清理。流入量受 gossip 规则约束为每个有效信标区块根最多一个 sidecar。七、小结EIP-8146 是 ePBSEIP-7732与区块级访问列表EIP-7928组合下的一次传输层重构它不改变 BAL 的构造、语义与校验规则而是改变其送达路径与时机。通过将约 72 KiB 的 BAL 移出执行载荷信封、以keccak256(rlp(BAL))承诺在 bid 中完成认证、经独立 gossip topic 与两类 Req/Resp 协议传播、并由engine_notifyBlockAccessListV1提前注入执行层该提案为执行客户端打开了状态预取与 post-state root 预计算的确定性窗口同时也为 EIP-7805 的包含列表构建者提供了不执行交易即可推进状态视图的能力。完整规范以 EIPS/eip-8146.md 为准相关实证数据可参考 assets/eip-7928/bal_size_analysis_60m.md 与 assets/eip-7928/bal_size_analysis.md。【免费下载链接】EIPsThe Ethereum Improvement Proposal repository项目地址: https://gitcode.com/GitHub_Trending/ei/EIPs创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。