ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Foundry `cast run` 区块重放复用单一 EVM 实例:执行器优化源码解析

Foundry `cast run` 区块重放复用单一 EVM 实例:执行器优化源码解析 Foundrycast run区块重放复用单一 EVM 实例执行器优化源码解析【免费下载链接】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/cast-run-single-evm-replay.md原文为一句发布说明Reused one EVM instance while replaying a block incast run展开逐层解读cast run的区块重放机制并结合cast、foundry-evm、foundry-evm-core三个 crate 的源码还原复用单一 EVM 实例这一优化在transact_with_ordinary_block_replay中的具体实现。读完本文你将理解cast run从 CLI 参数解析到单实例区块重放的完整调用链掌握--quick、--prestate-tracer、--debug-trace-transaction等标志对重放路径的影响并能通过日志与测试手段验证本次优化行为。一、变更片段解读一次 patch 级的执行器优化该 changelog 片段是 Foundry 发布说明体系的最小单元全文如下--- cast: patch foundry-evm: patch foundry-evm-core: patch --- Reused one EVM instance while replaying a block in cast run.frontmatter 语义每个条目将若干 workspace 包名映射到patch/minor/major版本增量正文为发布说明release note。这里cast、foundry-evm、foundry-evm-core三个包都标记为patch说明这是一次向后兼容的行为/性能修正不涉及 API 破坏。涉及的 crate从仓库目录结构看cast对应 crates/castfoundry-evm对应 crates/evm/evmfoundry-evm-core对应 crates/evm/core。变更同时触及 CLI 命令层与 EVM 执行器层说明优化既改变了cast run的重放编排逻辑也落实在底层执行器 API 上。片段制度按 .changelog/README.md 的说明每个 PR 必须新增或更新一个.changelog/*.md条目除非维护者打了L-ignore标签PR 校验会拒绝格式错误的 frontmatter、未知包名、非法增量值、空映射与空正文。因此这条片段本身就是一次真实合入变更的官方摘要其宣称的行为可以直接到源码中验证。二、cast run本地重放链上交易的调试命令cast run用于将链上某笔已确认交易在本地 EVM 中重新执行以便获取 opcode 级 trace、打开调试器或输出 gas 明细。其核心语义在 crates/cast/src/cmd/run.rs 的注释中写得很清楚This replays the entire block the transaction was mined in unlessquickis set to true. Note: This executes the transaction(s) as is: Cheatcodes are disabled.即默认情况下cast run会把目标交易所在区块的整块交易按顺序重放一遍再执行目标交易Cheatcode 在此路径上被禁用。这样能最大程度还原目标交易执行时的链上状态上下文。基本用法cast run TX_HASH --rpc-url RPC_URLRunArgs结构体crates/cast/src/cmd/run.rs定义了全部参数下表整理了与区块重放直接相关的选项参数说明对重放路径的影响--quick仅用前一区块的状态执行目标交易跳过区块内前缀交易的重放结果可能与链上真实执行不同--debug-d在调试器中打开该交易需要完整重放以获得逐指令状态--trace-printer-t打印 opcode 级 trace重放结束后输出指令流--replay-system-txes别名--sys重放系统交易默认遇到系统交易直接报错开启后才纳入重放--prestate-tracer用debug_traceTransaction获取 prestate 替代重放区块成功则跳过块重放失败静默回退到块重放--debug-trace-transaction直接拉取远端 callTracer 结果渲染完全跳过本地重放远程模式--labels-l已废弃地址标签仅影响 trace 解码显示--evm-version覆盖配置中的 EVM 版本影响本地执行所采用的硬分叉规则--with-local-artifacts别名--la用当前项目 artifacts 解码 trace需要将合约字节码与本地编译产物比对--disable-block-gas-limit关闭区块 gas 上限检查已确认交易必然通过链上检查故总是隐含--enable-tx-gas-limit开启 OsakaEIP-7825交易 gas 上限检查仅当目标硬分叉启用该规则时才有意义三、核心变更区块重放期间复用单一 EVM 实例变更片段的正文只有一句话但其技术含量集中在Reused one EVM instance复用单一 EVM 实例上。结合 crates/evm/evm/src/executors/mod.rs 中transact_with_ordinary_block_replay的实现可以完整还原本次优化/// Replays ordinary transactions and executes the target against one EVM instance. #[instrument(name transact_block_replay, level debug, skip_all)] pub fn transact_with_ordinary_block_replay( mut self, mut evm_env: EvmEnvForFEN, target_tx_env: TxEnvForFEN, replay: Vec(B256, TxEnvForFEN), ) - eyre::ResultRawCallResultFEN { ... }函数签名与文档注释直接呼应变更文案重放普通交易并在同一个 EVM 实例上执行目标交易。其内部流程分四个阶段一次性构造 EVM 实例mod.rs#L994-L996let mut evm FEN::EvmFactory::default() .create_foundry_evm_with_inspector(backend, evm_env, mut stack); *evm.chain_mut() target_chain_context;无论区块中有多少笔前缀交易只通过EvmFactory创建一次 EVM并将链上下文ChainForFEN设为目标交易的上下文。重放阶段关闭 inspectormod.rs#L997evm.disable_inspector()。前缀交易只是为构造目标交易执行前的状态不需要为其采集 trace关闭 inspector 可显著降低重放开销。逐笔重放并提交状态mod.rs#L998-L1012for (tx_hash, tx_env) in replay { let created match tx_env.kind() { ... }; let result evm.transact(tx_env) ...?; if result.result.is_success() let Some(address) created { evm.db_mut().add_persistent_account(address); } evm.db_mut().commit(result.state); }每笔前缀交易在同一个 EVM 上执行执行完立即把状态变更commit进同一个数据库后端若创建合约成功还将新地址标记为持久账户保证后续交易能正确引用。这正是单一实例复用的核心——状态在同一backend上连续累积无需为每笔交易重建执行环境。恢复 inspector 并执行目标交易mod.rs#L1014-L1016evm.enable_inspector()后调用evm.transact(target_tx_env)此时目标交易看到的状态即前 N 笔交易重放后的最新状态同时 trace 采集恢复正常。最后经convert_executed_result转换执行结果并调用self.commit(mut result)将变更集持久化mod.rs#L1022-L1040。从实现看本次优化的收益可以归纳为三点状态连续累积前缀交易与目标交易共享同一数据库视图天然还原链上顺序语义、避免重复构造 EVM一次EvmFactory创建替代了逐笔重建环境减少硬分叉/链上下文等环境初始化开销、重放阶段零 trace 开销inspector 关闭后再为目标交易单独开启。foundry-evm-core一并标记为 patch 也与此一致——EVM 与后端backend的基础能力位于 crates/evm/core复用逻辑依赖其数据库与快照语义。四、完整调用链从 CLI 参数到单实例重放cast run的执行入口在 crates/cast/src/cmd/run.rs 的RunArgs::run其典型链路如下RunArgs::run └─ load_cast_config_and_evm_opts合并 CLI/RPC 配置设置 fork URL └─ run_with_evm::FEN按网络选择 EthEvmNetwork / OpEvmNetwork / TempoEvmNetwork / MonadEvmNetwork ├─ fetch_target按 tx hash 拉取交易构造 RetryProvider ├─ prepare基于区块头构造 EVM 环境、执行器与 trace 上下文 ├─ execute_ordinary │ ├─ target_index定位目标交易在区块中的下标 │ ├─ for_each_prefix_transaction遍历目标之前的所有交易 │ └─ executor.transact_with_ordinary_block_replay单一 EVM 实例重放 └─ finishfetch 合约字节码 → handle_traces 渲染 trace / 打开调试器几个值得注意的细节系统交易防护run_with_evm中若目标是系统交易is_system_transaction判断发送方或交易类型run.rs#L731-L733且未传--replay-system-txes会直接报错Replaying system transactions is currently not supportedrun.rs#L262-L267。重放范围的裁剪for_each_prefix_transactionrun.rs#L594-L618在--quick或 prestate 已应用时直接返回不执行任何前缀交易否则打印Executing previous transactions from the block.状态行并用进度条init_progress单位为tx逐笔推进。可观测性执行器方法带#[instrument(name transact_block_replay, level debug)]使用RUST_LOGdebug运行cast run即可在日志中看到该 span前缀交易逐笔以trace!记录 hash 与目标地址重放完成后输出completed block replayrun.rs#L659。伪造签名场景若交易签名者与from不一致如被改造的 envelopeprepare_target会设置disable_balance_check true避免余额检查误伤run.rs#L572-L580。五、相关执行模式与边界行为除了默认的重放整块路径cast run还提供两条可绕过本地重放的加速通道理解它们有助于把握单一 EVM 复用的适用边界--prestate-tracerprestate 快照路径优先调用debug_traceTransaction的 prestate 模式直接取回目标交易执行前的状态从而跳过整块重放run.rs#L91-L97。源码注释明确说明该方法显著快于重放区块内所有先前交易但要求节点开放debug_命名空间大多数公共 RPC 不开放调用或解析失败时静默回退到块重放run.rs#L511-L535。--debug-trace-transaction远程 trace 路径完全不构造本地执行器通过debug_traceTransactioncallTracer从节点拉取调用树并渲染remote_tracerun.rs#L299。它反映链上真实执行结果包括本地重放无法复现的链特定 EVM 行为但只提供调用树视图--debug、--trace-printer、--quick、--prestate-tracer、--evm-version等本地执行标志均与其冲突run.rs#L109-L114。Monad 变体execute_monadrun.rs#L680-L728走transact_with_monad_block_replaycrates/evm/evm/src/executors/mod.rs#L186每笔交易额外携带ChainForMonadEvmNetwork上下文若目标为系统交易则报错退出并记录是否执行了 canonical 系统交易。此外还有两个与重放正确性相关的细节当存在前缀重放时evm_env.cfg_env.disable_balance_check true会被设置mod.rs#L991-L993避免中间交易因余额瞬时不足而中断整块重放parent_beacon_block_root_for_networkrun.rs#L806-L819对运行 Cancun 及以上 EVM 但没有以太坊 beacon 链的网络如 Polygon、Scroll不设置 EIP-4788 相关的 beacon root否则这类链的区块将无法重放。六、如何验证与复现本次变更本地复现启动anvil部署合约并发送一笔交易然后执行cast run TX_HASH --rpc-url http://127.0.0.1:8545默认会看到 Executing previous transactions from the block. 状态行与逐笔进度追加--quick则直接跳过重放执行目标交易。观察执行器 span以RUST_LOGdebug运行检索transact_block_replay日志 span 及其子 spanFailed to execute transaction: hash in block n等错误上下文可以确认前缀交易与目标交易是在同一次 span同一 EVM 实例内完成的。源码与测试佐证命令编排逻辑位于 crates/cast/src/cmd/run.rs执行器复用实现位于 crates/evm/evm/src/executors/mod.rscast 的 CLI 集成测试集中在 crates/cast/tests/cli。如需在本地跑相关测试可对castcrate 执行cargo test -p cast查看 CLI 测试套件覆盖的重放场景。结语一条简短的 patch 级 changelog 片段背后是一次贯穿 CLI 编排层与 EVM 执行器层的真实优化cast run在重放区块时由逐笔构造执行环境收敛为单实例连续重放状态在同一后端上顺序累积重放阶段关闭 inspector 以换取更低开销。理解transact_with_ordinary_block_replay的实现也就理解了 Foundry 本地重放调试能力的执行模型——这也是本次变更同时触及cast、foundry-evm、foundry-evm-core三个 crate 的原因所在。【免费下载链接】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),仅供参考
返回列表