foundry 学习记录3-模糊测试

1 Foundry 模糊测试概念

模糊测试(Fuzzing) 是指:

自动生成大量随机输入数据。
调用合约函数。
验证合约在各种随机输入下是否满足断言(assert/require)或者是否抛出异常。

作用:

捕捉边界条件错误。
发现潜在安全漏洞。
提高合约鲁棒性。

Foundry 模糊测试关键点

函数名: 以 testFuzz_ 或者 test_<功能>_Fuzz 开头,Foundry 自动识别。
参数类型:支持 uint, int, address, bytes, bool 等常见类型。

vm.assume(condition):
用于约束随机值范围。
Foundry 会丢弃不满足条件的随机值。

vm.expectRevert():
用于测试函数在非法输入下是否正确 revert。

多次执行:
Foundry 默认运行 256 次不同随机值(可以通过 --fuzz-runs 调整)。

默认运行次数可配置
foundry.toml 文件添加

[fuzz]
runs = 100

简单合约例子

// SPDX-License-Identifier: MIT
pragma solidity 0.8.30;
contract MyTest{

    uint256 public showAlaysBeZero = 0;
    uint256 public hiddenValue = 0;
    mapping(address => uint256) public balances;
    function doStuff(uint256 data) public {
       if (data == 2){
          showAlaysBeZero = 1;
       }
       if (hiddenValue == 7){
          showAlaysBeZero = 1;
       }
       hiddenValue = data;
    }
   

    function deposit(uint256 amount) public {
        balances[msg.sender] += amount;
    }
    function withdraw(uint256 amount) public {
        require(balances[msg.sender] >= amount, "Insufficient balance");
        balances[msg.sender] -= amount;
    }
}

测试合约

// SPDX-License-Identifier: MIT
pragma solidity 0.8.30;
import {Test} from "forge-std/Test.sol";
import {MyTest} from "../../src/MyTest.sol";
contract MyTestTest is Test{
    MyTest myTest;
    function setUp() public {
        myTest = new MyTest();
    }
    function testIsAlwaysBeZero() public {     
        myTest.doStuff(0);
        assertEq(myTest.showAlaysBeZero(),0);
    }
    //模糊测试
    function testFuzz_IsAlwaysBeZero(uint256 x) public {     
        myTest.doStuff(x);
        assertEq(myTest.showAlaysBeZero(),0);
    }

    // 模糊测试 withdraw 
    function testFuzz_Withdraw(uint256 depositAmount, uint256 withdrawAmount) public {
        vm.assume(depositAmount <= 1e18);  // 限制输入范围
        vm.assume(withdrawAmount <= 1e18);

        myTest.deposit(depositAmount);

        if (withdrawAmount <= depositAmount) {
            myTest.withdraw(withdrawAmount);
            assertEq(myTest.balances(address(this)), depositAmount - withdrawAmount);
        } else {
            // 应该 revert
            vm.expectRevert("Insufficient balance");
            myTest.withdraw(withdrawAmount);
        }
    }
}

1.测试 testFuzz_Withdraw 方法;默认执行 256

forge test --match-test testFuzz_Withdraw   

输出

Ran 1 test for test/unit/MyTest.t.sol:MyTestTest
[PASS] testFuzz_Withdraw(uint256,uint256) (runs: 256, μ: 34845, ~: 34350)
Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 16.09ms (12.86ms CPU time)

测试 执行 300次

forge test --match-test testFuzz_Withdraw --fuzz-runs 300

输出

[PASS] testFuzz_Withdraw(uint256,uint256) (runs: 300, μ: 34965, ~: 34350)

2.测试 testFuzz_IsAlwaysBeZero 方法
逻辑 运行doStuff 方法后 showAlaysBeZero 要不变,依然为0

forge test --match-test testFuzz_IsAlwaysBeZero 

输出

Ran 1 test for test/unit/MyTest.t.sol:MyTestTest
[FAIL: assertion failed: 1 != 0; counterexample: calldata=0xf637fa750000000000000000000000000000000000000000000000000000000000000002 args=[2]] testFuzz_IsAlwaysBeZero(uint256) (runs: 0, μ: 0, ~: 0)
Suite result: FAILED. 0 passed; 1 failed; 0 skipped; finished in 12.84ms (6.99ms CPU time)

直接定位到 args=[2]] 的时候有问题,可以修改源码

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容