ARTICLE DETAIL

资讯详情

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

Foundry 中 Monad reserve-balance 跟踪器与 deal / cloneAccount / loadAllocs 状态同步机制解析

Foundry 中 Monad reserve-balance 跟踪器与 deal / cloneAccount / loadAllocs 状态同步机制解析 Foundry 中 Monad reserve-balance 跟踪器与 deal / cloneAccount / loadAllocs 状态同步机制解析【免费下载链接】foundryFoundry is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust.项目地址: https://gitcode.com/GitHub_Trending/fo/foundryFoundry 在 .changelog/monad-state-splice-tracker.md 这一 patch 级变更记录中修复了一个与 Monad 网络支持相关的关键一致性问题让 Monad 的 reserve-balance 跟踪与deal、cloneAccount、loadAllocs三个 cheatcode 造成的账户变更及其回滚保持同步。本文以该变更记录为骨架结合foundry-evm-core、foundry-cheatcodes、foundry-evm等 crate 的源码实现深入讲解这一同步机制的来龙去脉什么是 reserve-balance 跟踪、为什么 cheatcode 直改账户会破坏它、修复如何在源码层面落地以及快照回滚与嵌套 EVM 场景下的完整处理路径。读完本文你将能理解 Foundry 多网络 EVM 抽象中派生状态derived state与外部状态注入之间的一致性保障思路并掌握FoundryJournal、refresh_chain_journal等核心抽象的实际用法。一、变更记录原文与波及范围该变更记录全文如下Kept Monad reserve-balance tracking synchronized with account changes and rollbacks from thedeal,cloneAccount, andloadAllocscheatcodes.即保持 Monad 的 reserve-balance 跟踪与deal、cloneAccount、loadAllocs三个 cheatcode 造成的账户变更及其回滚同步。其 frontmatter 声明了四个受影响包均为patch级别包名版本变更职责forgepatchForge 主程序承载vm.deal等 cheatcode 的 CLI 入口foundry-cheatcodespatchcheatcode 的实现层直接处理deal/cloneAccount/loadAllocs调用foundry-evmpatchEVM 执行与 inspector 栈处理嵌套 EVM、隔离调用等场景foundry-evm-corepatch核心抽象层定义FoundryJournal等 trait根据 .changelog/README.md 的约定每个.changelog/*.md条目将包名映射到patch/minor/major并提供一条发布说明——本条即是一次标准的缺陷修复记录因此统一使用patch级别。二、背景Monad 网络支持与 reserve-balance 跟踪2.1 Monad 网络进入 Foundry.changelog/monad-network.md 记录了 Monad 网络支持作为minor特性进入 FoundryAnvil、Cast、Chisel、Forge 四个工具同时获得 Monad 网络支持。从源码结构看Monad 支持被组织为crates/evm/networks/src/lib.rs中注册了 Monad 网络的相关定义crates/evm/core/src/evm/monad.rs提供了MonadEvmFactory、MonadChainContext、MonadContext等 Monad 专属类型相关实现通过#[cfg(feature monad)]特性门控仅在启用该特性时编译参见 .changelog/cleanup-monad-feature-imports.md、.changelog/gate-monad-fork-context-update.md 等同系列条目。2.2 什么是 reserve-balance 跟踪Monad 的 EVM 实现monad_revm在事务执行期间维护一个reserve-balance trackermonad_revm::reserve_balance::tracker::ReserveBalanceTracker它记录与当前链上下文block 参与者相关的账户储备余额会计状态。在 Anvil 的集成测试 crates/anvil/tests/it/monad.rs 中可以看到Monad 通过一个reserve-balance 预编译合约向合约暴露储备余额的查询能力测试辅助函数reserve_balance_call()位于该文件第 2304 行附近大量用例如monad_nine_exposes_reserve_balance_precompile_for_calls、monad_eight_config_filters_reserve_balance_precompile都围绕它展开。从 crates/evm/core/src/evm/monad.rs 中MonadChainContext::refresh_journal的实现可以看出该跟踪器的本质fn refresh_journalJ: FoundryJournal(self, journal: mut J) { let mut tracker journal.capture_reserve_balance(); tracker.rebase(self, journal.evm_state()); journal.restore_reserve_balance(tracker); }即tracker 是缓存于 journal 中的派生结构它根据链上下文self即 block 参与者集合与当前 EVM 账户状态journal.evm_state()通过rebase重新计算。正常情况下事务执行走 revm 的标准记账路径tracker 与账户状态自然保持一致。三、问题本质cheatcode 直改账户绕过常规记账路径deal、cloneAccount、loadAllocs三个 cheatcode 有一个共同点它们直接修改底层数据库backend中的账户状态而不是通过 revm 的事务执行路径。这会导致缓存于 journal 中的 reserve-balance tracker 变成陈旧数据stale。3.1deal直接替换账户余额crates/cheatcodes/src/evm.rs 中dealCall::apply_stateful的实现impl Cheatcode for dealCall { fn apply_statefulFEN: FoundryEvmNetwork(self, ccx: mut CheatsCtxt_, _, FEN) - Result { let Self { account: address, newBalance: new_balance } *self; let account journaled_account(ccx.ecx, address)?; let old_balance std::mem::replace(mut account.info.balance, new_balance); let record DealRecord { address, old_balance, new_balance }; ccx.state.eth_deals.push(record); refresh_chain_journal(ccx.ecx); Ok(Default::default()) } }它直接用std::mem::replace把账户余额换成new_balance并记录DealRecord以便测试结束后恢复。deal是对账户余额最直接的外科手术式修改跟踪器若未随之更新预编译查询到的储备余额就会与实际余额脱节。3.2cloneAccount克隆整个账户crates/cheatcodes/src/evm.rs 中cloneAccountCall::apply_statefulimpl Cheatcode for cloneAccountCall { fn apply_statefulFEN: FoundryEvmNetwork(self, ccx: mut CheatsCtxt_, _, FEN) - Result { let Self { source, target } self; let account ccx.ecx.journal_mut().load_account(*source)?; let genesis genesis_account(account.data); let (db, inner) ccx.ecx.db_journal_inner_mut(); db.clone_account(genesis, target, inner)?; // Cloned account should persist in forked envs. ccx.ecx.db_mut().add_persistent_account(*target); refresh_chain_journal(ccx.ecx); Ok(Default::default()) } }它把源账户的余额、nonce、存储等完整克隆到目标地址同样是在 backend 层面新增账户数据。3.3loadAllocs批量装载账户crates/cheatcodes/src/evm.rs 中loadAllocsCall::apply_stateful读取 allocs或 genesis文件后一次性db.load_allocs(allocs, inner)装载大量账户。3.4 统一的修复落点refresh_chain_journal注意这三个实现都以refresh_chain_journal(ccx.ecx)收尾。这正是本次修复的核心落点在每一次直改账户的 cheatcode 完成后立即基于新的链上下文与 EVM 状态重新计算rebasereserve-balance tracker使其与账户变更保持同步。refresh_chain_journal定义在 crates/evm/core/src/env.rspub fn refresh_chain_journalCTX: FoundryContextExt(context: mut CTX) { let chain context.chain().clone(); chain.refresh_journal(context.journal_mut()); }它克隆当前链上下文再调用FoundryChain::refresh_journal。对普通以太坊网络而言impl FoundryChainTx for ()是空操作对 Monad 网络则触发上面看到的 trackerrebase。这样设计让通用 cheatcode 代码只需调用一个统一的刷新函数具体语义由各网络族EVM family自行决定。四、FoundryJournaltraitMonad 专有逻辑的隔离与本次修复配套的 .changelog/foundry-journal-reserve-balance.md 记录了同系列重构将FoundryEvmFactory/NestedEvm上零散的交易状态捕获/恢复方法替换为通过 revm 原生ContextTr::journal/journal_mut直接访问并新增FoundryJournaltrait。其要点是Monad 的 reserve-balance tracker 方法在 trait 上是具体实现且以#[cfg(feature monad)]门控从而把最后一批 Monad 专属方法与关联类型从通用 trait 中移除。crates/evm/core/src/env.rs 中FoundryJournal的定义清晰地体现了这一点/// Foundry extension for Journal type pub trait FoundryJournal: JournalExt { /// Mutable access to the database and journal inner. fn db_journal_inner_mut(mut self) - (mut Self::Database, mut JournaledState); /// Reference to the journal inner. fn journal_inner(self) - JournaledState; /// Captures Monads reserve-balance tracker for the active transaction. #[cfg(feature monad)] fn capture_reserve_balance( self, ) - monad_revm::reserve_balance::tracker::ReserveBalanceTracker { monad_revm::reserve_balance::tracker::ReserveBalanceTracker::default() } /// Restores Monads reserve-balance tracker for the active transaction. #[cfg(feature monad)] fn restore_reserve_balance( mut self, _tracker: monad_revm::reserve_balance::tracker::ReserveBalanceTracker, ) { } /// Whether transaction boundaries currently preserve the reserve-balance tracker... #[cfg(feature monad)] fn preserves_reserve_balance(self) - bool { false } /// Sets whether transaction boundaries preserve the reserve-balance tracker. #[cfg(feature monad)] fn set_preserve_reserve_balance(mut self, _preserve: bool) {} }其设计要点trait 提供默认实现capture_reserve_balance默认返回ReserveBalanceTracker::default()restore_reserve_balance默认空操作。因此普通以太坊 journalimpl FoundryJournal for JournalDB无需任何 Monad 逻辑即可编译。Monad journal 覆盖实现#[cfg(feature monad)] impl FoundryJournal for monad_revm::MonadJournalDB中捕获通过monad_revm::MonadJournalTr::reserve_balance(self).clone()克隆 tracker恢复通过reserve_balance_mut(self)写回preserves_reserve_balance则透传 revm 原生判断。通用代码无感知FoundryContextExtcrates/evm/core/src/env.rs只依赖Journal: FoundryJournal这一约束即可获得db_journal_inner_mut()、journal_inner()、set_journal_inner()等能力cheatcode 层完全不需要关心当前是否运行在 Monad 上。五、快照与回滚路径上的同步变更记录强调and rollbacks——不仅账户变更时要同步vm.snapshot/vm.revertTo回滚时也要让 tracker 回到一致状态。这一部分在 crates/cheatcodes/src/evm.rs 的快照处理函数中体现。5.1 快照连带保存 trackerinner_snapshot_statecrates/cheatcodes/src/evm.rs在保存journaled_state、环境覆盖env_overrides与 fork 块号覆盖的同时Monad 门控地额外保存一份(chain 上下文, reserve-balance tracker)组合#[cfg(feature monad)] { use foundry_evm_core::FoundryJournal as _; ccx.state .context_snapshots .insert(id, (ccx.ecx.chain().clone(), ccx.ecx.journal().capture_reserve_balance())); }这里capture_reserve_balance克隆出 tracker 的当前副本与链上下文一起存入context_snapshots快照 id 与后端快照保持一致。5.2 回滚先还原 tracker 再刷新inner_revert_to_statecrates/cheatcodes/src/evm.rs和inner_revert_to_state_and_delete同文件第 1725 行起在调用后端revert_state恢复journaled_state之后if let Some((context, state)) ccx.state.context_snapshots.get(snapshot_id) { *ccx.ecx.chain_mut() context.clone(); ccx.ecx.journal_mut().restore_reserve_balance(state.clone()); } ... refresh_chain_journal(ccx.ecx);即先还原快照时刻的 tracker 副本再调用refresh_chain_journal做一次 rebase 校准最后才恢复 EVM 环境。两个函数分别对应RevertKeep保留快照以便再次回滚用.get不删除与RevertRemove删除快照用.remove。这样即使快照期间发生了deal/cloneAccount/loadAllocs造成的余额变化回滚后 tracker 也能与还原后的账户状态严格一致。六、嵌套 EVM 与隔离调用中的 capture / restore除 cheatcode 直改账户外Foundry 的测试框架还会创建嵌套 EVMnested EVM例如 cheatcode 内部再执行一段逻辑与隔离调用isolated call例如vm.ffi、startPrank相关的隔离执行。这些子执行体共享同一个数据库因此 tracker 需要在进入/退出子 EVM 时被捕获并恢复否则子事务对账户的修改会污染父事务的跟踪状态。6.1 inspector 栈中的嵌套 EVMcrates/evm/evm/src/inspectors/stack.rs 中InspectorStackInner::with_nested_evm的流程进入前capture_reserve_balance(ecx.journal())保存父上下文 tracker构建嵌套 EVM 后restore_reserve_balance(evm.journal_mut(), state)把 tracker 注入子 EVM并调用refresh_nested_chain_journal子执行体运行完毕再次capture_reserve_balance(evm.journal_mut())取回子结果回到父上下文把子结果restore_reserve_balance写回父 journal。TransparentCheatcodesExecutorcrates/cheatcodes/src/inspector.rs遵循完全相同的模式。这套进入捕获 → 注入子环境 → 退出回收 → 回写父环境的对称结构保证了嵌套执行期间 tracker 不会丢失或错乱。6.2 隔离调用的 preserve 语义隔离调用isolated call稍有不同crates/evm/evm/src/inspectors/stack.rs 中创建嵌套 EVM 时额外设置了foundry_evm_core::FoundryJournal::set_preserve_reserve_balance( evm.journal_mut(), true, );结合 trait 注释crates/evm/core/src/env.rs可以理解隔离调用建模的是外层事务的内部调用inner call而非一条新事务因此事务边界应保留 tracker而不是按新事务重置。执行失败时该文件第 1168-1174 行同样会restore_reserve_balance并把结果按Revert处理确保异常路径下父上下文也保持一致。这从侧面印证了该修复不只覆盖三个 cheatcode 本身还覆盖了所有会派生子执行体的周边路径。七、验证Anvil 集成测试中的 reserve-balance 预编译Monad 相关行为有完整的集成测试覆盖集中在 crates/anvil/tests/it/monad.rs。测试通过 HTTP provider 构造对 reserve-balance 预编译的调用辅助函数reserve_balance_call()第 2304 行起并断言返回值未配置/未启用时返回空如monad_eight_config_filters_reserve_balance_precompile启用后返回确定的 32 字节结果如assert_eq!(provider.call(reserve_balance_call()).await.unwrap(), Bytes::from(vec![0; 32]))覆盖普通节点、嵌套节点、fork 节点等多种执行形态文件内大量.call(reserve_balance_call())断言。这些测试是本次同步修复的验收底线无论账户状态如何被 cheatcode 改写或回滚预编译读到的储备余额都必须与真实状态一致。八、小结本次monad-state-splice-tracker变更虽然只是一条 patch 级修复记录但背后的工程模式值得借鉴统一刷新入口所有直改账户的 cheatcodedeal、cloneAccount、loadAllocs统一以refresh_chain_journal收尾由FoundryChain::refresh_journal按网络族决定是否需要重算派生状态trait 默认实现 特性门控FoundryJournal把 Monad 专有方法做成#[cfg(feature monad)]的具体默认实现通用代码零感知Monad 分支只在不影响主路径的前提下编译派生状态与快照同生命周期快照时连 tracker 一起保存回滚时先还原再 rebase保证账户状态 ↔ 跟踪器在任意回滚点都严格一致子执行体边界对称处理嵌套 EVM 与隔离调用采用 capture / restore 对称结构并用preserve_reserve_balance区分内部调用与新事务两种语义。理解这一机制有助于你在使用 Foundry 的 Monad 网络支持或为其贡献多网络 EVM 能力时准确把握 cheatcode 直改状态与派生会计结构之间的同步约束。相关源码入口FoundryJournal定义见 crates/evm/core/src/env.rsMonad 链上下文实现见 crates/evm/core/src/evm/monad.rscheatcode 实现见 crates/cheatcodes/src/evm.rs。【免费下载链接】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),仅供参考
返回列表