Skip to content

Add --prefill-corpus option to extract transaction sequences from Foundry tests#1523

Draft
gustavo-grieco wants to merge 14 commits intomasterfrom
dev-corpus-prefill
Draft

Add --prefill-corpus option to extract transaction sequences from Foundry tests#1523
gustavo-grieco wants to merge 14 commits intomasterfrom
dev-corpus-prefill

Conversation

@gustavo-grieco
Copy link
Collaborator

Summary

This PR adds a new feature that extracts transaction sequences from Foundry test functions and uses them to seed Echidna's corpus. This gives the fuzzer a head start by leveraging existing test cases as starting points for mutation-based fuzzing.

Motivation

When fuzzing a contract that already has Foundry tests, those tests contain valuable information about meaningful transaction sequences. Rather than starting from scratch, Echidna can use these sequences as seeds, potentially finding bugs faster by mutating known-good call patterns.

Usage

CLI flag

echidna contract.sol --contract MyContract --prefill-corpus

Config file (echidna.yaml)

prefillCorpus: true

How it works

  1. Detects Foundry project structure - Automatically locates the test/ directory relative to the source file
  2. Analyzes test functions - Uses Slither to find functions matching test*, testFuzz*, or invariant_* patterns
  3. Extracts call sequences - Parses the Slither IR to extract external calls made to the target contract
  4. Filters view/pure functions - Removes read-only calls that don't modify state
  5. Converts to corpus format - Transforms sequences into Echidna's transaction format for replay

Example output

[2024-01-15 10:30:00] Extracting Foundry test sequences from `./test`...
Extracted 6 test sequences with 17 transactions for corpus prefill.
Extracted corpus for Counter:
  Contract: CounterTest
    Function: test_increment_sequence
      - increment
      - increment
      - increment
    Function: test_set_and_increment
      - setValue
      - increment
      - increment
    Function: test_with_view_and_pure_calls
      - increment
      - setValue
      - setValue
      - decrement

Note: getCount (view) and add (pure) calls are automatically filtered out.

Implementation details

New files

  • lib/Echidna/SourceAnalysis/FoundryTests.hs - Haskell module for extraction orchestration
  • lib/Echidna/SourceAnalysis/assets/extract_foundry_tests.py - Python script using Slither (embedded at compile time via Template Haskell)
  • tests/solidity/foundry-prefill/ - Test fixtures for the feature
  • src/test/Tests/PrefillCorpus.hs - Unit tests

Modified files

  • lib/Echidna/Types/Solidity.hs - Added prefillCorpus :: Bool field to SolConf
  • lib/Echidna/Config.hs - Added YAML parser for prefillCorpus
  • src/Main.hs - Added --prefill-corpus CLI option and override logic
  • lib/Echidna.hs - Integrated prefilled corpus into loadInitialCorpus
  • package.yaml - Added template-haskell dependency

Key design decisions

  1. Embedded Python script - The extraction script is embedded at compile time using Template Haskell, similar to how mustache templates are embedded. This ensures the script is always available without requiring external file installation.

  2. Slither-based extraction - Uses Slither's IR (Intermediate Representation) to accurately identify external contract calls, rather than parsing Solidity source directly.

  3. Target contract filtering - When --contract is specified, only calls to that contract type are extracted, avoiding noise from helper contracts.

  4. View/pure filtering - Read-only functions are automatically excluded since they don't contribute to state exploration.

Requirements

  • Python 3
  • Slither (pip install slither-analyzer)

If Python or Slither is not available, the feature gracefully degrades with a warning and continues without prefilling.

Testing

stack test --test-arguments='-p "Prefill corpus"'

Tests verify:

  • Correct number of sequences extracted
  • Correct number of transactions (view/pure filtered)
  • View/pure functions are properly filtered
  • Function names and arguments are correctly extracted
  • Corpus conversion works correctly

Limitations

  • Only extracts constant argument values from test code. Non-constant arguments (variables, expressions) are left empty for the fuzzer to fill in.
  • Does not follow complex control flow (if/else, loops) within test functions.
  • Requires Slither to be installed for extraction to work.

Future improvements

  • Support for extracting sequences from Hardhat tests
  • Better handling of complex expressions and computed values
  • Integration with Foundry's forge test --list for test discovery

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant