EIP-8282 Builder Execution Requests 详解:EIP-7732 构建者的押金与退出请求预部署合约
【免费下载链接】EIPsThe Ethereum Improvement Proposal repository项目地址: https://gitcode.com/GitHub_Trending/ei/EIPs
导读
本文基于 EIP-8282(Builder Execution Requests,状态为 Review)展开,系统讲解如何为 EIP-7732(Enshrined Proposer-Builder Separation)引入的独立质押实体——builder(构建者)——提供两条专用生命周期通道:构建者押金预部署合约与构建者退出预部署合约。二者作为 EIP-7685 请求总线上的两个新请求类型(0x03/0x04)运行,遵循与 EIP-7002(执行层触发退出)和 EIP-7251(合并/加注)完全一致的请求队列、系统调用与动态费用模式。读完本文,你将掌握:两个预部署合约的地址与部署方式、写路径/费用读取/系统调用三条代码路径的调用规则、184 字节押金请求与 48 字节退出请求的字节级布局、动态请求费用的fake_exponential计算原理,以及共识层如何通过DOMAIN_BUILDER_DEPOSIT签名域与execution_address授权完成对构建者生命周期状态转换的收口。
背景:从"借用验证者流程"到"专用请求类型"
EIP-7732 在协议内引入了builder这一独立角色:它是质押实体,但其质押不参与信标链主动验证;它不经过常规的押金与退出 churn 队列,最低只需 1 ETH 即可质押。在该 EIP 的初始设计中,builder 的生命周期复用了验证者流程:
- 注册:通过验证者押金完成,押金取款凭据携带
0xB0前缀(BUILDER_WITHDRAWAL_PREFIX); - 退出:通过 voluntary-exit 操作中的 builder 分支完成。
EIP-8282 的核心动机是终结这种复用。专用请求类型带来的直接收益包括:
- 角色显式化:仅凭请求类型即可区分 actor,共识层不再需要检查凭据前缀来路由押金,验证者注册表与 builder 注册表各自独立键控;
- 即时验证 proof-of-possession:验证者押金请求的签名验证被推迟到受 churn 限制的
pending_deposits队列中,且继承验证者押金请求极高的单块上限;builder 押金请求则在处理时内联验证签名,并将每块验证工作量封顶在MAX_DEPOSIT_REQUESTS_PER_BLOCK; - 更安全的退出授权:EIP-7732 下 builder 只能由其 BLS 热密钥(持续签署出价的那个密钥)签名退出;而退出合约改为由 builder 的
execution_address授权,正如 EIP-7002 对验证者所做的那样; - 分叉兼容:分叉时必须存在的 builder 不受影响——EIP-7732 分叉过渡期对 builder 凭据 pending deposits 的上线逻辑被保留,只有分叉后的新注册流程迁移到新合约。
常量与预部署地址
EIP-8282 定义的核心常量为:
| 名称 | 值 | 说明 |
|---|---|---|
BUILDER_DEPOSIT_CONTRACT_ADDRESS | 0x0000bFF46984e3725691FA540a8C7589300D8282 | 构建者押金合约的预部署地址 |
BUILDER_EXIT_CONTRACT_ADDRESS | 0x000064D678505ad48F8cCb093BC65613800E8282 | 构建者退出合约的预部署地址 |
BUILDER_DEPOSIT_REQUEST_TYPE | 0x03 | EIP-7685 请求类型字节(构建者押金) |
BUILDER_EXIT_REQUEST_TYPE | 0x04 | EIP-7685 请求类型字节(构建者退出) |
SYSTEM_ADDRESS | 0xfffffffffffffffffffffffffffffffffffffffe | 在块结束时发起系统调用的地址(同 EIP-7002) |
MAX_DEPOSIT_REQUESTS_PER_BLOCK | 64 | 构建者押金合约每块最多排空的记录数 |
TARGET_DEPOSIT_REQUESTS_PER_BLOCK | 8 | 超过该每块请求数后押金合约费用上升 |
MAX_EXIT_REQUESTS_PER_BLOCK | 16 | 构建者退出合约每块最多排空的记录数 |
TARGET_EXIT_REQUESTS_PER_BLOCK | 2 | 超过该每块请求数后退出合约费用上升 |
MIN_REQUEST_FEE | 1 | 最小请求费用(wei) |
REQUEST_FEE_UPDATE_FRACTION | 17 | 控制费用的变化速率 |
BUILDER_MIN_DEPOSIT | 1000000000000000000 | 一笔押金的最小计值质押(wei,即 1 ETH——EIP-7732 的 builder 最小值) |
BUILDER_DEPOSIT_CONTRACT_RUNTIME_CODE | 见 参考实现 | 构建者押金合约的运行时代码 |
BUILDER_EXIT_CONTRACT_RUNTIME_CODE | 见 参考实现 | 构建者退出合约的运行时代码 |
最终请求类型值必须在所有活跃的 EIP-7685 请求类型中唯一。类型分配在 consensus-specs 中协调——现有类型(如 EIP-7002 的0x01、EIP-7251 的0x02、EIP-6110 的押金类型)已在electra/beacon-chain.md中定义。
部署:CREATE2 工厂与地址挖掘
两个合约都由CREATE2工厂(EIP-7997)部署。EIP-7997 将位于0x4e59b44847b379578588920cA78FbF26c0B4956C的著名无密钥CREATE2工厂作为正式要求:它调用 EIP-1014 的CREATE2指令,salt 取调用输入的前 32 字节,init code 取剩余数据,从而保证跨链确定性部署。每个合约地址由三要素决定:工厂地址、salt 与合约 init code。EIP-8282 中的 salt 是经过挖掘的,使得上述两个地址恰好由当前参考字节码得出。
部署要求非常严格:
- 合约必须在激活本 EIP 的分叉之前部署;
- 一旦 EIP 激活而任一地址上没有代码,则从激活时刻起的每一个区块都必须被判为无效。
这与 EIP-7002 的"空代码失败"安全考虑一脉相承:激活时预部署地址无代码,会导致分叉后首个及后续所有区块全部失效。
请求队列与系统调用:无 ABI 的调度设计
两个预部署合约沿用 EIP-7002 / EIP-7251 的合约设计(仅有细微调整),并复用其存储布局(excess 槽、count 槽、队列 head/tail 指针、队列存储偏移)。合约没有 Solidity 兼容的 ABI,仅凭caller与calldatasize进行调度。不匹配以下任何分支的调用必须 revert。
写路径(Write path)
调用者不是SYSTEM_ADDRESS,且 calldata 长度恰好等于合约的输入尺寸时,提交一个请求。合约必须:
- 校验请求与随附价值(见下文);
- 向队列追加一条记录;
- 递增本块计数;
- 将接受的记录作为匿名日志发出。
费用读取(Fee getter)
调用者不是SYSTEM_ADDRESS,且 calldata 为空时,返回当前费用且不修改状态。若附带了任何 value,合约必须 revert(读操作不允许带值)。
系统调用(System call)
每个区块结束时,合约被SYSTEM_ADDRESS调用:
- 带 calldata:通过 inhibitor 永久禁用队列(这是为未来"退役/挂起合约"预留的机制;一旦协议停止逐块系统调用,若不设此开关,合约会继续接受请求与费用进入一个永不排空的队列);
- 不带 calldata:按最旧优先顺序排空最多
MAX_DEPOSIT_REQUESTS_PER_BLOCK(押金)或MAX_EXIT_REQUESTS_PER_BLOCK(退出)条记录,以串联形式返回作为request_data,并重置本块计数;超出上限的记录留在队列中等待后续区块。
执行层在request_data前拼接合约的请求类型字节,形成request_type ++ request_data加入区块请求列表,通过requests_hash提交(见 EIP-7685 中compute_requests_hash的 sha256 中间哈希列表算法,区块请求按类型字节升序排列)。系统调用遵循 EIP-7002 的规则:专用 gas 上限30_000_000,不占用区块 gas 上限,不遵循 EIP-1559 的费用销毁语义。任一合约的系统调用失败,区块必须无效。
请求费用:EIP-1559 风格的指数动态定价
每个请求都携带费用,按 EIP-7002 的方式计算:
fee = fake_exponential(MIN_REQUEST_FEE, excess, REQUEST_FEE_UPDATE_FRACTION)其中fake_exponential是对MIN_REQUEST_FEE * e**(excess / REQUEST_FEE_UPDATE_FRACTION)的 EIP-1559 风格整数近似(EIP-7002 给出了完整的伪代码:累加器从factor * denominator出发,逐项除以denominator * i直至累加器归零)。费用在区块包含的请求数超过目标值时超线性上升,否则回落到MIN_REQUEST_FEE。费用加在任意质押价值之上,且永久锁定在合约中。
与 EIP-7002 的一个关键差异是:两个合约被修改为在每条写路径上应用费用上涨,而非像 EIP-7002 那样在块结束时更新,从而更细粒度地抑制突发请求。
押金请求:184 字节的精确布局
押金请求通过向BUILDER_DEPOSIT_CONTRACT_ADDRESS发起恰好184字节 calldata 的调用提交:
| 字节 | 字段 | 描述 |
|---|---|---|
0:48 | pubkey | 48 字节 BLS 公钥 |
48:80 | withdrawal_credentials | 32 字节承诺(version字节 +execution_address) |
80:88 | amount | 大端uint64,单位 gwei |
88:184 | signature | 96 字节 BLS proof-of-possession |
一笔押金请求同时服务 builder 的首笔押金与后续加注(top-up)。合约必须拒绝请求,除非同时满足:
amount * 1 gwei >= BUILDER_MIN_DEPOSIT(即至少 1 ETH);msg.value >= amount * 1 gwei + fee。
任何超出amount * 1 gwei + fee的价值被合约保留,不计入builder 的质押。
成功时合约将 184 字节输入原样入队。出队记录是输入的原样拷贝,仅amount转为小端(与 EIP-7002 一致)。合约不验证signature——它被携带在记录中,由共识层验证。提交方在广播首笔押金之前 SHOULD 在链下自行验证 proof-of-possession,这是避免首笔押金本金永久锁定的唯一保护(详见安全考量)。
退出请求:48 字节与 execution_address 授权
退出请求通过向BUILDER_EXIT_CONTRACT_ADDRESS发起恰好48字节 calldata 的调用提交,内容为要退出的 builder 的pubkey。合约必须要求msg.value >= fee,且不质押任何价值。成功时入队一条source_address (20) ++ pubkey (48)记录,其中source_address即msg.sender。
授权完全基于source_address,同 EIP-7002:合约记录msg.sender且不做任何进一步检查。共识层仅当source_address等于目标 builder 的execution_address时才兑现请求。这体现了"冷热分离"原则——builder 的 BLS 密钥是持续签署出价的热密钥,不应同时授权退出;退出授权交给偏冷的execution_address,与 EIP-7002 对验证者取款凭据的论证一致。
共识层处理:SSZ 容器与状态转换
共识层按请求类型将每条出队记录解码为两个 SSZ 容器之一:
class BuilderDepositRequest(Container): pubkey: Bytes48 withdrawal_credentials: Bytes32 amount: uint64 # Gwei signature: Bytes96 class BuilderExitRequest(Container): source_address: Bytes20 pubkey: Bytes48类型的request_data即其记录的定长 SSZ 序列化串联,恰好等于系统调用返回的字节且顺序一致。值得注意:BuilderDepositRequest正是 EIP-6110 的DepositRequest去掉index字段后的形态。
详细的状态转换行为由 consensus-specs 规定,EIP-8282 概括为三点:
- 新 builder 注册:针对不在 builder 注册表中的
pubkey的BuilderDepositRequest,若其signature是对(pubkey, withdrawal_credentials, amount)在DOMAIN_BUILDER_DEPOSIT(builder 专用签名域)下的有效 proof-of-possession,则注册新 builder;签名无效的记录被忽略且其质押被没收。押金立即应用,而非路由到验证者pending_deposits队列。 - 加注:针对已注册
pubkey的BuilderDepositRequest是加注,amount被计入,记录的withdrawal_credentials与signature被忽略(与验证者押金行为一致)。 - 退出:
BuilderExitRequest仅在pubkey是活跃 builder、source_address等于该 builder 的execution_address、且该 builder 无待取余额(pending balance)时发起全额退出;否则记录被丢弃(而非重新入队),请求必须重新提交。
对 EIP-7732 的修改
押金路由
process_deposit_request的 builder 分支被移除:对验证者押金合约的押金永远是普通验证者押金。Builder 只能通过BUILDER_DEPOSIT_REQUEST_TYPE创建与加注。分叉后若向验证者合约提交带0xB0凭据的押金,会产生一个无法取出余额的验证者(因为0xB0前缀不再是有效取款凭据)——因此 builder 押金必须发送到 builder 押金合约。
分叉过渡期上线
EIP-7732 在分叉时一次性对 builder 凭据 pending deposits 的上线被保留,使得 builder 从第一个 slot 就存在。这是唯一通过验证者押金合约上线上 builder 的路径。此后0xB0的BUILDER_WITHDRAWAL_PREFIX被弃用。其合理性在于:某些应用依赖 builder 在分叉首槽即存在,而分叉后对新合约的押金无法提供这一点。
退出路由
process_voluntary_exit的 builder 分支被移除,voluntary-exit 操作成为纯验证者操作。Builder 只能通过BUILDER_EXIT_REQUEST_TYPE退出。
设计权衡(Rationale)
- 两个预部署、两个请求类型:镜像取款(
0x01)与合并(0x02)的既有格局。执行层不需要任何新的读取语义,共识层按请求类型路由而非检查凭据。 - 一笔请求同时承担押金与加注:与验证者押金合约一致——proof-of-possession 在
pubkey首次出现时校验,后续押金只计入质押;加注无法重定向 builder 的取款,因为其withdrawal_credentials与signature被忽略。 - 按
execution_address退出:BLS 密钥是热的,不应同时授权退出;路由到冷的execution_address给 builder 一个单一、明确界定的退出授权者。 - 请求费用:与 EIP-7002 / EIP-7251 相同的需求响应式费用用于计量提交,配合每块上限与每笔押金的质押要求,构成完整的防滥用体系。
- 分叉时上线:保留既有 EIP-7732 上线路径以服务初始 builder 集合。
向后兼容性
本 EIP 在执行层是纯增量的:在先前为空的地址上引入新合约,不修改验证者押金合约或验证者请求预部署。共识层按 Changes to EIP-7732 修改了 EIP-7732 的 builder 生命周期;在分叉时上线的 builder 不受影响。
安全考量
退出授权
退出合约记录msg.sender为source_address且不做进一步检查。请求不携带签名,因此共识层处理阶段的source_address检查是唯一的退出授权——缺失它,任意调用者都能退出任何 builder。
签名域分离
Builder 押金在DOMAIN_BUILDER_DEPOSIT下签名,区别于验证者的DOMAIN_DEPOSIT,因此两类押金的 proof-of-possession 不能跨类重放。分叉过渡期的种子押金经由验证者押金合约提交,必然在DOMAIN_DEPOSIT下签名。
可重放的押金记录
押金字段在 calldata 中是公开的,第三方可以为已注册 builder 以自己的资金重放这些字段。这种重放只是加注(凭据与签名被忽略),计入质押但不重定向任何东西。
托管拆分退出僵局
退出要求零待取余额,而每次赢得出价都会增加待取余额,且只有execution_address能授权退出。当execution_address(资金所有者)与 BLS 密钥(出价操作者)分属不同方时,持续赢得出价的操作者可以无限期阻塞所有者的退出。委托方应在链下保留对操作者出价的控制权。
垃圾请求与状态增长
每块上限约束的是排空速率而非入队速率,因此链上队列可以跨块增长。增长由价值门槛限制——每笔押金至少锁定BUILDER_MIN_DEPOSIT加费用。攻击者提交有效 proof-of-possession 不会损失任何东西(质押仍是可取的 builder 余额),因此可以用锁定资本为代价延迟分叉后的上线。这是可容忍的,因为时间关键的初始 builder 集合通过分叉过渡期播种,而非依赖稳态合约。
锁定资金
请求费用、任何超额支付、以及共识层因无效 proof-of-possession 拒绝的首笔押金本金,都永久锁定在预部署合约中。执行层不验证 BLS 签名,因此押金请求一节建议的链下 proof-of-possession 检查是提交方防止首笔押金本金损失的唯一保护。
参考实现
参考实现位于以太坊 sys-asm 仓库(构建者押金合约与构建者退出合约的实现),EIP 正文中给出了对应源码链接。与 EIP-7002 / EIP-7251 相同,这类预部署合约以手写 EVM 汇编的形式存在,无需 Solidity 编译器的 ABI 开销,存储布局按槽位手工编排。
延伸阅读(本仓库相关文档)
- EIP-7685:通用执行层请求总线——
requests_hash承诺与请求列表编排 - EIP-7732:Enshrined Proposer-Builder Separation——builder 角色、注册表与分叉过渡期上线逻辑
- EIP-7002:执行层可触发取款——请求队列、系统调用与
fake_exponential费用的原型 - EIP-7251:提高 MAX_EFFECTIVE_BALANCE——合并请求的同类预部署模式
- EIP-6110:链上提供验证者押金——
DepositRequest容器与押金请求处理的先例 - EIP-7997:确定性工厂合约——两个预部署合约所依赖的 CREATE2 部署机制
【免费下载链接】EIPsThe Ethereum Improvement Proposal repository项目地址: https://gitcode.com/GitHub_Trending/ei/EIPs
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考