一个从Echidna和Medusa复现器自动生成单元测试的工具。
项目描述
智能合约模糊器的自动化工具库
fuzz-utils
是一组Python工具,旨在改善使用智能合约模糊时的开发者体验。工具包括
fuzz-utils
使用 Slither 确定类型,并使用 jinja2
通过字符串模板生成测试文件。
免责声明:请注意,fuzz-utils
正在 开发中。目前,并不支持所有 Solidity 类型,并且一些类型(如 bytes*
和 string
)可能无法正确地从语料库调用序列解码。我们正在调查一种更好的语料库格式,这将简化单元测试的创建。
特性
fuzz-utils
提供以下支持
- ✔️ 从单入口点模糊 harness 的 fuzzer 语料库生成 Foundry 单元测试。
- ✔️ 生成模糊 harness、
Actor
合同和模板化的attack
合同,以简化模糊设置。 - ✔️ 支持 Medusa 和 Echidna 语料库
- ✔️ 测试生成支持 Solidity 类型:
bool
、uint*
、int*
、address
、struct
、enum
、一维固定大小数组、动态数组和多维固定大小数组。
多维动态数组、函数指针和其他更复杂类型正在开发中,但目前不支持。
安装和先决条件
要安装 fuzz-utils
pip install fuzz-utils
这些命令将安装运行 fuzz-utils
所需的所有 Python 库和工具。但是,它不会安装 Echidna 或 Medusa,因此您需要从其官方发布版本(Echidna,Medusa)自行下载和安装。
工具
可用的工具命令是
生成单元测试
generate
命令用于从 Echidna 或 Medusa 语料库调用序列生成 Foundry 单元测试。
命令行选项
compilation_path
:Solidity 文件或 Foundry 目录的路径。默认为.
-cd
/--corpus-dir
path_to_corpus_dir
:相对于工作目录的语料库目录路径。默认为corpus
-c
/--contract
contract_name
:目标合约的名称。如果编译路径只包含一个合约,则目标将自动推导。-td
/--test-directory
path_to_test_directory
:相对于工作目录的测试目录路径。默认为test
-i
/--inheritance-path
relative_path_to_contract
:从测试目录到合约的相对路径(用于覆盖继承)。如果未提供此配置选项,继承路径将自动推导。-f
/--fuzzer
fuzzer_name
:模糊测试器的名称,目前支持:echidna
和medusa
。默认为medusa
--named-inputs
:在调用时包含函数输入名称。默认为false
--config
:fuzz-utils 配置 JSON 文件的路径。默认为空。--all-sequences
:在生成单元测试时包含所有语料库序列。默认为false
示例
为了为 BasicTypes.sol 合约生成测试文件,基于该合约的 Echidna 语料库复制器(corpus-basic),我们需要进入包含 Foundry 项目的 tests/test_data
目录并运行以下命令
fuzz-utils generate ./src/BasicTypes.sol --corpus-dir echidna-corpora/corpus-basic --contract "BasicTypes" --fuzzer echidna
运行此命令应在 Foundry 项目的 test 目录中生成 BasicTypes_Echidna_Test.sol
文件。
生成模糊测试工具
template
命令用于生成模糊测试工具。工具可以包含多个用作用户操作代理的 Actor
合约,以及可以从一组预制的合约中选择以执行某些常见攻击场景的 attack
合约。
命令行选项
compilation_path
:Solidity 文件或 Foundry 目录的路径-n
/--name
name: str
:模糊测试工具的名称。默认为DefaultHarness
-c
/--contracts
target_contracts: list
:目标合约的名称。默认为空。-o
/--output-dir
output_directory: str
:输出目录名称。默认为fuzzing
--config
:fuzz-utils 配置 JSON 文件的路径--mode
:生成工具时使用的策略。有效选项:simple
、prank
、actor
生成模式 工具支持三种工具生成策略
simple
- 模糊测试工具将包含目标合约的所有状态更改函数。所有函数调用都直接执行,工具合约作为msg.sender
。prank
- 与simple
模式类似,区别在于使用hevm.prank()
从不同的用户执行函数调用。用户可以在配置文件中定义为"actors": ["0xb4b3", "0xb0b", ...]
actor
- 将生成Actor
合约,并且所有 harness 函数调用将通过这些合约代理。可以认为Actor
合约是目标合约的用户,而这些 actor 包含的函数可以通过修饰符、外部调用或payable
进行过滤。这允许对用户能力进行细粒度控制。
示例
为了为 TestERC20.sol 合约生成模糊测试 harness,我们需要 cd
进入包含 Foundry 项目的 tests/test_data/
目录并运行以下命令:
fuzz-utils template ./src/TestERC20.sol --name "ERC20Harness" --contracts TestERC20
运行此命令应在 tests/test_data/test/fuzzing 中生成目录结构,该目录包含模糊测试 harness ERC20Harness 和 Actor 合约 DefaultActor。
我们可以看到,工具已经生成了包含我们 ERC20 令牌所有函数的 DefaultActor
合约,并且我们的模糊测试 harness ERC20Harness
能够通过随机选择一个已部署的 actor 来调用这些函数,模拟不同的用户。
这减少了设置模糊测试 harness 的样板代码所需的时间,让您能集中精力定义不变量并测试系统。
工具
初始化配置文件
可以使用 init
命令在项目根目录中初始化默认配置文件。
配置文件:使用配置文件可以比仅使用命令行选项提供更细粒度的控制。以下列出了有效的配置选项:
{
"generate": {
"targetContract": "BasicTypes", // The Echidna/Medusa fuzzing harness
"compilationPath": "./src/BasicTypes", // Path to the file or Foundry directory
"corpusDir": "echidna-corpora/corpus-basic", // Path to the corpus directory
"fuzzer": "echidna", // `echidna` | `medusa`
"testsDir": "./test/", // Path to the directory where the tests will be generated
"inheritancePath": "../src/", // Relative path from the testing directory to the contracts
"namedInputs": false, // True | False, whether to include function input names when making calls
"allSequences": false, // True | False, whether to generate tests for the entire corpus (including non-failing sequences)
},
"template": {
"name": "DefaultHarness", // The name of the fuzzing harness that will be generated
"targets": ["BasicTypes"], // The contracts to be included in the fuzzing harness
"outputDir": "./test/fuzzing", // The output directory where the files and directories will be saved
"compilationPath": ".", // The path to the Solidity file (if single target) or Foundry directory
"actors": [ // At least one actor is required. If the array is empty, the DefaultActor which wraps all of the functions from the target contracts will be generated
{
"name": "Default", // The name of the Actor contract, saved as `Actor{name}`
"targets": ["BasicTypes"], // The list of contracts that the Actor can interact with
"number": 3, // The number of instances of this Actor that will be used in the harness
"filters": { // Used to filter functions so that only functions that fulfill certain criteria are included
"strict": false, // If `true`, only functions that fulfill *all* the criteria will be included. If `false`, functions that fulfill *any* criteria will be included
"onlyModifiers": [], // List of modifiers to include
"onlyPayable": false, // If `true`, only `payable` functions will be included. If `false`, both payable and non-payable functions will be included
"onlyExternalCalls": [], // Only include functions that make a certain external call. E.g. [`transferFrom`]
},
}
],
"attacks": [ // A list of premade attack contracts to include.
{
"name": "Deposit", // The name of the attack contract.
"targets": ["BasicTypes"], // The list of contracts that the attack contract can interact with
"number": 1, // The number of instances of this attack contract that will be used in the harness
"filters": { // Used to filter functions so that only functions that fulfill certain criteria are included
"strict": false, // If `true`, only functions that fulfill *all* the criteria will be included. If `false`, functions that fulfill *any* criteria will be included
"onlyModifiers": [], // List of modifiers to include
"onlyPayable": false, // If `true`, only `payable` functions will be included. If `false`, both payable and non-payable functions will be included
"onlyExternalCalls": [], // Only include functions that make a certain external call. E.g. [`transferFrom`]
},
}
],
},
}
贡献
有关如何为此项目做出贡献的信息,请参阅 CONTRIBUTING 指南。
许可
fuzz-utils
在 AGPLv3 许可下授权和分发。
项目详情
下载文件
下载您平台上的文件。如果您不确定选择哪个,请了解更多关于 安装包 的信息。