Foundry Anvil eth_simulateV1 补丁解析:在模拟执行中保留 EIP-4844 Blob 交易费用上限
2026/9/15 12:23:29 网站建设 项目流程

Foundry Anvil eth_simulateV1 补丁解析:在模拟执行中保留 EIP-4844 Blob 交易费用上限

【免费下载链接】foundryFoundry is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust.项目地址: https://gitcode.com/GitHub_Trending/fo/foundry

导读

本文围绕 Foundry 仓库中 .changelog/anvil-simulate-blob-fee-cap.md 记录的anvil: patch级变更展开,深入剖析 Anvil 在eth_simulateV1模拟执行中**保留 Blob 交易费用上限(blob fee cap)**的底层实现。读完本文,你将理解eth_simulateV1的执行路径、EIP-4844 Blob 费用校验在模拟场景下的处理逻辑,以及如何构造包含maxFeePerBlobGasblobVersionedHashes的模拟请求并在本地测试中验证该行为。

一、变更背景:一次针对 eth_simulateV1 的 patch

该 changelog 条目全文如下:

--- anvil: patch --- Preserved blob transaction fee caps during `eth_simulateV1` execution.

虽然条目简短,但指向的是一个具体且影响深远的修复点:Anvil 的eth_simulateV1RPC 方法在执行模拟时,此前未能完整保留 EIP-4844 Blob 类型交易(type 3 交易)中的费用上限字段(maxFeePerBlobGas,该补丁修正了这一点,使模拟环境中的 Blob 交易与真实交易执行遵循一致的费用校验规则。

从源码结构看,这一修复同时涉及三个层面:

  1. 模拟请求的字段保真传递(preserve_simulation_request_fields);
  2. 后端 EVM 环境构建时的 blob 费用校验(BlobFeeCapTooLow);
  3. 模拟专用SimulationHandler对零价格 Blob 调用的豁免逻辑。

二、eth_simulateV1 的执行入口与字段保留

2.1 RPC 入口:simulate_v1

Anvil 的eth_simulateV1处理入口位于 crates/anvil/src/eth/api.rs 的EthApi::simulate_v1

pub async fn simulate_v1( &self, request: SimulatePayload, block_number: Option<BlockId>, ) -> Result<Vec<SimulatedBlock<AnyRpcBlock>>> { self.simulate_v1_raw(preserve_simulation_request_fields(request), block_number).await }

关键点在于:进入simulate_v1_raw之前,请求先经过preserve_simulation_request_fields预处理。simulate_v1_raw内部会校验空输入、块数量上限(MAX_SIMULATE_BLOCKS,超限返回-38026 too many blocks)、解析区块请求与区块间隔,最终在分叉(fork)模式下走fork.simulate_v1,否则调用backend.simulate_raw在阻塞任务中执行模拟(crates/anvil/src/eth/api.rs#L3285-L3359)。

2.2 字段保留:preserve_simulation_request_fields

补丁的核心机制是preserve_simulation_request_fields,实现在 crates/anvil/src/eth/mod.rs:

pub(crate) fn preserve_simulation_request_fields( request: SimulatePayload, ) -> SimulatePayload<WithOtherFields<TransactionRequest>> { SimulatePayload { block_state_calls: request .block_state_calls .into_iter() .map(|block| SimBlock { block_overrides: block.block_overrides, state_overrides: block.state_overrides, calls: block.calls.into_iter().map(WithOtherFields::new).collect(), }) .collect(), trace_transfers: request.trace_transfers, validation: request.validation, return_full_transactions: request.return_full_transactions, } }

这里使用了 alloy 的WithOtherFields<TransactionRequest>:它把每个TransactionRequest标准字段之外的所有其他字段other映射)完整保留。对 EIP-4844 交易而言,maxFeePerBlobGasblobVersionedHashesmaxPriorityFeePerGas等字段正是以这类"附加字段"形式存在。在补丁之前,这些字段在模拟请求转换过程中可能被剥离,导致模拟执行时 Blob 交易的费用上限丢失;补丁通过WithOtherFields::new包装,确保这些字段在进入 EVM 上下文前原样保留——这正是 changelog 中 "Preserved blob transaction fee caps" 的代码级体现。

三、模拟执行中的 Blob 费用校验

3.1 常规路径:BlobFeeCapTooLow 校验

在 crates/anvil/src/eth/backend/mem/mod.rs 的交易池校验逻辑中,EIP-4844 Blob 费用校验位于 EIP-1559 费用校验之后:

// EIP-4844 blob fee validation if evm_env.cfg_env.spec >= SpecId::CANCUN && tx.is_eip4844() && let Some(max_fee_per_blob_gas) = tx.max_fee_per_blob_gas() && let Some(blob_gas_and_price) = &evm_env.block_env.blob_excess_gas_and_price && max_fee_per_blob_gas < blob_gas_and_price.blob_gasprice { debug!(target: "backend", "max fee per blob gas={}, too low, block blob gas price={}", max_fee_per_blob_gas, blob_gas_and_price.blob_gasprice); return Err(InvalidTransactionError::BlobFeeCapTooLow( max_fee_per_blob_gas, blob_gas_and_price.blob_gasprice, )); }

条件链为:硬分叉不低于 Cancun(SpecId::CANCUN)+ 交易为 EIP-4844 类型 + 交易携带maxFeePerBlobGas+ 区块环境包含blob_excess_gas_and_price+费用上限低于区块 blob 气体价格→ 返回BlobFeeCapTooLow错误。在后续的资金充足性检查中,max_cost也计入blob_gas_used() * max_fee_per_blob_gas()(同文件 crates/anvil/src/eth/backend/mem/mod.rs#L9119-L9126)。

这意味着:eth_simulateV1的调用保留maxFeePerBlobGas后,模拟会像真实交易一样对过低的 blob 费用上限报错,从而真实反映主网/分叉环境的费用约束,而不是静默忽略。

3.2 模拟专用路径:SimulationHandler 的豁免

与普通eth_calltransact不同,零价格的 Blob 调用会走模拟专用的SimulationHandler(crates/anvil/src/eth/backend/mem/mod.rs#L544-L578):

fn validate_env(&self, evm: &mut Self::Evm) -> Result<(), Self::Error> { let skip_blob_fee_check = evm.ctx_ref().cfg().is_base_fee_check_disabled() && evm.ctx_ref().tx().tx_type == 3 && evm.ctx_ref().tx().max_fee_per_blob_gas == 0; if !skip_blob_fee_check { return validation::validate_env(evm.ctx()); } let block = evm.ctx_ref().block().clone(); let mut validation_block = block.clone(); if let Some(blob_gas_and_price) = &mut validation_block.blob_excess_gas_and_price { blob_gas_and_price.blob_gasprice = 0; } evm.ctx().set_block(validation_block); let result = validation::validate_env(evm.ctx()); evm.ctx().set_block(block); result }

逻辑要点:

  • base fee 校验被禁用交易类型为 3(Blob)max_fee_per_blob_gas == 0时,跳过标准 blob 费用校验;
  • 执行校验前将区块环境的blob_gasprice临时置 0,校验完再恢复,实现"零价格 blob 调用"也能通过环境校验。

这条豁免路径与"保留费用上限"的补丁形成互补:有上限的 blob 模拟按真实规则校验,显式传0x0的零价格 blob 调用则被宽容处理,便于开发者在不关心 gas 价格时做纯逻辑模拟。

四、测试验证:零价格 Blob 调用走模拟处理器

仓库的集成测试直接印证了上述行为。crates/anvil/tests/it/simulate.rs 中test_simulate_v1_preserves_precompile_warming_rpc(第 347-396 行)注释明确写道:

// A zero-priced blob call uses the simulation handler; an ordinary call uses transact.

测试构造了两类调用逐一验证:

for call in [ json!({"to": helper}), json!({ "to": helper, "blobVersionedHashes": [format!("0x01{}", "00".repeat(31))], "maxFeePerBlobGas": "0x0" }), ] { let response = rpc_request( &handle.http_endpoint(), "eth_simulateV1", json!([{"blockStateCalls": [{ "stateOverrides": { source: {"movePrecompileToAddress": destination}, helper: {"code": helper_code} }, "calls": [call] }]}, "latest"]), ) .await; assert!(response.get("error").is_none(), "{response}"); // 断言 access_costs 一致 … }

该用例同时验证:

  1. blobVersionedHashes+maxFeePerBlobGas: "0x0"的 blob 调用在eth_simulateV1不会报错error为 None);
  2. blob 调用与普通调用的模拟结果一致(访问成本数组[107, 2607, 107]相同)。

结合 crates/anvil/tests/it/eip4844.rs、crates/anvil/tests/it/simulate.rs#L1028-L1085(test_simulate_scopes_block_overrides_and_derives_base_fee_rpc等)可以看到 Anvil 对模拟场景的区块覆盖、base fee 推导与 EIP-4844 参数的组合测试体系。

五、实战:如何在本地复现与使用

5.1 启动 Anvil 并准备分叉环境

Blob 费用校验要求硬分叉不低于 Cancun。可以通过--hardfork参数显式指定:

# 本地开发链,显式启用 Cancun 硬分叉 anvil --hardfork cancun # 或分叉主网(自动继承主网 hardfork 与 blob 基础费用状态) anvil --fork-url https://eth-mainnet.example.com/rpc --fork-block-number 20000000

5.2 构造 eth_simulateV1 请求

调用eth_simulateV1模拟一个携带 blob 的调用,务必保留maxFeePerBlobGasblobVersionedHashes

{ "method": "eth_simulateV1", "params": [ { "blockStateCalls": [ { "blockOverrides": { "blobBaseFee": "0x15" }, "calls": [ { "to": "0x0000000000000000000000000000000000000000", "blobVersionedHashes": [ "0x0100000000000000000000000000000000000000000000000000000000000000" ], "maxFeePerBlobGas": "0x2540be400" } ] } ], "traceTransfers": false, "validation": true, "returnFullTransactions": true }, "latest" ], "id": 1, "jsonrpc": "2.0" }

要点说明:

  • blockOverrides.blobBaseFee覆盖模拟区块的 blob 基础费用(参考 crates/anvil/tests/it/simulate.rs#L1028-L1085 中"blobBaseFee": "0x15"的用法);
  • maxFeePerBlobGas低于区块 blob 气体价格,模拟将返回BlobFeeCapTooLow类错误——这正是补丁后费用上限被保留的直接体现;
  • 若显式传maxFeePerBlobGas: "0x0",则走SimulationHandler的豁免路径,零价格 blob 调用可通过校验(见test_simulate_v1_preserves_precompile_warming_rpc)。

5.3 通过集成测试回归验证

仓库已内置相关测试,可在本地运行验证该行为:

cargo test -p anvil --test it simulate

其中 crates/anvil/tests/it/simulate.rs 覆盖eth_simulateV1的字段保留、区块覆盖、blob 调用等场景,是理解本补丁行为最直接的参考用例。

六、小结

.changelog/anvil-simulate-blob-fee-cap.md虽然只有一行描述,但它对应的是一个贯穿 Anvil RPC 层(preserve_simulation_request_fields字段保真)、后端 EVM 层(BlobFeeCapTooLow校验与资金检查)与模拟处理器层(SimulationHandler零价格豁免)的完整修复:

层级文件职责
RPC 入口crates/anvil/src/eth/api.rssimulate_v1转发前调用字段保留函数
字段保留crates/anvil/src/eth/mod.rsWithOtherFields::new保留maxFeePerBlobGas等附加字段
费用校验crates/anvil/src/eth/backend/mem/mod.rs常规 blob 费用校验、资金上限计算
模拟豁免crates/anvil/src/eth/backend/mem/mod.rs#L544-L578零价格 blob 调用的validate_env豁免
测试佐证crates/anvil/tests/it/simulate.rs零价格 blob 调用走模拟处理器的集成测试

从源码结构可以推断,该补丁的动机是让eth_simulateV1的模拟结果与真实 blob 交易执行保持一致的费用语义:既能用maxFeePerBlobGas触发真实的费用校验,也能以显式零价格快速进行纯逻辑模拟。对于依赖 Anvil 做 EIP-4844 相关合约开发、分叉模拟与费用分析的开发者,理解这一行为有助于构造更贴近主网语义的模拟请求。

【免费下载链接】foundryFoundry is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust.项目地址: https://gitcode.com/GitHub_Trending/fo/foundry

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询