拓冰建站拓冰建站
首页 / 资讯中心 / 正文

Foundry Forge 零 Gas 快照误报修复:`forge snapshot --check --tolerance` 的边界处理与源码解析

Foundry Forge 零 Gas 快照误报修复forge snapshot --check --tolerance的边界处理与源码解析【免费下载链接】foundryFoundry is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust.项目地址: https://gitcode.com/GitHub_Trending/fo/foundry本篇指南聚焦 Foundry 中forge snapshot --check --tolerance在 CI 场景下的一个边界问题修复当被测用例的 gas 消耗为 0零 gas 测试时旧的容差计算会产生错误的 gas 快照差异false diff。本文以该修复为主线结合 crates/forge/src/cmd/snapshot.rs 的实现与测试讲解快照容差机制的底层原理、零值边界处理逻辑以及如何在项目中正确使用--tolerance读完即可理解并规避此类误报。一、背景gas 快照与容差机制Foundry 的forge snapshot命令用于生成测试用例的 gas 消耗基线文件默认.gas-snapshot而--check模式则将其用于 CI对比当前测试的 gas 消耗与预存的快照文件不一致时以退出码 1 失败。为了防止平台差异、编译器版本或微小的执行环境波动导致频繁告警Foundry 提供了--tolerance参数Tolerates gas deviations up to the specified percentage.容忍不超过指定百分比的 gas 偏差该参数定义于 crates/forge/src/cmd/snapshot.rs/// Tolerates gas deviations up to the specified percentage. #[arg( long, value_parser RangedU64ValueParser::u32::new().range(0..100), value_name SNAPSHOT_THRESHOLD )] tolerance: Optionu32,从源码可见其关键约束取值范围被限制在0..100百分比0表示零容忍100表示允许任意幅度的相对偏差类型为Optionu32即不传该参数时走严格相等比较路径见下文within_tolerance的None分支它只作用于--check模式在 crates/forge/src/cmd/snapshot.rs 中--check分支会将self.tolerance传给check()并依据比较结果决定进程退出码} else if let Some(path) self.check { let snap path.as_ref().unwrap_or(self.snap); let snaps read_gas_snapshot(snap)?; let code if check(tests, snaps, self.tolerance) { 0 } else { 1 }; std::process::exit(code) }即全部测试都在容差范围内时check()返回true进程以0退出CI 通过否则以1退出CI 失败。二、问题复现零 gas 测试的虚假差异--tolerance的容差比较按相对百分比进行。在修复之前比较逻辑存在一个零值边界漏洞当一个测试用例的 gas 消耗为0例如函数体为空、所有操作在编译期被折叠、或使用gasleft前即返回的极简用例而快照中的基准值同样为0时旧逻辑会因“除以零”或0/0的未定义语义产生一个不存在的差异false diff导致forge snapshot --check --tolerance N在明明没有任何 gas 变化的情况下以退出码 1 失败。这类零 gas 测试并不罕见空实现、常量返回、被优化器完全折叠的调用、以及部分纯视图/无状态用例都可能落入此场景。对于使用--tolerance的 CI 流水线来说这种误报会直接阻塞合并且难以排查——因为 diff 输出显示“有变化”实际却一无所有。三、修复核心within_tolerance的零值短路本次修复见 changelog 条目 .changelog/zero-gas-snapshot-tolerance.md类型标记为forge: patch在容差判断函数的零值路径上做了短路处理。核心实现在 crates/forge/src/cmd/snapshot.rs/// Returns true of the difference between the gas values exceeds the tolerance /// /// If tolerance is None, then this returns true if both gas values are equal fn within_tolerance(source_gas: u64, target_gas: u64, tolerance_pct: Optionu32) - bool { if let Some(tolerance) tolerance_pct { let (hi, lo) if source_gas target_gas { (source_gas, target_gas) } else { (target_gas, source_gas) }; if hi 0 { // No percentage difference when both values are zero. return true; } let diff (1. - (lo as f64 / hi as f64)) * 100.; diff tolerance as f64 } else { source_gas target_gas } }函数逻辑拆解严格模式tolerance为None直接比较source_gas target_gas0 0天然成立不存在此问题——因此误报只在显式传入--tolerance时触发容差模式先取出较大值hi与较小值lo若hi 0说明两个值都为 0因为hi是两者中的最大值此时不存在百分比差异直接返回true即视为“在容差内”常规路径按(1 - lo/hi) * 100计算相对偏差百分比与容差阈值比较。这里的修复关键在于先判断hi 0再进入除法从而避免了对零的浮点除法。修复前的行为可以推断为当hi 0时表达式lo / hi触发除零在浮点语义下产生NaN或无穷大diff tolerance的比较结果不确定从而把完全一致的(0, 0)对错误地判为超差。四、配套修复diff 输出中的零值百分比同一提交还对--diff模式的百分比计算做了零值一致性处理位于 crates/forge/src/cmd/snapshot.rs/// Determines the percentage change fn gas_diff(self) - f64 { let target_gas self.target_gas_used.gas(); if target_gas 0 { self.gas_change() as f64 / target_gas as f64 } else if self.source_gas_used.gas() 0 { // No percentage change when both values are zero. 0.0 } else { // Preserve an unbounded increase from zero. f64::INFINITY } }三种情况各有明确语义场景返回值含义target_gas 0正常百分比change / target常规比较target_gas 0且source_gas 00.0双方都是零无百分比变化target_gas 0且source_gas 0f64::INFINITY从零增长到正数视为无界的百分比增长可以看到修复不仅消除了零值误报还保留了“从零增长”这一真实 gas 回归的检测能力当基准快照为0而新执行消耗了 gas 时百分比被报告为无穷大确保这种回归不会被误判为“无变化”。这与within_tolerance中“两个零相等则通过、零对正数仍按实值比较”的策略相互呼应构成完整的零值语义闭环。五、测试验证单元测试与 CLI 集成测试5.1 单元测试零值容差断言修复的正确性由 crates/forge/src/cmd/snapshot.rs 的单元测试固化#[test] fn test_tolerance() { assert!(within_tolerance(100, 105, Some(5))); assert!(within_tolerance(105, 100, Some(5))); assert!(!within_tolerance(100, 106, Some(5))); assert!(!within_tolerance(106, 100, Some(5))); assert!(within_tolerance(100, 100, None)); assert!(within_tolerance(0, 0, Some(5))); }其中最后一行assert!(within_tolerance(0, 0, Some(5)))正是本次修复的回归断言零 gas 与零 gas 在 5% 容差下必须视为一致。其余断言验证了容差是对称的(100, 105)与(105, 100)结果一致严格边界语义5%容差下100 → 105通过而100 → 106超出 5%不通过None容差即严格相等。5.2 CLI 集成测试快照整体流程的端到端行为由 crates/forge/tests/cli/cmd.rs 中的forgetest!系列用例覆盖包括can_check_snapshot验证forge snapshot基本写入与校验流程snapshot_check_writes_diff_to_stderr预置空快照后执行snapshot --check断言其以失败退出且“No matching snapshot entry found for ...”未找到对应快照条目的报告写入stderr而非 stdout便于 CI 日志抓取snapshot_reports_when_snap_file_is_not_written测试失败时快照文件不被写入并给出明确错误提示snapshot_expands_invariant_campaign_predicates、snapshot_diff_summary_on_stderr分别覆盖不变量谓词展开与 diff 摘要输出通道。六、快照文件格式三类条目理解容差比较的前提是读懂.gas-snapshot文件。其条目解析由正则 RE_BASIC_SNAPSHOT_ENTRY 与 GasSnapshotEntry 完成共三种形态// 普通单元测试固定 gas 消耗 Test:deposit() (gas: 7222) // 模糊测试runs轮数、μ均值、~中位数 Test:deposit() (runs: 256, μ: 100, ~: 200) // 不变量测试runs轮数、calls调用数、reverts回滚数 ERC20Invariants:invariantBalanceSum() (runs: 256, calls: 3840, reverts: 2388)解析测试crates/forge/src/cmd/snapshot.rs#L624-L695分别验证了三种条目的解析结果。注意容差比较针对的是TestKindReport汇总出的 gas 值模糊/不变量测试使用其均值或中位数等报告值因此零 gas 修复对这三类条目同样生效。七、使用建议与影响面7.1 实战用法在 CI 或本地执行快照校验时为需要弹性阈值的项目加入容差# 生成基线首次或基线更新时 forge snapshot # 以 5% 容差校验未超出范围则以退出码 0 结束 forge snapshot --check --tolerance 5 # 指定快照文件默认 .gas-snapshot forge snapshot --check --snap .gas-snapshot --tolerance 5相关配置项也暴露在foundry.toml中见 crates/config/src/lib.rs# 快照存放目录 snapshots snapshots # 是否在测试后检查与既有快照的差异 gas_snapshot_check false # 是否将 gas 快照写入磁盘 gas_snapshot_emit true默认值crates/config/src/lib.rs为gas_snapshot_check false、gas_snapshot_emit true即默认只生成不校验启用校验可配合 CI 门禁使用。此外crates/forge/src/cmd/snapshot.rs#L102-L107 显示forge snapshot默认使用静态 fuzz 种子STATIC_FUZZ_SEED以保证快照可复现可通过--fuzz-seed覆盖。7.2 影响范围本次变更标记为forge: patch属于补丁级修复影响面限定在forge snapshot --check --tolerance路径。修复后的行为总结(0, 0)对不再产生误报CI 正常通过(0, N0)对按实际 gas 差值比较diff 模式报告无穷大百分比真实回归仍然可见(N0, M0)对原有相对百分比容差逻辑保持不变。若你的项目存在零 gas 测试且正在使用--tolerance做 CI 门禁升级到包含该修复的版本即可消除此类噪声若你希望严格约束所有 gas 变化则无需传入--tolerance此时走source_gas target_gas的严格相等路径。八、小结forge snapshot --check --tolerance的容差机制本质是相对百分比比较而零值正是百分比计算的边界陷阱。本次修复通过within_tolerance中的hi 0短路将“双方皆零”明确界定为“无差异”同时在gas_diff中保留了“从零增长”的无穷大语义兼顾了误报消除与真实回归检测。配套的单元测试与 CLI 集成测试为这一行为提供了长期保障使其成为 CI gas 门禁中稳定、可预期的一环。【免费下载链接】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),仅供参考
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门