Skip to content

Conversation

0xrusowsky
Copy link
Contributor

@0xrusowsky 0xrusowsky commented Oct 8, 2025

Motivation

closes #10233

Solution

  1. leverage solar::sema::Compiler to collect all relevant AST literals found in the sources (excluding libs and scripts) and seed the FuzzerDictionary with them at initialization.
  2. modify the strategies to source from a new pool of values (AST literals), when available
  3. modify the string strategy to not always generate random strings, but also source from the string literals pool

Future improvements

PR Checklist

  • Added Tests
  • Added Documentation
  • Breaking changes

@0xrusowsky 0xrusowsky marked this pull request as ready for review October 9, 2025 08:05
@0xalpharush
Copy link
Contributor

0xalpharush commented Oct 9, 2025

Not blocking but I would recommend implementing constant folding to some degree i.e. evaluate 2 * 2 ether, evaluate bytes32 IMPLEMENTATION_SLOT = bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1);, evaluate uint(-2). Arguably, solar will need this and the code could live there

See crytic/echidna#636

@grandizzy
Copy link
Collaborator

Not blocking but I would recommend implementing constant folding to some degree i.e. evaluate 2 * 2 ether, perform the keccack hash of a string, evaluate uint(-2).

See crytic/echidna#636

thanks! One thing here - this means we should collect from tests too which we don't do in PR, is this correct?

@0xalpharush
Copy link
Contributor

0xalpharush commented Oct 9, 2025

AFAIK neither Echidna or slither's printer filters tests out. I think not including forge-std makes sense.

Also, I am not sure how the push/pop/log dictionary is managed currently in Foundry, but I think Echidna will always keep the constant pool around and eject the dynamically collected values after running a full sequence. For example, a user's balance that is emitted in one run may help within the same sequence but probably unlikely to help in a totally unrelated sequence.

@grandizzy
Copy link
Collaborator

AFAIK neither Echidna or slither's printer filters tests out. I think not including forge-std makes sense.

👍 @0xrusowsky let's include too

Also, I am not sure how the push/pop/log dictionary is managed currently in Foundry, but I think Echidna will always keep the constant pool around and eject the dynamically collected values after running a full sequence. For example, a user's balance that is emitted in one run may help within the same sequence but probably unlikely to help in a totally unrelated sequence.

  • The push / pop dictionary + db addresses / storage values are populated when test starts and used across all runs, without being evicted.
    These are defined as

    /// Number of state values initially collected from db.
    /// Used to revert new collected values at the end of each run.
    db_state_values: usize,
    /// Number of address values initially collected from db.
    /// Used to revert new collected addresses at the end of each run.
    db_addresses: usize,

    and collected when dict is created
    // Create fuzz dictionary and insert values from db state.
    let mut dictionary = FuzzDictionary::new(config);
    dictionary.insert_db_values(accs);

  • We also maintain a dict of so called sample values, dynamically collected from logs, return values & state changes of runs up to a limit (set rn to the test depth) - these are also reused across all runs

    /// Sample typed values that are collected from call result and used across invariant runs.
    sample_values: HashMap<DynSolType, B256IndexSet>,

  • Then there are the regular values dynamically collected from runs that are not shared between runs

    /// Collected state values.
    state_values: B256IndexSet,

Please let us know if you see any redundant data / ways to improve the dict. Thank you!

@0xrusowsky
Copy link
Contributor Author

^ note that AST literals are injected into sample_values

@0xrusowsky 0xrusowsky force-pushed the rusowsky/ast-fuzz-dict branch from 6f35d1b to fc4f3d4 Compare October 10, 2025 05:46
@0xrusowsky 0xrusowsky force-pushed the rusowsky/ast-fuzz-dict branch from a9373d6 to 3440429 Compare October 10, 2025 05:51
DaniPopes
DaniPopes previously approved these changes Oct 21, 2025
DaniPopes
DaniPopes previously approved these changes Oct 21, 2025
Copy link
Collaborator

@grandizzy grandizzy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm! left a comment re tests, please check if worth adding them. thanks!

"#]]);
});

// TODO(rusowsky): figure out why it is flaky
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this still a TODO or good to be removed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good to be removed, always passes locally

"#]]);
});

forgetest_init!(should_fuzz_literals, |prj, cmd| {
Copy link
Collaborator

@grandizzy grandizzy Oct 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that's awesome! I assume this one should pass now quite quickly and we can include too?

forgetest_init!(
#[ignore = "slow"]
storage,
|prj, cmd| {

Since should_fuzz_literals test is only for stateless fuzz, would it make sense to add an invariant one as well? I am thinking in particular to replicate this one

forgetest_init!(invariant_fixtures, |prj, cmd| {
which we weren't able to break without providing the fixtures hints.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@grandizzy all passes as expected, see 4d9ee25 (#12015)

@0xrusowsky 0xrusowsky requested a review from grandizzy October 22, 2025 06:14
grandizzy
grandizzy previously approved these changes Oct 22, 2025
@0xrusowsky 0xrusowsky enabled auto-merge October 22, 2025 06:17
@0xrusowsky 0xrusowsky added this pull request to the merge queue Oct 22, 2025
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to failed status checks Oct 22, 2025
@grandizzy grandizzy self-requested a review October 22, 2025 10:31
@grandizzy grandizzy added this pull request to the merge queue Oct 22, 2025
Merged via the queue into master with commit 2a483b8 Oct 22, 2025
15 checks passed
@grandizzy grandizzy deleted the rusowsky/ast-fuzz-dict branch October 22, 2025 10:43
@github-project-automation github-project-automation bot moved this to Done in Foundry Oct 22, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

Using AST to seed the fuzzer dictionary

4 participants