本文介绍了 Foundry 的模糊测试功能,通过随机参数的测试自动发现 Solidity 智能合约的边缘情况。文章讲解了 vm.assume
,bound()
,createFork
等 cheatcode 的使用,以及如何在主网上进行测试,帮助开发者更好地进行智能合约测试。
使用 Foundry 的 fuzzing 和 fork 功能来发现 Solidity 测试中的极端情况。学习 vm.assume、bound()、createFork,以及如何针对主网状态进行测试。
Foundry
在第 5 部分的单行作弊码的基础上,我们现在将方向盘交给随机性。今天的重点是 Forge 内置的 fuzzing:你将看到参数化测试如何自动发现极端情况,以及关于 bounds、assumptions 和基于 fork 的 fuzz 运行的技巧。
Forge 内置的 fuzzing 使用随机输入运行测试,从而实现智能合约的基于属性的测试。任何带有参数的 forge test
函数都被视为一个 fuzz 测试:例如:
在这个 testFuzz_
函数中,Forge 将自动生成 amount
的多个值,以尝试寻找极端情况。它“运行任何至少接受一个参数的测试作为基于属性的测试”。在实践中,Forge 将运行数百个随机案例,寻找任何违反断言的输入。如果它发现失败,它会报告具体的反例。
测试结构: Fuzz 测试看起来类似于单元测试。函数名称的前缀(通常按惯例为 testFuzz_
)并非 Forge 严格要求的(它运行任何参数化测试),但它明确了意图。主体使用输入参数设置场景,然后进行断言。在上面的例子中,我们测试了对于任何 amount
,withdraw 都会返回恰好是存款的金额。运行此操作可能会发现一个错误,例如,极端值导致溢出或 revert。
Fork 测试:
单元级别的 fuzzing 功能强大,但有时你必须与真实的主网合约交互:读取 USDC 的 ERC-20 余额,调用 Uniswap 池,或验证针对 live 的代理的升级逻辑。Foundry 的答案是 forking 作弊码。fork 是给定区块处另一个链的完整内存副本;你可以使用 vm.createFork
创建一个,并使用 vm.selectFork
激活它。选择 fork 的那一刻,后续的每一次调用、日志和存储读取都会通过那个远程状态,而写入仍然保留在你的测试本地。
createFork 是重载的。最简单的形式是接受 RPC URL 或别名,并快照最新的区块。第二种变体允许你将 fork 锁定在显式的区块高度。
第三个重载接受一个交易哈希;Foundry 将 fork 滚动到挖掘该哈希的区块,重放该区块中的每个先前交易,并将执行定位到 post-state。当你想重放已知 exploit 交易的确切环境时,这非常有用。
缩小条件: 有时你只想 fuzz 输入的一个子集。Forge 提供了 vm.assume(bool cond)
用于此目的:如果条件为假,Forge 会丢弃该随机输入并尝试另一个。例如,要跳过零值,你可以编写 vm.assume(v != 0); require(v != 0);
。该 cheatsheet 警告说要谨慎使用 assume
(广泛的条件会因为拒绝多次尝试而减慢 fuzzing 的速度)。另一种策略是使用 bound()
(来自 Forge Std)将输入限制在一个范围内,而不是拒绝它们。例如:
- 原文链接: threesigma.xyz/blog/foun...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!