
Solfuzz is a Python-based fuzzer that compiles Solidity, hunts for echidna_* property functions, and hammers EVM contracts with random call sequences so auditors can find invariant breaks during authorized code review.
| Tool | Dropnation/contract-fuzzer — Python property-based fuzzer for EVM smart contracts, branded Solfuzz in the README |
| Category | Smart contract fuzzing / dynamic testing tooling |
| Primary Use | Verifying echidna_* Solidity invariants hold under randomized sequences of public/external calls, during audits and CI testing of contracts you own |
| Safe Use | Intended for authorized security assessments, code audits, and development/QA of your own smart contracts in local or lab environments |
| Telemetry Note | Runs entirely in-process via eth-tester and py-evm on the local machine; it makes no network calls to live chains, so defenders observe nothing beyond local compiler downloads through py-solc-x and artifacts written under artifacts/run-*/ |
Solfuzz, published under the repository Dropnation/contract-fuzzer, is a Python-based property fuzzer for EVM smart contracts that occupies the same conceptual niche as Trail of Bits' echidna while deliberately removing operational friction. Instead of asking you to wrangle a Haskell toolchain or container images, it runs an in-process EVM built on eth-tester and py-evm, compiles your Solidity with py-solc-x, and executes randomized sequences of calls against the compiled contract. The core contract with the user is simple: you write boolean properties as Solidity functions prefixed echidna_*, and the fuzzer tries to break them. With 1413 stars, an MIT license, and a Python 3.9+ requirement, it is a genuinely low-barrier entry point into property-based contract testing.
The workflow the README describes is a classic invariant-testing loop. First, Solfuzz compiles the Solidity sources you point it at via the solidity.sources configuration key, which accepts individual files or whole directories of .sol files. It then enumerates the contract surface: functions named echidna_* that return bool are treated as properties, and every other public or external non-view function becomes a fuzz target. The engine generates random call sequences up to fuzz.max_steps steps, and after each step it re-evaluates all properties. Any property returning false or reverting is reported as a violation, which is the correct semantics — a reverting invariant getter is just as much a broken invariant as a false return, since both indicate the contract drifted into a state the developer asserted was impossible.
Installation follows the standard editable-install pattern, which fits a tool that expects to be hacked on: pip install -e .. From there the CLI is solfuzz, with a diagnostic subcommand solfuzz doctor that the README explicitly recommends attaching to bug reports — a small but telling design choice, since reproducibility complaints dominate fuzzing tool issue trackers. The doctor output presumably captures versions of the EVM stack and compiler bindings so maintainers can reproduce environment-specific failures. The primary invocation is solfuzz run --config configs/example.yaml, driven by a declarative YAML configuration rather than a wall of CLI flags.
The configuration schema in configs/example.yaml exposes three logically distinct groups. The solidity.* group controls compilation: solidity.sources for input paths, plus solidity.optimize, solidity.runs, and solidity.evm_version, which mirror the knobs of solc itself and let you match the exact compiler settings your production build uses — important because optimizer runs and EVM version can change bytecode behavior enough to mask or expose bugs. The fuzz.* group covers engine behavior: fuzz.max_steps bounds sequence length, fuzz.stop_on_fail decides whether the run halts on first violation, fuzz.seed fixes randomness for reproducibility, and fuzz.gas_limit caps per-call gas. Finally report.dir controls where artifacts land.
Reproducibility is treated as a first-class feature rather than an afterthought. Failures are serialized to artifacts/run-*/failure.json, and a dedicated solfuzz replay artifacts/run-*/failure.json command re-executes a recorded failing sequence on a best-effort basis. For an audit workflow this matters enormously: when you hand a client a finding, you want a deterministic artifact they can re-run to confirm the invariant break, not a screenshot of a crash you cannot reproduce. The --seed flag complements this by letting CI pin a fuzzing session so a regression introduced by a refactor produces the same call sequence that previously passed.
The architecture choice of eth-tester plus py-evm deserves attention because it shapes what Solfuzz can and cannot see. Running the EVM in-process means no external binaries, no Docker dependency for normal use (the included Dockerfile is explicitly convenience-only), and native support on Windows, macOS, and Linux — a rare trifecta in contract tooling. The trade-off is that a pure-Python EVM is slower than compiled alternatives, and the README is honest that the engine is sequence-based rather than coverage-guided. Random call sequences without coverage feedback will find shallow invariant breaks quickly but can struggle with properties guarded by deep state preconditions.
The compiler integration through py-solc-x is also pragmatic: the appropriate solc binary is downloaded on first compile, selected per the pragma of your sources. That removes the classic failure mode where a developer's locally installed compiler version silently disagrees with the contract's declared pragma. Combined with solidity.evm_version overrides, this gives reasonably tight control over the compilation environment, though teams in air-gapped audit environments should be aware the first run needs network access to fetch the compiler.
The limitations section is unusually candid and tells you where the project is heading. Input shrinking and minimization are described as basic and slated for improvement, which is meaningful because the quality of a fuzzer's failing-case reduction often determines whether a developer can actually understand the bug — a thousand-step sequence that breaks an invariant is nearly useless; a five-step one is a finding. Coverage-guided scheduling and parallel workers are both on the roadmap, which confirms the current engine is essentially blind random walking plus property checking. For auditors, that means Solfuzz today is best positioned for well-written invariant suites on small-to-medium contracts, not for deep exploration of large protocols.
The development smoke-test command in the README, solfuzz run contracts/Counter.sol --max-steps 50 --seed 123, shows the CLI also accepts direct file input with inline flags, so you do not strictly need a YAML file for quick checks. This dual interface — config file for structured projects, flags for ad-hoc runs — keeps the tool comfortable both in a repo's CI pipeline and in an auditor's terminal during a live review. Optional containerized use via docker build -t solfuzz . followed by docker run --rm -it solfuzz solfuzz doctor exists for teams that standardize on containers.
In an authorized workflow, Solfuzz slots in early: write invariants alongside the contract, run the fuzzer in CI on every commit with fuzz.stop_on_fail enabled, and triage any failure.json artifacts before they reach mainnet. Because everything executes against a local in-process EVM, there is no interaction with live networks, no deployed contract can be touched, and the tool is intrinsically scoped to code you possess and compile yourself. For defenders and protocol teams, adopting property fuzzing at this stage of development is one of the cheapest high-yield practices available, and Solfuzz's low dependency footprint lowers the activation energy considerably.
For anyone comparing it against echidna proper, the echidna_* naming convention means existing property suites port over with zero rewriting — you can point Solfuzz at a codebase already written for echidna and it will discover the same properties. That interoperability is likely deliberate, positioning this tool not as a competing convention but as a lighter-weight execution engine for the same invariant expressions. Watch the roadmap items on coverage guidance and shrinking; if both land, the performance penalty of the Python EVM becomes much easier to justify for day-to-day auditing work.
Dropnation/contract-fuzzer.Educational analysis for authorized security professionals. Use only in controlled, authorized environments.
Related coverage
0 comentários:
Post a Comment
Note: Only a member of this blog may post a comment.